]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
imap: Avoid using mailbox_list_get_mailbox_name_status()
authorTimo Sirainen <tss@iki.fi>
Mon, 6 Dec 2010 00:30:36 +0000 (00:30 +0000)
committerTimo Sirainen <tss@iki.fi>
Mon, 6 Dec 2010 00:30:36 +0000 (00:30 +0000)
The validity checks should already be done by the actual commands in most
cases (if they didn't, there would be race condition bugs). The only
exception is SUBSCRIBE. It doesn't require that the mailbox exists, but
we want to enforce that anyway via IMAP.

src/imap/cmd-append.c
src/imap/cmd-copy.c
src/imap/cmd-create.c
src/imap/cmd-delete.c
src/imap/cmd-rename.c
src/imap/cmd-select.c
src/imap/cmd-status.c
src/imap/cmd-subscribe.c
src/imap/imap-commands-util.c
src/imap/imap-commands-util.h

index 7e6b9a1ba8bd94edaa6f96a2afa4a33e183c11c8..93e464fb136de29e8470a7f563667a5bc3c44bb1 100644 (file)
@@ -445,47 +445,6 @@ static bool cmd_append_continue_message(struct client_command_context *cmd)
        return FALSE;
 }
 
-static struct mailbox *
-get_mailbox(struct client_command_context *cmd, const char *name)
-{
-       struct mail_namespace *ns;
-       struct mailbox *box;
-       enum mailbox_name_status status;
-       const char *storage_name;
-
-       ns = client_find_namespace(cmd, name, &storage_name, &status);
-       if (ns == NULL)
-               return NULL;
-
-       switch (status) {
-       case MAILBOX_NAME_EXISTS_MAILBOX:
-               break;
-       case MAILBOX_NAME_EXISTS_DIR:
-               status = MAILBOX_NAME_VALID;
-               /* fall through */
-       case MAILBOX_NAME_VALID:
-       case MAILBOX_NAME_INVALID:
-       case MAILBOX_NAME_NOINFERIORS:
-               client_fail_mailbox_name_status(cmd, name, "TRYCREATE", status);
-               return NULL;
-       }
-
-       if (cmd->client->mailbox != NULL &&
-           mailbox_equals(cmd->client->mailbox, ns, storage_name))
-               return cmd->client->mailbox;
-
-       box = mailbox_alloc(ns->list, storage_name, MAILBOX_FLAG_SAVEONLY |
-                           MAILBOX_FLAG_KEEP_RECENT);
-       if (mailbox_open(box) < 0) {
-               client_send_storage_error(cmd, mailbox_get_storage(box));
-               mailbox_free(&box);
-               return NULL;
-       }
-       if (cmd->client->enabled_features != 0)
-               mailbox_enable(box, cmd->client->enabled_features);
-       return box;
-}
-
 bool cmd_append(struct client_command_context *cmd)
 {
        struct client *client = cmd->client;
@@ -509,8 +468,7 @@ bool cmd_append(struct client_command_context *cmd)
        ctx = p_new(cmd->pool, struct cmd_append_context, 1);
        ctx->cmd = cmd;
        ctx->client = client;
-       ctx->box = get_mailbox(cmd, mailbox);
-       if (ctx->box == NULL)
+       if (client_open_save_dest_box(cmd, mailbox, &ctx->box) < 0)
                ctx->failed = TRUE;
        else {
                ctx->storage = mailbox_get_storage(ctx->box);
index 3cd415a9afe46010ecb8255218d24f5e9d3156ff..38b393ab93c88cea7ace4ed7eb256e7a0cdf22a2 100644 (file)
@@ -85,13 +85,11 @@ static int fetch_and_copy(struct client *client,
 bool cmd_copy(struct client_command_context *cmd)
 {
        struct client *client = cmd->client;
-       struct mail_namespace *dest_ns;
        struct mail_storage *dest_storage;
        struct mailbox *destbox;
        struct mailbox_transaction_context *t;
         struct mail_search_args *search_args;
-       const char *messageset, *mailbox, *storage_name, *src_uidset;
-       enum mailbox_name_status status;
+       const char *messageset, *mailbox, *src_uidset;
        enum mailbox_sync_flags sync_flags = 0;
        enum imap_sync_flags imap_flags = 0;
        struct mail_transaction_commit_changes changes;
@@ -110,41 +108,9 @@ bool cmd_copy(struct client_command_context *cmd)
        if (ret <= 0)
                return ret < 0;
 
-       /* open the destination mailbox */
-       dest_ns = client_find_namespace(cmd, mailbox, &storage_name, &status);
-       if (dest_ns == NULL)
+       if (client_open_save_dest_box(cmd, mailbox, &destbox) < 0)
                return TRUE;
 
-       switch (status) {
-       case MAILBOX_NAME_EXISTS_MAILBOX:
-               break;
-       case MAILBOX_NAME_EXISTS_DIR:
-               status = MAILBOX_NAME_VALID;
-               /* fall through */
-       case MAILBOX_NAME_VALID:
-       case MAILBOX_NAME_INVALID:
-       case MAILBOX_NAME_NOINFERIORS:
-               client_fail_mailbox_name_status(cmd, mailbox,
-                                               "TRYCREATE", status);
-               return NULL;
-       }
-
-       if (mailbox_equals(client->mailbox, dest_ns, storage_name))
-               destbox = client->mailbox;
-       else {
-               destbox = mailbox_alloc(dest_ns->list, storage_name,
-                                       MAILBOX_FLAG_SAVEONLY |
-                                       MAILBOX_FLAG_KEEP_RECENT);
-               if (mailbox_open(destbox) < 0) {
-                       client_send_storage_error(cmd,
-                               mailbox_get_storage(destbox));
-                       mailbox_free(&destbox);
-                       return TRUE;
-               }
-               if (client->enabled_features != 0)
-                       mailbox_enable(destbox, client->enabled_features);
-       }
-
        t = mailbox_transaction_begin(destbox,
                                      MAILBOX_TRANSACTION_FLAG_EXTERNAL |
                                      MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS);
index e0a8184f7ba10e5562e1287330122cb451a835f4..941fcfd726f133798668cfecffcd7663d412c16c 100644 (file)
@@ -7,7 +7,6 @@
 
 bool cmd_create(struct client_command_context *cmd)
 {
-       enum mailbox_name_status status;
        struct mail_namespace *ns;
        const char *mailbox, *storage_name;
        struct mailbox *box;
@@ -18,7 +17,7 @@ bool cmd_create(struct client_command_context *cmd)
        if (!client_read_string_args(cmd, 1, &mailbox))
                return FALSE;
 
-       ns = client_find_namespace(cmd, mailbox, &storage_name, NULL);
+       ns = client_find_namespace(cmd, mailbox, &storage_name);
        if (ns == NULL)
                return TRUE;
 
@@ -34,26 +33,18 @@ bool cmd_create(struct client_command_context *cmd)
                   informing us that it wants to create children under this
                   mailbox. */
                 directory = TRUE;
-               storage_name = t_strndup(storage_name, strlen(storage_name)-1);
                mailbox = t_strndup(mailbox, len-1);
+
+               /* drop also from storage_name. it's already dropped when
+                  WORKAROUND_TB_EXTRA_MAILBOX_SEP is enabled */
+               len = strlen(storage_name);
+               if (storage_name[len-1] == ns->real_sep)
+                       storage_name = t_strndup(storage_name, len-1);
        }
 
-       ns = client_find_namespace(cmd, mailbox, &storage_name, &status);
+       ns = client_find_namespace(cmd, mailbox, &storage_name);
        if (ns == NULL)
                return TRUE;
-       switch (status) {
-       case MAILBOX_NAME_VALID:
-               break;
-       case MAILBOX_NAME_EXISTS_DIR:
-               if (!directory)
-                       break;
-               /* fall through */
-       case MAILBOX_NAME_EXISTS_MAILBOX:
-       case MAILBOX_NAME_INVALID:
-       case MAILBOX_NAME_NOINFERIORS:
-               client_fail_mailbox_name_status(cmd, mailbox, NULL, status);
-               return TRUE;
-       }
 
        box = mailbox_alloc(ns->list, storage_name, 0);
        if (mailbox_create(box, NULL, directory) < 0)
index 098780c1f2c36181ea1fc985efe75207e2f08862..a65327a8ae2a88a3830347c5187dd02505498fba 100644 (file)
@@ -6,7 +6,6 @@
 bool cmd_delete(struct client_command_context *cmd)
 {
        struct client *client = cmd->client;
-       enum mailbox_name_status status;
        struct mail_namespace *ns;
        struct mailbox *box;
        const char *name, *storage_name;
@@ -21,19 +20,9 @@ bool cmd_delete(struct client_command_context *cmd)
                return TRUE;
        }
 
-       ns = client_find_namespace(cmd, name, &storage_name, &status);
+       ns = client_find_namespace(cmd, name, &storage_name);
        if (ns == NULL)
                return TRUE;
-       switch (status) {
-       case MAILBOX_NAME_EXISTS_MAILBOX:
-       case MAILBOX_NAME_EXISTS_DIR:
-               break;
-       case MAILBOX_NAME_VALID:
-       case MAILBOX_NAME_INVALID:
-       case MAILBOX_NAME_NOINFERIORS:
-               client_fail_mailbox_name_status(cmd, name, NULL, status);
-               return TRUE;
-       }
 
        box = mailbox_alloc(ns->list, storage_name, 0);
        if (client->mailbox != NULL &&
index b31563891ddc9115f3d514d956047cb74041af3e..56a97fc2cac3418144ea9466032af03d8b198c33 100644 (file)
@@ -6,7 +6,6 @@
 
 bool cmd_rename(struct client_command_context *cmd)
 {
-       enum mailbox_name_status status;
        struct mail_namespace *old_ns, *new_ns;
        struct mailbox *old_box, *new_box;
        const char *oldname, *newname, *storage_oldname, *storage_newname;
@@ -16,33 +15,12 @@ bool cmd_rename(struct client_command_context *cmd)
        if (!client_read_string_args(cmd, 2, &oldname, &newname))
                return FALSE;
 
-       old_ns = client_find_namespace(cmd, oldname, &storage_oldname, &status);
+       old_ns = client_find_namespace(cmd, oldname, &storage_oldname);
        if (old_ns == NULL)
                return TRUE;
-       switch (status) {
-       case MAILBOX_NAME_EXISTS_MAILBOX:
-       case MAILBOX_NAME_EXISTS_DIR:
-               break;
-       case MAILBOX_NAME_VALID:
-       case MAILBOX_NAME_INVALID:
-       case MAILBOX_NAME_NOINFERIORS:
-               client_fail_mailbox_name_status(cmd, oldname, NULL, status);
-               return TRUE;
-       }
-
-       new_ns = client_find_namespace(cmd, newname, &storage_newname, &status);
+       new_ns = client_find_namespace(cmd, newname, &storage_newname);
        if (new_ns == NULL)
                return TRUE;
-       switch (status) {
-       case MAILBOX_NAME_VALID:
-               break;
-       case MAILBOX_NAME_EXISTS_MAILBOX:
-       case MAILBOX_NAME_EXISTS_DIR:
-       case MAILBOX_NAME_INVALID:
-       case MAILBOX_NAME_NOINFERIORS:
-               client_fail_mailbox_name_status(cmd, newname, NULL, status);
-               return TRUE;
-       }
 
        if (old_ns == new_ns) {
                /* disallow box -> box/child, because it may break clients and
index 5951f171d3cf3fecd3ed5154c3e7e2ec8233fa5c..14d43878f86462028a80cbd839445ca628defc9d 100644 (file)
@@ -372,7 +372,6 @@ bool cmd_select_full(struct client_command_context *cmd, bool readonly)
        struct client *client = cmd->client;
        struct imap_select_context *ctx;
        const struct imap_arg *args, *list_args;
-       enum mailbox_name_status status;
        const char *mailbox, *storage_name;
        int ret;
 
@@ -388,24 +387,11 @@ bool cmd_select_full(struct client_command_context *cmd, bool readonly)
 
        ctx = p_new(cmd->pool, struct imap_select_context, 1);
        ctx->cmd = cmd;
-       ctx->ns = client_find_namespace(cmd, mailbox, &storage_name, &status);
+       ctx->ns = client_find_namespace(cmd, mailbox, &storage_name);
        if (ctx->ns == NULL) {
                close_selected_mailbox(client);
                return TRUE;
        }
-       switch (status) {
-       case MAILBOX_NAME_EXISTS_MAILBOX:
-               break;
-       case MAILBOX_NAME_EXISTS_DIR:
-               status = MAILBOX_NAME_VALID;
-               /* fall through */
-       case MAILBOX_NAME_VALID:
-       case MAILBOX_NAME_INVALID:
-       case MAILBOX_NAME_NOINFERIORS:
-               client_fail_mailbox_name_status(cmd, mailbox, NULL, status);
-               close_selected_mailbox(client);
-               return TRUE;
-       }
 
        if (imap_arg_get_list(&args[1], &list_args)) {
                if (!select_parse_options(ctx, list_args)) {
index b4d6959c6781a6df62ad44a1a5333a5d7bc3734c..20fd2bf0864b1b75bba0308fffa0f0ea895e1490 100644 (file)
@@ -9,7 +9,6 @@
 bool cmd_status(struct client_command_context *cmd)
 {
        struct client *client = cmd->client;
-       enum mailbox_name_status status;
        const struct imap_arg *args, *list_args;
        struct imap_status_items items;
        struct imap_status_result result;
@@ -31,21 +30,9 @@ bool cmd_status(struct client_command_context *cmd)
        if (imap_status_parse_items(cmd, list_args, &items) < 0)
                return TRUE;
 
-       ns = client_find_namespace(cmd, mailbox, &storage_name, &status);
+       ns = client_find_namespace(cmd, mailbox, &storage_name);
        if (ns == NULL)
                return TRUE;
-       switch (status) {
-       case MAILBOX_NAME_EXISTS_MAILBOX:
-               break;
-       case MAILBOX_NAME_EXISTS_DIR:
-               status = MAILBOX_NAME_VALID;
-               /* fall through */
-       case MAILBOX_NAME_VALID:
-       case MAILBOX_NAME_INVALID:
-       case MAILBOX_NAME_NOINFERIORS:
-               client_fail_mailbox_name_status(cmd, mailbox, NULL, status);
-               return TRUE;
-       }
 
        selected_mailbox = client->mailbox != NULL &&
                mailbox_equals(client->mailbox, ns, storage_name);
index 8e44ada2787715e2ba3f97faaf77b82801b772b7..345c3e7eaa83e5306f658e79b5fbc467e07e5009 100644 (file)
@@ -26,9 +26,39 @@ static bool have_listable_namespace_prefix(struct mail_namespace *ns,
        return FALSE;
 }
 
+static bool
+subscribe_is_valid_name(struct client_command_context *cmd, const char *mailbox)
+{
+       enum mailbox_name_status name_status;
+       struct mail_namespace *ns;
+       const char *storage_name;
+
+       if (have_listable_namespace_prefix(cmd->client->user->namespaces,
+                                          mailbox)) {
+               /* subscribing to a listable namespace prefix, allow it. */
+               return TRUE;
+       }
+
+       /* see if the mailbox exists */
+       ns = client_find_namespace(cmd, mailbox, &storage_name);
+       if (ns == NULL)
+               return FALSE;
+
+       if (mailbox_list_get_mailbox_name_status(ns->list, storage_name,
+                                                &name_status) < 0) {
+               client_send_list_error(cmd, ns->list);
+               return FALSE;
+       }
+       if (name_status == MAILBOX_NAME_NONEXISTENT) {
+               client_send_tagline(cmd, t_strdup_printf(
+                       "NO "MAIL_ERRSTR_MAILBOX_NOT_FOUND, mailbox));
+               return FALSE;
+       }
+       return TRUE;
+}
+
 bool cmd_subscribe_full(struct client_command_context *cmd, bool subscribe)
 {
-       enum mailbox_name_status status;
        struct mail_namespace *ns, *box_ns;
        const char *mailbox, *storage_name, *subs_name, *subs_name2 = NULL;
        bool unsubscribed_mailbox2;
@@ -37,7 +67,7 @@ bool cmd_subscribe_full(struct client_command_context *cmd, bool subscribe)
        if (!client_read_string_args(cmd, 1, &mailbox))
                return FALSE;
 
-       box_ns = client_find_namespace(cmd, mailbox, &storage_name, NULL);
+       box_ns = client_find_namespace(cmd, mailbox, &storage_name);
        if (box_ns == NULL)
                return TRUE;
 
@@ -69,39 +99,9 @@ bool cmd_subscribe_full(struct client_command_context *cmd, bool subscribe)
                subs_name = t_strndup(subs_name, strlen(subs_name)-1);
        }
 
-       if (have_listable_namespace_prefix(cmd->client->user->namespaces,
-                                          mailbox)) {
-               /* subscribing to a listable namespace prefix, allow it. */
-       } else if (subscribe) {
-               if (client_find_namespace(cmd, mailbox,
-                                         &storage_name, &status) == NULL)
-                       return TRUE;
-               switch (status) {
-               case MAILBOX_NAME_EXISTS_MAILBOX:
-               case MAILBOX_NAME_EXISTS_DIR:
-                       break;
-               case MAILBOX_NAME_VALID:
-               case MAILBOX_NAME_INVALID:
-               case MAILBOX_NAME_NOINFERIORS:
-                       client_fail_mailbox_name_status(cmd, mailbox,
-                                                       NULL, status);
-                       return TRUE;
-               }
-       } else {
-               if (client_find_namespace(cmd, mailbox,
-                                         &storage_name, &status) == NULL)
-                       return TRUE;
-               switch (status) {
-               case MAILBOX_NAME_EXISTS_MAILBOX:
-               case MAILBOX_NAME_EXISTS_DIR:
-               case MAILBOX_NAME_VALID:
-                       break;
-               case MAILBOX_NAME_INVALID:
-               case MAILBOX_NAME_NOINFERIORS:
-                       client_fail_mailbox_name_status(cmd, mailbox,
-                                                       NULL, status);
+       if (subscribe) {
+               if (!subscribe_is_valid_name(cmd, mailbox))
                        return TRUE;
-               }
        }
 
        unsubscribed_mailbox2 = FALSE;
index 5e4bf9b8dd8a92b7a5d5a74647fe734e92802400..658ab66517575681380f23a71175852e2dbe5b3c 100644 (file)
 #include "mail-namespace.h"
 #include "imap-commands-util.h"
 
-/* Maximum length for mailbox name, including it's path. This isn't fully
-   exact since the user can create folder hierarchy with small names, then
-   rename them to larger names. Mail storages should set more strict limits
-   to them, mbox/maildir currently allow paths only up to PATH_MAX. */
-#define MAILBOX_MAX_NAME_LEN 512
-
 struct mail_namespace *
 client_find_namespace(struct client_command_context *cmd, const char *mailbox,
-                     const char **storage_name_r,
-                     enum mailbox_name_status *mailbox_status_r)
+                     const char **storage_name_r)
 {
        struct mail_namespace *namespaces = cmd->client->user->namespaces;
        struct mail_namespace *ns;
@@ -39,17 +32,6 @@ client_find_namespace(struct client_command_context *cmd, const char *mailbox,
                return NULL;
        }
 
-       if (mailbox_status_r == NULL) {
-               *storage_name_r = storage_name;
-               return ns;
-       }
-
-       /* make sure it even looks valid */
-       if (*storage_name == '\0' && !(*mailbox != '\0' && ns->list)) {
-               client_send_tagline(cmd, "NO Empty mailbox name.");
-               return NULL;
-       }
-
        storage_name_len = strlen(storage_name);
        if ((cmd->client->set->parsed_workarounds &
                        WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 &&
@@ -74,66 +56,10 @@ client_find_namespace(struct client_command_context *cmd, const char *mailbox,
                }
        }
 
-       /* make sure two hierarchy separators aren't next to each others */
-       for (p = storage_name+1; *p != '\0'; p++) {
-               if (p[0] == ns->real_sep && p[-1] == ns->real_sep) {
-                       client_send_tagline(cmd, "NO Invalid mailbox name.");
-                       return NULL;
-               }
-       }
-
-       if (storage_name_len > MAILBOX_MAX_NAME_LEN) {
-               client_send_tagline(cmd, "NO Mailbox name too long.");
-               return NULL;
-       }
-
-       /* check what our storage thinks of it */
-       if (mailbox_list_get_mailbox_name_status(ns->list, storage_name,
-                                                mailbox_status_r) < 0) {
-               client_send_list_error(cmd, ns->list);
-               return NULL;
-       }
        *storage_name_r = storage_name;
        return ns;
 }
 
-void client_fail_mailbox_name_status(struct client_command_context *cmd,
-                                    const char *mailbox_name,
-                                    const char *resp_code,
-                                    enum mailbox_name_status status)
-{
-       switch (status) {
-       case MAILBOX_NAME_EXISTS_MAILBOX:
-       case MAILBOX_NAME_EXISTS_DIR:
-               client_send_tagline(cmd, t_strconcat(
-                       "NO [", IMAP_RESP_CODE_ALREADYEXISTS,
-                       "] Mailbox already exists: ",
-                       str_sanitize(mailbox_name, MAILBOX_MAX_NAME_LEN),
-                       NULL));
-               break;
-       case MAILBOX_NAME_VALID:
-               if (resp_code == NULL)
-                       resp_code = "";
-               else
-                       resp_code = t_strconcat("[", resp_code, "] ", NULL);
-               client_send_tagline(cmd, t_strconcat(
-                       "NO ", resp_code, "Mailbox doesn't exist: ",
-                       str_sanitize(mailbox_name, MAILBOX_MAX_NAME_LEN),
-                       NULL));
-               break;
-       case MAILBOX_NAME_INVALID:
-               client_send_tagline(cmd, t_strconcat(
-                       "NO Invalid mailbox name: ",
-                       str_sanitize(mailbox_name, MAILBOX_MAX_NAME_LEN),
-                       NULL));
-               break;
-       case MAILBOX_NAME_NOINFERIORS:
-               client_send_tagline(cmd,
-                       "NO Parent mailbox doesn't allow child mailboxes.");
-               break;
-       }
-}
-
 bool client_verify_open_mailbox(struct client_command_context *cmd)
 {
        if (cmd->client->mailbox != NULL)
@@ -144,6 +70,44 @@ bool client_verify_open_mailbox(struct client_command_context *cmd)
        }
 }
 
+int client_open_save_dest_box(struct client_command_context *cmd,
+                             const char *name, struct mailbox **destbox_r)
+{
+       struct mail_namespace *ns;
+       struct mailbox *box;
+       const char *storage_name, *error_string;
+       enum mail_error error;
+
+       ns = client_find_namespace(cmd, name, &storage_name);
+       if (ns == NULL)
+               return -1;
+
+       if (cmd->client->mailbox != NULL &&
+           mailbox_equals(cmd->client->mailbox, ns, storage_name)) {
+               *destbox_r = cmd->client->mailbox;
+               return 0;
+       }
+       box = mailbox_alloc(ns->list, storage_name,
+                           MAILBOX_FLAG_SAVEONLY | MAILBOX_FLAG_KEEP_RECENT);
+       if (mailbox_open(box) < 0) {
+               struct mail_storage *storage = mailbox_get_storage(box);
+
+               error_string = mail_storage_get_last_error(storage, &error);
+               if (error == MAIL_ERROR_NOTFOUND) {
+                       client_send_tagline(cmd,  t_strdup_printf(
+                               "NO [TRYCREATE] %s", error_string));
+               } else {
+                       client_send_storage_error(cmd, storage);
+               }
+               mailbox_free(&box);
+               return -1;
+       }
+       if (cmd->client->enabled_features != 0)
+               mailbox_enable(box, cmd->client->enabled_features);
+       *destbox_r = box;
+       return 0;
+}
+
 const char *
 imap_get_error_string(struct client_command_context *cmd,
                      const char *error_string, enum mail_error error)
index 8fe49269f5aec31c8eb49659ff18d1bc8f55e039..6eb6c50473ef9e342513c93346fe0f19c8efd6d0 100644 (file)
@@ -13,19 +13,16 @@ struct mailbox_keywords;
    or mailbox name is invalid, sends a tagged NO reply to client. */
 struct mail_namespace *
 client_find_namespace(struct client_command_context *cmd, const char *mailbox,
-                     const char **storage_name_r,
-                     enum mailbox_name_status *mailbox_status_r);
-
-/* Send tagged NO reply based on mailbox name status. */
-void client_fail_mailbox_name_status(struct client_command_context *cmd,
-                                    const char *mailbox_name,
-                                    const char *resp_code,
-                                    enum mailbox_name_status status);
+                     const char **storage_name_r);
 
 /* Returns TRUE if mailbox is selected. If not, sends "No mailbox selected"
    error message to client. */
 bool client_verify_open_mailbox(struct client_command_context *cmd);
 
+/* Open APPEND/COPY destination mailbox. */
+int client_open_save_dest_box(struct client_command_context *cmd,
+                             const char *name, struct mailbox **destbox_r);
+
 const char *
 imap_get_error_string(struct client_command_context *cmd,
                      const char *error_string, enum mail_error error);