Mercurial > dovecot > core-2.2
view src/plugins/expire/expire-tool.c @ 10552:40abbeaa7a12 HEAD
expire-tool: Don't crash at startup.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 06 Jan 2010 15:25:52 +0200 |
parents | 50364f04cf13 |
children | 615eef3139c2 |
line wrap: on
line source
/* Copyright (c) 2006-2009 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "env-util.h" #include "dict.h" #include "master-service.h" #include "master-service-settings.h" #include "mail-index.h" #include "mail-search-build.h" #include "mail-storage.h" #include "mail-storage-service.h" #include "mail-namespace.h" #include "auth-client.h" #include "auth-master.h" #include "expire-env.h" #include <stdlib.h> #include <time.h> struct expire_context { struct mail_storage_service_ctx *storage_service; struct mail_storage_service_user *service_user; struct mail_user *mail_user; struct expire_env *env; bool testrun; }; static int expire_init_user(struct expire_context *ctx, const char *user) { struct mail_storage_service_input input; const char *errstr; int ret; i_set_failure_prefix(t_strdup_printf("expire-tool(%s): ", user)); memset(&input, 0, sizeof(input)); input.service = "expire-tool"; input.username = user; ret = mail_storage_service_lookup_next(ctx->storage_service, &input, &ctx->service_user, &ctx->mail_user, &errstr); if (ret <= 0) { if (ret < 0 || ctx->testrun) i_error("%s", errstr); return ret; } if (mail_user_set_plugin_getenv(ctx->mail_user->set, "expire") == NULL) i_fatal("expire settings not set"); ctx->env = expire_env_init(ctx->mail_user->namespaces); return 1; } static void expire_deinit_user(struct expire_context *ctx) { mail_user_unref(&ctx->mail_user); mail_storage_service_user_free(&ctx->service_user); expire_env_deinit(&ctx->env); } static int mailbox_delete_old_mails(struct expire_context *ctx, const char *user, const char *mailbox, time_t *next_expire_r) { struct mail_namespace *ns; struct mailbox *box; struct mail_search_context *search_ctx; struct mailbox_transaction_context *t; struct mail_search_args *search_args; struct mail *mail; const char *ns_mailbox, *errstr; unsigned int expunge_secs, altmove_secs; time_t now, save_time; enum mail_error error; enum mail_flags flags; int ret; *next_expire_r = 0; if (ctx->mail_user != NULL && strcmp(user, ctx->mail_user->username) != 0) expire_deinit_user(ctx); if (ctx->mail_user == NULL) { if ((ret = expire_init_user(ctx, user)) <= 0) return ret; } if (!expire_rule_find(ctx->env, mailbox, &expunge_secs, &altmove_secs)) { /* we're no longer expunging old messages from here */ if (ctx->testrun) { i_info("%s: mailbox '%s' removed from config", user, mailbox); } return 0; } ns_mailbox = mailbox; ns = mail_namespace_find(ctx->mail_user->namespaces, &ns_mailbox); if (ns == NULL) { /* entire namespace no longer exists, remove the entry */ if (ctx->testrun) i_info("Namespace lookup failed: %s", mailbox); return 0; } box = mailbox_alloc(ns->list, ns_mailbox, NULL, 0); if (mailbox_open(box) < 0) { errstr = mail_storage_get_last_error(mailbox_get_storage(box), &error); mailbox_close(&box); if (error != MAIL_ERROR_NOTFOUND) { i_error("%s: Opening mailbox %s failed: %s", user, mailbox, errstr); return -1; } /* mailbox no longer exists, remove the entry */ return 0; } search_args = mail_search_build_init(); mail_search_build_add_all(search_args); t = mailbox_transaction_begin(box, 0); search_ctx = mailbox_search_init(t, search_args, NULL); mail_search_args_unref(&search_args); mail = mail_alloc(t, 0, NULL); now = time(NULL); ret = 0; while (mailbox_search_next(search_ctx, mail)) { if (mail_get_save_date(mail, &save_time) < 0) { /* maybe just got expunged. anyway try again later. */ if (ctx->testrun) { i_info("%s/%s: seq=%u uid=%u: " "Save date lookup failed", user, mailbox, mail->seq, mail->uid); } ret = -1; break; } if (save_time + (time_t)expunge_secs <= now && expunge_secs != 0) { if (!ctx->testrun) mail_expunge(mail); else { i_info("%s/%s: seq=%u uid=%u: Expunge", user, mailbox, mail->seq, mail->uid); } } else if (save_time + (time_t)altmove_secs <= now && altmove_secs != 0) { /* works only with dbox */ flags = mail_get_flags(mail); if ((flags & MAIL_INDEX_MAIL_FLAG_BACKEND) != 0) { /* alread moved */ } else if (!ctx->testrun) { mail_update_flags(mail, MODIFY_ADD, MAIL_INDEX_MAIL_FLAG_BACKEND); } else { i_info("%s/%s: seq=%u uid=%u: Move to alt dir", user, mailbox, mail->seq, mail->uid); } } else { /* first non-expired one. */ *next_expire_r = save_time + (altmove_secs != 0 ? altmove_secs : expunge_secs); break; } } mail_free(&mail); if (mailbox_search_deinit(&search_ctx) < 0) ret = -1; if (!ctx->testrun) { if (mailbox_transaction_commit(&t) < 0) ret = -1; } else { mailbox_transaction_rollback(&t); } if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FAST) < 0) ret = -1; mailbox_close(&box); return ret; } static void expire_run(struct master_service *service, bool testrun) { struct expire_context ctx; struct dict *dict = NULL; const struct mail_user_settings *user_set; struct mail_storage_service_input input; void **sets; struct dict_transaction_context *trans; struct dict_iterate_context *iter; time_t next_expire, expire_time; const char *p, *key, *value, *expire_dict; const char *userp = NULL, *mailbox; int ret; memset(&ctx, 0, sizeof(ctx)); ctx.storage_service = mail_storage_service_init(service, NULL, MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP); memset(&input, 0, sizeof(input)); input.module = input.service = "expire-tool"; mail_storage_service_init_settings(ctx.storage_service, &input); sets = master_service_settings_get_others(service); user_set = sets[0]; expire_dict = mail_user_set_plugin_getenv(user_set, "expire_dict"); if (expire_dict == NULL) i_fatal("expire_dict setting not set"); ctx.testrun = testrun; dict = dict_init(expire_dict, DICT_DATA_TYPE_UINT32, "", user_set->base_dir); if (dict == NULL) i_fatal("dict_init() failed"); trans = dict_transaction_begin(dict); iter = dict_iterate_init(dict, DICT_EXPIRE_PREFIX, DICT_ITERATE_FLAG_RECURSE | DICT_ITERATE_FLAG_SORT_BY_VALUE); /* We'll get the oldest values (timestamps) first */ while (dict_iterate(iter, &key, &value) > 0) { /* key = DICT_EXPIRE_PREFIX<user>/<mailbox> */ userp = key + strlen(DICT_EXPIRE_PREFIX); p = strchr(userp, '/'); if (p == NULL) { i_error("Expire dictionary contains invalid key: %s", key); continue; } mailbox = p + 1; expire_time = strtoul(value, NULL, 10); if (time(NULL) < expire_time) { /* this and the rest of the timestamps are in future, so stop processing */ if (testrun) { i_info("%s: stop, expire time in future: %s", userp, ctime(&expire_time)); } break; } T_BEGIN { const char *username; username = t_strdup_until(userp, p); ret = mailbox_delete_old_mails(&ctx, username, mailbox, &next_expire); } T_END; if (ret < 0) { /* failed to update */ } else if (next_expire == 0) { /* no more messages or mailbox deleted */ if (!testrun) dict_unset(trans, key); else i_info("%s: no messages left", userp); } else { char new_value[MAX_INT_STRLEN]; i_snprintf(new_value, sizeof(new_value), "%lu", (unsigned long)next_expire); if (strcmp(value, new_value) == 0) { /* no change */ } else if (!testrun) dict_set(trans, key, new_value); else T_BEGIN { i_info("%s: timestamp %s (%s) -> %s (%s)", userp, value, t_strcut(ctime(&expire_time), '\n'), new_value, t_strcut(ctime(&next_expire), '\n')); } T_END; } } if (testrun && userp == NULL) i_info("No entries in dictionary"); dict_iterate_deinit(&iter); if (!testrun) dict_transaction_commit(&trans); else dict_transaction_rollback(&trans); dict_deinit(&dict); if (ctx.mail_user != NULL) expire_deinit_user(&ctx); mail_storage_service_deinit(&ctx.storage_service); } int main(int argc, char *argv[]) { bool test = FALSE; int c; master_service = master_service_init("expire-tool", MASTER_SERVICE_FLAG_STANDALONE, &argc, &argv, "t"); while ((c = master_getopt(master_service)) > 0) { switch (c) { case 't': test = TRUE; break; default: return FATAL_DEFAULT; } } if (optind != argc) i_fatal("Unknown parameter: %s", argv[optind]); expire_run(master_service, test); master_service_deinit(&master_service); return 0; }