diff options
Diffstat (limited to '')
-rw-r--r-- | src/doveadm/doveadm-mail.c | 990 |
1 files changed, 990 insertions, 0 deletions
diff --git a/src/doveadm/doveadm-mail.c b/src/doveadm/doveadm-mail.c new file mode 100644 index 0000000..574c3b7 --- /dev/null +++ b/src/doveadm/doveadm-mail.c @@ -0,0 +1,990 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "lib-signals.h" +#include "ioloop.h" +#include "istream.h" +#include "istream-dot.h" +#include "istream-seekable.h" +#include "str.h" +#include "unichar.h" +#include "module-dir.h" +#include "wildcard-match.h" +#include "master-service.h" +#include "mail-user.h" +#include "mail-namespace.h" +#include "mail-storage.h" +#include "mail-storage-settings.h" +#include "mail-storage-service.h" +#include "mail-storage-hooks.h" +#include "mail-search-build.h" +#include "mail-search-parser.h" +#include "mailbox-list-iter.h" +#include "doveadm.h" +#include "client-connection.h" +#include "doveadm-settings.h" +#include "doveadm-print.h" +#include "doveadm-dsync.h" +#include "doveadm-mail.h" + +#include <stdio.h> + +#define DOVEADM_MAIL_CMD_INPUT_TIMEOUT_MSECS (5*60*1000) + +struct force_resync_cmd_context { + struct doveadm_mail_cmd_context ctx; + bool fsck; +}; + +void (*hook_doveadm_mail_init)(struct doveadm_mail_cmd_context *ctx); +struct doveadm_mail_cmd_module_register + doveadm_mail_cmd_module_register = { 0 }; +char doveadm_mail_cmd_hide = '\0'; + +static int killed_signo = 0; + +bool doveadm_is_killed(void) +{ + return killed_signo != 0; +} + +int doveadm_killed_signo(void) +{ + return killed_signo; +} + +void doveadm_mail_failed_error(struct doveadm_mail_cmd_context *ctx, + enum mail_error error) +{ + int exit_code = EX_TEMPFAIL; + + switch (error) { + case MAIL_ERROR_NONE: + i_unreached(); + case MAIL_ERROR_TEMP: + case MAIL_ERROR_UNAVAILABLE: + break; + case MAIL_ERROR_NOTPOSSIBLE: + case MAIL_ERROR_EXISTS: + case MAIL_ERROR_CONVERSION: + case MAIL_ERROR_INVALIDDATA: + exit_code = DOVEADM_EX_NOTPOSSIBLE; + break; + case MAIL_ERROR_PARAMS: + exit_code = EX_USAGE; + break; + case MAIL_ERROR_PERM: + exit_code = EX_NOPERM; + break; + case MAIL_ERROR_NOQUOTA: + exit_code = EX_CANTCREAT; + break; + case MAIL_ERROR_NOTFOUND: + exit_code = DOVEADM_EX_NOTFOUND; + break; + case MAIL_ERROR_EXPUNGED: + break; + case MAIL_ERROR_INUSE: + case MAIL_ERROR_LIMIT: + exit_code = DOVEADM_EX_NOTPOSSIBLE; + break; + case MAIL_ERROR_LOOKUP_ABORTED: + break; + } + /* tempfail overrides all other exit codes, otherwise use whatever + error happened first */ + if (ctx->exit_code == 0 || exit_code == EX_TEMPFAIL) + ctx->exit_code = exit_code; +} + +void doveadm_mail_failed_storage(struct doveadm_mail_cmd_context *ctx, + struct mail_storage *storage) +{ + enum mail_error error; + + mail_storage_get_last_error(storage, &error); + doveadm_mail_failed_error(ctx, error); +} + +void doveadm_mail_failed_mailbox(struct doveadm_mail_cmd_context *ctx, + struct mailbox *box) +{ + doveadm_mail_failed_storage(ctx, mailbox_get_storage(box)); +} + +void doveadm_mail_failed_list(struct doveadm_mail_cmd_context *ctx, + struct mailbox_list *list) +{ + enum mail_error error; + + mailbox_list_get_last_error(list, &error); + doveadm_mail_failed_error(ctx, error); +} + +struct doveadm_mail_cmd_context * +doveadm_mail_cmd_alloc_size(size_t size) +{ + struct doveadm_mail_cmd_context *ctx; + pool_t pool; + + i_assert(size >= sizeof(struct doveadm_mail_cmd_context)); + + pool = pool_alloconly_create("doveadm mail cmd", 1024); + ctx = p_malloc(pool, size); + ctx->pool = pool; + ctx->cmd_input_fd = -1; + return ctx; +} + +static int +cmd_purge_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user) +{ + struct mail_namespace *ns; + struct mail_storage *storage; + int ret = 0; + + for (ns = user->namespaces; ns != NULL; ns = ns->next) { + if (ns->type != MAIL_NAMESPACE_TYPE_PRIVATE || + ns->alias_for != NULL) + continue; + + storage = mail_namespace_get_default_storage(ns); + if (mail_storage_purge(storage) < 0) { + i_error("Purging namespace '%s' failed: %s", ns->prefix, + mail_storage_get_last_internal_error(storage, NULL)); + doveadm_mail_failed_storage(ctx, storage); + ret = -1; + } + } + return ret; +} + +static struct doveadm_mail_cmd_context *cmd_purge_alloc(void) +{ + struct doveadm_mail_cmd_context *ctx; + + ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context); + ctx->v.run = cmd_purge_run; + return ctx; +} + +static void doveadm_mail_cmd_input_input(struct doveadm_mail_cmd_context *ctx) +{ + const unsigned char *data; + size_t size; + + while (i_stream_read_more(ctx->cmd_input, &data, &size) > 0) + i_stream_skip(ctx->cmd_input, size); + if (!ctx->cmd_input->eof) + return; + + if (ctx->cmd_input->stream_errno != 0) { + i_error("read(%s) failed: %s", + i_stream_get_name(ctx->cmd_input), + i_stream_get_error(ctx->cmd_input)); + } + io_loop_stop(current_ioloop); +} + +static void doveadm_mail_cmd_input_timeout(struct doveadm_mail_cmd_context *ctx) +{ + struct istream *input; + + input = i_stream_create_error_str(ETIMEDOUT, "Timed out in %u secs", + DOVEADM_MAIL_CMD_INPUT_TIMEOUT_MSECS/1000); + i_stream_set_name(input, i_stream_get_name(ctx->cmd_input)); + i_stream_destroy(&ctx->cmd_input); + ctx->cmd_input = input; + ctx->exit_code = EX_TEMPFAIL; + io_loop_stop(current_ioloop); +} + +static void doveadm_mail_cmd_input_read(struct doveadm_mail_cmd_context *ctx) +{ + struct ioloop *ioloop; + struct io *io; + struct timeout *to; + + ioloop = io_loop_create(); + /* Read the pending input from stream. Delay adding the IO in case + we're reading from a file. That would cause a panic with epoll. */ + io_loop_set_running(ioloop); + doveadm_mail_cmd_input_input(ctx); + if (io_loop_is_running(ioloop)) { + io = io_add(ctx->cmd_input_fd, IO_READ, + doveadm_mail_cmd_input_input, ctx); + to = timeout_add(DOVEADM_MAIL_CMD_INPUT_TIMEOUT_MSECS, + doveadm_mail_cmd_input_timeout, ctx); + io_loop_run(ioloop); + io_remove(&io); + timeout_remove(&to); + } + io_loop_destroy(&ioloop); + + i_assert(ctx->cmd_input->eof); + i_stream_seek(ctx->cmd_input, 0); +} + +void doveadm_mail_get_input(struct doveadm_mail_cmd_context *ctx) +{ + const struct doveadm_cmd_context *cctx = ctx->cctx; + bool cli = (cctx->conn_type == DOVEADM_CONNECTION_TYPE_CLI); + struct istream *inputs[2]; + + if (ctx->cmd_input != NULL) + return; + + if (!cli && cctx->input == NULL) { + ctx->cmd_input = i_stream_create_error_str(EINVAL, "Input stream missing (provide with file parameter)"); + return; + } + + if (!cli) + inputs[0] = i_stream_create_dot(cctx->input, FALSE); + else { + inputs[0] = i_stream_create_fd(STDIN_FILENO, 1024*1024); + i_stream_set_name(inputs[0], "stdin"); + } + inputs[1] = NULL; + ctx->cmd_input_fd = i_stream_get_fd(inputs[0]); + ctx->cmd_input = i_stream_create_seekable_path(inputs, 1024*256, + "/tmp/doveadm."); + i_stream_set_name(ctx->cmd_input, i_stream_get_name(inputs[0])); + i_stream_unref(&inputs[0]); + + doveadm_mail_cmd_input_read(ctx); +} + +struct mailbox * +doveadm_mailbox_find(struct mail_user *user, const char *mailbox) +{ + struct mail_namespace *ns; + + if (!uni_utf8_str_is_valid(mailbox)) { + i_fatal_status(EX_DATAERR, + "Mailbox name not valid UTF-8: %s", mailbox); + } + + ns = mail_namespace_find(user->namespaces, mailbox); + return mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_IGNORE_ACLS); +} + +struct mail_search_args * +doveadm_mail_build_search_args(const char *const args[]) +{ + struct mail_search_parser *parser; + struct mail_search_args *sargs; + const char *error, *charset = "UTF-8"; + + parser = mail_search_parser_init_cmdline(args); + if (mail_search_build(mail_search_register_get_human(), + parser, &charset, &sargs, &error) < 0) + i_fatal("%s", error); + mail_search_parser_deinit(&parser); + return sargs; +} + +static int cmd_force_resync_box(struct doveadm_mail_cmd_context *_ctx, + const struct mailbox_info *info) +{ + struct force_resync_cmd_context *ctx = + (struct force_resync_cmd_context *)_ctx; + enum mailbox_flags flags = MAILBOX_FLAG_IGNORE_ACLS; + struct mailbox *box; + int ret = 0; + + if (ctx->fsck) + flags |= MAILBOX_FLAG_FSCK; + + box = mailbox_alloc(info->ns->list, info->vname, flags); + if (mailbox_open(box) < 0) { + i_error("Opening mailbox %s failed: %s", info->vname, + mailbox_get_last_internal_error(box, NULL)); + doveadm_mail_failed_mailbox(_ctx, box); + ret = -1; + } else if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FORCE_RESYNC | + MAILBOX_SYNC_FLAG_FIX_INCONSISTENT) < 0) { + i_error("Forcing a resync on mailbox %s failed: %s", + info->vname, mailbox_get_last_internal_error(box, NULL)); + doveadm_mail_failed_mailbox(_ctx, box); + ret = -1; + } + mailbox_free(&box); + return ret; +} + +static int cmd_force_resync_prerun(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, + struct mail_storage_service_user *service_user, + const char **error_r) +{ + if (mail_storage_service_user_set_setting(service_user, + "mailbox_list_index_very_dirty_syncs", + "no", + error_r) <= 0) + i_unreached(); + return 0; +} + +static int cmd_force_resync_run(struct doveadm_mail_cmd_context *ctx, + struct mail_user *user) +{ + const enum mailbox_list_iter_flags iter_flags = + MAILBOX_LIST_ITER_NO_AUTO_BOXES | + MAILBOX_LIST_ITER_RETURN_NO_FLAGS | + MAILBOX_LIST_ITER_STAR_WITHIN_NS; + const enum mail_namespace_type ns_mask = MAIL_NAMESPACE_TYPE_MASK_ALL; + struct mailbox_list_iterate_context *iter; + const struct mailbox_info *info; + int ret = 0; + + iter = mailbox_list_iter_init_namespaces(user->namespaces, ctx->args, + ns_mask, iter_flags); + while ((info = mailbox_list_iter_next(iter)) != NULL) { + if ((info->flags & (MAILBOX_NOSELECT | + MAILBOX_NONEXISTENT)) == 0) T_BEGIN { + if (cmd_force_resync_box(ctx, info) < 0) + ret = -1; + } T_END; + } + if (mailbox_list_iter_deinit(&iter) < 0) { + i_error("Listing mailboxes failed: %s", + mailbox_list_get_last_internal_error(user->namespaces->list, NULL)); + doveadm_mail_failed_list(ctx, user->namespaces->list); + 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 bool +cmd_force_resync_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) +{ + struct force_resync_cmd_context *ctx = + (struct force_resync_cmd_context *)_ctx; + + switch (c) { + case 'f': + ctx->fsck = TRUE; + break; + default: + return FALSE; + } + return TRUE; +} + +static struct doveadm_mail_cmd_context *cmd_force_resync_alloc(void) +{ + struct force_resync_cmd_context *ctx; + + ctx = doveadm_mail_cmd_alloc(struct force_resync_cmd_context); + ctx->ctx.getopt_args = "f"; + ctx->ctx.v.parse_arg = cmd_force_resync_parse_arg; + ctx->ctx.v.init = cmd_force_resync_init; + ctx->ctx.v.run = cmd_force_resync_run; + ctx->ctx.v.prerun = cmd_force_resync_prerun; + return &ctx->ctx; +} + +static void +doveadm_cctx_to_storage_service_input(const struct doveadm_cmd_context *cctx, + struct mail_storage_service_input *input_r) +{ + i_zero(input_r); + input_r->service = "doveadm"; + input_r->remote_ip = cctx->remote_ip; + input_r->remote_port = cctx->remote_port; + input_r->local_ip = cctx->local_ip; + input_r->local_port = cctx->local_port; + input_r->username = cctx->username; +} + +static int +doveadm_mail_next_user(struct doveadm_mail_cmd_context *ctx, + const char **error_r) +{ + const struct doveadm_cmd_context *cctx = ctx->cctx; + struct mail_storage_service_input input; + const char *error, *ip; + int ret; + + i_assert(cctx != NULL); + + ip = net_ip2addr(&cctx->remote_ip); + if (ip[0] == '\0') + i_set_failure_prefix("doveadm(%s): ", cctx->username); + else + i_set_failure_prefix("doveadm(%s,%s): ", ip, cctx->username); + doveadm_cctx_to_storage_service_input(cctx, &input); + if (ctx->cmd_input != NULL) + i_stream_seek(ctx->cmd_input, 0); + + /* 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; + + 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_unref(&ctx->cur_service_user); + return -1; + } + } + + ret = mail_storage_service_next(ctx->storage_service, + ctx->cur_service_user, + &ctx->cur_mail_user, error_r); + if (ret < 0) { + mail_storage_service_user_unref(&ctx->cur_service_user); + return ret; + } + + struct event_reason *reason = + event_reason_begin(event_reason_code_prefix("doveadm", "cmd_", + ctx->cmd->name)); + T_BEGIN { + if (ctx->v.run(ctx, ctx->cur_mail_user) < 0) { + i_assert(ctx->exit_code != 0); + } + } T_END; + mail_user_deinit(&ctx->cur_mail_user); + /* user deinit may still do some work, so finish the reason after it */ + event_reason_end(&reason); + + mail_storage_service_user_unref(&ctx->cur_service_user); + return 1; +} + +static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED) +{ + killed_signo = si->si_signo; +} + +int doveadm_mail_single_user(struct doveadm_mail_cmd_context *ctx, + const char **error_r) +{ + const struct doveadm_cmd_context *cctx = ctx->cctx; + + i_assert(cctx->username != NULL); + + doveadm_cctx_to_storage_service_input(cctx, &ctx->storage_service_input); + ctx->storage_service = mail_storage_service_init(master_service, NULL, + ctx->service_flags); + ctx->v.init(ctx, ctx->args); + if (hook_doveadm_mail_init != NULL) + hook_doveadm_mail_init(ctx); + + lib_signals_set_handler(SIGINT, 0, sig_die, NULL); + lib_signals_set_handler(SIGTERM, 0, sig_die, NULL); + + return doveadm_mail_next_user(ctx, error_r); +} + +static void +doveadm_mail_all_users(struct doveadm_mail_cmd_context *ctx, + const char *wildcard_user) +{ + struct doveadm_cmd_context *cctx = ctx->cctx; + unsigned int user_idx; + const char *ip, *user, *error; + int ret; + + ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; + + doveadm_cctx_to_storage_service_input(cctx, &ctx->storage_service_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, ctx->args); + + mail_storage_service_all_init_mask(ctx->storage_service, + wildcard_user != NULL ? wildcard_user : ""); + + 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; + } + cctx->username = user; + doveadm_print_sticky("username", user); + T_BEGIN { + ret = doveadm_mail_next_user(ctx, &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(&cctx->remote_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) +{ + if (ctx->users_list_input == NULL) + return mail_storage_service_all_next(ctx->storage_service, username_r); + + *username_r = i_stream_read_next_line(ctx->users_list_input); + if (ctx->users_list_input->stream_errno != 0) { + i_error("read(%s) failed: %s", + i_stream_get_name(ctx->users_list_input), + i_stream_get_error(ctx->users_list_input)); + return -1; + } + return *username_r != NULL ? 1 : 0; +} + +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 struct doveadm_mail_cmd_context * +doveadm_mail_cmdline_init(const struct doveadm_mail_cmd *cmd) +{ + struct doveadm_mail_cmd_context *ctx; + + ctx = doveadm_mail_cmd_init(cmd, doveadm_settings); + ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT; + if (doveadm_debug) + ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG; + return ctx; +} + +static void +doveadm_mail_cmd_exec(struct doveadm_mail_cmd_context *ctx, + const char *wildcard_user) +{ + const struct doveadm_cmd_context *cctx = ctx->cctx; + bool cli = (cctx->conn_type == DOVEADM_CONNECTION_TYPE_CLI); + int ret; + const char *error; + + 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 || ctx->add_username_header)) { + doveadm_print_header("username", "Username", + DOVEADM_PRINT_HEADER_FLAG_STICKY | + DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); + } + + if (ctx->iterate_single_user) { + if (cctx->username == NULL) + i_fatal_status(EX_USAGE, "USER environment is missing and -u option not used"); + if (!cli) { + /* we may access multiple users */ + ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP; + } + + if (ctx->add_username_header) + doveadm_print_sticky("username", cctx->username); + ret = doveadm_mail_single_user(ctx, &error); + if (ret < 0) { + /* user lookup/init failed somehow */ + doveadm_exit_code = EX_TEMPFAIL; + i_error("%s", error); + } else if (ret == 0) { + doveadm_exit_code = EX_NOUSER; + i_error("User doesn't exist"); + } + } else { + ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP; + doveadm_mail_all_users(ctx, wildcard_user); + } + doveadm_mail_server_flush(); + doveadm_mail_cmd_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; +} + +void doveadm_mail_cmd_deinit(struct doveadm_mail_cmd_context *ctx) +{ + ctx->v.deinit(ctx); + if (ctx->search_args != NULL) + mail_search_args_unref(&ctx->search_args); +} + +void doveadm_mail_cmd_free(struct doveadm_mail_cmd_context *ctx) +{ + i_stream_unref(&ctx->users_list_input); + i_stream_unref(&ctx->cmd_input); + pool_unref(&ctx->pool); +} + +void doveadm_mail_help(const struct doveadm_mail_cmd *cmd) +{ + fprintf(stderr, "doveadm %s "DOVEADM_CMD_MAIL_USAGE_PREFIX" %s\n", + cmd->name, cmd->usage_args == NULL ? "" : cmd->usage_args); + lib_exit(EX_USAGE); +} + +void doveadm_mail_try_help_name(const char *cmd_name) +{ + const struct doveadm_cmd_ver2 *cmd2; + + cmd2 = doveadm_cmd_find_ver2(cmd_name); + if (cmd2 != NULL) + help_ver2(cmd2); +} + +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_cmd_ver2 doveadm_cmd_force_resync_ver2 = { + .name = "force-resync", + .mail_cmd = cmd_force_resync_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "[-f] <mailbox mask>", +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAM('f', "fsck", CMD_PARAM_BOOL, 0) +DOVEADM_CMD_PARAM('\0', "mailbox-mask", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +}; + +static struct doveadm_cmd_ver2 doveadm_cmd_purge_ver2 = { + .name = "purge", + .mail_cmd = cmd_purge_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX, +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAMS_END +}; + +static struct doveadm_cmd_ver2 *mail_commands_ver2[] = { + &doveadm_cmd_batch, + &doveadm_cmd_dsync_backup, + &doveadm_cmd_dsync_mirror, + &doveadm_cmd_dsync_server, + &doveadm_cmd_mailbox_metadata_set_ver2, + &doveadm_cmd_mailbox_metadata_unset_ver2, + &doveadm_cmd_mailbox_metadata_get_ver2, + &doveadm_cmd_mailbox_metadata_list_ver2, + &doveadm_cmd_mailbox_status_ver2, + &doveadm_cmd_mailbox_list_ver2, + &doveadm_cmd_mailbox_create_ver2, + &doveadm_cmd_mailbox_delete_ver2, + &doveadm_cmd_mailbox_rename_ver2, + &doveadm_cmd_mailbox_subscribe_ver2, + &doveadm_cmd_mailbox_unsubscribe_ver2, + &doveadm_cmd_mailbox_update_ver2, + &doveadm_cmd_mailbox_path_ver2, + &doveadm_cmd_fetch_ver2, + &doveadm_cmd_save_ver2, + &doveadm_cmd_index_ver2, + &doveadm_cmd_altmove_ver2, + &doveadm_cmd_deduplicate_ver2, + &doveadm_cmd_expunge_ver2, + &doveadm_cmd_flags_add_ver2, + &doveadm_cmd_flags_remove_ver2, + &doveadm_cmd_flags_replace_ver2, + &doveadm_cmd_import_ver2, + &doveadm_cmd_force_resync_ver2, + &doveadm_cmd_purge_ver2, + &doveadm_cmd_search_ver2, + &doveadm_cmd_copy_ver2, + &doveadm_cmd_move_ver2, + &doveadm_cmd_mailbox_cache_decision, + &doveadm_cmd_mailbox_cache_remove, + &doveadm_cmd_mailbox_cache_purge, + &doveadm_cmd_rebuild_attachments, +}; + +void doveadm_mail_init(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(mail_commands_ver2); i++) + doveadm_cmd_register_ver2(mail_commands_ver2[i]); +} + +void doveadm_mail_init_finish(void) +{ + struct module_dir_load_settings mod_set; + + i_zero(&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); + /* keep mail_storage_init() referenced so that its _deinit() doesn't + try to free doveadm plugins' hooks too early. */ + mail_storage_init(); +} + +void doveadm_mail_deinit(void) +{ + mail_storage_deinit(); + module_dir_unload(&mail_storage_service_modules); +} + +static int doveadm_cmd_parse_arg(struct doveadm_mail_cmd_context *mctx, + const struct doveadm_cmd_param *arg, + ARRAY_TYPE(const_string) *full_args) +{ + const char *short_opt_str = + p_strdup_printf(mctx->pool, "-%c", arg->short_opt); + const char *arg_value = NULL; + + switch (arg->type) { + case CMD_PARAM_BOOL: + break; + case CMD_PARAM_INT64: + arg_value = dec2str(arg->value.v_int64); + break; + case CMD_PARAM_IP: + arg_value = net_ip2addr(&arg->value.v_ip); + break; + case CMD_PARAM_STR: + arg_value = arg->value.v_string; + break; + case CMD_PARAM_ARRAY: { + const char *str; + + array_foreach_elem(&arg->value.v_array, str) { + optarg = (char *)str; + if (!mctx->v.parse_arg(mctx, arg->short_opt)) + return -1; + array_push_back(full_args, &short_opt_str); + array_push_back(full_args, &str); + } + return 0; + } + default: + i_panic("Cannot convert parameter %s to short opt", arg->name); + } + + optarg = (char *)arg_value; + if (!mctx->v.parse_arg(mctx, arg->short_opt)) + return -1; + + array_push_back(full_args, &short_opt_str); + if (arg_value != NULL) + array_push_back(full_args, &arg_value); + return 0; +} + +void +doveadm_cmd_ver2_to_mail_cmd_wrapper(struct doveadm_cmd_context *cctx) +{ + struct doveadm_mail_cmd_context *mctx; + const char *wildcard_user; + const char *fieldstr; + ARRAY_TYPE(const_string) pargv, full_args; + int i; + bool cli = (cctx->conn_type == DOVEADM_CONNECTION_TYPE_CLI); + bool tcp_server = (cctx->conn_type == DOVEADM_CONNECTION_TYPE_TCP); + struct doveadm_mail_cmd mail_cmd = { + cctx->cmd->mail_cmd, cctx->cmd->name, cctx->cmd->usage + }; + + if (!cli) { + mctx = doveadm_mail_cmd_init(&mail_cmd, doveadm_settings); + /* doveadm-server always does userdb lookups */ + mctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; + } else { + mctx = doveadm_mail_cmdline_init(&mail_cmd); + } + mctx->cctx = cctx; + mctx->iterate_all_users = FALSE; + wildcard_user = NULL; + p_array_init(&full_args, mctx->pool, 8); + p_array_init(&pargv, mctx->pool, 8); + + for(i=0;i<cctx->argc;i++) { + const struct doveadm_cmd_param *arg = &cctx->argv[i]; + + if (!arg->value_set) + continue; + + if (strcmp(arg->name, "all-users") == 0) { + if (tcp_server) + mctx->add_username_header = TRUE; + else + mctx->iterate_all_users = arg->value.v_bool; + fieldstr = "-A"; + array_push_back(&full_args, &fieldstr); + } else if (strcmp(arg->name, "socket-path") == 0) { + doveadm_settings->doveadm_socket_path = arg->value.v_string; + if (doveadm_settings->doveadm_worker_count == 0) + doveadm_settings->doveadm_worker_count = 1; + } else if (strcmp(arg->name, "user") == 0) { + mctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; + if (!tcp_server) + cctx->username = arg->value.v_string; + + fieldstr = "-u"; + array_push_back(&full_args, &fieldstr); + array_push_back(&full_args, &arg->value.v_string); + if (strchr(arg->value.v_string, '*') != NULL || + strchr(arg->value.v_string, '?') != NULL) { + if (tcp_server) + mctx->add_username_header = TRUE; + else { + wildcard_user = arg->value.v_string; + cctx->username = NULL; + } + } + } else if (strcmp(arg->name, "user-file") == 0) { + mctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP; + wildcard_user = "*"; + mctx->users_list_input = arg->value.v_istream; + fieldstr = "-F"; + array_push_back(&full_args, &fieldstr); + fieldstr = ""; /* value doesn't really matter */ + array_push_back(&full_args, &fieldstr); + i_stream_ref(mctx->users_list_input); + } else if (strcmp(arg->name, "field") == 0 || + strcmp(arg->name, "flag") == 0) { + /* mailbox status, fetch, flags: convert an array into a + single space-separated parameter (alternative to + fieldstr) */ + fieldstr = p_array_const_string_join(mctx->pool, + &arg->value.v_array, " "); + array_push_back(&pargv, &fieldstr); + } else if (strcmp(arg->name, "file") == 0) { + /* input for doveadm_mail_get_input(), + used by e.g. save */ + if (mctx->cmd_input != NULL) { + i_error("Only one file input allowed: %s", arg->name); + doveadm_mail_cmd_free(mctx); + doveadm_exit_code = EX_USAGE; + return; + } + mctx->cmd_input = arg->value.v_istream; + i_stream_ref(mctx->cmd_input); + + } else if (strcmp(arg->name, "trans-flags") == 0) { + /* This parameter allows to set additional + * mailbox transaction flags. */ + mctx->transaction_flags = arg->value.v_int64; + + /* Keep all named special parameters above this line */ + + } else if (mctx->v.parse_arg != NULL && arg->short_opt != '\0') { + if (doveadm_cmd_parse_arg(mctx, arg, &full_args) < 0) { + i_error("Invalid parameter %c", arg->short_opt); + doveadm_mail_cmd_free(mctx); + doveadm_exit_code = EX_USAGE; + } + } else if ((arg->flags & CMD_PARAM_FLAG_POSITIONAL) != 0) { + /* feed this into pargv */ + if (arg->type == CMD_PARAM_ARRAY) + array_append_array(&pargv, &arg->value.v_array); + else if (arg->type == CMD_PARAM_STR) + array_push_back(&pargv, &arg->value.v_string); + } else { + doveadm_exit_code = EX_USAGE; + i_error("invalid parameter: %s", arg->name); + doveadm_mail_cmd_free(mctx); + return; + } + } + + const char *dashdash = "--"; + array_push_back(&full_args, &dashdash); + + array_append_zero(&pargv); + /* All the -parameters need to be included in full_args so that + they're sent to doveadm-server. */ + unsigned int args_pos = array_count(&full_args); + array_append_array(&full_args, &pargv); + + mctx->args = array_idx(&full_args, args_pos); + mctx->full_args = array_front(&full_args); + + doveadm_mail_cmd_exec(mctx, wildcard_user); + doveadm_mail_cmd_free(mctx); +} |