quota-storage.c revision a9a77f4632b1f00cc3c6664c91ef5f23746e099b
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen MODULE_CONTEXT(obj, quota_mailbox_list_module)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct mailbox_transaction_context *expunge_trans;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen MODULE_CONTEXT_INIT(&mail_user_module_register);
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 void quota_mail_expunge(struct mail *_mail)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(_mail->box);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen union mail_module_context *qmail = QUOTA_MAIL_CONTEXT(mail);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* We need to handle the situation where multiple transactions expunged
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo 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 if (mail_get_physical_size(_mail, &size) == 0) {
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);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo 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 mail_transaction_commit_changes *changes_r)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (qbox->module_ctx.super.transaction_commit(ctx, changes_r) < 0) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo 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);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen qbox->module_ctx.super.transaction_rollback(ctx);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(_mail->box);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen qmail = p_new(mail->pool, union mail_module_context, 1);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen MODULE_CONTEXT_SET_SELF(mail, quota_mail_module, qmail);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic int quota_check(struct mailbox_transaction_context *t, struct mail *mail)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else if (ret == 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen mail_storage_set_error(t->box->storage, MAIL_ERROR_NOSPACE,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "Internal quota calculation error");
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainenquota_copy(struct mail_save_context *ctx, struct mail *mail)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct mailbox_transaction_context *t = ctx->transaction;
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* we always want to know the mail size */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (qbox->module_ctx.super.copy(ctx, mail) < 0)
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen /* if copying used saving internally, we already checked the quota */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return ctx->copying ? 0 : quota_check(t, ctx->dest_mail);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenquota_save_begin(struct mail_save_context *ctx, struct istream *input)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct mailbox_transaction_context *t = ctx->transaction;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (i_stream_get_size(input, TRUE, &size) > 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* Input size is known, check for quota immediately. This
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen check isn't perfect, especially because input stream's
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen linefeeds may contain CR+LFs while physical message would
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen only contain LFs. With mbox some headers might be skipped
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen I think these don't really matter though compared to the
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen benefit of giving "out of quota" error before sending the
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen full mail. */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else if (ret < 0) {
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen "Internal quota calculation error");
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen /* we always want to know the mail size */
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen return qbox->module_ctx.super.save_begin(ctx, input);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int quota_save_finish(struct mail_save_context *ctx)
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->transaction->box);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (qbox->module_ctx.super.save_finish(ctx) < 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return quota_check(ctx->transaction, ctx->dest_mail);
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainenstatic void quota_mailbox_sync_cleanup(struct quota_mailbox *qbox)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (qbox->expunge_qt != NULL && qbox->expunge_qt->tmp_mail != NULL) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen mailbox_transaction_rollback(&qbox->expunge_trans);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic void quota_mailbox_sync_commit(struct quota_mailbox *qbox)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen (void)quota_transaction_commit(&qbox->expunge_qt);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo 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 /* free the transaction before view syncing begins,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen otherwise it'll crash. */
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(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 if (mail_set_uid(qbox->expunge_qt->tmp_mail, uid) &&
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen mail_get_physical_size(qbox->expunge_qt->tmp_mail, &size) == 0)
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 ret = qbox->module_ctx.super.sync_deinit(ctx, status_r);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* update quota only after syncing is finished. the quota commit may
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen recalculate the quota and cause all mailboxes to be synced,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen including the one we're already syncing. */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenstatic void quota_mailbox_close(struct mailbox *box)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* sync_notify() may be called outside sync_begin()..sync_deinit().
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen make sure we apply changes at close time at latest. */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenquota_mailbox_delete_shrink_quota(struct mailbox *box)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (mailbox_mark_index_deleted(box, TRUE) < 0)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ctx = mailbox_search_init(t, search_args, NULL);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* maybe we missed some mails. */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic int quota_mailbox_delete(struct mailbox *box)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (quota_mailbox_delete_shrink_quota(box) < 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic void quota_mailbox_free(struct mailbox *box)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainenvoid quota_mailbox_allocated(struct mailbox *box)
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen qbox = p_new(box->pool, struct quota_mailbox, 1);
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen v->transaction_begin = quota_mailbox_transaction_begin;
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen v->transaction_commit = quota_mailbox_transaction_commit;
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen v->transaction_rollback = quota_mailbox_transaction_rollback;
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen MODULE_CONTEXT_SET(box, quota_storage_module, qbox);
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainenstatic void quota_mailbox_list_deinit(struct mailbox_list *list)
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(list);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstruct quota *quota_get_mail_user_quota(struct mail_user *user)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_user *quser = QUOTA_USER_CONTEXT(user);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic void quota_user_deinit(struct mail_user *user)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct quota_user *quser = QUOTA_USER_CONTEXT(user);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct quota_settings *quota_set = quser->quota->set;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainenvoid quota_mail_user_created(struct mail_user *user)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen if ((ret = quota_user_read_settings(user, &set, &error)) > 0) {
bda9a6d9b021c122a01a85cb3cee2f996263d8f0Timo Sirainen if (quota_init(set, user, "a, &error) < 0) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen quser = p_new(user->pool, struct quota_user, 1);
static struct quota_root *
unsigned int i, count;
for (i = 0; i < count; i++) {
return roots[i];
return NULL;
bool add;
if (add) {
const char *name;
unsigned int i, count;
for (i = 0; i < count; i++)