From f7548d6d28c313cf80e6f3ef89aed16a19815df1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 11:51:24 +0200 Subject: Adding upstream version 1:2.3.19.1+dfsg1. Signed-off-by: Daniel Baumann --- src/doveadm/doveadm-mail-fetch.c | 683 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 683 insertions(+) create mode 100644 src/doveadm/doveadm-mail-fetch.c (limited to 'src/doveadm/doveadm-mail-fetch.c') diff --git a/src/doveadm/doveadm-mail-fetch.c b/src/doveadm/doveadm-mail-fetch.c new file mode 100644 index 0000000..932a54d --- /dev/null +++ b/src/doveadm/doveadm-mail-fetch.c @@ -0,0 +1,683 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "istream.h" +#include "ostream.h" +#include "str.h" +#include "message-address.h" +#include "message-size.h" +#include "message-parser.h" +#include "message-header-decode.h" +#include "message-decoder.h" +#include "imap-util.h" +#include "mail-user.h" +#include "mail-storage.h" +#include "mail-search.h" +#include "mail-namespace.h" +#include "imap-msgpart.h" +#include "doveadm-print.h" +#include "doveadm-mail.h" +#include "doveadm-mailbox-list-iter.h" +#include "doveadm-mail-iter.h" + +#include + +struct fetch_cmd_context { + struct doveadm_mail_cmd_context ctx; + + struct mail *mail; + + ARRAY(struct fetch_field) fields; + ARRAY_TYPE(const_string) header_fields; + enum mail_fetch_field wanted_fields; + + const struct fetch_field *cur_field; + /* if print() returns -1, log this error if non-NULL. otherwise log + the storage error. */ + const char *print_error; +}; + +struct fetch_field { + const char *name; + enum mail_fetch_field wanted_fields; + int (*print)(struct fetch_cmd_context *ctx); +}; + +static int fetch_user(struct fetch_cmd_context *ctx) +{ + doveadm_print(ctx->ctx.cur_mail_user->username); + return 0; +} + +static int fetch_mailbox(struct fetch_cmd_context *ctx) +{ + const char *value; + + if (mail_get_special(ctx->mail, MAIL_FETCH_MAILBOX_NAME, &value) < 0) + return -1; + + doveadm_print(value); + return 0; +} + +static int fetch_mailbox_guid(struct fetch_cmd_context *ctx) +{ + struct mailbox_metadata metadata; + + if (mailbox_get_metadata(ctx->mail->box, MAILBOX_METADATA_GUID, + &metadata) < 0) + return -1; + doveadm_print(guid_128_to_string(metadata.guid)); + return 0; +} + +static int fetch_seq(struct fetch_cmd_context *ctx) +{ + doveadm_print_num(ctx->mail->seq); + return 0; +} + +static int fetch_uid(struct fetch_cmd_context *ctx) +{ + doveadm_print_num(ctx->mail->uid); + return 0; +} + +static int fetch_guid(struct fetch_cmd_context *ctx) +{ + const char *value; + + if (mail_get_special(ctx->mail, MAIL_FETCH_GUID, &value) < 0) + return -1; + doveadm_print(value); + return 0; +} + +static int fetch_flags(struct fetch_cmd_context *ctx) +{ + string_t *str = t_str_new(64); + + imap_write_flags(str, mail_get_flags(ctx->mail), + mail_get_keywords(ctx->mail)); + doveadm_print(str_c(str)); + return 0; +} + +static int fetch_modseq(struct fetch_cmd_context *ctx) +{ + doveadm_print_num(mail_get_modseq(ctx->mail)); + return 0; +} + +static void +fetch_set_istream_error(struct fetch_cmd_context *ctx, struct istream *input) +{ + ctx->print_error = t_strdup_printf("read(%s) failed: %s", + i_stream_get_name(input), i_stream_get_error(input)); +} + +static int fetch_hdr(struct fetch_cmd_context *ctx) +{ + struct istream *input; + struct message_size hdr_size; + int ret; + + if (mail_get_hdr_stream(ctx->mail, &hdr_size, &input) < 0) + return -1; + + input = i_stream_create_limit(input, hdr_size.physical_size); + if ((ret = doveadm_print_istream(input)) < 0) + fetch_set_istream_error(ctx, input); + i_stream_unref(&input); + return ret; +} + +static int fetch_hdr_field(struct fetch_cmd_context *ctx) +{ + const char *const *value, *filter, *name = ctx->cur_field->name; + string_t *str = t_str_new(256); + bool add_lf = FALSE; + + filter = strchr(name, '.'); + if (filter != NULL) + name = t_strdup_until(name, filter++); + + if (filter != NULL && strcmp(filter, "utf8") == 0) { + if (mail_get_headers_utf8(ctx->mail, name, &value) < 0) + return -1; + } else { + if (mail_get_headers(ctx->mail, name, &value) < 0) + return -1; + } + + for (; *value != NULL; value++) { + if (add_lf) + str_append_c(str, '\n'); + str_append(str, *value); + add_lf = TRUE; + } + + if (filter == NULL || strcmp(filter, "utf8") == 0) { + /* print the header as-is */ + } else if (strcmp(filter, "address") == 0 || + strcmp(filter, "address_name") == 0 || + strcmp(filter, "address_name.utf8") == 0) { + struct message_address *addr; + + addr = message_address_parse(pool_datastack_create(), + str_data(str), str_len(str), + UINT_MAX, 0); + str_truncate(str, 0); + add_lf = FALSE; + for (; addr != NULL; addr = addr->next) { + if (add_lf) + str_append_c(str, '\n'); + if (strcmp(filter, "address") == 0) { + if (addr->mailbox != NULL) + str_append(str, addr->mailbox); + if (addr->domain != NULL) { + str_append_c(str, '@'); + str_append(str, addr->domain); + } + } else if (addr->name != NULL) { + if (strcmp(filter, "address_name") == 0) + str_append(str, addr->name); + else { + message_header_decode_utf8( + (const void *)addr->name, + strlen(addr->name), str, NULL); + } + } + add_lf = TRUE; + } + } else { + i_fatal("Unknown header filter: %s", filter); + } + doveadm_print(str_c(str)); + return 0; +} + +static int fetch_body_field(struct fetch_cmd_context *ctx) +{ + const char *name = ctx->cur_field->name; + struct imap_msgpart *msgpart; + struct imap_msgpart_open_result result; + bool binary; + int ret; + + binary = str_begins(name, "binary."); + name += binary ? 7 : 5; + if (imap_msgpart_parse(name, &msgpart) < 0) + i_unreached(); /* we already verified this was ok */ + if (binary) + imap_msgpart_set_decode_to_binary(msgpart); + + if (imap_msgpart_open(ctx->mail, msgpart, &result) < 0) { + imap_msgpart_free(&msgpart); + return -1; + } + if ((ret = doveadm_print_istream(result.input)) < 0) + fetch_set_istream_error(ctx, result.input); + i_stream_unref(&result.input); + imap_msgpart_free(&msgpart); + return ret; +} + +static int fetch_body(struct fetch_cmd_context *ctx) +{ + struct istream *input; + struct message_size hdr_size; + int ret; + + if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0) + return -1; + + i_stream_skip(input, hdr_size.physical_size); + if ((ret = doveadm_print_istream(input)) < 0) + fetch_set_istream_error(ctx, input); + return ret; +} + +static int fetch_body_snippet(struct fetch_cmd_context *ctx) +{ + const char *value; + + if (mail_get_special(ctx->mail, MAIL_FETCH_BODY_SNIPPET, &value) < 0) + return -1; + /* [0] contains the snippet algorithm, skip over it */ + i_assert(value[0] != '\0'); + doveadm_print(value + 1); + return 0; +} + +static int fetch_text(struct fetch_cmd_context *ctx) +{ + struct istream *input; + int ret; + + if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0) + return -1; + if ((ret = doveadm_print_istream(input)) < 0) + fetch_set_istream_error(ctx, input); + return ret; +} + +static int fetch_text_utf8(struct fetch_cmd_context *ctx) +{ + const struct message_parser_settings parser_set = { + .hdr_flags = MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE, + }; + struct istream *input; + struct message_parser_ctx *parser; + struct message_decoder_context *decoder; + struct message_block raw_block, block; + struct message_part *parts; + int ret = 0; + + if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0) + return -1; + + parser = message_parser_init(pool_datastack_create(), input, &parser_set); + decoder = message_decoder_init(NULL, 0); + + while ((ret = message_parser_parse_next_block(parser, &raw_block)) > 0) { + if (!message_decoder_decode_next_block(decoder, &raw_block, + &block)) + continue; + + if (block.hdr == NULL) { + if (block.size > 0) + doveadm_print_stream(block.data, block.size); + } else if (block.hdr->eoh) + doveadm_print_stream("\n", 1); + else { + i_assert(block.hdr->name_len > 0); + doveadm_print_stream(block.hdr->name, + block.hdr->name_len); + doveadm_print_stream(": ", 2); + if (block.hdr->full_value_len > 0) { + doveadm_print_stream(block.hdr->full_value, + block.hdr->full_value_len); + } + doveadm_print_stream("\n", 1); + } + } + i_assert(ret != 0); + message_decoder_deinit(&decoder); + message_parser_deinit(&parser, &parts); + + doveadm_print_stream("", 0); + if (input->stream_errno != 0) { + i_error("read(%s) failed: %s", i_stream_get_name(input), + i_stream_get_error(input)); + return -1; + } + return 0; +} + +static int fetch_size_physical(struct fetch_cmd_context *ctx) +{ + uoff_t size; + + if (mail_get_physical_size(ctx->mail, &size) < 0) + return -1; + doveadm_print_num(size); + return 0; +} + +static int fetch_size_virtual(struct fetch_cmd_context *ctx) +{ + uoff_t size; + + if (mail_get_virtual_size(ctx->mail, &size) < 0) + return -1; + doveadm_print_num(size); + return 0; +} + +static int fetch_date_received(struct fetch_cmd_context *ctx) +{ + time_t t; + + if (mail_get_received_date(ctx->mail, &t) < 0) + return -1; + doveadm_print(unixdate2str(t)); + return 0; +} + +static int fetch_date_sent(struct fetch_cmd_context *ctx) +{ + time_t t; + int tz; + char chr; + + if (mail_get_date(ctx->mail, &t, &tz) < 0) + return -1; + + chr = tz < 0 ? '-' : '+'; + if (tz < 0) tz = -tz; + doveadm_print(t_strdup_printf("%s (%c%02u%02u)", unixdate2str(t), + chr, tz/60, tz%60)); + return 0; +} + +static int fetch_date_saved(struct fetch_cmd_context *ctx) +{ + time_t t; + + if (mail_get_save_date(ctx->mail, &t) < 0) + return -1; + doveadm_print(unixdate2str(t)); + return 0; +} + +static int fetch_date_received_unixtime(struct fetch_cmd_context *ctx) +{ + time_t t; + + if (mail_get_received_date(ctx->mail, &t) < 0) + return -1; + doveadm_print(dec2str(t)); + return 0; +} + +static int fetch_date_sent_unixtime(struct fetch_cmd_context *ctx) +{ + time_t t; + int tz; + + if (mail_get_date(ctx->mail, &t, &tz) < 0) + return -1; + + doveadm_print(dec2str(t)); + return 0; +} + +static int fetch_date_saved_unixtime(struct fetch_cmd_context *ctx) +{ + time_t t; + + if (mail_get_save_date(ctx->mail, &t) < 0) + return -1; + doveadm_print(dec2str(t)); + return 0; +} + +static int fetch_imap_envelope(struct fetch_cmd_context *ctx) +{ + const char *value; + + if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_ENVELOPE, &value) < 0) + return -1; + doveadm_print(value); + return 0; +} + +static int fetch_imap_body(struct fetch_cmd_context *ctx) +{ + const char *value; + + if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_BODY, &value) < 0) + return -1; + doveadm_print(value); + return 0; +} + +static int fetch_imap_bodystructure(struct fetch_cmd_context *ctx) +{ + const char *value; + + if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, &value) < 0) + return -1; + doveadm_print(value); + return 0; +} +static int fetch_pop3_uidl(struct fetch_cmd_context *ctx) +{ + const char *value; + + if (mail_get_special(ctx->mail, MAIL_FETCH_UIDL_BACKEND, &value) < 0) + return -1; + doveadm_print(value); + return 0; +} + +static int fetch_pop3_order(struct fetch_cmd_context *ctx) +{ + const char *value; + + if (mail_get_special(ctx->mail, MAIL_FETCH_POP3_ORDER, &value) < 0) + return -1; + doveadm_print(value); + return 0; +} + +static int fetch_refcount(struct fetch_cmd_context *ctx) +{ + const char *value; + + if (mail_get_special(ctx->mail, MAIL_FETCH_REFCOUNT, &value) < 0) + return -1; + doveadm_print(value); + return 0; +} + +static int fetch_storageid(struct fetch_cmd_context *ctx) +{ + const char *value; + + if (mail_get_special(ctx->mail, MAIL_FETCH_STORAGE_ID, &value) < 0) + return -1; + doveadm_print(value); + return 0; +} + +static const struct fetch_field fetch_fields[] = { + { "user", 0, fetch_user }, + { "mailbox", 0, fetch_mailbox }, + { "mailbox-guid", 0, fetch_mailbox_guid }, + { "seq", 0, fetch_seq }, + { "uid", 0, fetch_uid }, + { "guid", 0, fetch_guid }, + { "flags", MAIL_FETCH_FLAGS, fetch_flags }, + { "modseq", 0, fetch_modseq }, + { "hdr", MAIL_FETCH_STREAM_HEADER, fetch_hdr }, + { "body", MAIL_FETCH_STREAM_BODY, fetch_body }, + { "body.preview", MAIL_FETCH_BODY_SNIPPET, fetch_body_snippet }, + { "body.snippet", MAIL_FETCH_BODY_SNIPPET, fetch_body_snippet }, + { "text", MAIL_FETCH_STREAM_HEADER | + MAIL_FETCH_STREAM_BODY, fetch_text }, + { "text.utf8", MAIL_FETCH_STREAM_HEADER | + MAIL_FETCH_STREAM_BODY, fetch_text_utf8 }, + { "size.physical", MAIL_FETCH_PHYSICAL_SIZE, fetch_size_physical }, + { "size.virtual", MAIL_FETCH_VIRTUAL_SIZE, fetch_size_virtual }, + { "date.received", MAIL_FETCH_RECEIVED_DATE, fetch_date_received }, + { "date.sent", MAIL_FETCH_DATE, fetch_date_sent }, + { "date.saved", MAIL_FETCH_SAVE_DATE, fetch_date_saved }, + { "date.received.unixtime", MAIL_FETCH_RECEIVED_DATE, fetch_date_received_unixtime }, + { "date.sent.unixtime", MAIL_FETCH_DATE, fetch_date_sent_unixtime }, + { "date.saved.unixtime", MAIL_FETCH_SAVE_DATE, fetch_date_saved_unixtime }, + { "imap.envelope", MAIL_FETCH_IMAP_ENVELOPE, fetch_imap_envelope }, + { "imap.body", MAIL_FETCH_IMAP_BODY, fetch_imap_body }, + { "imap.bodystructure", MAIL_FETCH_IMAP_BODYSTRUCTURE, fetch_imap_bodystructure }, + { "pop3.uidl", MAIL_FETCH_UIDL_BACKEND, fetch_pop3_uidl }, + { "pop3.order", MAIL_FETCH_POP3_ORDER, fetch_pop3_order }, + { "refcount", MAIL_FETCH_REFCOUNT, fetch_refcount }, + { "storageid", MAIL_FETCH_STORAGE_ID, fetch_storageid } +}; + +static const struct fetch_field *fetch_field_find(const char *name) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(fetch_fields); i++) { + if (strcmp(fetch_fields[i].name, name) == 0) + return &fetch_fields[i]; + } + return NULL; +} + +static void print_fetch_fields(void) +{ + unsigned int i; + + fprintf(stderr, "Available fetch fields: hdr. body.
binary.
%s", fetch_fields[0].name); + for (i = 1; i < N_ELEMENTS(fetch_fields); i++) + fprintf(stderr, " %s", fetch_fields[i].name); + fprintf(stderr, "\n"); +} + +static void parse_fetch_fields(struct fetch_cmd_context *ctx, const char *str) +{ + const char *const *fields, *name; + const struct fetch_field *field; + struct fetch_field hdr_field, body_field; + struct imap_msgpart *msgpart; + + i_zero(&hdr_field); + hdr_field.print = fetch_hdr_field; + + i_zero(&body_field); + body_field.print = fetch_body_field; + + t_array_init(&ctx->fields, 32); + t_array_init(&ctx->header_fields, 32); + fields = t_strsplit_spaces(str, " "); + for (; *fields != NULL; fields++) { + name = t_str_lcase(*fields); + + doveadm_print_header_simple(name); + if ((field = fetch_field_find(name)) != NULL) { + ctx->wanted_fields |= field->wanted_fields; + array_push_back(&ctx->fields, field); + } else if (str_begins(name, "hdr.")) { + name += 4; + hdr_field.name = name; + array_push_back(&ctx->fields, &hdr_field); + name = t_strcut(name, '.'); + array_push_back(&ctx->header_fields, &name); + } else if (str_begins(name, "body.") || + str_begins(name, "binary.")) { + bool binary = str_begins(name, "binary."); + body_field.name = t_strarray_join(t_strsplit(name, ","), " "); + + name += binary ? 7 : 5; + if (imap_msgpart_parse(name, &msgpart) < 0) { + print_fetch_fields(); + i_fatal("Unknown fetch section: %s", name); + } + array_push_back(&ctx->fields, &body_field); + ctx->wanted_fields |= imap_msgpart_get_fetch_data(msgpart); + imap_msgpart_free(&msgpart); + } else { + print_fetch_fields(); + i_fatal("Unknown fetch field: %s", name); + } + } + array_append_zero(&ctx->header_fields); +} + +static int cmd_fetch_mail(struct fetch_cmd_context *ctx) +{ + const struct fetch_field *field; + struct mail *mail = ctx->mail; + int ret = 0; + + array_foreach(&ctx->fields, field) { + ctx->cur_field = field; + if (field->print(ctx) < 0) { + i_error("fetch(%s) failed for box=%s uid=%u: %s", + field->name, mailbox_get_vname(mail->box), + mail->uid, + ctx->print_error != NULL ? ctx->print_error : + mailbox_get_last_internal_error(mail->box, NULL)); + doveadm_mail_failed_mailbox(&ctx->ctx, mail->box); + ctx->print_error = NULL; + ret = -1; + } + } + return ret; +} + +static int +cmd_fetch_box(struct fetch_cmd_context *ctx, const struct mailbox_info *info) +{ + struct doveadm_mail_iter *iter; + int ret = 0; + + if (doveadm_mail_iter_init(&ctx->ctx, info, ctx->ctx.search_args, + ctx->wanted_fields, + array_front(&ctx->header_fields), + DOVEADM_MAIL_ITER_FLAG_STOP_WITH_CLIENT, + &iter) < 0) + return -1; + + while (doveadm_mail_iter_next(iter, &ctx->mail)) { + T_BEGIN { + if (cmd_fetch_mail(ctx) < 0) + ret = -1; + } T_END; + } + if (doveadm_mail_iter_deinit(&iter) < 0) + ret = -1; + return ret; +} + +static int +cmd_fetch_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user) +{ + struct fetch_cmd_context *ctx = (struct fetch_cmd_context *)_ctx; + const enum mailbox_list_iter_flags iter_flags = + MAILBOX_LIST_ITER_NO_AUTO_BOXES | + MAILBOX_LIST_ITER_RETURN_NO_FLAGS; + struct doveadm_mailbox_list_iter *iter; + const struct mailbox_info *info; + int ret = 0; + + iter = doveadm_mailbox_list_iter_init(_ctx, user, _ctx->search_args, + iter_flags); + while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN { + if (cmd_fetch_box(ctx, info) < 0) + ret = -1; + } T_END; + if (doveadm_mailbox_list_iter_deinit(&iter) < 0) + ret = -1; + return ret; +} + +static void cmd_fetch_init(struct doveadm_mail_cmd_context *_ctx, + const char *const args[]) +{ + struct fetch_cmd_context *ctx = (struct fetch_cmd_context *)_ctx; + const char *fetch_fields = args[0]; + + if (fetch_fields == NULL || args[1] == NULL) + doveadm_mail_help_name("fetch"); + + parse_fetch_fields(ctx, fetch_fields); + _ctx->search_args = doveadm_mail_build_search_args(args + 1); +} + +static struct doveadm_mail_cmd_context *cmd_fetch_alloc(void) +{ + struct fetch_cmd_context *ctx; + + ctx = doveadm_mail_cmd_alloc(struct fetch_cmd_context); + ctx->ctx.v.init = cmd_fetch_init; + ctx->ctx.v.run = cmd_fetch_run; + doveadm_print_init(DOVEADM_PRINT_TYPE_PAGER); + return &ctx->ctx; +} + +struct doveadm_cmd_ver2 doveadm_cmd_fetch_ver2 = { + .name = "fetch", + .mail_cmd = cmd_fetch_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX" ", +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAM('\0', "field", CMD_PARAM_ARRAY, 0) +DOVEADM_CMD_PARAM('\0', "fieldstr", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL | CMD_PARAM_FLAG_DO_NOT_EXPOSE) /* FIXME: horrible hack, remove me when possible */ +DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +}; -- cgit v1.2.3