]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
doveadm: Added "move" command for moving mails between mailboxes.
authorTimo Sirainen <tss@iki.fi>
Wed, 23 Mar 2011 20:16:24 +0000 (22:16 +0200)
committerTimo Sirainen <tss@iki.fi>
Wed, 23 Mar 2011 20:16:24 +0000 (22:16 +0200)
src/doveadm/Makefile.am
src/doveadm/doveadm-mail-expunge.c
src/doveadm/doveadm-mail-iter.c
src/doveadm/doveadm-mail-iter.h
src/doveadm/doveadm-mail-move.c [new file with mode: 0644]
src/doveadm/doveadm-mail.c
src/doveadm/doveadm-mail.h

index 04fb33fd71d988f638ea71a78277799644384641..05c39eefdd65f138a356e2fe6dfa6ece88651f32 100644 (file)
@@ -66,6 +66,7 @@ common = \
        doveadm-mail-iter.c \
        doveadm-mail-mailbox.c \
        doveadm-mail-mailbox-status.c \
+       doveadm-mail-move.c \
        doveadm-mail-list-iter.c \
        doveadm-mail-search.c \
        doveadm-print.c \
index b9d63f15c3bcb98b5c3d97e8ee0363a14161cb53..b366bba4015cd238e53e908f1ffe66c42425f9b5 100644 (file)
@@ -178,6 +178,20 @@ cmd_expunge_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user)
        doveadm_mail_list_iter_deinit(&iter);
 }
 
+void expunge_search_args_check(struct mail_search_args *args, const char *cmd)
+{
+       mail_search_args_simplify(args);
+       if (!expunge_search_args_is_mailbox_ok(args->args)) {
+               i_fatal("%s: To avoid accidents, search query "
+                       "must contain MAILBOX in all search branches", cmd);
+       }
+       if (!expunge_search_args_is_msgset_ok(args->args)) {
+               i_fatal("%s: To avoid accidents, each branch in "
+                       "search query must contain something else "
+                       "besides MAILBOX", cmd);
+       }
+}
+
 static void cmd_expunge_init(struct doveadm_mail_cmd_context *ctx,
                             const char *const args[])
 {
@@ -185,17 +199,7 @@ static void cmd_expunge_init(struct doveadm_mail_cmd_context *ctx,
                doveadm_mail_help_name("expunge");
 
        ctx->search_args = doveadm_mail_build_search_args(args);
-       mail_search_args_simplify(ctx->search_args);
-
-       if (!expunge_search_args_is_mailbox_ok(ctx->search_args->args)) {
-               i_fatal("expunge: To avoid accidents, search query "
-                       "must contain MAILBOX in all search branches");
-       }
-       if (!expunge_search_args_is_msgset_ok(ctx->search_args->args)) {
-               i_fatal("expunge: To avoid accidents, each branch in "
-                       "search query must contain something else "
-                       "besides MAILBOX");
-       }
+       expunge_search_args_check(ctx->search_args, "expunge");
 }
 
 static struct doveadm_mail_cmd_context *cmd_expunge_alloc(void)
index 2560d25aa457fcbf21a174d86a417681ceff59cb..30a3e11a9ea3fd3cef66a0c439a54ae19ee1b871 100644 (file)
@@ -50,7 +50,8 @@ int doveadm_mail_iter_init(const struct mailbox_info *info,
 }
 
 static int
-doveadm_mail_iter_deinit_transaction(struct doveadm_mail_iter *iter)
+doveadm_mail_iter_deinit_transaction(struct doveadm_mail_iter *iter,
+                                    bool commit)
 {
        int ret = 0;
 
@@ -60,25 +61,30 @@ doveadm_mail_iter_deinit_transaction(struct doveadm_mail_iter *iter)
                        mail_storage_get_last_error(iter->storage, NULL));
                ret = -1;
        }
-       if (mailbox_transaction_commit(&iter->t) < 0) {
-               i_error("Commiting mailbox %s failed: %s",
-                       mailbox_get_vname(iter->box),
-                       mail_storage_get_last_error(iter->storage, NULL));
-               ret = -1;
+       if (commit) {
+               if (mailbox_transaction_commit(&iter->t) < 0) {
+                       i_error("Commiting mailbox %s failed: %s",
+                               mailbox_get_vname(iter->box),
+                               mail_storage_get_last_error(iter->storage, NULL));
+                       ret = -1;
+               }
+       } else {
+               mailbox_transaction_rollback(&iter->t);
        }
        mail_search_args_deinit(iter->search_args);
        return ret;
 }
 
 static int
-doveadm_mail_iter_deinit_full(struct doveadm_mail_iter **_iter, bool sync)
+doveadm_mail_iter_deinit_full(struct doveadm_mail_iter **_iter,
+                             bool sync, bool commit)
 {
        struct doveadm_mail_iter *iter = *_iter;
        int ret;
 
        *_iter = NULL;
 
-       ret = doveadm_mail_iter_deinit_transaction(iter);
+       ret = doveadm_mail_iter_deinit_transaction(iter, commit);
        if (ret == 0 && sync)
                ret = mailbox_sync(iter->box, 0);
        mailbox_free(&iter->box);
@@ -88,12 +94,17 @@ doveadm_mail_iter_deinit_full(struct doveadm_mail_iter **_iter, bool sync)
 
 int doveadm_mail_iter_deinit(struct doveadm_mail_iter **_iter)
 {
-       return doveadm_mail_iter_deinit_full(_iter, FALSE);
+       return doveadm_mail_iter_deinit_full(_iter, FALSE, TRUE);
 }
 
 int doveadm_mail_iter_deinit_sync(struct doveadm_mail_iter **_iter)
 {
-       return doveadm_mail_iter_deinit_full(_iter, TRUE);
+       return doveadm_mail_iter_deinit_full(_iter, TRUE, TRUE);
+}
+
+void doveadm_mail_iter_deinit_rollback(struct doveadm_mail_iter **_iter)
+{
+       (void)doveadm_mail_iter_deinit_full(_iter, FALSE, FALSE);
 }
 
 bool doveadm_mail_iter_next(struct doveadm_mail_iter *iter, struct mail *mail)
index 037a317977c4286d86090f10a4922652a1d3eaf4..34807d08f3bca9e3c72f58357013a638f6da2e74 100644 (file)
@@ -9,6 +9,7 @@ int doveadm_mail_iter_init(const struct mailbox_info *info,
                           struct doveadm_mail_iter **iter_r);
 int doveadm_mail_iter_deinit(struct doveadm_mail_iter **iter);
 int doveadm_mail_iter_deinit_sync(struct doveadm_mail_iter **iter);
+void doveadm_mail_iter_deinit_rollback(struct doveadm_mail_iter **iter);
 
 bool doveadm_mail_iter_next(struct doveadm_mail_iter *iter, struct mail *mail);
 
diff --git a/src/doveadm/doveadm-mail-move.c b/src/doveadm/doveadm-mail-move.c
new file mode 100644 (file)
index 0000000..cb677b8
--- /dev/null
@@ -0,0 +1,137 @@
+/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+#include "doveadm-print.h"
+#include "doveadm-mail-list-iter.h"
+#include "doveadm-mail-iter.h"
+#include "doveadm-mail.h"
+
+#include <stdio.h>
+
+struct move_cmd_context {
+       struct doveadm_mail_cmd_context ctx;
+
+       const char *destname;
+};
+
+static int
+cmd_move_box(struct move_cmd_context *ctx, struct mailbox *destbox,
+            const struct mailbox_info *info)
+{
+       struct doveadm_mail_iter *iter;
+       struct mailbox_transaction_context *trans;
+       struct mailbox_transaction_context *desttrans;
+       struct mail_storage *deststorage = mailbox_get_storage(destbox);
+       struct mail_save_context *save_ctx;
+       struct mail *mail;
+       int ret = 0;
+
+       if (doveadm_mail_iter_init(info, ctx->ctx.search_args,
+                                  &trans, &iter) < 0)
+               return -1;
+
+       /* use a separately committed transaction for each mailbox.
+          this guarantees that mails aren't expunged without actually having
+          been copied. */
+       desttrans = mailbox_transaction_begin(destbox,
+                                       MAILBOX_TRANSACTION_FLAG_EXTERNAL);
+
+       mail = mail_alloc(trans, 0, NULL);
+       while (doveadm_mail_iter_next(iter, mail)) {
+               save_ctx = mailbox_save_alloc(desttrans);
+               mailbox_save_copy_flags(save_ctx, mail);
+               if (mailbox_copy(&save_ctx, mail) == 0)
+                       mail_expunge(mail);
+               else {
+                       i_error("Copying messsage UID %u from '%s' failed: %s",
+                               mail->uid, info->name,
+                               mail_storage_get_last_error(deststorage, NULL));
+                       ret = -1;
+               }
+       }
+       mail_free(&mail);
+
+       if (mailbox_transaction_commit(&desttrans) < 0) {
+               i_error("Committing moved mails failed: %s",
+                       mail_storage_get_last_error(deststorage, NULL));
+               /* rollback expunges */
+               doveadm_mail_iter_deinit_rollback(&iter);
+               ret = -1;
+       } else {
+               if (doveadm_mail_iter_deinit_sync(&iter) < 0)
+                       ret = -1;
+       }
+       return ret;
+}
+
+static void
+cmd_move_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user)
+{
+       struct move_cmd_context *ctx = (struct move_cmd_context *)_ctx;
+       const enum mailbox_list_iter_flags iter_flags =
+               MAILBOX_LIST_ITER_RAW_LIST |
+               MAILBOX_LIST_ITER_NO_AUTO_INBOX |
+               MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
+       struct doveadm_mail_list_iter *iter;
+       struct mail_namespace *ns;
+       struct mailbox *destbox;
+       struct mail_storage *storage;
+       const struct mailbox_info *info;
+       const char *storage_name = ctx->destname;
+
+       ns = mail_namespace_find(user->namespaces, &storage_name);
+       if (ns == NULL)
+               i_fatal("Can't find namespace for: %s", ctx->destname);
+
+       destbox = mailbox_alloc(ns->list, storage_name,
+                                    MAILBOX_FLAG_SAVEONLY |
+                                    MAILBOX_FLAG_KEEP_RECENT);
+       storage = mailbox_get_storage(destbox);
+       if (mailbox_open(destbox) < 0) {
+               i_error("Can't open mailbox '%s': %s", ctx->destname,
+                       mail_storage_get_last_error(storage, NULL));
+               mailbox_free(&destbox);
+               return;
+       }
+
+       iter = doveadm_mail_list_iter_init(user, _ctx->search_args, iter_flags);
+       while ((info = doveadm_mail_list_iter_next(iter)) != NULL) T_BEGIN {
+               (void)cmd_move_box(ctx, destbox, info);
+       } T_END;
+       doveadm_mail_list_iter_deinit(&iter);
+
+       (void)mailbox_sync(destbox, 0);
+       mailbox_free(&destbox);
+
+}
+
+static void cmd_move_init(struct doveadm_mail_cmd_context *_ctx,
+                         const char *const args[])
+{
+       struct move_cmd_context *ctx = (struct move_cmd_context *)_ctx;
+       const char *destname = args[0];
+
+       if (destname == NULL || args[1] == NULL)
+               doveadm_mail_help_name("move");
+
+       ctx->destname = p_strdup(ctx->ctx.pool, destname);
+       ctx->ctx.search_args = doveadm_mail_build_search_args(args + 1);
+       expunge_search_args_check(ctx->ctx.search_args, "move");
+}
+
+static struct doveadm_mail_cmd_context *cmd_move_alloc(void)
+{
+       struct move_cmd_context *ctx;
+
+       ctx = doveadm_mail_cmd_alloc(struct move_cmd_context);
+       ctx->ctx.v.init = cmd_move_init;
+       ctx->ctx.v.run = cmd_move_run;
+       doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW);
+       return &ctx->ctx;
+}
+
+struct doveadm_mail_cmd cmd_move = {
+       cmd_move_alloc, "move", "<destination> <search query>"
+};
index 87e8582a9cc367b65ef37a2706c13dffa3f172b6..fe4c5f7016f6a183273cd1a9730ae349c832a4f5 100644 (file)
@@ -566,6 +566,7 @@ static struct doveadm_mail_cmd *mail_commands[] = {
        &cmd_fetch,
        &cmd_import,
        &cmd_altmove,
+       &cmd_move,
        &cmd_mailbox_list,
        &cmd_mailbox_create,
        &cmd_mailbox_delete,
index df113f41af1bbab2d966594489dc6046a2738735..03c7935f1253b341a2cae483caabfe4a928e6a85 100644 (file)
@@ -97,6 +97,8 @@ const char *const *doveadm_mailbox_args_to_mutf7(const char *const args[]);
 struct mail_search_args *
 doveadm_mail_mailbox_search_args_build(const char *const args[]);
 
+void expunge_search_args_check(struct mail_search_args *args, const char *cmd);
+
 struct doveadm_mail_cmd_context *
 doveadm_mail_cmd_alloc_size(size_t size);
 #define doveadm_mail_cmd_alloc(type) \
@@ -107,6 +109,7 @@ struct doveadm_mail_cmd cmd_search;
 struct doveadm_mail_cmd cmd_fetch;
 struct doveadm_mail_cmd cmd_import;
 struct doveadm_mail_cmd cmd_altmove;
+struct doveadm_mail_cmd cmd_move;
 struct doveadm_mail_cmd cmd_mailbox_list;
 struct doveadm_mail_cmd cmd_mailbox_create;
 struct doveadm_mail_cmd cmd_mailbox_delete;