Mercurial > dovecot > core-2.2
changeset 11206:c8981561c5f2 HEAD
doveadm fetch: Added "fields to fetch" parameter.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 28 Apr 2010 23:13:12 +0300 |
parents | dab4fb9f8140 |
children | 1ec4eb879f0e |
files | src/doveadm/doveadm-mail-fetch.c src/doveadm/doveadm-mail.c |
diffstat | 2 files changed, 264 insertions(+), 30 deletions(-) [+] |
line wrap: on
line diff
--- a/src/doveadm/doveadm-mail-fetch.c Wed Apr 28 22:26:15 2010 +0300 +++ b/src/doveadm/doveadm-mail-fetch.c Wed Apr 28 23:13:12 2010 +0300 @@ -1,11 +1,14 @@ /* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "array.h" #include "istream.h" #include "ostream.h" #include "base64.h" #include "randgen.h" #include "str.h" +#include "message-size.h" +#include "imap-util.h" #include "mail-namespace.h" #include "mail-storage.h" #include "mail-search-build.h" @@ -16,9 +19,208 @@ struct fetch_context { struct mail_search_args *search_args; struct ostream *output; + struct mail *mail; - string_t *prefix; - unsigned int prefix_len; + ARRAY_DEFINE(fields, const struct fetch_field); + enum mail_fetch_field wanted_fields; + + string_t *hdr; + const char *prefix; +}; + +static int fetch_mailbox(struct fetch_context *ctx) +{ + const char *value; + + if (mail_get_special(ctx->mail, MAIL_FETCH_MAILBOX_NAME, &value) < 0) + return -1; + str_append(ctx->hdr, value); + return 0; +} + +static int fetch_seq(struct fetch_context *ctx) +{ + str_printfa(ctx->hdr, "%u", ctx->mail->seq); + return 0; +} + +static int fetch_uid(struct fetch_context *ctx) +{ + str_printfa(ctx->hdr, "%u", ctx->mail->seq); + return 0; +} + +static int fetch_guid(struct fetch_context *ctx) +{ + const char *value; + + if (mail_get_special(ctx->mail, MAIL_FETCH_GUID, &value) < 0) + return -1; + str_append(ctx->hdr, value); + return 0; +} + +static int fetch_flags(struct fetch_context *ctx) +{ + imap_write_flags(ctx->hdr, mail_get_flags(ctx->mail), + mail_get_keywords(ctx->mail)); + return 0; +} + +static void flush_hdr(struct fetch_context *ctx) +{ + o_stream_send(ctx->output, str_data(ctx->hdr), str_len(ctx->hdr)); + str_truncate(ctx->hdr, 0); +} + +static int fetch_hdr(struct fetch_context *ctx) +{ + struct istream *input; + struct message_size hdr_size; + int ret = 0; + + if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0) + return -1; + + str_append_c(ctx->hdr, '\n'); + flush_hdr(ctx); + input = i_stream_create_limit(input, hdr_size.physical_size); + while (!i_stream_is_eof(input)) { + if (o_stream_send_istream(ctx->output, input) <= 0) + i_fatal("write(stdout) failed: %m"); + } + if (input->stream_errno != 0) { + i_error("read() failed: %m"); + ret = -1; + } + i_stream_unref(&input); + o_stream_flush(ctx->output); + return ret; +} + +static int fetch_body(struct fetch_context *ctx) +{ + struct istream *input; + struct message_size hdr_size; + int ret = 0; + + if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0) + return -1; + + str_append_c(ctx->hdr, '\n'); + flush_hdr(ctx); + i_stream_skip(input, hdr_size.physical_size); + while (!i_stream_is_eof(input)) { + if (o_stream_send_istream(ctx->output, input) <= 0) + i_fatal("write(stdout) failed: %m"); + } + if (input->stream_errno != 0) { + i_error("read() failed: %m"); + ret = -1; + } + o_stream_flush(ctx->output); + return ret; +} + +static int fetch_text(struct fetch_context *ctx) +{ + struct istream *input; + int ret = 0; + + if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0) + return -1; + + str_append_c(ctx->hdr, '\n'); + flush_hdr(ctx); + while (!i_stream_is_eof(input)) { + if (o_stream_send_istream(ctx->output, input) <= 0) + i_fatal("write(stdout) failed: %m"); + } + if (input->stream_errno != 0) { + i_error("read() failed: %m"); + ret = -1; + } + o_stream_flush(ctx->output); + return ret; +} + +static int fetch_size_physical(struct fetch_context *ctx) +{ + uoff_t size; + + if (mail_get_physical_size(ctx->mail, &size) < 0) + return -1; + str_printfa(ctx->hdr, "%"PRIuUOFF_T, size); + return 0; +} + +static int fetch_size_virtual(struct fetch_context *ctx) +{ + uoff_t size; + + if (mail_get_virtual_size(ctx->mail, &size) < 0) + return -1; + str_printfa(ctx->hdr, "%"PRIuUOFF_T, size); + return 0; +} + +static int fetch_date_received(struct fetch_context *ctx) +{ + time_t t; + + if (mail_get_received_date(ctx->mail, &t) < 0) + return -1; + str_printfa(ctx->hdr, "%s", unixdate2str(t)); + return 0; +} + +static int fetch_date_sent(struct fetch_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; + str_printfa(ctx->hdr, "%s (%c%02u%02u)", unixdate2str(t), + chr, tz/60, tz%60); + return 0; +} + +static int fetch_date_saved(struct fetch_context *ctx) +{ + time_t t; + + if (mail_get_save_date(ctx->mail, &t) < 0) + return -1; + str_printfa(ctx->hdr, "%s", unixdate2str(t)); + return 0; +} + +struct fetch_field { + const char *name; + enum mail_fetch_field wanted_fields; + int (*print)(struct fetch_context *ctx); +}; + +static const struct fetch_field fetch_fields[] = { + { "mailbox", 0, fetch_mailbox }, + { "seq", 0, fetch_seq }, + { "uid", 0, fetch_uid }, + { "guid", 0, fetch_guid }, + { "flags", MAIL_FETCH_FLAGS, fetch_flags }, + { "hdr", MAIL_FETCH_STREAM_HEADER, fetch_hdr }, + { "body", MAIL_FETCH_STREAM_BODY, fetch_body }, + { "text", MAIL_FETCH_STREAM_HEADER | + MAIL_FETCH_STREAM_BODY, fetch_text }, + { "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 } }; static struct mail_search_args *build_search_args(const char *const args[]) @@ -35,6 +237,36 @@ return sargs; } +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 parse_fetch_fields(struct fetch_context *ctx, const char *str) +{ + const char *const *fields, *name; + const struct fetch_field *field; + + t_array_init(&ctx->fields, 32); + fields = t_strsplit_spaces(str, " "); + for (; *fields != NULL; fields++) { + name = t_str_lcase(*fields); + + field = fetch_field_find(name); + if (field == NULL) + i_fatal("Unknown fetch field: %s", name); + ctx->wanted_fields |= field->wanted_fields; + + array_append(&ctx->fields, field, 1); + } +} + static void cmd_fetch_box(struct fetch_context *ctx, struct mailbox *box) { @@ -42,7 +274,7 @@ struct mailbox_transaction_context *t; struct mail_search_context *search_ctx; struct mail *mail; - struct istream *input; + const struct fetch_field *field; if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { i_error("Syncing mailbox %s failed: %s", mailbox_get_vname(box), @@ -53,25 +285,24 @@ mail_search_args_init(ctx->search_args, box, FALSE, NULL); t = mailbox_transaction_begin(box, 0); search_ctx = mailbox_search_init(t, ctx->search_args, NULL); - mail = mail_alloc(t, 0, NULL); + mail = mail_alloc(t, ctx->wanted_fields, NULL); while (mailbox_search_next(search_ctx, mail)) { - if (mail_get_stream(mail, NULL, NULL, &input) < 0) { - i_error("Couldn't open mail uid=%u: %s", mail->uid, - mail_storage_get_last_error(storage, NULL)); - continue; - } + str_truncate(ctx->hdr, 0); + str_append(ctx->hdr, ctx->prefix); - str_truncate(ctx->prefix, ctx->prefix_len); - str_printfa(ctx->prefix, "seq=%u uid=%u\n", - mail->seq, mail->uid); - if (o_stream_send(ctx->output, str_data(ctx->prefix), - str_len(ctx->prefix)) < 0) - i_fatal("write(stdout) failed: %m"); + ctx->mail = mail; + array_foreach(&ctx->fields, field) { + str_printfa(ctx->hdr, "%s: ", field->name); + if (field->print(ctx) < 0) { + i_error("fetch(%s) failed for box=%s uid=%u: %s", + field->name, mailbox_get_vname(box), + mail->uid, mail_storage_get_last_error(storage, NULL)); + } + str_append_c(ctx->hdr, '\n'); + } + flush_hdr(ctx); - while (!i_stream_is_eof(input)) { - if (o_stream_send_istream(ctx->output, input) <= 0) - i_fatal("write(stdout) failed: %m"); - } + ctx->mail = NULL; } mail_free(&mail); if (mailbox_search_deinit(&search_ctx) < 0) { @@ -88,6 +319,7 @@ MAILBOX_LIST_ITER_VIRTUAL_NAMES | MAILBOX_LIST_ITER_NO_AUTO_INBOX | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; + const char *fetch_fields = args[0]; struct fetch_context ctx; struct doveadm_mail_list_iter *iter; const struct mailbox_info *info; @@ -96,18 +328,20 @@ unsigned char prefix_buf[9]; memset(&ctx, 0, sizeof(ctx)); + if (fetch_fields == NULL || args[1] == NULL) + doveadm_mail_help_name("fetch"); + parse_fetch_fields(&ctx, fetch_fields); + ctx.search_args = build_search_args(args + 1); + ctx.output = o_stream_create_fd(STDOUT_FILENO, 0, FALSE); random_fill_weak(prefix_buf, sizeof(prefix_buf)); - ctx.prefix = str_new(default_pool, 512); - str_append(ctx.prefix, "==="); - base64_encode(prefix_buf, sizeof(prefix_buf), ctx.prefix); - str_append_c(ctx.prefix, ' '); - ctx.prefix_len = str_len(ctx.prefix); - - if (args[0] == NULL) - doveadm_mail_help_name("fetch"); - ctx.search_args = build_search_args(args); + ctx.hdr = str_new(default_pool, 512); + str_append(ctx.hdr, "==="); + base64_encode(prefix_buf, sizeof(prefix_buf), ctx.hdr); + str_append_c(ctx.hdr, '\n'); + ctx.prefix = t_strdup(str_c(ctx.hdr)); + str_truncate(ctx.hdr, 0); iter = doveadm_mail_list_iter_init(user, ctx.search_args, iter_flags); while ((info = doveadm_mail_list_iter_next(iter)) != NULL) T_BEGIN { @@ -121,5 +355,5 @@ } T_END; doveadm_mail_list_iter_deinit(&iter); o_stream_unref(&ctx.output); - str_free(&ctx.prefix); + str_free(&ctx.hdr); }
--- a/src/doveadm/doveadm-mail.c Wed Apr 28 22:26:15 2010 +0300 +++ b/src/doveadm/doveadm-mail.c Wed Apr 28 23:13:12 2010 +0300 @@ -312,7 +312,7 @@ static struct doveadm_mail_cmd mail_commands[] = { { cmd_purge, "purge", NULL }, { cmd_force_resync, "force-resync", "<mailbox>" }, - { cmd_fetch, "fetch", "<search query>" }, + { cmd_fetch, "fetch", "<fields> <search query>" }, { cmd_altmove, "altmove", "<search query>" }, { cmd_list, "list", "[<mailbox> [...]]" } };