diff --git a/src/plugins/quota-clone/quota-clone-plugin.c b/src/plugins/quota-clone/quota-clone-plugin.c index d99ed90a1d..8cddd05575 100644 --- a/src/plugins/quota-clone/quota-clone-plugin.c +++ b/src/plugins/quota-clone/quota-clone-plugin.c @@ -31,20 +31,15 @@ static MODULE_CONTEXT_DEFINE_INIT(quota_clone_storage_module, struct quota_clone_user { union mail_user_module_context module_ctx; struct dict *dict; - bool quota_flushing; -}; - -struct quota_clone_mailbox { - union mailbox_module_context module_ctx; struct timeout *to_quota_flush; bool quota_changed; + bool quota_flushing; }; -static void quota_clone_flush_real(struct mailbox *box) +static void quota_clone_flush_real(struct mail_user *user) { - struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box); struct quota_clone_user *quser = - QUOTA_CLONE_USER_CONTEXT_REQUIRE(box->storage->user); + QUOTA_CLONE_USER_CONTEXT_REQUIRE(user); struct dict_transaction_context *trans; struct quota_root_iter *iter; struct quota_root *root; @@ -53,12 +48,12 @@ static void quota_clone_flush_real(struct mailbox *box) enum quota_get_result bytes_res, count_res; /* we'll clone the first quota root */ - iter = quota_root_iter_init(box); + iter = quota_root_iter_init_user(user); root = quota_root_iter_next(iter); quota_root_iter_deinit(&iter); if (root == NULL) { - /* no quota roots defined for this mailbox - ignore */ - qbox->quota_changed = FALSE; + /* no quota roots defined - ignore */ + quser->quota_changed = FALSE; return; } @@ -109,103 +104,121 @@ static void quota_clone_flush_real(struct mailbox *box) if (dict_transaction_commit(&trans, &error) < 0) i_error("quota_clone_plugin: Failed to commit dict update: %s", error); else - qbox->quota_changed = FALSE; + quser->quota_changed = FALSE; } -static void quota_clone_flush(struct mailbox *box) +static void quota_clone_flush(struct mail_user *user) { - struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box); struct quota_clone_user *quser = - QUOTA_CLONE_USER_CONTEXT_REQUIRE(box->storage->user); + QUOTA_CLONE_USER_CONTEXT_REQUIRE(user); - timeout_remove(&qbox->to_quota_flush); + timeout_remove(&quser->to_quota_flush); if (quser->quota_flushing) { /* recursing back from quota recalculation */ - } else if (qbox->quota_changed) { + } else if (quser->quota_changed) { quser->quota_flushing = TRUE; - quota_clone_flush_real(box); + quota_clone_flush_real(user); quser->quota_flushing = FALSE; } } +static struct mail_user *quota_mailbox_get_user(struct mailbox *box) +{ + struct mail_namespace *ns = mailbox_list_get_namespace(box->list); + return ns->owner != NULL ? ns->owner : ns->user; +} + static void quota_clone_changed(struct mailbox *box) { - struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box); + struct mail_user *user = quota_mailbox_get_user(box); + struct quota_clone_user *quser = + QUOTA_CLONE_USER_CONTEXT_REQUIRE(user); - qbox->quota_changed = TRUE; - if (qbox->to_quota_flush == NULL) { - qbox->to_quota_flush = timeout_add(QUOTA_CLONE_FLUSH_DELAY_MSECS, - quota_clone_flush, box); + quser->quota_changed = TRUE; + if (quser->to_quota_flush == NULL) { + quser->to_quota_flush = timeout_add(QUOTA_CLONE_FLUSH_DELAY_MSECS, + quota_clone_flush, user); } } static int quota_clone_save_finish(struct mail_save_context *ctx) { - struct quota_clone_mailbox *qbox = + union mailbox_module_context *qbox = QUOTA_CLONE_CONTEXT(ctx->transaction->box); quota_clone_changed(ctx->transaction->box); - return qbox->module_ctx.super.save_finish(ctx); + return qbox->super.save_finish(ctx); } static int quota_clone_copy(struct mail_save_context *ctx, struct mail *mail) { - struct quota_clone_mailbox *qbox = + union mailbox_module_context *qbox = QUOTA_CLONE_CONTEXT(ctx->transaction->box); quota_clone_changed(ctx->transaction->box); - return qbox->module_ctx.super.copy(ctx, mail); + return qbox->super.copy(ctx, mail); } static void quota_clone_mailbox_sync_notify(struct mailbox *box, uint32_t uid, enum mailbox_sync_type sync_type) { - struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box); + union mailbox_module_context *qbox = QUOTA_CLONE_CONTEXT(box); - if (qbox->module_ctx.super.sync_notify != NULL) - qbox->module_ctx.super.sync_notify(box, uid, sync_type); + if (qbox->super.sync_notify != NULL) + qbox->super.sync_notify(box, uid, sync_type); if (sync_type == MAILBOX_SYNC_TYPE_EXPUNGE) quota_clone_changed(box); } -static void quota_clone_mailbox_close(struct mailbox *box) -{ - struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box); - - qbox->module_ctx.super.close(box); - - quota_clone_flush(box); -} - static void quota_clone_mailbox_allocated(struct mailbox *box) { struct quota_clone_user *quser = QUOTA_CLONE_USER_CONTEXT(box->storage->user); struct mailbox_vfuncs *v = box->vlast; - struct quota_clone_mailbox *qbox; + union mailbox_module_context *qbox; if (quser == NULL) return; - qbox = p_new(box->pool, struct quota_clone_mailbox, 1); - qbox->module_ctx.super = *v; - box->vlast = &qbox->module_ctx.super; + qbox = p_new(box->pool, union mailbox_module_context, 1); + qbox->super = *v; + box->vlast = &qbox->super; v->save_finish = quota_clone_save_finish; v->copy = quota_clone_copy; v->sync_notify = quota_clone_mailbox_sync_notify; - v->close = quota_clone_mailbox_close; - MODULE_CONTEXT_SET(box, quota_clone_storage_module, qbox); + MODULE_CONTEXT_SET_SELF(box, quota_clone_storage_module, qbox); +} + +static void quota_clone_mail_user_deinit_pre(struct mail_user *user) +{ + struct quota_clone_user *quser = QUOTA_CLONE_USER_CONTEXT_REQUIRE(user); + + dict_wait(quser->dict); + /* Check once more if quota needs to be updated. This needs to be done + in deinit_pre(), because at deinit() the quota is already + deinitialized. */ + if (quser->to_quota_flush != NULL) { + i_assert(!quser->quota_flushing); + quota_clone_flush(user); + dict_wait(quser->dict); + i_assert(quser->to_quota_flush == NULL); + } + quser->module_ctx.super.deinit_pre(user); } static void quota_clone_mail_user_deinit(struct mail_user *user) { struct quota_clone_user *quser = QUOTA_CLONE_USER_CONTEXT_REQUIRE(user); + /* wait once more, just in case something changed quota during + deinit_pre() */ + dict_wait(quser->dict); + i_assert(quser->to_quota_flush == NULL); dict_deinit(&quser->dict); quser->module_ctx.super.deinit(user); } @@ -239,6 +252,7 @@ static void quota_clone_mail_user_created(struct mail_user *user) quser = p_new(user->pool, struct quota_clone_user, 1); quser->module_ctx.super = *v; user->vlast = &quser->module_ctx.super; + v->deinit_pre = quota_clone_mail_user_deinit_pre; v->deinit = quota_clone_mail_user_deinit; quser->dict = dict; MODULE_CONTEXT_SET(user, quota_clone_user_module, quser);