doveadm-expire.c revision eca38954bcf972618f6b85932a3690acbd2b673a
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2005-2012 Dovecot authors, see the included COPYING file */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen MODULE_CONTEXT(obj, doveadm_expire_mail_cmd_module)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen union doveadm_mail_cmd_module_context module_ctx;
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen /* username => enum expire_user_state */
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainenconst char *doveadm_expire_plugin_version = DOVECOT_ABI_VERSION;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenvoid doveadm_expire_plugin_init(struct module *module);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(doveadm_expire_mail_cmd_module,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic void (*next_hook_doveadm_mail_init)(struct doveadm_mail_cmd_context *ctx);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainendoveadm_expire_mail_match_mailbox(struct doveadm_expire_mail_cmd_context *ectx,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (imap_match(query->glob, mailbox) == IMAP_MATCH_YES)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainendoveadm_expire_mail_want(struct doveadm_mail_cmd_context *ctx,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct doveadm_expire_mail_cmd_context *ectx =
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen /* dict_key = DICT_EXPIRE_PREFIX<user>/<mailbox> */
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen username = dict_key + strlen(DICT_EXPIRE_PREFIX);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* invalid record, ignore */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen username = t_strdup_until(username, mailbox++);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen if (!hash_table_lookup_full(ectx->user_states, username,
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen /* user no longer exists, delete the record */
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen state = POINTER_CAST_TO(value, enum expire_user_state);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* seen this user already, skip the record */
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen if (!doveadm_expire_mail_match_mailbox(ectx, mailbox,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* this mailbox doesn't have any matching messages */
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen hash_table_update(ectx->user_states, orig_username,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainendoveadm_expire_mail_cmd_get_next_user(struct doveadm_mail_cmd_context *ctx,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct doveadm_expire_mail_cmd_context *ectx =
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen while (dict_iterate(ectx->iter, &key, &value)) {
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen if (str_to_ulong(value, &oldest_savedate) < 0) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* invalid record */
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen i_error("expire: Invalid timestamp: %s", value);
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen if ((time_t)oldest_savedate > ectx->oldest_before_time) {
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen i_debug("expire: Stopping iteration on key %s "
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen if (ret < 0 && ectx->delete_nonexistent_users) {
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen /* user has been deleted */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* finished */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic const char *const *doveadm_expire_get_patterns(void)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const char *str;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen unsigned int i;
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (i_snprintf(set_name, sizeof(set_name), "expire%u", i) < 0)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainendoveadm_expire_get_or_mailboxes(struct doveadm_mail_cmd_context *ctx,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct doveadm_expire_mail_cmd_context *ectx =
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen for (arg = args; arg != NULL; arg = arg->next) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen query.glob = imap_match_init(ctx->pool, arg->value.str,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* fall through */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* require mailbox to be in expire patterns */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen query.mailbox = p_strdup(ctx->pool, arg->value.str);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* there are something else besides mailboxes,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen can't optimize this. */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainendoveadm_expire_analyze_and_query(struct doveadm_mail_cmd_context *ctx,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct doveadm_expire_mail_cmd_context *ectx =
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen for (arg = args; arg != NULL; arg = arg->next) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen query.glob = imap_match_init(ctx->pool, arg->value.str,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* fall through */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* require mailbox to be in expire patterns */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen query.mailbox = p_strdup(ctx->pool, arg->value.str);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_SAVED)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* no SAVEDBEFORE, can't optimize */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* one mailbox */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* no MAILBOX, but check if one of the ORs lists mailboxes */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen for (arg = args; arg != NULL; arg = arg->next) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen doveadm_expire_get_or_mailboxes(ctx, arg->value.subargs,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainendoveadm_expire_analyze_or_query(struct doveadm_mail_cmd_context *ctx,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* all of the subqueries must have mailbox and savedbefore */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen for (arg = args; arg != NULL; arg = arg->next) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (!doveadm_expire_analyze_and_query(ctx, arg->value.subargs))
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic bool doveadm_expire_analyze_query(struct doveadm_mail_cmd_context *ctx)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct doveadm_expire_mail_cmd_context *ectx =
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct mail_search_arg *args = ctx->search_args->args;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen unsigned int i, count;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* we support two kinds of queries:
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen 1) mailbox-pattern savedbefore <stamp> ...
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen 2) or 2*(mailbox-pattern savedbefore <stamp> ...)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen mailbox-pattern can be:
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen a) mailbox <name>
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen b) or 2*(mailbox <name>)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (!doveadm_expire_analyze_and_query(ctx, args) &&
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen (args->type != SEARCH_OR || args->next != NULL ||
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen !doveadm_expire_analyze_or_query(ctx, args->value.subargs))) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen i_debug("expire: Couldn't optimize search query");
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* make sure all mailboxes match expire patterns */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen set = expire_set_init(doveadm_expire_get_patterns());
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen for (i = 0; i < count; i++) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (!expire_set_lookup(set, queries[i].mailbox)) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen i_debug("expire: Couldn't optimize search query: "
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen "mailbox %s not in expire database",
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic void doveadm_expire_mail_cmd_deinit(struct doveadm_mail_cmd_context *ctx)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct doveadm_expire_mail_cmd_context *ectx =
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen i_error("expire: Dictionary iteration failed");
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (dict_transaction_commit(&ectx->trans) < 0)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic void doveadm_expire_mail_init(struct doveadm_mail_cmd_context *ctx)
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen const char *expire_dict, *username, *value, *error;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen expire_dict = doveadm_plugin_getenv("expire_dict");
db8770388432fcfecfd4a71b1a92d8ef5217a852Timo Sirainen i_debug("expire: Iterating only a single user, "
db8770388432fcfecfd4a71b1a92d8ef5217a852Timo Sirainen "ignoring expire database");
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx = p_new(ctx->pool, struct doveadm_expire_mail_cmd_context, 1);
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen value = doveadm_plugin_getenv("expire_keep_nonexistent_users");
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen MODULE_CONTEXT_SET(ctx, doveadm_expire_mail_cmd_module, ectx);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* we can potentially optimize this query. see if the search args
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen are valid for optimization. */
db8770388432fcfecfd4a71b1a92d8ef5217a852Timo Sirainen i_debug("expire: Searching only users listed in expire database");
10399559650f552a23949772be79eb6a80198c5aTimo Sirainen if (dict_init(expire_dict, DICT_DATA_TYPE_UINT32, "",
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen doveadm_settings->base_dir, &dict, &error) < 0) {
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen i_error("dict_init(%s) failed, not using it: %s",
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (ectx->oldest_before_time > query->before_time ||
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx->oldest_before_time = query->before_time;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ctx->v.deinit = doveadm_expire_mail_cmd_deinit;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ctx->v.get_next_user = doveadm_expire_mail_cmd_get_next_user;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_table_create(&ectx->user_states, ctx->pool, 0, str_hash, strcmp);
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen while (mail_storage_service_all_next(ctx->storage_service, &username) > 0) {
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen hash_table_insert(ectx->user_states, username_dup,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx->iter = dict_iterate_init(dict, DICT_EXPIRE_PREFIX,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenvoid doveadm_expire_plugin_init(struct module *module ATTR_UNUSED)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen next_hook_doveadm_mail_init = hook_doveadm_mail_init;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen hook_doveadm_mail_init = doveadm_expire_mail_init;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen i_assert(hook_doveadm_mail_init == doveadm_expire_mail_init);