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