lazy-expunge-plugin.c revision 71748cca1bacd74451fd228db5536828bdfeb190
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2006-2016 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen#include "lib.h"
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#include "ioloop.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "array.h"
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen#include "str.h"
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen#include "hash.h"
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen#include "seq-range-array.h"
82fb1ead38f4e1197993adb2f9c12e26531e9c92Timo Sirainen#include "mkdir-parents.h"
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#include "mail-storage-private.h"
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#include "mail-search-build.h"
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen#include "mailbox-list-private.h"
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen#include "mail-namespace.h"
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#include "lazy-expunge-plugin.h"
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen#include <stdio.h>
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen#include <unistd.h>
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#include <dirent.h>
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen#include <time.h>
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define LAZY_EXPUNGE_CONTEXT(obj) \
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MODULE_CONTEXT(obj, lazy_expunge_mail_storage_module)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define LAZY_EXPUNGE_LIST_CONTEXT(obj) \
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MODULE_CONTEXT(obj, lazy_expunge_mailbox_list_module)
7fa573e6ea36024f618492e7d3649a69c1b41028Timo Sirainen#define LAZY_EXPUNGE_USER_CONTEXT(obj) \
7fa573e6ea36024f618492e7d3649a69c1b41028Timo Sirainen MODULE_CONTEXT(obj, lazy_expunge_mail_user_module)
7fa573e6ea36024f618492e7d3649a69c1b41028Timo Sirainen#define LAZY_EXPUNGE_MAIL_CONTEXT(obj) \
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainen MODULE_CONTEXT(obj, lazy_expunge_mail_module)
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainen
d99107ddf4d9bccb710994482daf65276a9d6321Timo Sirainenstruct lazy_expunge_mail_user {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen union mail_user_module_context module_ctx;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_namespace *lazy_ns;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *lazy_mailbox_vname;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *env;
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen bool copy_only_last_instance;
a05fec120ecd8c4ed6331c42100cba42adf22893Stephan Bosch};
a05fec120ecd8c4ed6331c42100cba42adf22893Stephan Bosch
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenstruct lazy_expunge_mailbox_list {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen union mailbox_list_module_context module_ctx;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen unsigned int allow_rename:1;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen unsigned int internal_namespace:1;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen};
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenstruct lazy_expunge_transaction {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen union mailbox_transaction_module_context module_ctx;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen struct mailbox *dest_box;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen struct mailbox_transaction_context *dest_trans;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen pool_t pool;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen HASH_TABLE(const char *, void *) guids;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen bool failed;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bool copy_only_last_instance;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen};
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenconst char *lazy_expunge_plugin_version = DOVECOT_ABI_VERSION;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mail_storage_module,
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainen &mail_storage_module_register);
009217abb57a24a4076092e8e4e165545747839eStephan Boschstatic MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mail_module,
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen &mail_module_register);
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mailbox_list_module,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen &mailbox_list_module_register);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mail_user_module,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen &mail_user_module_register);
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic const char *
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenget_dest_vname(struct mailbox_list *list, struct mailbox *src_box)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct lazy_expunge_mail_user *luser =
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen LAZY_EXPUNGE_USER_CONTEXT(list->ns->user);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *name;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen char src_sep, dest_sep;
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (luser->lazy_mailbox_vname != NULL)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen return luser->lazy_mailbox_vname;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen /* use the (canonical / unaliased) storage name */
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen name = src_box->name;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen /* replace hierarchy separators with destination virtual separator */
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen src_sep = mailbox_list_get_hierarchy_sep(src_box->list);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen dest_sep = mail_namespace_get_sep(list->ns);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (src_sep != dest_sep) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen string_t *str = t_str_new(128);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int i;
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen for (i = 0; name[i] != '\0'; i++) {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (name[i] == src_sep)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen str_append_c(str, dest_sep);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen else
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen str_append_c(str, name[i]);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen name = str_c(str);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen }
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen /* add expunge namespace prefix. the name is now a proper vname */
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen return t_strconcat(list->ns->prefix, name, NULL);
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen}
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainenstatic struct mailbox *
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenmailbox_open_or_create(struct mailbox_list *list, struct mailbox *src_box,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char **error_r)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mailbox *box;
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen enum mail_error error;
64e244defe74f513ce94f33d000a048ddbe2ea23Timo Sirainen const char *name;
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen
325d17cdbb7a338f7c413788f5e8e42d2e80a7f8Timo Sirainen name = get_dest_vname(list, src_box);
009217abb57a24a4076092e8e4e165545747839eStephan Bosch
009217abb57a24a4076092e8e4e165545747839eStephan Bosch box = mailbox_alloc(list, name, MAILBOX_FLAG_NO_INDEX_FILES |
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MAILBOX_FLAG_SAVEONLY | MAILBOX_FLAG_IGNORE_ACLS);
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen if (mailbox_open(box) == 0) {
f29756821a4c6b12b73e4a2a3e1c230117a43773Timo Sirainen *error_r = NULL;
8ce3071e80b9973230048ecadfcb073fb82cc69fTimo Sirainen return box;
bf72c930996df0691932fb1143f360d260f27a06Timo Sirainen }
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen *error_r = mailbox_get_last_error(box, &error);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (error != MAIL_ERROR_NOTFOUND) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *error_r = t_strdup_printf("Failed to open mailbox %s: %s",
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen name, *error_r);
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainen mailbox_free(&box);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return NULL;
bbba7d0fce1b6ce5baa2d7ef946eb1b63e2ab518Timo Sirainen }
bbba7d0fce1b6ce5baa2d7ef946eb1b63e2ab518Timo Sirainen
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen /* try creating and re-opening it. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mailbox_create(box, NULL, FALSE) < 0 ||
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mailbox_open(box) < 0) {
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen *error_r = t_strdup_printf("Failed to create mailbox %s: %s", name,
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen mailbox_get_last_error(box, NULL));
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen mailbox_free(&box);
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen return NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen return box;
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen}
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen
7a94f950fd1dcc81537acfc8adb030b5e703d722Timo Sirainenstatic unsigned int
aebfda1f6e3a924c31e8f54237b81fabf4b2264cTimo Sirainenlazy_expunge_count_in_transaction(struct lazy_expunge_transaction *lt,
2dfd08e8aa16dfcc975d8a62bc8d20b2ef849d71Timo Sirainen const char *guid)
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen{
38cb3d139aefb7c65919cf4aba5ded7b5fd50e6fTimo Sirainen void *refcountp;
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen unsigned int refcount;
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen
bf72c930996df0691932fb1143f360d260f27a06Timo Sirainen if (lt->pool == NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen lt->pool = pool_alloconly_create("lazy expunge transaction",
843640f0ca224bb9999acb290bca5f76037ab984Timo Sirainen 1024);
087939d3fa9c4056419386c9d6c81f147de534cdTimo Sirainen hash_table_create(&lt->guids, lt->pool, 0, str_hash, strcmp);
02ccba3d3be96444abd15b5254864c9151bbeb30Timo Sirainen }
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen refcountp = hash_table_lookup(lt->guids, guid);
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen refcount = POINTER_CAST_TO(refcountp, unsigned int) + 1;
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen refcountp = POINTER_CAST(refcount);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (refcount == 1) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen guid = p_strdup(lt->pool, guid);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen hash_table_insert(lt->guids, guid, refcountp);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen hash_table_update(lt->guids, guid, refcountp);
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen }
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen return refcount-1;
93c3674da4dc3d179abdf01b4a0d986d89397325Timo Sirainen}
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen
02ccba3d3be96444abd15b5254864c9151bbeb30Timo Sirainenstatic int lazy_expunge_mail_is_last_instace(struct mail *_mail)
3ffb7fd86484c474b42f3f1e981ab0f7168b5df9Timo Sirainen{
3ffb7fd86484c474b42f3f1e981ab0f7168b5df9Timo Sirainen struct lazy_expunge_transaction *lt =
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen LAZY_EXPUNGE_CONTEXT(_mail->transaction);
7c849dbc7be089175c1a83a84ee7249ed695810dTimo Sirainen const char *value, *errstr;
80980955bb1bbcc1bd73623fe0912f334194ddd2Timo Sirainen unsigned long refcount;
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen enum mail_error error;
6998ca95b4947c90647ac5d4794ebd6311acada2Timo Sirainen
ca843e046e98b12f4730f4b87ee2e1a659c26e78Timo Sirainen if (mail_get_special(_mail, MAIL_FETCH_REFCOUNT, &value) < 0) {
ca843e046e98b12f4730f4b87ee2e1a659c26e78Timo Sirainen errstr = mailbox_get_last_error(_mail->box, &error);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (error == MAIL_ERROR_EXPUNGED) {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen /* already expunged - just ignore it */
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen return 0;
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen }
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen mail_storage_set_critical(_mail->box->storage,
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen "lazy_expunge: Couldn't lookup message's refcount: %s",
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen errstr);
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen return -1;
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen }
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen if (*value == '\0') {
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen /* refcounts not supported by backend. assume all mails are
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen the last instance. */
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen return 1;
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen }
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen if (str_to_ulong(value, &refcount) < 0)
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen i_panic("Invalid mail refcount number: %s", value);
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen if (refcount > 1) {
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen /* this probably isn't the last instance of the mail, but
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen it's possible that the same mail was copied to this mailbox
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen multiple times and we're deleting more than one instance
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen within this transaction. in those cases each expunge will
a05fec120ecd8c4ed6331c42100cba42adf22893Stephan Bosch see the same refcount, so we need to adjust the refcount
f29756821a4c6b12b73e4a2a3e1c230117a43773Timo Sirainen by tracking the expunged message GUIDs. */
f29756821a4c6b12b73e4a2a3e1c230117a43773Timo Sirainen if (mail_get_special(_mail, MAIL_FETCH_GUID, &value) < 0) {
a05fec120ecd8c4ed6331c42100cba42adf22893Stephan Bosch mail_storage_set_critical(_mail->box->storage,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "lazy_expunge: Couldn't lookup message's GUID");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (*value == '\0') {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* GUIDs not supported by backend, but refcounts are?
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen not with our current backends. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mail_storage_set_critical(_mail->box->storage,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "lazy_expunge: Message unexpectedly has no GUID");
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen return -1;
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen }
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen refcount -= lazy_expunge_count_in_transaction(lt, value);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return refcount <= 1 ? 1 : 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic bool lazy_expunge_is_internal_mailbox(struct mailbox *box)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen struct mail_namespace *ns = box->list->ns;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen struct lazy_expunge_mail_user *luser =
6a9e034441607c0c5a61858ff559af4615ac31caTimo Sirainen LAZY_EXPUNGE_USER_CONTEXT(ns->user);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen struct lazy_expunge_mailbox_list *llist =
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen LAZY_EXPUNGE_LIST_CONTEXT(box->list);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (llist == NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* lazy_expunge not enabled at all */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return FALSE;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen }
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (llist->internal_namespace) {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen /* lazy-expunge namespace */
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen return TRUE;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen }
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (luser->lazy_mailbox_vname != NULL &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen strcmp(luser->lazy_mailbox_vname, box->vname) == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* lazy-expunge mailbox */
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen return TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen return FALSE;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void lazy_expunge_mail_expunge(struct mail *_mail)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen struct mail_namespace *ns = _mail->box->list->ns;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct lazy_expunge_mail_user *luser =
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen LAZY_EXPUNGE_USER_CONTEXT(ns->user);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen union mail_module_context *mmail = LAZY_EXPUNGE_MAIL_CONTEXT(mail);
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen struct lazy_expunge_transaction *lt =
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen LAZY_EXPUNGE_CONTEXT(_mail->transaction);
71f1783adc89b4fe3588c72b23e059b320da8fadTimo Sirainen struct mail *real_mail;
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen struct mail_save_context *save_ctx;
717bb0dbaf4bd3f745669570647845e6d493bfe0Timo Sirainen const char *error;
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen int ret;
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen
/* don't copy the mail if we're expunging from lazy_expunge
namespace (even if it's via a virtual mailbox) */
if (mail_get_backend_mail(_mail, &real_mail) < 0) {
lt->failed = TRUE;
return;
}
if (lazy_expunge_is_internal_mailbox(real_mail->box)) {
mmail->super.expunge(_mail);
return;
}
if (lt->copy_only_last_instance) {
/* we want to copy only the last instance of the mail to
lazy_expunge namespace. other instances will be expunged
immediately. */
if ((ret = lazy_expunge_mail_is_last_instace(_mail)) < 0) {
lt->failed = TRUE;
return;
}
if (ret == 0) {
mmail->super.expunge(_mail);
return;
}
}
if (lt->dest_box == NULL) {
lt->dest_box = mailbox_open_or_create(luser->lazy_ns->list,
_mail->box, &error);
if (lt->dest_box == NULL) {
mail_storage_set_critical(_mail->box->storage,
"lazy_expunge: Couldn't open expunge mailbox: "
"%s", error);
lt->failed = TRUE;
return;
}
if (mailbox_sync(lt->dest_box, 0) < 0) {
mail_storage_set_critical(_mail->box->storage,
"lazy_expunge: Couldn't sync expunge mailbox");
mailbox_free(&lt->dest_box);
lt->failed = TRUE;
return;
}
lt->dest_trans = mailbox_transaction_begin(lt->dest_box,
MAILBOX_TRANSACTION_FLAG_EXTERNAL);
}
save_ctx = mailbox_save_alloc(lt->dest_trans);
mailbox_save_copy_flags(save_ctx, _mail);
save_ctx->data.flags &= ~MAIL_DELETED;
if (mailbox_copy(&save_ctx, _mail) < 0 && !_mail->expunged)
lt->failed = TRUE;
mmail->super.expunge(_mail);
}
static struct mailbox_transaction_context *
lazy_expunge_transaction_begin(struct mailbox *box,
enum mailbox_transaction_flags flags)
{
struct lazy_expunge_mail_user *luser =
LAZY_EXPUNGE_USER_CONTEXT(box->list->ns->user);
union mailbox_module_context *mbox = LAZY_EXPUNGE_CONTEXT(box);
struct mailbox_transaction_context *t;
struct lazy_expunge_transaction *lt;
t = mbox->super.transaction_begin(box, flags);
lt = i_new(struct lazy_expunge_transaction, 1);
lt->copy_only_last_instance = luser->copy_only_last_instance;
MODULE_CONTEXT_SET(t, lazy_expunge_mail_storage_module, lt);
return t;
}
static void lazy_expunge_transaction_free(struct lazy_expunge_transaction *lt)
{
if (lt->dest_trans != NULL)
mailbox_transaction_rollback(&lt->dest_trans);
if (lt->dest_box != NULL)
mailbox_free(&lt->dest_box);
if (hash_table_is_created(lt->guids))
hash_table_destroy(&lt->guids);
if (lt->pool != NULL)
pool_unref(&lt->pool);
i_free(lt);
}
static int
lazy_expunge_transaction_commit(struct mailbox_transaction_context *ctx,
struct mail_transaction_commit_changes *changes_r)
{
union mailbox_module_context *mbox = LAZY_EXPUNGE_CONTEXT(ctx->box);
struct lazy_expunge_transaction *lt = LAZY_EXPUNGE_CONTEXT(ctx);
int ret;
if (lt->dest_trans != NULL && !lt->failed) {
if (mailbox_transaction_commit(&lt->dest_trans) < 0)
lt->failed = TRUE;
}
if (lt->failed) {
mbox->super.transaction_rollback(ctx);
ret = -1;
} else {
ret = mbox->super.transaction_commit(ctx, changes_r);
}
lazy_expunge_transaction_free(lt);
return ret;
}
static void
lazy_expunge_transaction_rollback(struct mailbox_transaction_context *ctx)
{
union mailbox_module_context *mbox = LAZY_EXPUNGE_CONTEXT(ctx->box);
struct lazy_expunge_transaction *lt = LAZY_EXPUNGE_CONTEXT(ctx);
mbox->super.transaction_rollback(ctx);
lazy_expunge_transaction_free(lt);
}
static void lazy_expunge_mail_allocated(struct mail *_mail)
{
struct lazy_expunge_transaction *lt =
LAZY_EXPUNGE_CONTEXT(_mail->transaction);
struct mail_private *mail = (struct mail_private *)_mail;
struct mail_vfuncs *v = mail->vlast;
union mail_module_context *mmail;
if (lt == NULL)
return;
mmail = p_new(mail->pool, union mail_module_context, 1);
mmail->super = *v;
mail->vlast = &mmail->super;
v->expunge = lazy_expunge_mail_expunge;
MODULE_CONTEXT_SET_SELF(mail, lazy_expunge_mail_module, mmail);
}
static int
lazy_expunge_mailbox_rename(struct mailbox *src, struct mailbox *dest)
{
union mailbox_module_context *lbox = LAZY_EXPUNGE_CONTEXT(src);
struct lazy_expunge_mailbox_list *src_llist =
LAZY_EXPUNGE_LIST_CONTEXT(src->list);
struct lazy_expunge_mailbox_list *dest_llist =
LAZY_EXPUNGE_LIST_CONTEXT(dest->list);
if (!src_llist->allow_rename &&
(src_llist->internal_namespace ||
dest_llist->internal_namespace)) {
mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE,
"Can't rename mailboxes to/from expunge namespace.");
return -1;
}
return lbox->super.rename_box(src, dest);
}
static void lazy_expunge_mailbox_allocated(struct mailbox *box)
{
struct lazy_expunge_mailbox_list *llist =
LAZY_EXPUNGE_LIST_CONTEXT(box->list);
union mailbox_module_context *mbox;
struct mailbox_vfuncs *v = box->vlast;
if (llist == NULL)
return;
mbox = p_new(box->pool, union mailbox_module_context, 1);
mbox->super = *v;
box->vlast = &mbox->super;
MODULE_CONTEXT_SET_SELF(box, lazy_expunge_mail_storage_module, mbox);
if (!lazy_expunge_is_internal_mailbox(box)) {
v->transaction_begin = lazy_expunge_transaction_begin;
v->transaction_commit = lazy_expunge_transaction_commit;
v->transaction_rollback = lazy_expunge_transaction_rollback;
v->rename_box = lazy_expunge_mailbox_rename;
} else if (llist->internal_namespace) {
v->rename_box = lazy_expunge_mailbox_rename;
} else {
/* internal mailbox in a non-internal namespace -
don't add any unnecessary restrictions to it. if it's not
wanted, just use the ACL plugin. */
}
}
static void lazy_expunge_mailbox_list_created(struct mailbox_list *list)
{
struct lazy_expunge_mail_user *luser =
LAZY_EXPUNGE_USER_CONTEXT(list->ns->user);
struct lazy_expunge_mailbox_list *llist;
if (luser == NULL)
return;
/* if this is one of our internal namespaces, mark it as such before
quota plugin sees it */
if (strcmp(list->ns->prefix, luser->env) == 0)
list->ns->flags |= NAMESPACE_FLAG_NOQUOTA;
if (list->ns->type == MAIL_NAMESPACE_TYPE_PRIVATE) {
llist = p_new(list->pool, struct lazy_expunge_mailbox_list, 1);
MODULE_CONTEXT_SET(list, lazy_expunge_mailbox_list_module,
llist);
}
}
static void
lazy_expunge_mail_namespaces_created(struct mail_namespace *namespaces)
{
struct lazy_expunge_mail_user *luser =
LAZY_EXPUNGE_USER_CONTEXT(namespaces->user);
struct lazy_expunge_mailbox_list *llist;
if (luser == NULL)
return;
luser->lazy_ns = mail_namespace_find_prefix(namespaces, luser->env);
if (luser->lazy_ns != NULL) {
/* we don't want to override this namespace's expunge operation. */
llist = LAZY_EXPUNGE_LIST_CONTEXT(luser->lazy_ns->list);
llist->internal_namespace = TRUE;
} else {
/* store the the expunged mails to the specified mailbox. */
luser->lazy_ns = mail_namespace_find(namespaces, luser->env);
luser->lazy_mailbox_vname = luser->env;
}
mail_namespace_ref(luser->lazy_ns);
}
static void lazy_expunge_user_deinit(struct mail_user *user)
{
struct lazy_expunge_mail_user *luser = LAZY_EXPUNGE_USER_CONTEXT(user);
/* mail_namespaces_created hook isn't necessarily ever called */
if (luser->lazy_ns != NULL)
mail_namespace_unref(&luser->lazy_ns);
luser->module_ctx.super.deinit(user);
}
static void lazy_expunge_mail_user_created(struct mail_user *user)
{
struct mail_user_vfuncs *v = user->vlast;
struct lazy_expunge_mail_user *luser;
const char *env;
env = mail_user_plugin_getenv(user, "lazy_expunge");
if (env != NULL && env[0] != '\0') {
luser = p_new(user->pool, struct lazy_expunge_mail_user, 1);
luser->module_ctx.super = *v;
user->vlast = &luser->module_ctx.super;
v->deinit = lazy_expunge_user_deinit;
luser->env = env;
luser->copy_only_last_instance =
mail_user_plugin_getenv_bool(user, "lazy_expunge_only_last_instance");
MODULE_CONTEXT_SET(user, lazy_expunge_mail_user_module, luser);
} else if (user->mail_debug) {
i_debug("lazy_expunge: No lazy_expunge setting - "
"plugin disabled");
}
}
static struct mail_storage_hooks lazy_expunge_mail_storage_hooks = {
.mail_user_created = lazy_expunge_mail_user_created,
.mail_namespaces_created = lazy_expunge_mail_namespaces_created,
.mailbox_list_created = lazy_expunge_mailbox_list_created,
.mailbox_allocated = lazy_expunge_mailbox_allocated,
.mail_allocated = lazy_expunge_mail_allocated
};
void lazy_expunge_plugin_init(struct module *module)
{
mail_storage_hooks_add(module, &lazy_expunge_mail_storage_hooks);
}
void lazy_expunge_plugin_deinit(void)
{
mail_storage_hooks_remove(&lazy_expunge_mail_storage_hooks);
}