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