]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
doveadm: Added import command for importing mails from other storages.
authorTimo Sirainen <tss@iki.fi>
Mon, 18 Oct 2010 17:30:42 +0000 (18:30 +0100)
committerTimo Sirainen <tss@iki.fi>
Mon, 18 Oct 2010 17:30:42 +0000 (18:30 +0100)
.hgignore
doc/man/Makefile.am
doc/man/doveadm-import.1.in [new file with mode: 0644]
src/doveadm/Makefile.am
src/doveadm/doveadm-mail-import.c [new file with mode: 0644]
src/doveadm/doveadm-mail.c
src/doveadm/doveadm-mail.h

index 336146189a1c4b89ed4edf099fbca5cf8854cc2d..e2179ac53a81091090c5d165b01ed6b6f889e64e 100644 (file)
--- 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$
index b53107358e0f0d91347bd06126d408cce5ce7a3c..98fd5186e6ec547258f88949215e7dedb0cca9e0 100644 (file)
@@ -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 (file)
index 0000000..3e792cb
--- /dev/null
@@ -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)
index 037b387bdebb0e90387b9d1d591a0f5e36f4a5dc..778d90d40dceb72f80844f6232fe30dfc1fac134 100644 (file)
@@ -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 (file)
index 0000000..1f769d6
--- /dev/null
@@ -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",
+       "<source mail location> <dest parent mailbox> <search query>"
+};
index 70653ab7d0371513748f1042682a8fbf87540ead..d37e813a37babf1794eab1d12a98335ec2306b4e 100644 (file)
@@ -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,
index 415b0a181bdf77286bab20b66ddb0e0c8afccd48..93f9ddb4c0c7687c19b8acbd6fb79c30082e661d 100644 (file)
@@ -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;