quota-storage.c revision 45312f52ff3a3d4c137447be4c7556500c2f8bf2
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MODULE_CONTEXT(obj, quota_mailbox_list_module)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mailbox_transaction_context *expunge_trans;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(quota_storage_module,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(quota_mail_module, &mail_module_register);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(quota_mailbox_list_module,
b215322367dbd94df3e2e4bb643b53460e6adc51Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(quota_user_module,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void quota_mail_expunge(struct mail *_mail)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(_mail->box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen union mail_module_context *qmail = QUOTA_MAIL_CONTEXT(mail);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* We need to handle the situation where multiple transactions expunged
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen the mail at the same time. In here we'll just save the message's
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen physical size and do the quota freeing later when the message was
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen known to be expunged. */
35ef661bd85c64834e3e34eeeb3c393b81108760Timo Sirainen if (mail_get_physical_size(_mail, &size) == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen array_append(&qbox->expunge_uids, &_mail->uid, 1);
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainenquota_mailbox_transaction_begin(struct mailbox *box,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t = qbox->module_ctx.super.transaction_begin(box, flags);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MODULE_CONTEXT_SET(t, quota_storage_module, qt);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenquota_mailbox_transaction_commit(struct mailbox_transaction_context *ctx,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (qbox->module_ctx.super.transaction_commit(ctx,
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainenquota_mailbox_transaction_rollback(struct mailbox_transaction_context *ctx)
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen qbox->module_ctx.super.transaction_rollback(ctx);
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainenstatic struct mail *
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainenquota_mail_alloc(struct mailbox_transaction_context *t,
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen struct mailbox_header_lookup_ctx *wanted_headers)
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen qmail = p_new(mail->pool, union mail_module_context, 1);
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen MODULE_CONTEXT_SET_SELF(mail, quota_mail_module, qmail);
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainenstatic int quota_check(struct mailbox_transaction_context *t, struct mail *mail)
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen else if (ret == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_error(t->box->storage, MAIL_ERROR_NOSPACE,
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen "Internal quota calculation error");
8c3872c26b18421d62c52cbfe0b81b1d79239f89Timo Sirainenquota_copy(struct mailbox_transaction_context *t, struct mail *mail,
8c3872c26b18421d62c52cbfe0b81b1d79239f89Timo Sirainen enum mail_flags flags, struct mail_keywords *keywords,
8c3872c26b18421d62c52cbfe0b81b1d79239f89Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
8c3872c26b18421d62c52cbfe0b81b1d79239f89Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
8c3872c26b18421d62c52cbfe0b81b1d79239f89Timo Sirainen /* we always want to know the mail size */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (qbox->module_ctx.super.copy(t, mail, flags, keywords,
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen /* if copying used saving internally, we already checked the quota
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen and set qbox->save_hack = TRUE. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return qbox->save_hack ? 0 : quota_check(t, dest_mail);
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainenquota_save_begin(struct mail_save_context *ctx, struct istream *input)
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen struct mailbox_transaction_context *t = ctx->transaction;
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen /* Input size is known, check for quota immediately. This
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen check isn't perfect, especially because input stream's
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen linefeeds may contain CR+LFs while physical message would
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen only contain LFs. With mbox some headers might be skipped
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen I think these don't really matter though compared to the
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen benefit of giving "out of quota" error before sending the
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen full mail. */
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen ret = quota_test_alloc(qt, st->st_size, &too_large);
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen } else if (ret < 0) {
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen "Internal quota calculation error");
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen /* we always want to know the mail size */
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen return qbox->module_ctx.super.save_begin(ctx, input);
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainenstatic int quota_save_finish(struct mail_save_context *ctx)
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->transaction->box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (qbox->module_ctx.super.save_finish(ctx) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return quota_check(ctx->transaction, ctx->dest_mail);
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainenstatic void quota_mailbox_sync_cleanup(struct quota_mailbox *qbox)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (qbox->expunge_qt != NULL && qbox->expunge_qt->tmp_mail != NULL) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mailbox_transaction_rollback(&qbox->expunge_trans);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void quota_mailbox_sync_commit(struct quota_mailbox *qbox)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)quota_transaction_commit(&qbox->expunge_qt);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void quota_mailbox_sync_notify(struct mailbox *box, uint32_t uid,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int i, count;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (qbox->module_ctx.super.sync_notify != NULL)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen qbox->module_ctx.super.sync_notify(box, uid, sync_type);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (sync_type != MAILBOX_SYNC_TYPE_EXPUNGE || qbox->recalculate) {
b215322367dbd94df3e2e4bb643b53460e6adc51Timo Sirainen /* free the transaction before view syncing begins,
b3070aca2aa0dbdcc7f30e6e2bb1f0455127a343Timo Sirainen otherwise it'll crash. */
7e8bfb5b0af9606f131fc440e61f3752da335ac9Timo Sirainen /* we're in the middle of syncing the mailbox, so it's a bad idea to
b215322367dbd94df3e2e4bb643b53460e6adc51Timo Sirainen try and get the message sizes at this point. Rely on sizes that
b215322367dbd94df3e2e4bb643b53460e6adc51Timo Sirainen we saved earlier, or recalculate the whole quota if we don't know
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uids = array_get(&qbox->expunge_uids, &count);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (i = 0; i < count; i++) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen qbox->expunge_qt = quota_transaction_begin(box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* we already know the size */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* try to look up the size. this works only if it's cached. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen qbox->expunge_trans = mailbox_transaction_begin(box, 0);
ba57ea2c696f9e9aae909f073069848876a641f4Timo Sirainen if (mail_set_uid(qbox->expunge_qt->tmp_mail, uid) &&
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_get_physical_size(qbox->expunge_qt->tmp_mail, &size) == 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* there's no way to get the size. recalculate the quota. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int quota_mailbox_sync_deinit(struct mailbox_sync_context *ctx,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ret = qbox->module_ctx.super.sync_deinit(ctx, status_items, status_r);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* update quota only after syncing is finished. the quota commit may
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen recalculate the quota and cause all mailboxes to be synced,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen including the one we're already syncing. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int quota_mailbox_close(struct mailbox *box)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic struct mailbox *
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenquota_mailbox_open(struct mail_storage *storage, const char *name,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct istream *input, enum mailbox_open_flags flags)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen union mail_storage_module_context *qstorage = QUOTA_CONTEXT(storage);
ba57ea2c696f9e9aae909f073069848876a641f4Timo Sirainen box = qstorage->super.mailbox_open(storage, name, input, flags);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen qbox = p_new(box->pool, struct quota_mailbox, 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen box->v.transaction_begin = quota_mailbox_transaction_begin;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen box->v.transaction_commit = quota_mailbox_transaction_commit;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen box->v.transaction_rollback = quota_mailbox_transaction_rollback;
b215322367dbd94df3e2e4bb643b53460e6adc51Timo Sirainen box->v.sync_notify = quota_mailbox_sync_notify;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen box->v.sync_deinit = quota_mailbox_sync_deinit;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MODULE_CONTEXT_SET(box, quota_storage_module, qbox);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenquota_mailbox_delete_shrink_quota(struct mailbox *box)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ, 0, NULL) < 0)
b215322367dbd94df3e2e4bb643b53460e6adc51Timo Sirainen ctx = mailbox_search_init(t, search_args, NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenquota_mailbox_list_delete(struct mailbox_list *list, const char *name)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(list);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *str;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* This is a bit annoying to handle. We'll have to open the mailbox
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen and free the quota for all the messages existing in it. Open the
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mailbox locked so that other processes can't mess up the quota
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen calculations by adding/removing mails while we're doing this. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen box = mailbox_open(&storage, name, NULL, MAILBOX_OPEN_FAST |
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MAILBOX_OPEN_KEEP_RECENT | MAILBOX_OPEN_KEEP_LOCKED);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str = mail_storage_get_last_error(qlist->storage, &error);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* mailbox isn't selectable */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str = mail_storage_get_last_error(qlist->storage, &error);
ba57ea2c696f9e9aae909f073069848876a641f4Timo Sirainen /* FIXME: here's an unfortunate race condition */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen qlist->module_ctx.super.delete_mailbox(list, name);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void quota_storage_destroy(struct mail_storage *storage)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen union mail_storage_module_context *qstorage = QUOTA_CONTEXT(storage);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstruct quota *quota_get_mail_user_quota(struct mail_user *user)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct quota_user *quser = QUOTA_USER_CONTEXT(user);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void quota_user_deinit(struct mail_user *user)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct quota_user *quser = QUOTA_USER_CONTEXT(user);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainenvoid quota_mail_user_created(struct mail_user *user)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen quser = p_new(user->pool, struct quota_user, 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MODULE_CONTEXT_SET(user, quota_user_module, quser);
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen if (quota_next_hook_mail_user_created != NULL)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenvoid quota_mail_storage_created(struct mail_storage *storage)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(storage->list);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen qstorage = p_new(storage->pool, union mail_storage_module_context, 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MODULE_CONTEXT_SET_SELF(storage, quota_storage_module, qstorage);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (storage->ns->flags & NAMESPACE_FLAG_INTERNAL) == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* register to owner's quota roots */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen quota = quota_get_mail_user_quota(storage->ns->owner);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (quota_next_hook_mail_storage_created != NULL)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen quota_next_hook_mail_storage_created(storage);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenvoid quota_mailbox_list_created(struct mailbox_list *list)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen qlist = p_new(list->pool, struct quota_mailbox_list, 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen list->v.delete_mailbox = quota_mailbox_list_delete;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MODULE_CONTEXT_SET(list, quota_mailbox_list_module, qlist);