doveadm-expire.c revision b26a773eaa28e3f77c7b760cb4570aa18107a465
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen#include "lib.h"
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen#include "module-dir.h"
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen#include "str.h"
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen#include "hash.h"
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen#include "dict.h"
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen#include "imap-match.h"
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen#include "expire-set.h"
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen#include "mail-search.h"
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen#include "doveadm-mail.h"
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen#define DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(obj) \
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen MODULE_CONTEXT(obj, doveadm_expire_mail_cmd_module)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstruct expire_query {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const char *mailbox;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct imap_match_glob *glob;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen time_t before_time;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen};
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstruct doveadm_expire_mail_cmd_context {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen union doveadm_mail_cmd_module_context module_ctx;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct dict *dict;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct dict_transaction_context *trans;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct dict_iterate_context *iter;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct hash_table *seen_users;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ARRAY_DEFINE(queries, struct expire_query);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen time_t oldest_before_time;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen};
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenconst char *doveadm_expire_plugin_version = DOVECOT_VERSION;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenvoid doveadm_expire_plugin_init(struct module *module);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenvoid doveadm_expire_plugin_deinit(void);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(doveadm_expire_mail_cmd_module,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen &doveadm_mail_cmd_module_register);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic void (*next_hook_doveadm_mail_init)(struct doveadm_mail_cmd_context *ctx);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic bool
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainendoveadm_expire_mail_match_mailbox(struct doveadm_expire_mail_cmd_context *ectx,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const char *mailbox, time_t oldest_savedate)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen{
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const struct expire_query *query;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen array_foreach(&ectx->queries, query) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (oldest_savedate >= query->before_time)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen continue;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (query->glob == NULL) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (strcmp(query->mailbox, mailbox) == 0)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return TRUE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen } else {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (imap_match(query->glob, mailbox) == IMAP_MATCH_YES)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return TRUE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return FALSE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen}
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic bool
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainendoveadm_expire_mail_want(struct doveadm_mail_cmd_context *ctx,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const char *key, time_t stamp, const char **username_r)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen{
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct doveadm_expire_mail_cmd_context *ectx =
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const char *username, *mailbox;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen char *username_dup;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* key = DICT_EXPIRE_PREFIX<user>/<mailbox> */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen username = key + strlen(DICT_EXPIRE_PREFIX);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen mailbox = strchr(username, '/');
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (mailbox == NULL) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* invalid record, ignore */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return FALSE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen username = t_strdup_until(username, mailbox++);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (hash_table_lookup(ectx->seen_users, username) != NULL) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* seen this user already, skip the record */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return FALSE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (!doveadm_expire_mail_match_mailbox(ectx, mailbox, stamp)) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* this mailbox doesn't have any matching messages */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return FALSE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen username_dup = p_strdup(ctx->pool, username);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen hash_table_insert(ectx->seen_users, username_dup, username_dup);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen *username_r = username_dup;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return TRUE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen}
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic int
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainendoveadm_expire_mail_cmd_get_next_user(struct doveadm_mail_cmd_context *ctx,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const char **username_r)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen{
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct doveadm_expire_mail_cmd_context *ectx =
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const char *key, *value;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen unsigned long stamp;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen bool ret;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen while (dict_iterate(ectx->iter, &key, &value)) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (str_to_ulong(value, &stamp) < 0) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* invalid record */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen continue;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if ((time_t)stamp > ectx->oldest_before_time)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen break;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen T_BEGIN {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ret = doveadm_expire_mail_want(ctx, key, stamp,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen username_r);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen } T_END;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (ret)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return TRUE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* finished */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (dict_iterate_deinit(&ectx->iter) < 0) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen i_error("Dictionary iteration failed");
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return -1;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return 0;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen}
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic const char *const *doveadm_expire_get_patterns(void)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen{
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ARRAY_TYPE(const_string) patterns;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const char *str;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen char set_name[20];
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen unsigned int i;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen t_array_init(&patterns, 16);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen str = doveadm_plugin_getenv("expire");
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen for (i = 2; str != NULL; i++) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen array_append(&patterns, &str, 1);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen i_snprintf(set_name, sizeof(set_name), "expire%u", i);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen str = doveadm_plugin_getenv(set_name);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen (void)array_append_space(&patterns);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return array_idx(&patterns, 0);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen}
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic bool
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainendoveadm_expire_get_or_mailboxes(struct doveadm_mail_cmd_context *ctx,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const struct mail_search_arg *args,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct expire_query query)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen{
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct doveadm_expire_mail_cmd_context *ectx =
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const struct mail_search_arg *arg;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen unsigned int query_count;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen query.mailbox = NULL;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen query_count = array_count(&ectx->queries);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen for (arg = args; arg != NULL; arg = arg->next) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen switch (arg->type) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen case SEARCH_MAILBOX_GLOB:
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen query.glob = imap_match_init(ctx->pool, arg->value.str,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen TRUE, '/');
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* fall through */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen case SEARCH_MAILBOX:
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* require mailbox to be in expire patterns */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen query.mailbox = p_strdup(ctx->pool, arg->value.str);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen array_append(&ectx->queries, &query, 1);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen break;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen default:
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* there are something else besides mailboxes,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen can't optimize this. */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen array_delete(&ectx->queries, query_count,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen array_count(&ectx->queries) - query_count);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return FALSE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return query.mailbox != NULL;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen}
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic bool
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainendoveadm_expire_analyze_and_query(struct doveadm_mail_cmd_context *ctx,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const struct mail_search_arg *args)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen{
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct doveadm_expire_mail_cmd_context *ectx =
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const struct mail_search_arg *arg;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct expire_query query;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen bool have_or = FALSE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen memset(&query, 0, sizeof(query));
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen query.before_time = (time_t)-1;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen for (arg = args; arg != NULL; arg = arg->next) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen switch (arg->type) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen case SEARCH_OR:
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen have_or = TRUE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen break;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen case SEARCH_MAILBOX_GLOB:
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen query.glob = imap_match_init(ctx->pool, arg->value.str,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen TRUE, '/');
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* fall through */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen case SEARCH_MAILBOX:
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* require mailbox to be in expire patterns */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen query.mailbox = p_strdup(ctx->pool, arg->value.str);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen break;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen case SEARCH_BEFORE:
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_SAVED)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen break;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if ((arg->value.search_flags &
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen MAIL_SEARCH_ARG_FLAG_USE_TZ) == 0)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen break;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen query.before_time = arg->value.time;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen break;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen default:
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen break;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (query.before_time == (time_t)-1) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* no SAVEDBEFORE, can't optimize */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return FALSE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (query.mailbox != NULL) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* one mailbox */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen array_append(&ectx->queries, &query, 1);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return TRUE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* no MAILBOX, but check if one of the ORs lists mailboxes */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (!have_or)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return FALSE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen for (arg = args; arg != NULL; arg = arg->next) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (arg->type == SEARCH_OR &&
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen doveadm_expire_get_or_mailboxes(ctx, arg->value.subargs,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen query))
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return TRUE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return FALSE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen}
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic time_t
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainendoveadm_expire_analyze_or_query(struct doveadm_mail_cmd_context *ctx,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const struct mail_search_arg *args)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen{
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const struct mail_search_arg *arg;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* all of the subqueries must have mailbox and savedbefore */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen for (arg = args; arg != NULL; arg = arg->next) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (arg->type != SEARCH_SUB)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return FALSE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (!doveadm_expire_analyze_and_query(ctx, arg->value.subargs))
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return FALSE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return TRUE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen}
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic bool doveadm_expire_analyze_query(struct doveadm_mail_cmd_context *ctx)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen{
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct doveadm_expire_mail_cmd_context *ectx =
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct mail_search_arg *args = ctx->search_args->args;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct expire_set *set;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const struct expire_query *queries;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen unsigned int i, count;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* we support two kinds of queries:
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen 1) mailbox-pattern savedbefore <stamp> ...
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen 2) or 2*(mailbox-pattern savedbefore <stamp> ...)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen mailbox-pattern can be:
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen a) mailbox <name>
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen b) or 2*(mailbox <name>)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen p_array_init(&ectx->queries, ctx->pool, 8);
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 if (doveadm_debug)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen i_debug("expire: Couldn't optimize search query");
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return FALSE;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* make sure all mailboxes match expire patterns */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen set = expire_set_init(doveadm_expire_get_patterns());
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen queries = array_get(&ectx->queries, &count);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen for (i = 0; i < count; i++) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (!expire_set_lookup(set, queries[i].mailbox)) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (doveadm_debug) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen i_debug("expire: Couldn't optimize search query: "
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen "mailbox %s not in expire database",
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen queries[i].mailbox);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen break;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen expire_set_deinit(&set);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return i == count;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen}
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic void doveadm_expire_mail_cmd_deinit(struct doveadm_mail_cmd_context *ctx)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen{
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct doveadm_expire_mail_cmd_context *ectx =
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (ectx->iter != NULL) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (dict_iterate_deinit(&ectx->iter) < 0)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen i_error("Dictionary iteration failed");
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen dict_transaction_commit(&ectx->trans);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen dict_deinit(&ectx->dict);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen hash_table_destroy(&ectx->seen_users);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
b26a773eaa28e3f77c7b760cb4570aa18107a465Timo Sirainen ectx->module_ctx.super.deinit(ctx);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen}
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic void doveadm_expire_mail_init(struct doveadm_mail_cmd_context *ctx)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen{
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct doveadm_expire_mail_cmd_context *ectx;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct dict *dict;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const struct expire_query *query;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const char *expire_dict;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (ctx->search_args == NULL)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen expire_dict = doveadm_plugin_getenv("expire_dict");
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (expire_dict == NULL)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx = p_new(ctx->pool, struct doveadm_expire_mail_cmd_context, 1);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx->module_ctx.super = ctx->v;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen MODULE_CONTEXT_SET(ctx, doveadm_expire_mail_cmd_module, ectx);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* we can potentially optimize this query. see if the search args
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen are valid for optimization. */
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (!doveadm_expire_analyze_query(ctx))
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen i_debug("expire: Searching only users listed in expire database");
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen dict = dict_init(expire_dict, DICT_DATA_TYPE_UINT32, "",
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen doveadm_settings->base_dir);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (dict == NULL) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen i_error("dict_init(%s) failed, not using it", expire_dict);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen return;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx->oldest_before_time = (time_t)-1;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen array_foreach(&ectx->queries, query) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (ectx->oldest_before_time > query->before_time ||
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx->oldest_before_time == (time_t)-1)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx->oldest_before_time = query->before_time;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ctx->v.deinit = doveadm_expire_mail_cmd_deinit;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ctx->v.get_next_user = doveadm_expire_mail_cmd_get_next_user;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx->seen_users =
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen hash_table_create(default_pool, ctx->pool, 0,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen str_hash, (hash_cmp_callback_t *)strcmp);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx->dict = dict;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx->trans = dict_transaction_begin(dict);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx->iter = dict_iterate_init(dict, DICT_EXPIRE_PREFIX,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen DICT_ITERATE_FLAG_RECURSE |
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen DICT_ITERATE_FLAG_SORT_BY_VALUE);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen}
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenvoid doveadm_expire_plugin_init(struct module *module ATTR_UNUSED)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen{
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen next_hook_doveadm_mail_init = hook_doveadm_mail_init;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen hook_doveadm_mail_init = doveadm_expire_mail_init;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen}
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenvoid doveadm_expire_plugin_deinit(void)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen{
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen i_assert(hook_doveadm_mail_init == doveadm_expire_mail_init);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen hook_doveadm_mail_init = next_hook_doveadm_mail_init;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen}