doveadm-mail.c revision 8b7feed47b94d484b3e7e0aef2b78ea09433aeaa
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2009-2014 Dovecot authors, see the included COPYING file */
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "lib.h"
345648b341f228bd7f0b89f8aa3ecb9c470d817eTimo Sirainen#include "array.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "lib-signals.h"
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen#include "ioloop.h"
5a2cb3d097a2d9a9e930af997e7bf3400a8d840dTimo Sirainen#include "str.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "unichar.h"
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen#include "module-dir.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "wildcard-match.h"
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen#include "master-service.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "mail-user.h"
16f46efe0e090fe6975acf012a61a160f4787985Andrey Panin#include "mail-namespace.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "mail-storage.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "mail-storage-settings.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "mail-storage-service.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "mail-search-build.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "mail-search-parser.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "mailbox-list-iter.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "doveadm.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "doveadm-settings.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "doveadm-print.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "doveadm-dsync.h"
16f46efe0e090fe6975acf012a61a160f4787985Andrey Panin#include "doveadm-mail.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include <stdio.h>
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include <stdlib.h>
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo SirainenARRAY_TYPE(doveadm_mail_cmd) doveadm_mail_cmds;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenvoid (*hook_doveadm_mail_init)(struct doveadm_mail_cmd_context *ctx);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenstruct doveadm_mail_cmd_module_register
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen doveadm_mail_cmd_module_register = { 0 };
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenchar doveadm_mail_cmd_hide = '\0';
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenstatic int killed_signo = 0;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenvoid doveadm_mail_failed_error(struct doveadm_mail_cmd_context *ctx,
7242e1ce7803b83bc82e239ef111b47c1c72dd4bAndrey Panin enum mail_error error)
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen{
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen int exit_code = 0;
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen switch (error) {
c57776c06ec99ba9b0dafdbf9475ea72ea8ca134Timo Sirainen case MAIL_ERROR_NONE:
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen i_unreached();
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen case MAIL_ERROR_TEMP:
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen exit_code = EX_TEMPFAIL;
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen break;
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen case MAIL_ERROR_NOTPOSSIBLE:
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen case MAIL_ERROR_EXISTS:
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen case MAIL_ERROR_CONVERSION:
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen case MAIL_ERROR_INVALIDDATA:
4051fa1f367553cac34f74c2e332a678390bcee5Timo Sirainen exit_code = DOVEADM_EX_NOTPOSSIBLE;
5965eaa2d972e6264cecaf54091cd43019bc7d1fTimo Sirainen break;
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen case MAIL_ERROR_PARAMS:
4051fa1f367553cac34f74c2e332a678390bcee5Timo Sirainen exit_code = EX_USAGE;
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen break;
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen case MAIL_ERROR_PERM:
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen exit_code = EX_NOPERM;
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen break;
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen case MAIL_ERROR_NOQUOTA:
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen exit_code = EX_CANTCREAT;
648d24583c1574441c4fa0331a90bd4d6e7996c5Timo Sirainen break;
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen case MAIL_ERROR_NOTFOUND:
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen exit_code = DOVEADM_EX_NOTFOUND;
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen break;
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen case MAIL_ERROR_EXPUNGED:
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen case MAIL_ERROR_INUSE:
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen exit_code = EX_TEMPFAIL;
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen break;
59beb411159176b39e48a52d60dd3239732e67b4Timo Sirainen }
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen /* tempfail overrides all other exit codes, otherwise use whatever
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen error happened first */
15cb9549422ccee416b21d26fec97a556ad0fa36Florian Zeitz if (ctx->exit_code == 0 || exit_code == EX_TEMPFAIL)
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen ctx->exit_code = exit_code;
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen}
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainenvoid doveadm_mail_failed_storage(struct doveadm_mail_cmd_context *ctx,
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen struct mail_storage *storage)
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen{
704fbadd78375da18dcaf2c5d93ac8cfe2c61358Timo Sirainen enum mail_error error;
704fbadd78375da18dcaf2c5d93ac8cfe2c61358Timo Sirainen
704fbadd78375da18dcaf2c5d93ac8cfe2c61358Timo Sirainen mail_storage_get_last_error(storage, &error);
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen doveadm_mail_failed_error(ctx, error);
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen}
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainenvoid doveadm_mail_failed_mailbox(struct doveadm_mail_cmd_context *ctx,
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen struct mailbox *box)
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen{
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen doveadm_mail_failed_storage(ctx, mailbox_get_storage(box));
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen}
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainenstruct doveadm_mail_cmd_context *
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainendoveadm_mail_cmd_alloc_size(size_t size)
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen{
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen struct doveadm_mail_cmd_context *ctx;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen pool_t pool;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen i_assert(size >= sizeof(struct doveadm_mail_cmd_context));
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen pool = pool_alloconly_create("doveadm mail cmd", 1024);
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen ctx = p_malloc(pool, size);
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen ctx->pool = pool;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return ctx;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen}
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainenstatic int
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainencmd_purge_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user)
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen{
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen struct mail_namespace *ns;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen struct mail_storage *storage;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen int ret = 0;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen for (ns = user->namespaces; ns != NULL; ns = ns->next) {
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen if (ns->type != MAIL_NAMESPACE_TYPE_PRIVATE ||
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen ns->alias_for != NULL)
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen continue;
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen storage = mail_namespace_get_default_storage(ns);
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen if (mail_storage_purge(storage) < 0) {
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen i_error("Purging namespace '%s' failed: %s", ns->prefix,
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen mail_storage_get_last_error(storage, NULL));
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen doveadm_mail_failed_storage(ctx, storage);
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen ret = -1;
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen }
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen }
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen return ret;
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen}
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainenstatic struct doveadm_mail_cmd_context *cmd_purge_alloc(void)
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen{
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen struct doveadm_mail_cmd_context *ctx;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context);
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen ctx->v.run = cmd_purge_run;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return ctx;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen}
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainenstruct mailbox *
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainendoveadm_mailbox_find(struct mail_user *user, const char *mailbox)
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen{
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen struct mail_namespace *ns;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen if (!uni_utf8_str_is_valid(mailbox)) {
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen i_fatal_status(EX_DATAERR,
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen "Mailbox name not valid UTF-8: %s", mailbox);
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen }
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen ns = mail_namespace_find(user->namespaces, mailbox);
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_IGNORE_ACLS);
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen}
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainenstatic int
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainendoveadm_mailbox_find_and_open(struct mail_user *user, const char *mailbox,
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen struct mailbox **box_r)
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen{
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen struct mailbox *box;
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen box = doveadm_mailbox_find(user, mailbox);
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen if (mailbox_open(box) < 0) {
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen i_error("Opening mailbox %s failed: %s", mailbox,
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen mailbox_get_last_error(box, NULL));
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen mailbox_free(&box);
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return -1;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen }
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen *box_r = box;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return 0;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen}
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainenint doveadm_mailbox_find_and_sync(struct mail_user *user, const char *mailbox,
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen struct mailbox **box_r)
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen{
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen if (doveadm_mailbox_find_and_open(user, mailbox, box_r) < 0)
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return -1;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen if (mailbox_sync(*box_r, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen i_error("Syncing mailbox %s failed: %s", mailbox,
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen mailbox_get_last_error(*box_r, NULL));
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch mailbox_free(box_r);
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch mailbox_free(box_r);
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch return -1;
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch }
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch return 0;
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch}
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Boschstruct mail_search_args *
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Boschdoveadm_mail_build_search_args(const char *const args[])
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch{
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch struct mail_search_parser *parser;
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch struct mail_search_args *sargs;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const char *error, *charset = "UTF-8";
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen parser = mail_search_parser_init_cmdline(args);
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen if (mail_search_build(mail_search_register_get_human(),
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen parser, &charset, &sargs, &error) < 0)
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen i_fatal("%s", error);
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen mail_search_parser_deinit(&parser);
59beb411159176b39e48a52d60dd3239732e67b4Timo Sirainen return sargs;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainenstatic int cmd_force_resync_box(struct doveadm_mail_cmd_context *ctx,
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen const struct mailbox_info *info)
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen{
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen struct mailbox *box;
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen int ret = 0;
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen box = mailbox_alloc(info->ns->list, info->vname,
e9e2d23e1ea5a149a7d8828d2a45b9f2313c3785Timo Sirainen MAILBOX_FLAG_IGNORE_ACLS);
15cb9549422ccee416b21d26fec97a556ad0fa36Florian Zeitz if (mailbox_open(box) < 0) {
e9e2d23e1ea5a149a7d8828d2a45b9f2313c3785Timo Sirainen i_error("Opening mailbox %s failed: %s", info->vname,
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen mailbox_get_last_error(box, NULL));
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen doveadm_mail_failed_mailbox(ctx, box);
f97cf1c086715db87094bc3d0a4fefdd80bd869cTimo Sirainen ret = -1;
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen } else if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FORCE_RESYNC |
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen MAILBOX_SYNC_FLAG_FIX_INCONSISTENT) < 0) {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen i_error("Forcing a resync on mailbox %s failed: %s",
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen info->vname, mailbox_get_last_error(box, NULL));
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen doveadm_mail_failed_mailbox(ctx, box);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen ret = -1;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen }
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen mailbox_free(&box);
8eea67470c1bd8562a62e7445d930bb2079b1a43Timo Sirainen return ret;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
59beb411159176b39e48a52d60dd3239732e67b4Timo Sirainenstatic int cmd_force_resync_run(struct doveadm_mail_cmd_context *ctx,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mail_user *user)
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen{
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen const enum mailbox_list_iter_flags iter_flags =
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen MAILBOX_LIST_ITER_RETURN_NO_FLAGS |
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen MAILBOX_LIST_ITER_STAR_WITHIN_NS;
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen const enum mail_namespace_type ns_mask = MAIL_NAMESPACE_TYPE_MASK_ALL;
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen struct mailbox_list_iterate_context *iter;
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen const struct mailbox_info *info;
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen int ret = 0;
e9e2d23e1ea5a149a7d8828d2a45b9f2313c3785Timo Sirainen
81419a8dd69d8cef0e93d1e04bda77d135202452Timo Sirainen iter = mailbox_list_iter_init_namespaces(user->namespaces, ctx->args,
e9e2d23e1ea5a149a7d8828d2a45b9f2313c3785Timo Sirainen ns_mask, iter_flags);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen while ((info = mailbox_list_iter_next(iter)) != NULL) {
b7c2065b3f10f9ae27787a9db5aaefbfc70d4502Timo Sirainen if ((info->flags & (MAILBOX_NOSELECT |
f97cf1c086715db87094bc3d0a4fefdd80bd869cTimo Sirainen MAILBOX_NONEXISTENT)) == 0) T_BEGIN {
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen if (cmd_force_resync_box(ctx, info) < 0)
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen ret = -1;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen } T_END;
}
if (mailbox_list_iter_deinit(&iter) < 0) {
i_error("Listing mailboxes failed");
ret = -1;
}
return ret;
}
static void
cmd_force_resync_init(struct doveadm_mail_cmd_context *_ctx ATTR_UNUSED,
const char *const args[])
{
if (args[0] == NULL)
doveadm_mail_help_name("force-resync");
}
static struct doveadm_mail_cmd_context *cmd_force_resync_alloc(void)
{
struct doveadm_mail_cmd_context *ctx;
ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context);
ctx->v.init = cmd_force_resync_init;
ctx->v.run = cmd_force_resync_run;
return ctx;
}
static int
doveadm_mail_next_user(struct doveadm_mail_cmd_context *ctx, bool init_user,
const struct mail_storage_service_input *input,
const char **error_r)
{
const char *error, *ip;
int ret;
ip = net_ip2addr(&input->remote_ip);
if (ip[0] == '\0')
i_set_failure_prefix("doveadm(%s): ", input->username);
else
i_set_failure_prefix("doveadm(%s,%s): ", ip, input->username);
/* see if we want to execute this command via (another)
doveadm server */
ret = doveadm_mail_server_user(ctx, input, error_r);
if (ret != 0)
return ret;
if (init_user) {
/* call init() after we've checked whether we want to do this
via doveadm-server or not */
ctx->v.init(ctx, ctx->args);
if (hook_doveadm_mail_init != NULL)
hook_doveadm_mail_init(ctx);
}
ret = mail_storage_service_lookup(ctx->storage_service, input,
&ctx->cur_service_user, &error);
if (ret <= 0) {
if (ret < 0) {
*error_r = t_strdup_printf("User lookup failed: %s",
error);
}
return ret;
}
if (ctx->v.prerun != NULL) {
if (ctx->v.prerun(ctx, ctx->cur_service_user, error_r) < 0) {
mail_storage_service_user_free(&ctx->cur_service_user);
return -1;
}
}
ret = mail_storage_service_next(ctx->storage_service,
ctx->cur_service_user,
&ctx->cur_mail_user);
if (ret < 0) {
*error_r = "User init failed";
mail_storage_service_user_free(&ctx->cur_service_user);
return ret;
}
if (ctx->v.run(ctx, ctx->cur_mail_user) < 0) {
i_assert(ctx->exit_code != 0);
}
mail_user_unref(&ctx->cur_mail_user);
mail_storage_service_user_free(&ctx->cur_service_user);
return 1;
}
int doveadm_mail_single_user(struct doveadm_mail_cmd_context *ctx,
const struct mail_storage_service_input *input,
const char **error_r)
{
i_assert(input->username != NULL);
ctx->cur_client_ip = input->remote_ip;
ctx->cur_username = input->username;
ctx->storage_service_input = *input;
ctx->storage_service = mail_storage_service_init(master_service, NULL,
ctx->service_flags);
return doveadm_mail_next_user(ctx, TRUE, input, error_r);
}
static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
{
killed_signo = si->si_signo;
}
static void
doveadm_mail_all_users(struct doveadm_mail_cmd_context *ctx, char *argv[],
const char *wildcard_user)
{
struct mail_storage_service_input input;
unsigned int user_idx;
const char *ip, *user, *error;
int ret;
ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
memset(&input, 0, sizeof(input));
input.service = "doveadm";
ctx->storage_service_input = input;
ctx->storage_service = mail_storage_service_init(master_service, NULL,
ctx->service_flags);
lib_signals_set_handler(SIGINT, 0, sig_die, NULL);
lib_signals_set_handler(SIGTERM, 0, sig_die, NULL);
ctx->v.init(ctx, (const void *)argv);
mail_storage_service_all_init(ctx->storage_service);
if (hook_doveadm_mail_init != NULL)
hook_doveadm_mail_init(ctx);
user_idx = 0;
while ((ret = ctx->v.get_next_user(ctx, &user)) > 0) {
if (wildcard_user != NULL) {
if (!wildcard_match_icase(user, wildcard_user))
continue;
}
input.username = user;
ctx->cur_username = user;
doveadm_print_sticky("username", user);
T_BEGIN {
ret = doveadm_mail_next_user(ctx, FALSE, &input, &error);
if (ret < 0)
i_error("%s", error);
else if (ret == 0)
i_info("User no longer exists, skipping");
} T_END;
if (ret == -1)
break;
if (doveadm_verbose) {
if (++user_idx % 100 == 0) {
printf("\r%d", user_idx);
fflush(stdout);
}
}
if (killed_signo != 0) {
i_warning("Killed with signal %d", killed_signo);
ret = -1;
break;
}
}
if (doveadm_verbose)
printf("\n");
ip = net_ip2addr(&ctx->cur_client_ip);
if (ip[0] == '\0')
i_set_failure_prefix("doveadm: ");
else
i_set_failure_prefix("doveadm(%s): ", ip);
if (ret < 0) {
i_error("Failed to iterate through some users");
ctx->exit_code = EX_TEMPFAIL;
}
}
static void
doveadm_mail_cmd_init_noop(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED,
const char *const args[] ATTR_UNUSED)
{
}
static int
doveadm_mail_cmd_get_next_user(struct doveadm_mail_cmd_context *ctx,
const char **username_r)
{
return mail_storage_service_all_next(ctx->storage_service, username_r);
}
static void
doveadm_mail_cmd_deinit_noop(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED)
{
}
struct doveadm_mail_cmd_context *
doveadm_mail_cmd_init(const struct doveadm_mail_cmd *cmd,
const struct doveadm_settings *set)
{
struct doveadm_mail_cmd_context *ctx;
ctx = cmd->alloc();
ctx->set = set;
ctx->cmd = cmd;
if (ctx->v.init == NULL)
ctx->v.init = doveadm_mail_cmd_init_noop;
if (ctx->v.get_next_user == NULL)
ctx->v.get_next_user = doveadm_mail_cmd_get_next_user;
if (ctx->v.deinit == NULL)
ctx->v.deinit = doveadm_mail_cmd_deinit_noop;
p_array_init(&ctx->module_contexts, ctx->pool, 5);
return ctx;
}
static void
doveadm_mail_cmd(const struct doveadm_mail_cmd *cmd, int argc, char *argv[])
{
struct doveadm_mail_cmd_context *ctx;
const char *getopt_args, *wildcard_user, *error;
int ret, c;
ctx = doveadm_mail_cmd_init(cmd, doveadm_settings);
ctx->full_args = (const void *)(argv + 1);
ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT;
if (doveadm_debug)
ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG;
getopt_args = "AS:u:";
/* keep context's getopt_args first in case it contains '+' */
if (ctx->getopt_args != NULL)
getopt_args = t_strconcat(ctx->getopt_args, getopt_args, NULL);
ctx->cur_username = getenv("USER");
wildcard_user = NULL;
while ((c = getopt(argc, argv, getopt_args)) > 0) {
switch (c) {
case 'A':
ctx->iterate_all_users = TRUE;
break;
case 'S':
doveadm_settings->doveadm_socket_path = optarg;
if (doveadm_settings->doveadm_worker_count == 0)
doveadm_settings->doveadm_worker_count = 1;
break;
case 'u':
ctx->service_flags |=
MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
ctx->cur_username = optarg;
if (strchr(ctx->cur_username, '*') != NULL ||
strchr(ctx->cur_username, '?') != NULL) {
wildcard_user = ctx->cur_username;
ctx->cur_username = NULL;
}
break;
default:
if (ctx->v.parse_arg == NULL ||
!ctx->v.parse_arg(ctx, c))
doveadm_mail_help(cmd);
}
}
argv += optind;
if (argv[0] != NULL && cmd->usage_args == NULL) {
i_fatal_status(EX_USAGE, "doveadm %s: Unknown parameter: %s",
cmd->name, argv[0]);
}
ctx->args = (const void *)argv;
if (ctx->v.preinit != NULL)
ctx->v.preinit(ctx);
ctx->iterate_single_user =
!ctx->iterate_all_users && wildcard_user == NULL;
if (doveadm_print_is_initialized() && !ctx->iterate_single_user) {
doveadm_print_header("username", "Username",
DOVEADM_PRINT_HEADER_FLAG_STICKY |
DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE);
}
if (ctx->iterate_single_user) {
struct mail_storage_service_input input;
if (ctx->cur_username == NULL)
i_fatal_status(EX_USAGE, "USER environment is missing and -u option not used");
memset(&input, 0, sizeof(input));
input.service = "doveadm";
input.username = ctx->cur_username;
ret = doveadm_mail_single_user(ctx, &input, &error);
if (ret < 0)
i_fatal("%s", error);
else if (ret == 0)
i_fatal_status(EX_NOUSER, "User doesn't exist");
} else {
ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP;
doveadm_mail_all_users(ctx, argv, wildcard_user);
}
if (ctx->search_args != NULL)
mail_search_args_unref(&ctx->search_args);
doveadm_mail_server_flush();
ctx->v.deinit(ctx);
doveadm_print_flush();
/* service deinit unloads mail plugins, so do it late */
mail_storage_service_deinit(&ctx->storage_service);
if (ctx->exit_code != 0)
doveadm_exit_code = ctx->exit_code;
pool_unref(&ctx->pool);
}
static bool
doveadm_mail_cmd_try_find_multi_word(const struct doveadm_mail_cmd *cmd,
const char *cmdname, int *argc,
const char *const **argv)
{
unsigned int len;
if (*argc < 2)
return FALSE;
*argc -= 1;
*argv += 1;
len = strlen((*argv)[0]);
if (strncmp(cmdname, (*argv)[0], len) != 0)
return FALSE;
if (cmdname[len] == ' ') {
/* more args */
return doveadm_mail_cmd_try_find_multi_word(cmd, cmdname + len + 1,
argc, argv);
}
if (cmdname[len] != '\0')
return FALSE;
/* match */
return TRUE;
}
const struct doveadm_mail_cmd *
doveadm_mail_cmd_find_from_argv(const char *cmd_name, int *argc,
const char *const **argv)
{
const struct doveadm_mail_cmd *cmd;
unsigned int cmd_name_len;
const char *const *orig_argv;
int orig_argc;
i_assert(*argc > 0);
cmd_name_len = strlen(cmd_name);
array_foreach(&doveadm_mail_cmds, cmd) {
if (strcmp(cmd->name, cmd_name) == 0)
return cmd;
/* see if it matches a multi-word command */
if (strncmp(cmd->name, cmd_name, cmd_name_len) == 0 &&
cmd->name[cmd_name_len] == ' ') {
const char *subcmd = cmd->name + cmd_name_len + 1;
orig_argc = *argc;
orig_argv = *argv;
if (doveadm_mail_cmd_try_find_multi_word(cmd, subcmd,
argc, argv))
return cmd;
*argc = orig_argc;
*argv = orig_argv;
}
}
return NULL;
}
bool doveadm_mail_try_run(const char *cmd_name, int argc, char *argv[])
{
const struct doveadm_mail_cmd *cmd;
cmd = doveadm_mail_cmd_find_from_argv(cmd_name, &argc, (void *)&argv);
if (cmd == NULL)
return FALSE;
doveadm_mail_cmd(cmd, argc, argv);
return TRUE;
}
void doveadm_mail_register_cmd(const struct doveadm_mail_cmd *cmd)
{
/* for now we'll just assume that cmd will be permanently in memory */
array_append(&doveadm_mail_cmds, cmd, 1);
}
const struct doveadm_mail_cmd *doveadm_mail_cmd_find(const char *cmd_name)
{
const struct doveadm_mail_cmd *cmd;
array_foreach(&doveadm_mail_cmds, cmd) {
if (strcmp(cmd->name, cmd_name) == 0)
return cmd;
}
return NULL;
}
void doveadm_mail_usage(string_t *out)
{
const struct doveadm_mail_cmd *cmd;
array_foreach(&doveadm_mail_cmds, cmd) {
if (cmd->usage_args == &doveadm_mail_cmd_hide)
continue;
str_printfa(out, "%s\t[-u <user>|-A] [-S <socket_path>]",
cmd->name);
if (cmd->usage_args != NULL)
str_printfa(out, " %s", cmd->usage_args);
str_append_c(out, '\n');
}
}
void doveadm_mail_help(const struct doveadm_mail_cmd *cmd)
{
fprintf(stderr, "doveadm %s [-u <user>|-A] [-S <socket_path>] %s\n",
cmd->name, cmd->usage_args == NULL ? "" : cmd->usage_args);
exit(EX_USAGE);
}
void doveadm_mail_try_help_name(const char *cmd_name)
{
const struct doveadm_mail_cmd *cmd;
cmd = doveadm_mail_cmd_find(cmd_name);
if (cmd != NULL)
doveadm_mail_help(cmd);
}
bool doveadm_mail_has_subcommands(const char *cmd_name)
{
const struct doveadm_mail_cmd *cmd;
unsigned int len = strlen(cmd_name);
array_foreach(&doveadm_mail_cmds, cmd) {
if (strncmp(cmd->name, cmd_name, len) == 0 &&
cmd->name[len] == ' ')
return TRUE;
}
return FALSE;
}
void doveadm_mail_help_name(const char *cmd_name)
{
doveadm_mail_try_help_name(cmd_name);
i_fatal("Missing help for command %s", cmd_name);
}
static struct doveadm_mail_cmd cmd_force_resync = {
cmd_force_resync_alloc, "force-resync", "<mailbox mask>"
};
static struct doveadm_mail_cmd cmd_purge = {
cmd_purge_alloc, "purge", NULL
};
static struct doveadm_mail_cmd *mail_commands[] = {
&cmd_force_resync,
&cmd_purge,
&cmd_expunge,
&cmd_search,
&cmd_fetch,
&cmd_flags_add,
&cmd_flags_remove,
&cmd_flags_replace,
&cmd_import,
&cmd_index,
&cmd_altmove,
&cmd_copy,
&cmd_deduplicate,
&cmd_move,
&cmd_mailbox_list,
&cmd_mailbox_create,
&cmd_mailbox_delete,
&cmd_mailbox_rename,
&cmd_mailbox_subscribe,
&cmd_mailbox_unsubscribe,
&cmd_mailbox_status,
&cmd_mailbox_metadata_set,
&cmd_mailbox_metadata_unset,
&cmd_mailbox_metadata_get,
&cmd_mailbox_metadata_list,
&cmd_batch,
&cmd_dsync_backup,
&cmd_dsync_mirror,
&cmd_dsync_server
};
void doveadm_mail_init(void)
{
struct module_dir_load_settings mod_set;
unsigned int i;
i_array_init(&doveadm_mail_cmds, 32);
for (i = 0; i < N_ELEMENTS(mail_commands); i++)
doveadm_mail_register_cmd(mail_commands[i]);
memset(&mod_set, 0, sizeof(mod_set));
mod_set.abi_version = DOVECOT_ABI_VERSION;
mod_set.require_init_funcs = TRUE;
mod_set.debug = doveadm_debug;
mod_set.binary_name = "doveadm";
/* load all configured mail plugins */
mail_storage_service_modules =
module_dir_load_missing(mail_storage_service_modules,
doveadm_settings->mail_plugin_dir,
doveadm_settings->mail_plugins,
&mod_set);
}
void doveadm_mail_deinit(void)
{
array_free(&doveadm_mail_cmds);
}