doveadm-mail-fetch.c revision e4ac89c5e7bb96598a47011993718f0e654912b4
a8c5a86d183db25a57bf193c06b41e092ec2e151Timo Sirainen/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen#include "lib.h"
46552a931924c2d743f045e95b08c3ce6beda91aTimo Sirainen#include "array.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "istream.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "ostream.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "base64.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "randgen.h"
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen#include "str.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "message-size.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "imap-util.h"
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen#include "mail-storage.h"
f29756821a4c6b12b73e4a2a3e1c230117a43773Timo Sirainen#include "mail-search.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "doveadm-mail.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "doveadm-mail-list-iter.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "doveadm-mail-iter.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen#include <stdio.h>
f0d93763f210ecdb85a115fdd0210a16cfc5ff5cTimo Sirainen
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainenstruct fetch_context {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_search_args *search_args;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct ostream *output;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail *mail;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ARRAY_DEFINE(fields, const struct fetch_field);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen enum mail_fetch_field wanted_fields;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen string_t *hdr;
2e78f05b11df23ec2731afaf8f19d5b5240cb29fTimo Sirainen const char *prefix;
2e78f05b11df23ec2731afaf8f19d5b5240cb29fTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bool print_field_prefix;
d1e7425048c61d71f41f737ba947687198842dc2Timo Sirainen};
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int fetch_mailbox(struct fetch_context *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *value;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen if (mail_get_special(ctx->mail, MAIL_FETCH_MAILBOX_NAME, &value) < 0)
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_append(ctx->hdr, value);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenstatic int fetch_mailbox_guid(struct fetch_context *ctx)
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen uint8_t guid[MAIL_GUID_128_SIZE];
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (mailbox_get_guid(ctx->mail->box, guid) < 0)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_append(ctx->hdr, mail_guid_128_to_string(guid));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int fetch_seq(struct fetch_context *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_printfa(ctx->hdr, "%u", ctx->mail->seq);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen return 0;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen}
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int fetch_uid(struct fetch_context *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_printfa(ctx->hdr, "%u", ctx->mail->seq);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int fetch_guid(struct fetch_context *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *value;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mail_get_special(ctx->mail, MAIL_FETCH_GUID, &value) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_append(ctx->hdr, value);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int fetch_flags(struct fetch_context *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen imap_write_flags(ctx->hdr, mail_get_flags(ctx->mail),
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mail_get_keywords(ctx->mail));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void flush_hdr(struct fetch_context *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen o_stream_send(ctx->output, str_data(ctx->hdr), str_len(ctx->hdr));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_truncate(ctx->hdr, 0);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
ca843e046e98b12f4730f4b87ee2e1a659c26e78Timo Sirainen
ca843e046e98b12f4730f4b87ee2e1a659c26e78Timo Sirainenstatic int fetch_hdr(struct fetch_context *ctx)
ca843e046e98b12f4730f4b87ee2e1a659c26e78Timo Sirainen{
ca843e046e98b12f4730f4b87ee2e1a659c26e78Timo Sirainen struct istream *input;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct message_size hdr_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int ret = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0)
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainen return -1;
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainen
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainen if (ctx->print_field_prefix)
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainen str_append_c(ctx->hdr, '\n');
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen flush_hdr(ctx);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen input = i_stream_create_limit(input, hdr_size.physical_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen while (!i_stream_is_eof(input)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (o_stream_send_istream(ctx->output, input) <= 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_fatal("write(stdout) failed: %m");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen if (input->stream_errno != 0) {
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen i_error("read() failed: %m");
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen ret = -1;
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_stream_unref(&input);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen o_stream_flush(ctx->output);
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen return ret;
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int fetch_body(struct fetch_context *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen struct istream *input;
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen struct message_size hdr_size;
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen int ret = 0;
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen
5a250816ffc4cc5db203f9410ea99b6601c7b91aTimo Sirainen if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen if (ctx->print_field_prefix)
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen str_append_c(ctx->hdr, '\n');
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen flush_hdr(ctx);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_stream_skip(input, hdr_size.physical_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen while (!i_stream_is_eof(input)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (o_stream_send_istream(ctx->output, input) <= 0)
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk i_fatal("write(stdout) failed: %m");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
2e2a1d720ed53490e8e5c5031e773d395bd5683dTimo Sirainen if (input->stream_errno != 0) {
5a250816ffc4cc5db203f9410ea99b6601c7b91aTimo Sirainen i_error("read() failed: %m");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen o_stream_flush(ctx->output);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int fetch_text(struct fetch_context *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct istream *input;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int ret = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ctx->print_field_prefix)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_append_c(ctx->hdr, '\n');
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen flush_hdr(ctx);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen while (!i_stream_is_eof(input)) {
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen if (o_stream_send_istream(ctx->output, input) <= 0)
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen i_fatal("write(stdout) failed: %m");
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen }
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen if (input->stream_errno != 0) {
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen i_error("read() failed: %m");
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen ret = -1;
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen }
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen o_stream_flush(ctx->output);
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen return ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int fetch_size_physical(struct fetch_context *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uoff_t size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mail_get_physical_size(ctx->mail, &size) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_printfa(ctx->hdr, "%"PRIuUOFF_T, size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainenstatic int fetch_size_virtual(struct fetch_context *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uoff_t size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (mail_get_virtual_size(ctx->mail, &size) < 0)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen return -1;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen str_printfa(ctx->hdr, "%"PRIuUOFF_T, size);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen return 0;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int fetch_date_received(struct fetch_context *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen time_t t;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mail_get_received_date(ctx->mail, &t) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_printfa(ctx->hdr, "%s", unixdate2str(t));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int fetch_date_sent(struct fetch_context *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen time_t t;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int tz;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen char chr;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mail_get_date(ctx->mail, &t, &tz) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen chr = tz < 0 ? '-' : '+';
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (tz < 0) tz = -tz;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_printfa(ctx->hdr, "%s (%c%02u%02u)", unixdate2str(t),
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen chr, tz/60, tz%60);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainenstatic int fetch_date_saved(struct fetch_context *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen time_t t;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mail_get_save_date(ctx->mail, &t) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_printfa(ctx->hdr, "%s", unixdate2str(t));
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainenstruct fetch_field {
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainen const char *name;
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainen enum mail_fetch_field wanted_fields;
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainen int (*print)(struct fetch_context *ctx);
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainen};
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainen
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainenstatic const struct fetch_field fetch_fields[] = {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { "mailbox", 0, fetch_mailbox },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { "mailbox-guid", 0, fetch_mailbox_guid },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { "seq", 0, fetch_seq },
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainen { "uid", 0, fetch_uid },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { "guid", 0, fetch_guid },
77f1da4b5e2b800197d8db548235497d5e9d6a4fTimo Sirainen { "flags", MAIL_FETCH_FLAGS, fetch_flags },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { "hdr", MAIL_FETCH_STREAM_HEADER, fetch_hdr },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { "body", MAIL_FETCH_STREAM_BODY, fetch_body },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { "text", MAIL_FETCH_STREAM_HEADER |
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MAIL_FETCH_STREAM_BODY, fetch_text },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { "size.physical", MAIL_FETCH_PHYSICAL_SIZE, fetch_size_physical },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { "size.virtual", MAIL_FETCH_VIRTUAL_SIZE, fetch_size_virtual },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { "date.received", MAIL_FETCH_RECEIVED_DATE, fetch_date_received },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { "date.sent", MAIL_FETCH_DATE, fetch_date_sent },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { "date.saved", MAIL_FETCH_SAVE_DATE, fetch_date_saved }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen};
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic const struct fetch_field *fetch_field_find(const char *name)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int i;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (i = 0; i < N_ELEMENTS(fetch_fields); i++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (strcmp(fetch_fields[i].name, name) == 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return &fetch_fields[i];
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void print_fetch_fields(void)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int i;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen fprintf(stderr, "Available fetch fields: %s", fetch_fields[0].name);
77f1da4b5e2b800197d8db548235497d5e9d6a4fTimo Sirainen for (i = 1; i < N_ELEMENTS(fetch_fields); i++)
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainen fprintf(stderr, " %s", fetch_fields[i].name);
77f1da4b5e2b800197d8db548235497d5e9d6a4fTimo Sirainen fprintf(stderr, "\n");
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen}
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainen
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainenstatic void parse_fetch_fields(struct fetch_context *ctx, const char *str)
77f1da4b5e2b800197d8db548235497d5e9d6a4fTimo Sirainen{
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen const char *const *fields, *name;
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen const struct fetch_field *field;
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen t_array_init(&ctx->fields, 32);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fields = t_strsplit_spaces(str, " ");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (; *fields != NULL; fields++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen name = t_str_lcase(*fields);
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainen
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainen field = fetch_field_find(name);
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainen if (field == NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen print_fetch_fields();
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_fatal("Unknown fetch field: %s", name);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx->wanted_fields |= field->wanted_fields;
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainen
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainen array_append(&ctx->fields, field, 1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx->print_field_prefix = array_count(&ctx->fields) > 1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void cmd_fetch_mail(struct fetch_context *ctx)
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen{
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen const struct fetch_field *field;
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen struct mail *mail = ctx->mail;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen array_foreach(&ctx->fields, field) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (ctx->print_field_prefix)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_printfa(ctx->hdr, "%s: ", field->name);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (field->print(ctx) < 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_storage *storage =
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mailbox_get_storage(mail->box);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("fetch(%s) failed for box=%s uid=%u: %s",
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen field->name, mailbox_get_vname(mail->box),
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mail->uid, mail_storage_get_last_error(storage, NULL));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen str_append_c(ctx->hdr, '\n');
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen }
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen flush_hdr(ctx);
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen}
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainencmd_fetch_box(struct fetch_context *ctx, const struct mailbox_info *info)
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen{
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen struct doveadm_mail_iter *iter;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen struct mailbox_transaction_context *trans;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen struct mail *mail;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (doveadm_mail_iter_init(info, ctx->search_args, &trans, &iter) < 0)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return -1;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen mail = mail_alloc(trans, ctx->wanted_fields, NULL);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen while (doveadm_mail_iter_next(iter, mail)) {
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen str_truncate(ctx->hdr, 0);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen str_append(ctx->hdr, ctx->prefix);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx->mail = mail;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cmd_fetch_mail(ctx);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx->mail = NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mail_free(&mail);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return doveadm_mail_iter_deinit(&iter);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic bool search_args_have_unique_fetch(struct mail_search_args *args)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen struct mail_search_arg *arg;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct seq_range *seqset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int count;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bool have_mailbox = FALSE, have_msg = FALSE;
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen for (arg = args->args; arg != NULL; arg = arg->next) {
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainen switch (arg->type) {
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainen case SEARCH_MAILBOX:
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainen case SEARCH_MAILBOX_GUID:
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen have_mailbox = TRUE;
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen break;
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen case SEARCH_SEQSET:
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen case SEARCH_UIDSET:
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen seqset = array_get(&arg->value.seqset, &count);
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen if (count == 1 && seqset->seq1 == seqset->seq2)
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen have_msg = TRUE;
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen break;
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen default:
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen break;
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen }
f0d93763f210ecdb85a115fdd0210a16cfc5ff5cTimo Sirainen }
f0d93763f210ecdb85a115fdd0210a16cfc5ff5cTimo Sirainen return have_mailbox && have_msg;
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen}
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainenvoid cmd_fetch(struct mail_user *user, const char *const args[])
2ef0e8ee48c9683f7bd6698798efa3328e4322d1Timo Sirainen{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen const enum mailbox_list_iter_flags iter_flags =
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MAILBOX_LIST_ITER_VIRTUAL_NAMES |
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MAILBOX_LIST_ITER_NO_AUTO_INBOX |
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen const char *fetch_fields = args[0];
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct fetch_context ctx;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct doveadm_mail_list_iter *iter;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mailbox_info *info;
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen unsigned char prefix_buf[9];
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memset(&ctx, 0, sizeof(ctx));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fetch_fields == NULL || args[1] == NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen doveadm_mail_help_name("fetch");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen parse_fetch_fields(&ctx, fetch_fields);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx.search_args = doveadm_mail_build_search_args(args + 1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen ctx.output = o_stream_create_fd(STDOUT_FILENO, 0, FALSE);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen ctx.hdr = str_new(default_pool, 512);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (search_args_have_unique_fetch(ctx.search_args))
280503e88a6b2f72a32a8fbe363794abaaa845d6Timo Sirainen ctx.prefix = "";
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen else {
280503e88a6b2f72a32a8fbe363794abaaa845d6Timo Sirainen random_fill_weak(prefix_buf, sizeof(prefix_buf));
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen str_append(ctx.hdr, "===");
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen base64_encode(prefix_buf, sizeof(prefix_buf), ctx.hdr);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_append_c(ctx.hdr, '\n');
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx.prefix = t_strdup(str_c(ctx.hdr));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_truncate(ctx.hdr, 0);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iter = doveadm_mail_list_iter_init(user, ctx.search_args, iter_flags);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen while ((info = doveadm_mail_list_iter_next(iter)) != NULL) T_BEGIN {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (void)cmd_fetch_box(&ctx, info);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } T_END;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen doveadm_mail_list_iter_deinit(&iter);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen o_stream_unref(&ctx.output);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str_free(&ctx.hdr);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen