summaryrefslogtreecommitdiffstats
path: root/src/doveadm/doveadm-mail-copymove.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/doveadm/doveadm-mail-copymove.c')
-rw-r--r--src/doveadm/doveadm-mail-copymove.c224
1 files changed, 224 insertions, 0 deletions
diff --git a/src/doveadm/doveadm-mail-copymove.c b/src/doveadm/doveadm-mail-copymove.c
new file mode 100644
index 0000000..7e26ed8
--- /dev/null
+++ b/src/doveadm/doveadm-mail-copymove.c
@@ -0,0 +1,224 @@
+/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+#include "doveadm-print.h"
+#include "doveadm-mailbox-list-iter.h"
+#include "doveadm-mail-iter.h"
+#include "doveadm-mail.h"
+
+#include <stdio.h>
+
+struct copy_cmd_context {
+ struct doveadm_mail_cmd_context ctx;
+
+ const char *source_username;
+ struct mail_storage_service_user *source_service_user;
+ struct mail_user *source_user;
+
+ const char *destname;
+ bool move;
+};
+
+static int
+cmd_copy_box(struct copy_cmd_context *ctx, struct mailbox *destbox,
+ const struct mailbox_info *info)
+{
+ struct doveadm_mail_iter *iter;
+ struct mailbox_transaction_context *desttrans;
+ struct mail_save_context *save_ctx;
+ struct mail *mail;
+ int ret = 0, ret2;
+
+ if (doveadm_mail_iter_init(&ctx->ctx, info, ctx->ctx.search_args, 0,
+ NULL, 0, &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 |
+ ctx->ctx.transaction_flags, __func__);
+
+ while (doveadm_mail_iter_next(iter, &mail)) {
+ save_ctx = mailbox_save_alloc(desttrans);
+ mailbox_save_copy_flags(save_ctx, mail);
+ if (ctx->move)
+ ret2 = mailbox_move(&save_ctx, mail);
+ else
+ ret2 = mailbox_copy(&save_ctx, mail);
+ if (ret2 < 0) {
+ i_error("%s message UID %u from '%s' failed: %s",
+ ctx->move ? "Moving" : "Copying",
+ mail->uid, info->vname,
+ mailbox_get_last_internal_error(destbox, NULL));
+ doveadm_mail_failed_mailbox(&ctx->ctx, destbox);
+ ret = -1;
+ }
+ }
+
+ if (mailbox_transaction_commit(&desttrans) < 0) {
+ i_error("Committing %s mails failed: %s",
+ ctx->move ? "moved" : "copied",
+ mailbox_get_last_internal_error(destbox, NULL));
+ doveadm_mail_failed_mailbox(&ctx->ctx, destbox);
+ /* 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_copy_alloc_source_user(struct copy_cmd_context *ctx)
+{
+ struct mail_storage_service_input input;
+ const char *error;
+
+ input = ctx->ctx.storage_service_input;
+ input.username = ctx->source_username;
+
+ mail_storage_service_io_deactivate_user(ctx->ctx.cur_service_user);
+ if (mail_storage_service_lookup_next(ctx->ctx.storage_service, &input,
+ &ctx->source_service_user,
+ &ctx->source_user,
+ &error) < 0)
+ i_fatal("Couldn't lookup user %s: %s", input.username, error);
+ mail_storage_service_io_deactivate_user(ctx->source_service_user);
+ mail_storage_service_io_activate_user(ctx->ctx.cur_service_user);
+}
+
+static int
+cmd_copy_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user)
+{
+ struct copy_cmd_context *ctx = (struct copy_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;
+ struct mail_user *src_user;
+ struct mail_namespace *ns;
+ struct mailbox *destbox;
+ const struct mailbox_info *info;
+ int ret = 0;
+
+ if (ctx->source_username != NULL && ctx->source_user == NULL)
+ cmd_copy_alloc_source_user(ctx);
+
+ ns = mail_namespace_find(user->namespaces, ctx->destname);
+ destbox = mailbox_alloc(ns->list, ctx->destname, MAILBOX_FLAG_SAVEONLY);
+ if (mailbox_open(destbox) < 0) {
+ i_error("Can't open mailbox '%s': %s", ctx->destname,
+ mailbox_get_last_internal_error(destbox, NULL));
+ doveadm_mail_failed_mailbox(&ctx->ctx, destbox);
+ mailbox_free(&destbox);
+ return -1;
+ }
+
+ src_user = ctx->source_user != NULL ? ctx->source_user : user;
+ iter = doveadm_mailbox_list_iter_init(_ctx, src_user, _ctx->search_args,
+ iter_flags);
+ while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN {
+ if (cmd_copy_box(ctx, destbox, info) < 0)
+ ret = -1;
+ } T_END;
+ if (doveadm_mailbox_list_iter_deinit(&iter) < 0)
+ ret = -1;
+
+ if (mailbox_sync(destbox, 0) < 0) {
+ i_error("Syncing mailbox '%s' failed: %s", ctx->destname,
+ mailbox_get_last_internal_error(destbox, NULL));
+ doveadm_mail_failed_mailbox(&ctx->ctx, destbox);
+ ret = -1;
+ }
+ mailbox_free(&destbox);
+ return ret;
+}
+
+static void cmd_copy_init(struct doveadm_mail_cmd_context *_ctx,
+ const char *const args[])
+{
+ struct copy_cmd_context *ctx = (struct copy_cmd_context *)_ctx;
+ const char *destname = args[0], *cmdname = ctx->move ? "move" : "copy";
+
+ if (destname == NULL || args[1] == NULL)
+ doveadm_mail_help_name(cmdname);
+ args++;
+
+ if (args[0] != NULL && args[1] != NULL &&
+ strcasecmp(args[0], "user") == 0) {
+ if ((_ctx->service_flags &
+ MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0)
+ i_fatal("Use -u parameter to specify destination user");
+
+ ctx->source_username = p_strdup(_ctx->pool, args[1]);
+ args += 2;
+ }
+
+ ctx->destname = p_strdup(ctx->ctx.pool, destname);
+ _ctx->search_args = doveadm_mail_build_search_args(args);
+ if (ctx->move)
+ expunge_search_args_check(ctx->ctx.search_args, cmdname);
+}
+
+static void cmd_copy_deinit(struct doveadm_mail_cmd_context *_ctx)
+{
+ struct copy_cmd_context *ctx = (struct copy_cmd_context *)_ctx;
+
+ if (ctx->source_user != NULL) {
+ mail_storage_service_user_unref(&ctx->source_service_user);
+ mail_user_deinit(&ctx->source_user);
+ }
+}
+
+static struct doveadm_mail_cmd_context *cmd_copy_alloc(void)
+{
+ struct copy_cmd_context *ctx;
+
+ ctx = doveadm_mail_cmd_alloc(struct copy_cmd_context);
+ ctx->ctx.v.init = cmd_copy_init;
+ ctx->ctx.v.deinit = cmd_copy_deinit;
+ ctx->ctx.v.run = cmd_copy_run;
+ doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW);
+ return &ctx->ctx;
+}
+
+static struct doveadm_mail_cmd_context *cmd_move_alloc(void)
+{
+ struct copy_cmd_context *ctx;
+
+ ctx = (struct copy_cmd_context *)cmd_copy_alloc();
+ ctx->move = TRUE;
+ return &ctx->ctx;
+}
+
+struct doveadm_cmd_ver2 doveadm_cmd_copy_ver2 = {
+ .name = "copy",
+ .mail_cmd = cmd_copy_alloc,
+ .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "<destination> [user <source user>] <search query>",
+DOVEADM_CMD_PARAMS_START
+DOVEADM_CMD_MAIL_COMMON
+DOVEADM_CMD_PARAM('\0', "destination-mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAM('\0', "source-type", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAM('\0', "source-user", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAMS_END
+};
+
+struct doveadm_cmd_ver2 doveadm_cmd_move_ver2 = {
+ .name = "move",
+ .mail_cmd = cmd_move_alloc,
+ .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "<destination> [user <source user>] <search query>",
+DOVEADM_CMD_PARAMS_START
+DOVEADM_CMD_MAIL_COMMON
+DOVEADM_CMD_PARAM('\0', "destination-mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAM('\0', "source-type", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAM('\0', "source-user", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAMS_END
+};