lazy-expunge-plugin.c revision 462a3d92adcde4bfa9a575875bd8ae740b89ce9e
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
c659a04853561c3116d88e04931a181bc13559acMichael Bergknoff MODULE_CONTEXT(obj, lazy_expunge_mail_storage_module)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki MODULE_CONTEXT_REQUIRE(obj, lazy_expunge_mail_storage_module)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki const char *env;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkiconst char *lazy_expunge_plugin_version = DOVECOT_ABI_VERSION;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mail_storage_module,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mail_module,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mailbox_list_module,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mail_user_module,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic const char *
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkiget_dest_vname(struct mailbox_list *list, struct mailbox *src_box)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki const char *name;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* use the (canonical / unaliased) storage name */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* replace hierarchy separators with destination virtual separator */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki src_sep = mailbox_list_get_hierarchy_sep(src_box->list);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki unsigned int i;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* add expunge namespace prefix. the name is now a proper vname */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic struct mailbox *
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkimailbox_open_or_create(struct mailbox_list *list, struct mailbox *src_box,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki const char **error_r)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki const char *name;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki box = mailbox_alloc(list, name, MAILBOX_FLAG_NO_INDEX_FILES |
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki *error_r = mailbox_get_last_internal_error(box, &error);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki *error_r = t_strdup_printf("Failed to open mailbox %s: %s",
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* try creating and re-opening it. */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki mailbox_get_last_mail_error(box) != MAIL_ERROR_EXISTS) {
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki *error_r = t_strdup_printf("Failed to create mailbox %s: %s", name,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki *error_r = t_strdup_printf("Failed to open created mailbox %s: %s", name,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic unsigned int
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkilazy_expunge_count_in_transaction(struct lazy_expunge_transaction *lt,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki const char *guid)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki unsigned int refcount;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki lt->pool = pool_alloconly_create("lazy expunge transaction",
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki hash_table_create(<->guids, lt->pool, 0, str_hash, strcmp);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki refcount = POINTER_CAST_TO(refcountp, unsigned int) + 1;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic int lazy_expunge_mail_is_last_instance(struct mail *_mail)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki unsigned long refcount;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki if (mail_get_special(_mail, MAIL_FETCH_REFCOUNT, &value) < 0) {
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki errstr = mailbox_get_last_internal_error(_mail->box, &error);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* already expunged - just ignore it */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki "lazy_expunge: Couldn't lookup message's refcount: %s",
193974072f41a843678abf5f61979c748687e66bSherry Moore /* refcounts not supported by backend. assume all mails are
193974072f41a843678abf5f61979c748687e66bSherry Moore the last instance. */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* this probably isn't the last instance of the mail, but
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki it's possible that the same mail was copied to this mailbox
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki multiple times and we're deleting more than one instance
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki within this transaction. in those cases each expunge will
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki see the same refcount, so we need to adjust the refcount
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki by tracking the expunged message GUIDs. */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki if (mail_get_special(_mail, MAIL_FETCH_GUID, &value) < 0) {
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki errstr = mailbox_get_last_internal_error(_mail->box, &error);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* already expunged - just ignore it */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki "lazy_expunge: Couldn't lookup message's GUID: %s", errstr);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* GUIDs not supported by backend, but refcounts are?
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki not with our current backends. */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki "lazy_expunge: Message unexpectedly has no GUID");
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki refcount -= lazy_expunge_count_in_transaction(lt, value);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic bool lazy_expunge_is_internal_mailbox(struct mailbox *box)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* lazy_expunge not enabled at all */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* lazy-expunge namespace */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* lazy-expunge mailbox */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic void lazy_expunge_set_error(struct lazy_expunge_transaction *lt,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki const char *errstr;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki errstr = mail_storage_get_last_error(storage, &error);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* expunging failed because the mail was already expunged.
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki we don't want to fail because of that. */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki i_strdup(mail_storage_get_last_internal_error(storage, NULL));
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic void lazy_expunge_mail_expunge(struct mail *_mail)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki struct mail_private *mail = (struct mail_private *)_mail;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki struct lazy_expunge_mail *mmail = LAZY_EXPUNGE_MAIL_CONTEXT(mail);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki const char *error;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* Clear this in case the mail is used for non-move later on. */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* don't copy the mail if we're expunging from lazy_expunge
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki namespace (even if it's via a virtual mailbox) */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki if (lazy_expunge_is_internal_mailbox(real_mail->box)) {
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* we want to copy only the last instance of the mail to
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki lazy_expunge namespace. other instances will be expunged
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki immediately. */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki else if ((ret = lazy_expunge_mail_is_last_instance(_mail)) < 0) {
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki if (ret == 0) {
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki lt->dest_box = mailbox_open_or_create(luser->lazy_ns->list,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki "lazy_expunge: Couldn't open expunge mailbox: "
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki "lazy_expunge: Couldn't sync expunge mailbox");
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki lt->dest_trans = mailbox_transaction_begin(lt->dest_box,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki if (mailbox_move(&save_ctx, _mail) < 0 && !_mail->expunged)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic int lazy_expunge_copy(struct mail_save_context *ctx, struct mail *_mail)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki struct mail_private *mail = (struct mail_private *)_mail;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki struct lazy_expunge_mail *mmail = LAZY_EXPUNGE_MAIL_CONTEXT(mail);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki const char *reason)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki union mailbox_module_context *mbox = LAZY_EXPUNGE_CONTEXT_REQUIRE(box);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki t = mbox->super.transaction_begin(box, flags, reason);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki lt->copy_only_last_instance = luser->copy_only_last_instance;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki MODULE_CONTEXT_SET(t, lazy_expunge_mail_storage_module, lt);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic void lazy_expunge_transaction_free(struct lazy_expunge_transaction *lt)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkilazy_expunge_transaction_commit(struct mailbox_transaction_context *ctx,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki union mailbox_module_context *mbox = LAZY_EXPUNGE_CONTEXT_REQUIRE(ctx->box);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki struct lazy_expunge_transaction *lt = LAZY_EXPUNGE_CONTEXT_REQUIRE(ctx);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki if (lt->dest_trans != NULL && lt->delayed_error == MAIL_ERROR_NONE) {
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki if (mailbox_transaction_commit(<->dest_trans) < 0) {
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki mail_storage_set_error(ctx->box->storage, lt->delayed_error,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki "Lazy-expunge transaction failed: %s",
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkilazy_expunge_transaction_rollback(struct mailbox_transaction_context *ctx)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki union mailbox_module_context *mbox = LAZY_EXPUNGE_CONTEXT_REQUIRE(ctx->box);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki struct lazy_expunge_transaction *lt = LAZY_EXPUNGE_CONTEXT_REQUIRE(ctx);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic void lazy_expunge_mail_allocated(struct mail *_mail)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki struct mail_private *mail = (struct mail_private *)_mail;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki mmail = p_new(mail->pool, struct lazy_expunge_mail, 1);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki MODULE_CONTEXT_SET(mail, lazy_expunge_mail_module, mmail);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkilazy_expunge_mailbox_rename(struct mailbox *src, struct mailbox *dest)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki union mailbox_module_context *lbox = LAZY_EXPUNGE_CONTEXT_REQUIRE(src);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki "Can't rename mailboxes to/from expunge namespace.");
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic void lazy_expunge_mailbox_allocated(struct mailbox *box)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki if (llist == NULL || (box->flags & MAILBOX_FLAG_DELETE_UNSAFE) != 0)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki mbox = p_new(box->pool, union mailbox_module_context, 1);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki MODULE_CONTEXT_SET_SELF(box, lazy_expunge_mail_storage_module, mbox);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki v->transaction_begin = lazy_expunge_transaction_begin;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki v->transaction_commit = lazy_expunge_transaction_commit;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki v->transaction_rollback = lazy_expunge_transaction_rollback;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* internal mailbox in a non-internal namespace -
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki don't add any unnecessary restrictions to it. if it's not
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki wanted, just use the ACL plugin. */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic void lazy_expunge_mailbox_list_created(struct mailbox_list *list)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* if this is one of our internal namespaces, mark it as such before
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki quota plugin sees it */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki llist = p_new(list->pool, struct lazy_expunge_mailbox_list, 1);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki MODULE_CONTEXT_SET(list, lazy_expunge_mailbox_list_module,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkilazy_expunge_mail_namespaces_created(struct mail_namespace *namespaces)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki luser->lazy_ns = mail_namespace_find_prefix(namespaces, luser->env);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* we don't want to override this namespace's expunge operation. */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki llist = LAZY_EXPUNGE_LIST_CONTEXT(luser->lazy_ns->list);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* store the the expunged mails to the specified mailbox. */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki luser->lazy_ns = mail_namespace_find(namespaces, luser->env);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic void lazy_expunge_user_deinit(struct mail_user *user)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki struct lazy_expunge_mail_user *luser = LAZY_EXPUNGE_USER_CONTEXT(user);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki /* mail_namespaces_created hook isn't necessarily ever called */
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic void lazy_expunge_mail_user_created(struct mail_user *user)
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki const char *env;
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki luser = p_new(user->pool, struct lazy_expunge_mail_user, 1);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki mail_user_plugin_getenv_bool(user, "lazy_expunge_only_last_instance");
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki MODULE_CONTEXT_SET(user, lazy_expunge_mail_user_module, luser);
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki "plugin disabled");
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenkistatic struct mail_storage_hooks lazy_expunge_mail_storage_hooks = {
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki .mail_namespaces_created = lazy_expunge_mail_namespaces_created,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki .mailbox_list_created = lazy_expunge_mailbox_list_created,
0d63ce2b32a9e1cc8ed71d4d92536c44d66a530avenki mail_storage_hooks_add(module, &lazy_expunge_mail_storage_hooks);