doveadm-expire.c revision 6264b51bcce8ae98efdcda3e55a765d7a13d15ed
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2005-2016 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"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "doveadm-settings.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
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainenenum expire_user_state {
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen EXPIRE_USER_STATE_NONEXISTENT = 0,
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen EXPIRE_USER_STATE_EXISTS = 1,
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen EXPIRE_USER_STATE_SEEN = 2
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen};
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo 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
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen /* username => enum expire_user_state */
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen HASH_TABLE(char *, void *) user_states;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen ARRAY(struct expire_query) queries;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen time_t oldest_before_time;
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen bool delete_nonexistent_users;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen};
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainenconst char *doveadm_expire_plugin_version = DOVECOT_ABI_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
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainenstatic int
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainendoveadm_expire_mail_want(struct doveadm_mail_cmd_context *ctx,
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen const char *dict_key, time_t oldest_savedate,
88339cce70862ea6c6fac5615975e1e94a52a72eTimo 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 *username, *mailbox;
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen enum expire_user_state state;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen char *orig_username;
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen void *value;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen /* dict_key = DICT_EXPIRE_PREFIX<user>/<mailbox> */
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen username = dict_key + strlen(DICT_EXPIRE_PREFIX);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen mailbox = strchr(username, '/');
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (mailbox == NULL) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* invalid record, ignore */
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen i_error("expire: Invalid key: %s", dict_key);
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen return -1;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen username = t_strdup_until(username, mailbox++);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen if (!hash_table_lookup_full(ectx->user_states, username,
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen &orig_username, &value)) {
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen /* user no longer exists, delete the record */
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen return -1;
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen }
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen state = POINTER_CAST_TO(value, enum expire_user_state);
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen switch (state) {
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen case EXPIRE_USER_STATE_NONEXISTENT:
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen i_unreached();
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen case EXPIRE_USER_STATE_EXISTS:
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen break;
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen case EXPIRE_USER_STATE_SEEN:
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* seen this user already, skip the record */
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen return 0;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen if (!doveadm_expire_mail_match_mailbox(ectx, mailbox,
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen oldest_savedate)) {
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* this mailbox doesn't have any matching messages */
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen return 0;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen state = EXPIRE_USER_STATE_SEEN;
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen hash_table_update(ectx->user_states, orig_username,
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen POINTER_CAST(state));
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen *username_r = orig_username;
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen return 1;
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);
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen const char *key, *value, *error;
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen unsigned long oldest_savedate;
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen int ret;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
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);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen continue;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen if ((time_t)oldest_savedate > ectx->oldest_before_time) {
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen if (doveadm_debug) {
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen i_debug("expire: Stopping iteration on key %s "
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen "(%lu > %ld)", key, oldest_savedate,
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen (long)ectx->oldest_before_time);
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen break;
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen T_BEGIN {
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen ret = doveadm_expire_mail_want(ctx, key,
88339cce70862ea6c6fac5615975e1e94a52a72eTimo Sirainen oldest_savedate,
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen username_r);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen } T_END;
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen if (ret > 0)
d9a7e950a9cd21f2b4a90ec7759fca9e8fcc7995Timo Sirainen return 1;
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen if (ret < 0 && ectx->delete_nonexistent_users) {
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen /* user has been deleted */
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen dict_unset(ectx->trans, key);
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen /* finished */
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen if (dict_iterate_deinit(&ectx->iter, &error) < 0) {
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen i_error("Dictionary iteration failed: %s", error);
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;
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen char set_name[6+MAX_INT_STRLEN+1];
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
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (i_snprintf(set_name, sizeof(set_name), "expire%u", i) < 0)
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen i_unreached();
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen str = doveadm_plugin_getenv(set_name);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen array_append_zero(&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 &
6264b51bcce8ae98efdcda3e55a765d7a13d15edTimo Sirainen MAIL_SEARCH_ARG_FLAG_UTC_TIMES) == 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
d9a7e950a9cd21f2b4a90ec7759fca9e8fcc7995Timo Sirainenstatic bool
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
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen i_assert(args != NULL);
35283613d4c04ce18836e9fc431582c87b3710a0Timo 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);
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen const char *error;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (ectx->iter != NULL) {
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen if (dict_iterate_deinit(&ectx->iter, &error) < 0)
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen i_error("expire: Dictionary iteration failed: %s", error);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen }
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen if (dict_transaction_commit(&ectx->trans, &error) < 0)
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen i_error("expire: Dictionary commit failed: %s", error);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen dict_deinit(&ectx->dict);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_table_destroy(&ectx->user_states);
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;
20e04227229970d148801c507946666e2a9bd838Timo Sirainen struct dict_settings dict_set;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen struct dict *dict;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen const struct expire_query *query;
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen const char *expire_dict, *username, *value, *error;
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen char *username_dup;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen enum expire_user_state state;
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
a8281b7c770f4a9a842b19303083fc7f6859e756Timo Sirainen /* doveadm proxying uses expire database only locally. the remote
a8281b7c770f4a9a842b19303083fc7f6859e756Timo Sirainen doveadm handles each user one at a time (even though
a8281b7c770f4a9a842b19303083fc7f6859e756Timo Sirainen iterate_single_user=FALSE) */
a8281b7c770f4a9a842b19303083fc7f6859e756Timo Sirainen if (ctx->iterate_single_user || ctx->proxying) {
db8770388432fcfecfd4a71b1a92d8ef5217a852Timo Sirainen if (doveadm_debug) {
db8770388432fcfecfd4a71b1a92d8ef5217a852Timo Sirainen i_debug("expire: Iterating only a single user, "
db8770388432fcfecfd4a71b1a92d8ef5217a852Timo Sirainen "ignoring expire database");
db8770388432fcfecfd4a71b1a92d8ef5217a852Timo Sirainen }
db8770388432fcfecfd4a71b1a92d8ef5217a852Timo Sirainen return;
db8770388432fcfecfd4a71b1a92d8ef5217a852Timo Sirainen }
db8770388432fcfecfd4a71b1a92d8ef5217a852Timo Sirainen
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx = p_new(ctx->pool, struct doveadm_expire_mail_cmd_context, 1);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen ectx->module_ctx.super = ctx->v;
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen value = doveadm_plugin_getenv("expire_keep_nonexistent_users");
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen ectx->delete_nonexistent_users =
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen value == NULL || strcmp(value, "yes") != 0;
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;
db8770388432fcfecfd4a71b1a92d8ef5217a852Timo Sirainen
db8770388432fcfecfd4a71b1a92d8ef5217a852Timo Sirainen if (doveadm_debug)
db8770388432fcfecfd4a71b1a92d8ef5217a852Timo Sirainen i_debug("expire: Searching only users listed in expire database");
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen
20e04227229970d148801c507946666e2a9bd838Timo Sirainen memset(&dict_set, 0, sizeof(dict_set));
20e04227229970d148801c507946666e2a9bd838Timo Sirainen dict_set.value_type = DICT_DATA_TYPE_UINT32;
20e04227229970d148801c507946666e2a9bd838Timo Sirainen dict_set.username = "";
20e04227229970d148801c507946666e2a9bd838Timo Sirainen dict_set.base_dir = doveadm_settings->base_dir;
20e04227229970d148801c507946666e2a9bd838Timo Sirainen if (dict_init(expire_dict, &dict_set, &dict, &error) < 0) {
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen i_error("dict_init(%s) failed, not using it: %s",
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen expire_dict, error);
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
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) {
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen username_dup = p_strdup(ctx->pool, username);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen state = EXPIRE_USER_STATE_EXISTS;
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen hash_table_insert(ectx->user_states, username_dup,
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen POINTER_CAST(state));
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen }
3c3002ee03e2c20034f73cfec80c6647320bd27cTimo Sirainen
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}