From: Timo Sirainen Date: Thu, 19 Dec 2013 21:08:03 +0000 (+0200) Subject: imap: Allow wildcards in GETMETADATA mailbox-name. X-Git-Tag: 2.2.10~7 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b6709d9c413338d32187fb0cf5ba32abaf16c20e;p=thirdparty%2Fdovecot%2Fcore.git imap: Allow wildcards in GETMETADATA mailbox-name. RFC 5464 doesn't specify this, but its earlier draft did, and Kolab uses it. --- diff --git a/src/imap/cmd-getmetadata.c b/src/imap/cmd-getmetadata.c index 73759e0b47..2a73c8aa31 100644 --- a/src/imap/cmd-getmetadata.c +++ b/src/imap/cmd-getmetadata.c @@ -4,6 +4,7 @@ #include "str.h" #include "istream.h" #include "ostream.h" +#include "mailbox-list-iter.h" #include "imap-quote.h" #include "imap-metadata.h" @@ -12,6 +13,7 @@ struct imap_getmetadata_context { struct mailbox *box; struct mailbox_transaction_context *trans; + struct mailbox_list_iterate_context *list_iter; ARRAY_TYPE(const_string) entries; uint32_t maxsize; @@ -30,6 +32,8 @@ struct imap_getmetadata_context { bool failed; }; +static bool cmd_getmetadata_iter_next(struct imap_getmetadata_context *ctx); + static bool cmd_getmetadata_parse_options(struct imap_getmetadata_context *ctx, const struct imap_arg *options) @@ -267,14 +271,37 @@ static int cmd_getmetadata_send_entry_tree(struct imap_getmetadata_context *ctx, } } -static void cmd_getmetadata_deinit(struct imap_getmetadata_context *ctx) +static void cmd_getmetadata_mailbox_deinit(struct imap_getmetadata_context *ctx) { - ctx->cmd->client->output_cmd_lock = NULL; - if (ctx->iter != NULL) (void)mailbox_attribute_iter_deinit(&ctx->iter); - (void)mailbox_transaction_commit(&ctx->trans); - mailbox_free(&ctx->box); + if (ctx->box != NULL) { + (void)mailbox_transaction_commit(&ctx->trans); + mailbox_free(&ctx->box); + } + ctx->first_entry_sent = FALSE; + ctx->entry_idx = 0; +} + +static void cmd_getmetadata_deinit(struct imap_getmetadata_context *ctx) +{ + struct client_command_context *cmd = ctx->cmd; + + cmd_getmetadata_mailbox_deinit(ctx); + cmd->client->output_cmd_lock = NULL; + + if (ctx->list_iter != NULL && + mailbox_list_iter_deinit(&ctx->list_iter) < 0) + client_send_list_error(cmd, cmd->client->user->namespaces->list); + else if (ctx->failed) { + client_send_tagline(cmd, "NO Getmetadata failed to send some entries"); + } else if (ctx->largest_seen_size != 0) { + client_send_tagline(cmd, t_strdup_printf( + "OK [METADATA LONGENTRIES %"PRIuUOFF_T"] " + "Getmetadata completed.", ctx->largest_seen_size)); + } else { + client_send_tagline(cmd, "OK Getmetadata completed."); + } } static bool cmd_getmetadata_continue(struct client_command_context *cmd) @@ -308,14 +335,47 @@ static bool cmd_getmetadata_continue(struct client_command_context *cmd) if (ctx->first_entry_sent) o_stream_nsend_str(cmd->client->output, ")\r\n"); - if (ctx->failed) { - client_send_tagline(cmd, "NO Getmetadata failed to send some entries"); - } else if (ctx->largest_seen_size != 0) { - client_send_tagline(cmd, t_strdup_printf( - "OK [METADATA LONGENTRIES %"PRIuUOFF_T"] " - "Getmetadata completed.", ctx->largest_seen_size)); - } else { - client_send_tagline(cmd, "OK Getmetadata completed."); + cmd_getmetadata_mailbox_deinit(ctx); + if (ctx->list_iter != NULL) + return cmd_getmetadata_iter_next(ctx); + cmd_getmetadata_deinit(ctx); + return TRUE; +} + +static bool +cmd_getmetadata_mailbox(struct imap_getmetadata_context *ctx, + struct mail_namespace *ns, const char *mailbox) +{ + struct client_command_context *cmd = ctx->cmd; + + ctx->box = mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_READONLY); + if (mailbox_open(ctx->box) < 0) { + client_send_box_error(cmd, ctx->box); + mailbox_free(&ctx->box); + return TRUE; + } + ctx->trans = mailbox_transaction_begin(ctx->box, 0); + + if (ctx->depth > 0) + ctx->iter_entry_prefix = str_new(cmd->pool, 128); + + if (!cmd_getmetadata_continue(cmd)) { + cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; + cmd->func = cmd_getmetadata_continue; + return FALSE; + } + return TRUE; +} + +static bool cmd_getmetadata_iter_next(struct imap_getmetadata_context *ctx) +{ + const struct mailbox_info *info; + + while ((info = mailbox_list_iter_next(ctx->list_iter)) != NULL) { + if ((info->flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) != 0) + continue; + /* we'll get back here recursively */ + return cmd_getmetadata_mailbox(ctx, info->ns, info->vname); } cmd_getmetadata_deinit(ctx); return TRUE; @@ -365,28 +425,24 @@ bool cmd_getmetadata(struct client_command_context *cmd) /* server attribute */ ctx->key_prefix = MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER; ns = mail_namespace_find_inbox(cmd->client->user->namespaces); - mailbox = "INBOX"; - } else { + return cmd_getmetadata_mailbox(ctx, ns, "INBOX"); + } else if (strchr(mailbox, '*') == NULL && + strchr(mailbox, '%') == NULL) { ns = client_find_namespace(cmd, &mailbox); if (ns == NULL) return TRUE; + return cmd_getmetadata_mailbox(ctx, ns, mailbox); + } else { + /* wildcards in mailbox name. this isn't supported by RFC 5464, + but it was in the earlier drafts and is already used by + some software (Kolab). */ + const char *patterns[2]; + patterns[0] = mailbox; patterns[1] = NULL; + + ctx->list_iter = + mailbox_list_iter_init_namespaces( + cmd->client->user->namespaces, + patterns, MAIL_NAMESPACE_TYPE_MASK_ALL, 0); + return cmd_getmetadata_iter_next(ctx); } - - ctx->box = mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_READONLY); - if (mailbox_open(ctx->box) < 0) { - client_send_box_error(cmd, ctx->box); - mailbox_free(&ctx->box); - return TRUE; - } - ctx->trans = mailbox_transaction_begin(ctx->box, 0); - - if (ctx->depth > 0) - ctx->iter_entry_prefix = str_new(cmd->pool, 128); - - if (!cmd_getmetadata_continue(cmd)) { - cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; - cmd->func = cmd_getmetadata_continue; - return FALSE; - } - return TRUE; }