From: Timo Sirainen Date: Mon, 18 Oct 2010 17:30:42 +0000 (+0100) Subject: doveadm: Added import command for importing mails from other storages. X-Git-Tag: 2.0.6~32 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=88c816e8be4e1a29bca8b67d67a92c67a33f3795;p=thirdparty%2Fdovecot%2Fcore.git doveadm: Added import command for importing mails from other storages. --- diff --git a/.hgignore b/.hgignore index 336146189a..e2179ac53a 100644 --- a/.hgignore +++ b/.hgignore @@ -94,5 +94,5 @@ src/plugins/quota/rquota.h syntax: regexp src/.*/test-[^\.]*$ -doc/man/doveadm-(altmove|auth|director|dump|expunge|fetch|force-resync|help|kick|log|mailbox|penalty|purge|pw|quota|search|user|who)\.1$ +doc/man/doveadm-(altmove|auth|director|dump|expunge|fetch|import|force-resync|help|kick|log|mailbox|penalty|purge|pw|quota|search|user|who)\.1$ doc/man/(doveadm|doveconf|dovecot-lda|dovecot|dsync)\.1$ diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index b53107358e..98fd5186e6 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -17,6 +17,7 @@ nodist_man1_MANS = \ doveadm-dump.1 \ doveadm-expunge.1 \ doveadm-fetch.1 \ + doveadm-import.1 \ doveadm-force-resync.1 \ doveadm-help.1 \ doveadm-kick.1 \ @@ -49,6 +50,7 @@ EXTRA_DIST = \ doveadm-dump.1.in \ doveadm-expunge.1.in \ doveadm-fetch.1.in \ + doveadm-import.1.in \ doveadm-force-resync.1.in \ doveadm-help.1.in \ doveadm-kick.1.in \ @@ -98,6 +100,10 @@ doveadm-fetch.1: $(srcdir)/doveadm-fetch.1.in $(man_includefiles) Makefile $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ < $(srcdir)/doveadm-fetch.1.in > doveadm-fetch.1 +doveadm-import.1: $(srcdir)/doveadm-import.1.in $(man_includefiles) Makefile + $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ + < $(srcdir)/doveadm-import.1.in > doveadm-import.1 + doveadm-force-resync.1: $(srcdir)/doveadm-force-resync.1.in $(man_includefiles) Makefile $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ < $(srcdir)/doveadm-force-resync.1.in > doveadm-force-resync.1 diff --git a/doc/man/doveadm-import.1.in b/doc/man/doveadm-import.1.in new file mode 100644 index 0000000000..3e792cb121 --- /dev/null +++ b/doc/man/doveadm-import.1.in @@ -0,0 +1,81 @@ +.\" Copyright (c) 2010 Dovecot authors, see the included COPYING file +.TH DOVEADM\-IMPORT 1 "2010-10-18" "Dovecot v2.0" "Dovecot" +.SH NAME +doveadm\-import \- Import messages matching given search query +.\"------------------------------------------------------------------------ +.SH SYNOPSIS +.BR doveadm " [" \-Dv "] " import +.I source_location dest_parent search_query +.br +.\"------------------------------------- +.BR doveadm " [" \-Dv "] " "import \-A" +.I source_location dest_parent search_query +.br +.\"------------------------------------- +.BR doveadm " [" \-Dv "] " "import \-u" +.I user source_location dest_parent search_query +.\"------------------------------------------------------------------------ +.SH DESCRIPTION +This command can be used to import mails from another mail storage specified by +.I source_location +to one or more user\(aqs mailboxes. All the mailboxes are imported under the +given +.I dest_parent +mailbox. The +.I search_query +can be used to restrict which mailboxes or messages are imported. + +In the first form, +.BR doveadm (1) +will executed the +.B import +action with the environment of the logged in system user. +.PP +In the second form, the mails will be imported for all users. +.PP +In the third form, the mails will be imported only for given +.IR user (s) +.\"------------------------------------------------------------------------ +@INCLUDE:global-options@ +.\" --- command specific options --- "/. +.PP +Command specific +.IR options : +.\"------------------------------------- +@INCLUDE:option-A@ +.\"------------------------------------- +@INCLUDE:option-u-user@ +.\"------------------------------------------------------------------------ +.SH ARGUMENTS +.TP +.I search_query +Copy messages matching this search query. +See +.BR doveadm\-search\-query (7) +for details. +.\"------------------------------------------------------------------------ +.SH EXAMPLE +This example imports all mails from a backup under a +.I backup-20101026 +mailbox: +.PP +.nf +.B doveadm import \-u jane.doe@example.org \(rs +.B mdbox:/backup/20101026/jane.doe/mdbox backup-20101026 all +.fi +.PP +Another example that imports only messages from foo@example.org in the +backup mdbox\(aqs INBOX to jane\(aqs INBOX: +.PP +.nf +.B doveadm import \-u jane.doe@example.org \(rs +.B mdbox:~/mdbox-backup "" mailbox INBOX from foo@example.org +.fi +.\"------------------------------------------------------------------------ +@INCLUDE:reporting-bugs@ +.\"------------------------------------------------------------------------ +.SH SEE ALSO +.BR doveadm (1), +.BR doveadm\-fetch (1), +.BR doveadm\-search (1), +.BR doveadm\-search\-query (7) diff --git a/src/doveadm/Makefile.am b/src/doveadm/Makefile.am index 037b387bde..778d90d40d 100644 --- a/src/doveadm/Makefile.am +++ b/src/doveadm/Makefile.am @@ -61,6 +61,7 @@ common = \ doveadm-mail-altmove.c \ doveadm-mail-expunge.c \ doveadm-mail-fetch.c \ + doveadm-mail-import.c \ doveadm-mail-iter.c \ doveadm-mail-mailbox.c \ doveadm-mail-mailbox-status.c \ diff --git a/src/doveadm/doveadm-mail-import.c b/src/doveadm/doveadm-mail-import.c new file mode 100644 index 0000000000..1f769d6a29 --- /dev/null +++ b/src/doveadm/doveadm-mail-import.c @@ -0,0 +1,198 @@ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "mail-storage.h" +#include "mail-storage-service.h" +#include "mail-namespace.h" +#include "doveadm-mail-list-iter.h" +#include "doveadm-mail-iter.h" +#include "doveadm-mail.h" + +struct import_cmd_context { + struct doveadm_mail_cmd_context ctx; + + struct mail_user *src_user; + const char *dest_parent; +}; + +static int +dest_mailbox_open_or_create(struct import_cmd_context *ctx, + struct mail_user *user, const char *name, + struct mailbox **box_r) +{ + struct mail_namespace *ns; + struct mailbox *box; + enum mail_error error; + const char *errstr, *storage_name; + + if (*ctx->dest_parent != '\0') { + /* prefix destination mailbox name with given parent mailbox */ + storage_name = ctx->dest_parent; + ns = mail_namespace_find(user->namespaces, &storage_name); + if (ns == NULL) { + i_error("Can't find namespace for parent mailbox %s", + ctx->dest_parent); + return -1; + } + name = t_strdup_printf("%s%c%s", ctx->dest_parent, + ns->sep, name); + } + + storage_name = name; + ns = mail_namespace_find(user->namespaces, &storage_name); + if (ns == NULL) { + i_error("Can't find namespace for mailbox %s", name); + return -1; + } + + box = mailbox_alloc(ns->list, storage_name, MAILBOX_FLAG_SAVEONLY | + MAILBOX_FLAG_KEEP_RECENT); + if (mailbox_create(box, NULL, FALSE) < 0) { + errstr = mail_storage_get_last_error(mailbox_get_storage(box), + &error); + if (error != MAIL_ERROR_EXISTS) { + i_error("Couldn't create mailbox %s: %s", name, errstr); + mailbox_free(&box); + return -1; + } + } + if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) { + i_error("Syncing mailbox %s failed: %s", name, + mail_storage_get_last_error(mailbox_get_storage(box), + NULL)); + mailbox_free(&box); + return -1; + } + *box_r = box; + return 0; +} + +static int +cmd_import_box_contents(struct doveadm_mail_iter *iter, struct mail *src_mail, + struct mailbox *dest_box) +{ + struct mail_storage *dest_storage = mailbox_get_storage(dest_box); + struct mail_save_context *save_ctx; + struct mailbox_transaction_context *dest_trans; + const char *mailbox = mailbox_get_vname(dest_box); + int ret; + + 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); + if (mailbox_copy(&save_ctx, src_mail) < 0) { + i_error("Copying box=%s uid=%u failed: %s", + mailbox, src_mail->uid, + mail_storage_get_last_error(dest_storage, 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, + mail_storage_get_last_error(dest_storage, 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(info, search_args, &trans, &iter) < 0) + return -1; + + mail = mail_alloc(trans, 0, NULL); + 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->name, + &box) == 0) { + if (cmd_import_box_contents(iter, mail, box) < 0) + ret = -1; + mailbox_free(&box); + } + } + mail_free(&mail); + if (doveadm_mail_iter_deinit_sync(&iter) < 0) + ret = -1; + return ret; +} + +static void +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_RAW_LIST | + MAILBOX_LIST_ITER_NO_AUTO_INBOX | + MAILBOX_LIST_ITER_RETURN_NO_FLAGS; + struct doveadm_mail_list_iter *iter; + const struct mailbox_info *info; + + iter = doveadm_mail_list_iter_init(ctx->src_user, + _ctx->search_args, iter_flags); + while ((info = doveadm_mail_list_iter_next(iter)) != NULL) T_BEGIN { + (void)cmd_import_box(ctx, user, info, _ctx->search_args); + } T_END; + doveadm_mail_list_iter_deinit(&iter); +} + +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, *userdb_fields[2]; + + 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); + + /* @UNSAFE */ + userdb_fields[0] = t_strconcat("mail=", src_location, NULL); + userdb_fields[1] = NULL; + + /* create a user for accessing the source storage */ + memset(&input, 0, sizeof(input)); + input.module = "module"; + input.username = "doveadm"; + input.no_userdb_lookup = TRUE; + input.userdb_fields = userdb_fields; + if (mail_storage_service_lookup_next(ctx->ctx.storage_service, &input, + &service_user, &user, &error) < 0) + i_fatal("Import user initialization failed: %s", error); + ctx->src_user = user; +} + +static struct doveadm_mail_cmd_context *cmd_import_alloc(void) +{ + struct doveadm_mail_cmd_context *ctx; + + ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); + ctx->v.init = cmd_import_init; + ctx->v.run = cmd_import_run; + return ctx; +} + +struct doveadm_mail_cmd cmd_import = { + cmd_import_alloc, "import", + " " +}; diff --git a/src/doveadm/doveadm-mail.c b/src/doveadm/doveadm-mail.c index 70653ab7d0..d37e813a37 100644 --- a/src/doveadm/doveadm-mail.c +++ b/src/doveadm/doveadm-mail.c @@ -556,6 +556,7 @@ static struct doveadm_mail_cmd *mail_commands[] = { &cmd_expunge, &cmd_search, &cmd_fetch, + &cmd_import, &cmd_altmove, &cmd_mailbox_list, &cmd_mailbox_create, diff --git a/src/doveadm/doveadm-mail.h b/src/doveadm/doveadm-mail.h index 415b0a181b..93f9ddb4c0 100644 --- a/src/doveadm/doveadm-mail.h +++ b/src/doveadm/doveadm-mail.h @@ -104,6 +104,7 @@ doveadm_mail_cmd_alloc_size(size_t size); struct doveadm_mail_cmd cmd_expunge; 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_mailbox_list; struct doveadm_mail_cmd cmd_mailbox_create;