doveadm-mail-import.c revision 8c072aac2949d0c840162d1a1d334e8367fd2993
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2010-2013 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen#include "array.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include "mail-storage.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include "mail-storage-service.h"
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen#include "mail-namespace.h"
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen#include "doveadm-mailbox-list-iter.h"
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen#include "doveadm-mail-iter.h"
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen#include "doveadm-mail.h"
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenstruct import_cmd_context {
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen struct doveadm_mail_cmd_context ctx;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen struct mail_user *src_user;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen const char *dest_parent;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen bool subscribe;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen};
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenstatic int
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainendest_mailbox_open_or_create(struct import_cmd_context *ctx,
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen struct mail_user *user, const char *name,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mailbox **box_r)
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen{
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen struct mail_namespace *ns;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mailbox *box;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen enum mail_error error;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen const char *errstr;
c251a38df327599a62d341bf5c2282f31352faa5Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (*ctx->dest_parent != '\0') {
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen /* prefix destination mailbox name with given parent mailbox */
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen ns = mail_namespace_find(user->namespaces, ctx->dest_parent);
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen name = t_strdup_printf("%s%c%s", ctx->dest_parent,
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen mail_namespace_get_sep(ns), name);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen } else {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen ns = mail_namespace_find(user->namespaces, name);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen }
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen box = mailbox_alloc(ns->list, name, MAILBOX_FLAG_SAVEONLY);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if (mailbox_create(box, NULL, FALSE) < 0) {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen errstr = mailbox_get_last_error(box, &error);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if (error != MAIL_ERROR_EXISTS) {
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen i_error("Couldn't create mailbox %s: %s", name, errstr);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen doveadm_mail_failed_mailbox(&ctx->ctx, box);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen mailbox_free(&box);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen return -1;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen }
687bb904e1bb76c21a6e392f60c990486b298ea4Timo Sirainen if (ctx->subscribe) {
687bb904e1bb76c21a6e392f60c990486b298ea4Timo Sirainen if (mailbox_set_subscribed(box, TRUE) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("Couldn't subscribe to mailbox %s: %s",
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen name, mailbox_get_last_error(box, NULL));
406393bc328f056c49df0804f894ac2070aa5846Timo Sirainen }
7c95b03620a03a43dd72d39608cea5fc77393ad6Timo Sirainen }
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen i_error("Syncing mailbox %s failed: %s", name,
73e7998716853b5b7621c06aea0022dccda70ad1Timo Sirainen mailbox_get_last_error(box, NULL));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen doveadm_mail_failed_mailbox(&ctx->ctx, box);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mailbox_free(&box);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen return -1;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen *box_r = box;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen return 0;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen}
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainenstatic int
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainencmd_import_box_contents(struct doveadm_mail_iter *iter, struct mail *src_mail,
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen struct mailbox *dest_box)
985fa802913c96ce6f2e25bbc788ee39c416a7e0Timo Sirainen{
985fa802913c96ce6f2e25bbc788ee39c416a7e0Timo Sirainen struct mail_save_context *save_ctx;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen struct mailbox_transaction_context *dest_trans;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen const char *mailbox = mailbox_get_vname(dest_box);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen int ret = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
dest_trans = mailbox_transaction_begin(dest_box,
MAILBOX_TRANSACTION_FLAG_EXTERNAL);
do {
if (doveadm_debug) {
i_debug("import: box=%s uid=%u",
mailbox, src_mail->uid);
}
save_ctx = mailbox_save_alloc(dest_trans);
mailbox_save_copy_flags(save_ctx, src_mail);
if (mailbox_copy(&save_ctx, src_mail) < 0) {
i_error("Copying box=%s uid=%u failed: %s",
mailbox, src_mail->uid,
mailbox_get_last_error(dest_box, NULL));
ret = -1;
}
} while (doveadm_mail_iter_next(iter, &src_mail));
if (mailbox_transaction_commit(&dest_trans) < 0) {
i_error("Committing copied mails to %s failed: %s", mailbox,
mailbox_get_last_error(dest_box, NULL));
ret = -1;
}
return ret;
}
static int
cmd_import_box(struct import_cmd_context *ctx, struct mail_user *dest_user,
const struct mailbox_info *info,
struct mail_search_args *search_args)
{
struct doveadm_mail_iter *iter;
struct mailbox_transaction_context *trans;
struct mailbox *box;
struct mail *mail;
int ret = 0;
if (doveadm_mail_iter_init(&ctx->ctx, info, search_args, 0, NULL,
&trans, &iter) < 0)
return -1;
if (doveadm_mail_iter_next(iter, &mail)) {
/* at least one mail matches in this mailbox */
if (dest_mailbox_open_or_create(ctx, dest_user, info->vname,
&box) < 0)
ret = -1;
else {
if (cmd_import_box_contents(iter, mail, box) < 0) {
doveadm_mail_failed_mailbox(&ctx->ctx, mail->box);
ret = -1;
}
mailbox_free(&box);
}
}
if (doveadm_mail_iter_deinit_sync(&iter) < 0)
ret = -1;
return ret;
}
static int
cmd_import_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user)
{
struct import_cmd_context *ctx = (struct import_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;
const struct mailbox_info *info;
int ret = 0;
iter = doveadm_mailbox_list_iter_init(_ctx, ctx->src_user,
_ctx->search_args, iter_flags);
while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN {
if (cmd_import_box(ctx, user, info, _ctx->search_args) < 0)
ret = -1;
} T_END;
if (doveadm_mailbox_list_iter_deinit(&iter) < 0)
ret = -1;
return ret;
}
static void cmd_import_init(struct doveadm_mail_cmd_context *_ctx,
const char *const args[])
{
struct import_cmd_context *ctx = (struct import_cmd_context *)_ctx;
struct mail_storage_service_input input;
struct mail_storage_service_user *service_user;
struct mail_user *user;
const char *src_location, *error;
if (str_array_length(args) < 3)
doveadm_mail_help_name("import");
src_location = args[0];
ctx->dest_parent = p_strdup(_ctx->pool, args[1]);
ctx->ctx.search_args = doveadm_mail_build_search_args(args+2);
/* create a user for accessing the source storage */
memset(&input, 0, sizeof(input));
input.module = "mail";
input.username = "doveadm";
input.flags_override_add = MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES |
MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS;
input.flags_override_remove = MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
if (mail_storage_service_lookup_next(ctx->ctx.storage_service, &input,
&service_user, &user, &error) < 0)
i_fatal("Import user initialization failed: %s", error);
if (mail_namespaces_init_location(user, src_location, &error) < 0)
i_fatal("Import namespace initialization failed: %s", error);
ctx->src_user = user;
mail_storage_service_user_free(&service_user);
}
static void cmd_import_deinit(struct doveadm_mail_cmd_context *_ctx)
{
struct import_cmd_context *ctx = (struct import_cmd_context *)_ctx;
mail_user_unref(&ctx->src_user);
}
static bool cmd_import_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c)
{
struct import_cmd_context *ctx = (struct import_cmd_context *)_ctx;
switch (c) {
case 's':
ctx->subscribe = TRUE;
break;
default:
return FALSE;
}
return TRUE;
}
static struct doveadm_mail_cmd_context *cmd_import_alloc(void)
{
struct import_cmd_context *ctx;
ctx = doveadm_mail_cmd_alloc(struct import_cmd_context);
ctx->ctx.getopt_args = "s";
ctx->ctx.v.parse_arg = cmd_import_parse_arg;
ctx->ctx.v.init = cmd_import_init;
ctx->ctx.v.deinit = cmd_import_deinit;
ctx->ctx.v.run = cmd_import_run;
return &ctx->ctx;
}
struct doveadm_mail_cmd cmd_import = {
cmd_import_alloc, "import",
"[-s] <source mail location> <dest parent mailbox> <search query>"
};