bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen/* There are several race conditions in this plugin, but they should be
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen happening pretty rarely and usually it's not a big problem if the results
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen are temporarily wrong. Fixing the races would likely be a lot of work,
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen so it's not really worth it. */
de9dea4ff806dcdd471fb340c41b0eb57a6b56deAki Tuomi MODULE_CONTEXT_REQUIRE(obj, expire_storage_module)
de9dea4ff806dcdd471fb340c41b0eb57a6b56deAki Tuomi MODULE_CONTEXT_REQUIRE(obj, expire_mail_user_module)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen union mailbox_transaction_module_context module_ctx;
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainenconst char *expire_plugin_version = DOVECOT_ABI_VERSION;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(expire_storage_module,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(expire_mail_module, &mail_module_register);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(expire_mail_user_module,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenexpire_mailbox_transaction_begin(struct mailbox *box,
de9dea4ff806dcdd471fb340c41b0eb57a6b56deAki Tuomi struct expire_mailbox *xpr_box = EXPIRE_CONTEXT_REQUIRE(box);
c6ce2e251ac75fa650c7fbfa52150eae69386293Martti Rannanjärvi t = xpr_box->module_ctx.super.transaction_begin(box, flags, reason);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen xt = i_new(struct expire_transaction_context, 1);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen MODULE_CONTEXT_SET(t, expire_storage_module, xt);
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainenstatic void first_save_timestamp(struct mailbox *box, time_t *stamp_r)
0dab9cb35a976c49b28a11e28d5570f5191f1a7aMartti Rannanjärvi t = mailbox_transaction_begin(box, 0, __func__);
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen /* find the first non-expunged mail. we're here because the first
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen mail was expunged, so don't bother checking it. */
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainenstatic uint32_t expire_get_ext_id(struct mailbox *box)
de9dea4ff806dcdd471fb340c41b0eb57a6b56deAki Tuomi struct expire_mailbox *xpr_box = EXPIRE_CONTEXT_REQUIRE(box);
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen sizeof(struct expire_mail_index_header), 0, 0);
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainenstatic int expire_lookup(struct mailbox *box, const char *key,
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen /* default to ioloop_time for newly saved mails. it may not be exactly
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen the first message's save time, but a few seconds difference doesn't
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen mail_index_get_header_ext(box->view, expire_get_ext_id(box),
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen /* preserve the original timestamp */
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen /* cache doesn't exist yet */
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen ret = dict_lookup(euser->db, pool_datastack_create(),
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainen i_error("expire: dict_lookup(%s) failed: %s", key, error);
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainenexpire_update(struct mailbox *box, const char *key, time_t timestamp)
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen if (dict_transaction_commit(&dctx, &error) < 0)
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen i_error("expire: dict commit failed: %s", error);
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen trans = mail_index_transaction_begin(box->view,
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen mail_index_update_header_ext(trans, expire_get_ext_id(box),
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen if (mail_index_transaction_commit(&trans) < 0)
b78bed724fad909bf84008a3eb0f35b5a4379c39Timo Sirainen i_error("expire: index transaction commit failed");
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainenstatic void first_nonexpunged_timestamp(struct mailbox_transaction_context *t,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* find the first non-expunged mail. we're here because the first
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen mail was expunged, so don't bother checking it. */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen for (seq = 2; seq <= hdr->messages_count; seq++) {
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen /* everything expunged */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenexpire_mailbox_transaction_commit(struct mailbox_transaction_context *t,
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen struct mail_transaction_commit_changes *changes_r)
bbd4c4cf902539c25c471157eb9849459734759cTimo Sirainen struct mail_user *user = t->box->storage->user;
de9dea4ff806dcdd471fb340c41b0eb57a6b56deAki Tuomi struct expire_mailbox *xpr_box = EXPIRE_CONTEXT_REQUIRE(t->box);
de9dea4ff806dcdd471fb340c41b0eb57a6b56deAki Tuomi struct expire_transaction_context *xt = EXPIRE_CONTEXT_REQUIRE(t);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* first mail expunged. dict needs updating. */
fd35227c47190afc832579ca5c76524792701bf7Timo Sirainen /* everything was expunged, but also within this
fd35227c47190afc832579ca5c76524792701bf7Timo Sirainen transaction a new message was saved */
2e263a9d901483a902720a30c474761bd3324fe8Timo Sirainen i_debug("expire: Expunging first message in %s, "
2e263a9d901483a902720a30c474761bd3324fe8Timo Sirainen "updating timestamp to %ld",
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen if (xpr_box->module_ctx.super.transaction_commit(t, changes_r) < 0) {
d66be2bebfa96e7d3d20e2153f60e6e25dcc9a18Timo Sirainen /* transaction is freed now */
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen if (xt->first_expunged || xt->saves) T_BEGIN {
fd35227c47190afc832579ca5c76524792701bf7Timo Sirainen /* new_stamp is already set */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen /* saved new mails. dict needs to be updated only if
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen this is the first mail in the database */
3e7565a7b39694bcdf448d8eb2a7f0774733297bTimo Sirainen /* first time saving here with expire enabled.
3e7565a7b39694bcdf448d8eb2a7f0774733297bTimo Sirainen also handle lookup errors by just assuming
3e7565a7b39694bcdf448d8eb2a7f0774733297bTimo Sirainen it didn't exist */
3e7565a7b39694bcdf448d8eb2a7f0774733297bTimo Sirainen "assuming update is needed");
bbd4c4cf902539c25c471157eb9849459734759cTimo Sirainen /* already exists */
2e263a9d901483a902720a30c474761bd3324fe8Timo Sirainen i_debug("expire: Saving first message to %s, "
2e263a9d901483a902720a30c474761bd3324fe8Timo Sirainen "updating timestamp to %ld",
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenexpire_mailbox_transaction_rollback(struct mailbox_transaction_context *t)
de9dea4ff806dcdd471fb340c41b0eb57a6b56deAki Tuomi struct expire_mailbox *xpr_box = EXPIRE_CONTEXT_REQUIRE(t->box);
de9dea4ff806dcdd471fb340c41b0eb57a6b56deAki Tuomi struct expire_transaction_context *xt = EXPIRE_CONTEXT_REQUIRE(t);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen xpr_box->module_ctx.super.transaction_rollback(t);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainenstatic void expire_mail_expunge(struct mail *_mail)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen union mail_module_context *xpr_mail = EXPIRE_MAIL_CONTEXT(mail);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* first mail expunged, database needs to be updated */
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainenstatic void expire_mail_allocated(struct mail *_mail)
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(_mail->box);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen xpr_mail = p_new(mail->pool, union mail_module_context, 1);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen MODULE_CONTEXT_SET_SELF(mail, expire_mail_module, xpr_mail);
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainenstatic int expire_save_finish(struct mail_save_context *ctx)
de9dea4ff806dcdd471fb340c41b0eb57a6b56deAki Tuomi struct expire_mailbox *xpr_box = EXPIRE_CONTEXT_REQUIRE(ctx->transaction->box);
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen return xpr_box->module_ctx.super.save_finish(ctx);
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainenexpire_copy(struct mail_save_context *ctx, struct mail *mail)
de9dea4ff806dcdd471fb340c41b0eb57a6b56deAki Tuomi struct expire_mailbox *xpr_box = EXPIRE_CONTEXT_REQUIRE(ctx->transaction->box);
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen return xpr_box->module_ctx.super.copy(ctx, mail);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic void expire_mailbox_allocate_init(struct mailbox *box)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen xpr_box = p_new(box->pool, struct expire_mailbox, 1);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->transaction_begin = expire_mailbox_transaction_begin;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->transaction_commit = expire_mailbox_transaction_commit;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->transaction_rollback = expire_mailbox_transaction_rollback;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen MODULE_CONTEXT_SET(box, expire_storage_module, xpr_box);
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainenstatic void expire_mailbox_allocated(struct mailbox *box)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen if (euser != NULL && expire_set_lookup(euser->set, box->vname))
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic void expire_mail_user_deinit(struct mail_user *user)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct expire_mail_user *euser = EXPIRE_USER_CONTEXT(user);
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainenstatic const char *const *expire_get_patterns(struct mail_user *user)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen unsigned int i;
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen str = mail_user_set_plugin_getenv(user->set, "expire");
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (i_snprintf(set_name, sizeof(set_name), "expire%u", i) < 0)
f6d57a2c182f63cd52819f0abb3c3d9f828afe19Timo Sirainen str = mail_user_set_plugin_getenv(user->set, set_name);
17ac264fe7efd383f10c67548cadcc5aca505b92Timo Sirainenstatic void expire_mail_user_created(struct mail_user *user)
71748cca1bacd74451fd228db5536828bdfeb190Baofeng Wang if (!mail_user_plugin_getenv_bool(user, "expire")) {
b87daa509bf5b306189282a9df795d094a6d7150Timo Sirainen i_debug("expire: No expire setting - plugin disabled");
d34a07d36865fa4438d6c6af8408376188343eb9Baofeng dict_uri = mail_user_plugin_getenv(user, "expire_dict");
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_error("expire plugin: expire_dict setting missing");
877912710a33c5645c85d6902a0ec6c79fcd0649Timo Sirainen /* we're using only shared dictionary, the username doesn't matter. */
20e04227229970d148801c507946666e2a9bd838Timo Sirainen if (dict_init(dict_uri, &dict_set, &db, &error) < 0) {
877912710a33c5645c85d6902a0ec6c79fcd0649Timo Sirainen i_error("expire plugin: dict_init(%s) failed: %s",
877912710a33c5645c85d6902a0ec6c79fcd0649Timo Sirainen euser = p_new(user->pool, struct expire_mail_user, 1);
877912710a33c5645c85d6902a0ec6c79fcd0649Timo Sirainen euser->set = expire_set_init(expire_get_patterns(user));
71748cca1bacd74451fd228db5536828bdfeb190Baofeng Wang euser->expire_cache = mail_user_plugin_getenv_bool(user, "expire_cache");
877912710a33c5645c85d6902a0ec6c79fcd0649Timo Sirainen MODULE_CONTEXT_SET(user, expire_mail_user_module, euser);
5666a3d6a7ea89362b8d9e8b39b15424cd9d6388Timo Sirainenstatic struct mail_storage_hooks expire_mail_storage_hooks = {
17ac264fe7efd383f10c67548cadcc5aca505b92Timo Sirainen .mail_user_created = expire_mail_user_created,
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen .mailbox_allocated = expire_mailbox_allocated,
5666a3d6a7ea89362b8d9e8b39b15424cd9d6388Timo Sirainen mail_storage_hooks_add(module, &expire_mail_storage_hooks);