quota-storage.c revision 6f42ea227d5623d7960c8d8cd29e668870a78752
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2005-2012 Dovecot authors, see the included COPYING file */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen MODULE_CONTEXT(obj, quota_mailbox_list_module)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct mailbox_transaction_context *expunge_trans;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo 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,
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainenstatic void 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. */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo 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);
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,
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen struct mail_transaction_commit_changes *changes_r)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen if (qbox->module_ctx.super.transaction_commit(ctx, changes_r) < 0) {
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);
26550dc10a8c2057d09894c4e3f9dc12b124e90cTimo Sirainen qbox->module_ctx.super.transaction_rollback(ctx);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(_mail->box);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
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 "Internal quota calculation error");
6f42ea227d5623d7960c8d8cd29e668870a78752Timo Sirainen return qt->quota->set->ignore_save_errors ? 0 : -1;
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainenquota_copy(struct mail_save_context *ctx, struct mail *mail)
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen struct mailbox_transaction_context *t = ctx->transaction;
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,
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen if (qbox->module_ctx.super.copy(ctx, mail) < 0)
263e4b2733768062cb0b8b8917cad78fa2a04ff9Timo Sirainen /* if copying used saving internally, we already checked the quota */
abec3f4c5ec486c393e1513950f2d4819dcbc30fTimo Sirainen return ctx->copying_via_save ? 0 : quota_check(t, ctx->dest_mail);
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainenquota_save_begin(struct mail_save_context *ctx, struct istream *input)
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen struct mailbox_transaction_context *t = ctx->transaction;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if (i_stream_get_size(input, TRUE, &size) > 0) {
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. */
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,
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen return qbox->module_ctx.super.save_begin(ctx, input);
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainenstatic int quota_save_finish(struct mail_save_context *ctx)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->transaction->box);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (qbox->module_ctx.super.save_finish(ctx) < 0)
cf942dce0253075911a96cff323b5f30eb654ae0Timo Sirainen return quota_check(ctx->transaction, ctx->dest_mail);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainenstatic void quota_mailbox_sync_cleanup(struct quota_mailbox *qbox)
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen if (qbox->expunge_qt != NULL && qbox->expunge_qt->tmp_mail != NULL) {
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen mailbox_transaction_rollback(&qbox->expunge_trans);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainenstatic void quota_mailbox_sync_commit(struct quota_mailbox *qbox)
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo 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);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen if (sync_type != MAILBOX_SYNC_TYPE_EXPUNGE || qbox->recalculate) {
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen /* free the transaction before view syncing begins,
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo 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++) {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo 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. */
6ae329de09afb7214c906d762320847e05469d53Timo Sirainen /* FIXME: ugly kludge to open the transaction for sync_view.
6ae329de09afb7214c906d762320847e05469d53Timo Sirainen box->view may not have all the new messages that
6ae329de09afb7214c906d762320847e05469d53Timo Sirainen sync_notify() notifies about, and those messages would
6ae329de09afb7214c906d762320847e05469d53Timo Sirainen cause a quota recalculation. */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen qbox->expunge_trans = mailbox_transaction_begin(box, 0);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (mail_set_uid(qbox->expunge_qt->tmp_mail, uid) &&
83bb013a99f0936995f9c7a1077822662d8fefdbTimo 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);
2584e86cc2d8c31ba30a4109cf4ba09d1e37e28aTimo Sirainen ret = qbox->module_ctx.super.sync_deinit(ctx, status_r);
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen /* update quota only after syncing is finished. the quota commit may
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen recalculate the quota and cause all mailboxes to be synced,
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen including the one we're already syncing. */
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainenstatic void quota_mailbox_close(struct mailbox *box)
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen /* sync_notify() may be called outside sync_begin()..sync_deinit().
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen make sure we apply changes at close time at latest. */
e03d986a74128f5ba30fcfda9f6e36578f5d8decTimo Sirainenstatic void quota_mailbox_free(struct mailbox *box)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
ea9d9d99948cff5f9b881f79b28fa3b80da0f2a7Timo Sirainenvoid quota_mailbox_allocated(struct mailbox *box)
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) != 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen qbox = p_new(box->pool, struct quota_mailbox, 1);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->transaction_begin = quota_mailbox_transaction_begin;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->transaction_commit = quota_mailbox_transaction_commit;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->transaction_rollback = quota_mailbox_transaction_rollback;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen MODULE_CONTEXT_SET(box, quota_storage_module, qbox);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic void quota_mailbox_list_deinit(struct mailbox_list *list)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(list);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenstruct quota *quota_get_mail_user_quota(struct mail_user *user)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct quota_user *quser = QUOTA_USER_CONTEXT(user);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenstatic void quota_user_deinit(struct mail_user *user)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct quota_user *quser = QUOTA_USER_CONTEXT(user);
bb34c6538784b564c05eefccc74af2bd7c9505f5Timo Sirainen struct quota_settings *quota_set = quser->quota->set;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenvoid quota_mail_user_created(struct mail_user *user)
4316355ca8b7698516272520a972291378698140Timo Sirainen if ((ret = quota_user_read_settings(user, &set, &error)) > 0) {
4316355ca8b7698516272520a972291378698140Timo Sirainen if (quota_init(set, user, "a, &error) < 0) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen quser = p_new(user->pool, struct quota_user, 1);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen MODULE_CONTEXT_SET(user, quota_user_module, quser);
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk i_debug("quota: No quota setting - plugin disabled");
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainenstatic struct quota_root *
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainenquota_find_root_for_ns(struct quota *quota, struct mail_namespace *ns)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen unsigned int i, count;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen for (i = 0; i < count; i++) {
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainenvoid quota_mailbox_list_created(struct mailbox_list *list)
a9a77f4632b1f00cc3c6664c91ef5f23746e099bTimo Sirainen if (QUOTA_USER_CONTEXT(list->ns->user) == NULL)
805872b9f885c1cc4bddf0b6ff71ca61f6cdd7bfTimo Sirainen /* see if we have a quota explicitly defined for this namespace */
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen quota = quota_get_mail_user_quota(list->ns->user);
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen root = quota_find_root_for_ns(quota, list->ns);
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen if ((list->ns->flags & NAMESPACE_FLAG_NOQUOTA) != 0)
805872b9f885c1cc4bddf0b6ff71ca61f6cdd7bfTimo Sirainen /* public namespace - add quota only if namespace is
805872b9f885c1cc4bddf0b6ff71ca61f6cdd7bfTimo Sirainen explicitly defined for it */
f7fcdca120f1b591f885408d08fc90cfd725bf94Timo Sirainen qlist = p_new(list->pool, struct quota_mailbox_list, 1);
f7fcdca120f1b591f885408d08fc90cfd725bf94Timo Sirainen MODULE_CONTEXT_SET(list, quota_mailbox_list_module, qlist);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* register to owner's quota roots */
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainenstatic void quota_root_set_namespace(struct quota_root *root,
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen if (root->ns_prefix != NULL && root->ns == NULL) {
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen root->ns = mail_namespace_find_prefix(namespaces,
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen if (mail_namespace_find(namespaces, name) == NULL)
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen i_error("quota: Unknown namespace: %s", name);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainenvoid quota_mail_namespaces_created(struct mail_namespace *namespaces)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen unsigned int i, count;
a9a77f4632b1f00cc3c6664c91ef5f23746e099bTimo Sirainen if (QUOTA_USER_CONTEXT(namespaces->user) == NULL)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen quota = quota_get_mail_user_quota(namespaces->user);
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen for (i = 0; i < count; i++)