bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "lib.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "array.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "istream.h"
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen#include "mail-search-build.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "mail-storage-private.h"
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen#include "mailbox-list-private.h"
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#include "maildir-storage.h"
9963bef626fd9ea227fb606e8b1694cdb1ab39aaTimo Sirainen#include "index-mailbox-size.h"
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen#include "quota-private.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "quota-plugin.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include <sys/stat.h>
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#define QUOTA_CONTEXT(obj) \
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen MODULE_CONTEXT(obj, quota_storage_module)
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi#define QUOTA_CONTEXT_REQUIRE(obj) \
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi MODULE_CONTEXT_REQUIRE(obj, quota_storage_module)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#define QUOTA_MAIL_CONTEXT(obj) \
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi MODULE_CONTEXT_REQUIRE(obj, quota_mail_module)
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen#define QUOTA_LIST_CONTEXT(obj) \
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen MODULE_CONTEXT(obj, quota_mailbox_list_module)
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainenstruct quota_mailbox_list {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen union mailbox_list_module_context module_ctx;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen};
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstruct quota_mailbox {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen union mailbox_module_context module_ctx;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct mailbox_transaction_context *expunge_trans;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct quota_transaction_context *expunge_qt;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen ARRAY(uint32_t) expunge_uids;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen ARRAY(uoff_t) expunge_sizes;
62f2f6bafd6253443a8ec9715278db60df660254Timo Sirainen unsigned int prev_idx;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool recalculate:1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool sync_transaction_expunge:1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen};
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstruct quota_user_module quota_user_module =
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen MODULE_CONTEXT_INIT(&mail_user_module_register);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(quota_storage_module,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen &mail_storage_module_register);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(quota_mail_module, &mail_module_register);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(quota_mailbox_list_module,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen &mailbox_list_module_register);
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvistatic void quota_set_storage_error(struct quota_transaction_context *qt,
0060557e0171750089c3cee3878ddbf67ac7c4e8Martti Rannanjärvi struct mailbox *box,
4b5c9cdb6061973fd3d4d3191a8c21b2ce4b8f3eMartti Rannanjärvi enum quota_alloc_result res,
4b5c9cdb6061973fd3d4d3191a8c21b2ce4b8f3eMartti Rannanjärvi const char *internal_err)
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi{
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi const char *errstr = quota_alloc_result_errstr(res, qt);
0060557e0171750089c3cee3878ddbf67ac7c4e8Martti Rannanjärvi struct mail_storage *storage = box->storage;
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi switch (res) {
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi case QUOTA_ALLOC_RESULT_OVER_MAXSIZE:
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi mail_storage_set_error(storage, MAIL_ERROR_LIMIT, errstr);
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi break;
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi case QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT:
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi case QUOTA_ALLOC_RESULT_OVER_QUOTA:
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi mail_storage_set_error(storage, MAIL_ERROR_NOQUOTA, errstr);
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi break;
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi case QUOTA_ALLOC_RESULT_TEMPFAIL:
b14f93ea4f7e9b78e5adbabcc78bd2f63d2bcd36Martti Rannanjärvi case QUOTA_ALLOC_RESULT_BACKGROUND_CALC:
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(box, "quota: %s", internal_err);
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi break;
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi case QUOTA_ALLOC_RESULT_OK:
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi i_unreached();
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi }
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi}
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainenstatic void quota_mail_expunge(struct mail *_mail)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_mailbox *qbox = QUOTA_CONTEXT_REQUIRE(_mail->box);
9390fe0c1b5b7495363f9194ff701470e242dd0eAki Tuomi struct quota_user *quser = QUOTA_USER_CONTEXT_REQUIRE(_mail->box->storage->user);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen union mail_module_context *qmail = QUOTA_MAIL_CONTEXT(mail);
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_transaction_context *qt = QUOTA_CONTEXT_REQUIRE(_mail->transaction);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen uoff_t size;
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen int ret;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen if (qt->auto_updating) {
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen qmail->super.expunge(_mail);
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen return;
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen }
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen
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. */
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen if (quser->quota->set->vsizes)
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen ret = mail_get_virtual_size(_mail, &size);
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen else
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen ret = mail_get_physical_size(_mail, &size);
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen if (ret == 0) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (!array_is_created(&qbox->expunge_uids)) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_array_init(&qbox->expunge_uids, 64);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_array_init(&qbox->expunge_sizes, 64);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen array_append(&qbox->expunge_uids, &_mail->uid, 1);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen array_append(&qbox->expunge_sizes, &size, 1);
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen if ((_mail->transaction->flags & MAILBOX_TRANSACTION_FLAG_SYNC) != 0) {
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen /* we're running dsync. if this brings the quota below
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen a negative quota warning, don't execute it, because
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen it probably was already executed by the replica. */
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen qbox->sync_transaction_expunge = TRUE;
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen } else {
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen qbox->sync_transaction_expunge = FALSE;
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen qmail->super.expunge(_mail);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainenstatic int
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainenquota_get_status(struct mailbox *box, enum mailbox_status_items items,
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen struct mailbox_status *status_r)
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen{
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_mailbox *qbox = QUOTA_CONTEXT_REQUIRE(box);
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen struct quota_transaction_context *qt;
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen int ret = 0;
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen if ((items & STATUS_CHECK_OVER_QUOTA) != 0) {
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen qt = quota_transaction_begin(box);
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi const char *error;
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi enum quota_alloc_result qret = quota_test_alloc(qt, 0, &error);
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi if (qret != QUOTA_ALLOC_RESULT_OK) {
0060557e0171750089c3cee3878ddbf67ac7c4e8Martti Rannanjärvi quota_set_storage_error(qt, box, qret, error);
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen ret = -1;
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen }
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen quota_transaction_rollback(&qt);
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen if ((items & ~STATUS_CHECK_OVER_QUOTA) == 0) {
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen /* don't bother calling parent, it may unnecessarily
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen try to open the mailbox */
95d50c6f87501978687a1bc0685d8bc469645e7fTimo Sirainen return ret < 0 ? -1 : 0;
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen }
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen }
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen if (qbox->module_ctx.super.get_status(box, items, status_r) < 0)
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen ret = -1;
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen return ret < 0 ? -1 : 0;
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen}
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic struct mailbox_transaction_context *
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenquota_mailbox_transaction_begin(struct mailbox *box,
c6ce2e251ac75fa650c7fbfa52150eae69386293Martti Rannanjärvi enum mailbox_transaction_flags flags,
c6ce2e251ac75fa650c7fbfa52150eae69386293Martti Rannanjärvi const char *reason)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_mailbox *qbox = QUOTA_CONTEXT_REQUIRE(box);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct mailbox_transaction_context *t;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct quota_transaction_context *qt;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
c6ce2e251ac75fa650c7fbfa52150eae69386293Martti Rannanjärvi t = qbox->module_ctx.super.transaction_begin(box, flags, reason);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen qt = quota_transaction_begin(box);
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen qt->sync_transaction = (flags & MAILBOX_TRANSACTION_FLAG_SYNC) != 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen MODULE_CONTEXT_SET(t, quota_storage_module, qt);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return t;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic int
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenquota_mailbox_transaction_commit(struct mailbox_transaction_context *ctx,
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen struct mail_transaction_commit_changes *changes_r)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_mailbox *qbox = QUOTA_CONTEXT_REQUIRE(ctx->box);
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_transaction_context *qt = QUOTA_CONTEXT_REQUIRE(ctx);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
880f7a015f7dc2df2a06cec91dcecbbbe7e8dce3Timo Sirainen i_assert(qt->tmp_mail == NULL);
12ea3aeeb61df84632f04f86f47902a3750d61f8Timo Sirainen
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen if (qbox->module_ctx.super.transaction_commit(ctx, changes_r) < 0) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen quota_transaction_rollback(&qt);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen (void)quota_transaction_commit(&qt);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic void
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenquota_mailbox_transaction_rollback(struct mailbox_transaction_context *ctx)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_mailbox *qbox = QUOTA_CONTEXT_REQUIRE(ctx->box);
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_transaction_context *qt = QUOTA_CONTEXT_REQUIRE(ctx);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
880f7a015f7dc2df2a06cec91dcecbbbe7e8dce3Timo Sirainen i_assert(qt->tmp_mail == NULL);
26550dc10a8c2057d09894c4e3f9dc12b124e90cTimo Sirainen
26550dc10a8c2057d09894c4e3f9dc12b124e90cTimo Sirainen qbox->module_ctx.super.transaction_rollback(ctx);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen quota_transaction_rollback(&qt);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainenvoid quota_mail_allocated(struct mail *_mail)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(_mail->box);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen struct mail_vfuncs *v = mail->vlast;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen union mail_module_context *qmail;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen if (qbox == NULL)
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen return;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen qmail = p_new(mail->pool, union mail_module_context, 1);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen qmail->super = *v;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen mail->vlast = &qmail->super;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->expunge = quota_mail_expunge;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen MODULE_CONTEXT_SET_SELF(mail, quota_mail_module, qmail);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainenstatic bool
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainenquota_move_requires_check(struct mailbox *dest_box, struct mailbox *src_box)
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen{
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen struct mail_namespace *src_ns = src_box->list->ns;
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen struct mail_namespace *dest_ns = dest_box->list->ns;
9390fe0c1b5b7495363f9194ff701470e242dd0eAki Tuomi struct quota_user *quser = QUOTA_USER_CONTEXT_REQUIRE(src_ns->user);
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen struct quota_root *const *rootp;
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen array_foreach(&quser->quota->roots, rootp) {
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen bool have_src_quota, have_dest_quota;
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen have_src_quota = quota_root_is_namespace_visible(*rootp, src_ns);
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen have_dest_quota = quota_root_is_namespace_visible(*rootp, dest_ns);
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen if (have_src_quota == have_dest_quota) {
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen /* Both/neither have this quota */
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen } else if (have_dest_quota) {
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen /* Destination mailbox has a quota that doesn't exist
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen in source. We'll need to check if it's being
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen exceeded. */
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen return TRUE;
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen } else {
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen /* Source mailbox has a quota root that doesn't exist
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen in destination. We're not increasing the source
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen quota, so ignore it. */
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen }
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen }
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen return FALSE;
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen}
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainenstatic int quota_check(struct mail_save_context *ctx, struct mailbox *src_box)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
406ea4e54dc603e381749b5c29176a4e756e9a99Timo Sirainen struct mailbox_transaction_context *t = ctx->transaction;
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_transaction_context *qt = QUOTA_CONTEXT_REQUIRE(t);
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi enum quota_alloc_result ret;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen i_assert(!ctx->moving || src_box != NULL);
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen if (ctx->moving &&
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen !quota_move_requires_check(ctx->transaction->box, src_box)) {
406ea4e54dc603e381749b5c29176a4e756e9a99Timo Sirainen /* the mail is being moved. the quota won't increase (after
406ea4e54dc603e381749b5c29176a4e756e9a99Timo Sirainen the following expunge), so allow this even if user is
406ea4e54dc603e381749b5c29176a4e756e9a99Timo Sirainen currently over quota */
406ea4e54dc603e381749b5c29176a4e756e9a99Timo Sirainen quota_alloc(qt, ctx->dest_mail);
406ea4e54dc603e381749b5c29176a4e756e9a99Timo Sirainen return 0;
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi } else if (qt->failed) {
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi return 0;
406ea4e54dc603e381749b5c29176a4e756e9a99Timo Sirainen }
406ea4e54dc603e381749b5c29176a4e756e9a99Timo Sirainen
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi const char *error;
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi ret = quota_try_alloc(qt, ctx->dest_mail, &error);
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi switch (ret) {
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi case QUOTA_ALLOC_RESULT_OK:
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return 0;
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi case QUOTA_ALLOC_RESULT_TEMPFAIL:
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi /* Log the error, but allow saving anyway. */
d81702ddd148abde32cbe2a45f6239e1baee6907Timo Sirainen i_error("quota: Failed to check if user is under quota: %s - saving mail anyway", error);
74b0ef7d147bda1e8a2f7b0e9ff0041ab58e2ebaTimo Sirainen return 0;
b14f93ea4f7e9b78e5adbabcc78bd2f63d2bcd36Martti Rannanjärvi case QUOTA_ALLOC_RESULT_BACKGROUND_CALC:
b14f93ea4f7e9b78e5adbabcc78bd2f63d2bcd36Martti Rannanjärvi /* Could not determine if there is enough space due to ongoing
b14f93ea4f7e9b78e5adbabcc78bd2f63d2bcd36Martti Rannanjärvi background quota calculation, allow saving anyway. */
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi i_warning("quota: Failed to check if user is under quota: %s - saving mail anyway", error);
b14f93ea4f7e9b78e5adbabcc78bd2f63d2bcd36Martti Rannanjärvi return 0;
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi default:
0060557e0171750089c3cee3878ddbf67ac7c4e8Martti Rannanjärvi quota_set_storage_error(qt, t->box, ret, error);
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic int
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainenquota_copy(struct mail_save_context *ctx, struct mail *mail)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen struct mailbox_transaction_context *t = ctx->transaction;
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_transaction_context *qt = QUOTA_CONTEXT_REQUIRE(t);
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_mailbox *qbox = QUOTA_CONTEXT_REQUIRE(t->box);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
880f7a015f7dc2df2a06cec91dcecbbbe7e8dce3Timo Sirainen /* we always want to know the mail size */
880f7a015f7dc2df2a06cec91dcecbbbe7e8dce3Timo Sirainen mail_add_temp_wanted_fields(ctx->dest_mail, MAIL_FETCH_PHYSICAL_SIZE, NULL);
880f7a015f7dc2df2a06cec91dcecbbbe7e8dce3Timo Sirainen
9cc53ac30c33f403964cc3f4f51456301dc044bcAki Tuomi /* get quota before copying any mails. this avoids dovecot-vsize.lock
5b5da56e477dd55fbac23be9c96d8baa00482069Timo Sirainen deadlocks with backends that lock mails for expunging/copying. */
c975f4c0c6a767050755e824d5a5ddeb1dbf7f6eMartti Rannanjärvi enum quota_get_result error_res;
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi const char *error;
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi if (quota_transaction_set_limits(qt, &error_res, &error) < 0) {
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi if (error_res == QUOTA_GET_RESULT_BACKGROUND_CALC)
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi i_warning("quota: %s - copying mail anyway", error);
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi else
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi i_error("quota: %s - copying mail anyway", error);
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen if (qbox->module_ctx.super.copy(ctx, mail) < 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
807b06485623fe389f9414ba9f5ed08df01a4b57Timo Sirainen if (ctx->copying_via_save) {
807b06485623fe389f9414ba9f5ed08df01a4b57Timo Sirainen /* copying used saving internally, we already checked the
807b06485623fe389f9414ba9f5ed08df01a4b57Timo Sirainen quota */
807b06485623fe389f9414ba9f5ed08df01a4b57Timo Sirainen return 0;
807b06485623fe389f9414ba9f5ed08df01a4b57Timo Sirainen }
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen return quota_check(ctx, mail->box);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainenstatic int
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainenquota_save_begin(struct mail_save_context *ctx, struct istream *input)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen struct mailbox_transaction_context *t = ctx->transaction;
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_transaction_context *qt = QUOTA_CONTEXT_REQUIRE(t);
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_mailbox *qbox = QUOTA_CONTEXT_REQUIRE(t->box);
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi const char *error;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen uoff_t size;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi if (!ctx->moving && i_stream_get_size(input, TRUE, &size) > 0 &&
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi !qt->failed) {
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 entirely.
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen
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
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi enum quota_alloc_result qret = quota_test_alloc(qt, size, &error);
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi switch (qret) {
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi case QUOTA_ALLOC_RESULT_OK:
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi /* Great, there is space. */
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi break;
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi case QUOTA_ALLOC_RESULT_TEMPFAIL:
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi /* Log the error, but allow saving anyway. */
d81702ddd148abde32cbe2a45f6239e1baee6907Timo Sirainen i_error("quota: Failed to check if user is under quota: %s - saving mail anyway", error);
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi break;
b14f93ea4f7e9b78e5adbabcc78bd2f63d2bcd36Martti Rannanjärvi case QUOTA_ALLOC_RESULT_BACKGROUND_CALC:
b14f93ea4f7e9b78e5adbabcc78bd2f63d2bcd36Martti Rannanjärvi /* Could not determine if there is enough space due to
b14f93ea4f7e9b78e5adbabcc78bd2f63d2bcd36Martti Rannanjärvi * ongoing background quota calculation, allow saving
b14f93ea4f7e9b78e5adbabcc78bd2f63d2bcd36Martti Rannanjärvi * anyway. */
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi i_warning("quota: Failed to check if user is under quota: %s - saving mail anyway", error);
b14f93ea4f7e9b78e5adbabcc78bd2f63d2bcd36Martti Rannanjärvi break;
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi default:
0060557e0171750089c3cee3878ddbf67ac7c4e8Martti Rannanjärvi quota_set_storage_error(qt, t->box, qret, error);
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return -1;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
880f7a015f7dc2df2a06cec91dcecbbbe7e8dce3Timo Sirainen /* we always want to know the mail size */
880f7a015f7dc2df2a06cec91dcecbbbe7e8dce3Timo Sirainen mail_add_temp_wanted_fields(ctx->dest_mail, MAIL_FETCH_PHYSICAL_SIZE, NULL);
880f7a015f7dc2df2a06cec91dcecbbbe7e8dce3Timo Sirainen
9cc53ac30c33f403964cc3f4f51456301dc044bcAki Tuomi /* get quota before copying any mails. this avoids dovecot-vsize.lock
5b5da56e477dd55fbac23be9c96d8baa00482069Timo Sirainen deadlocks with backends that lock mails for expunging/copying. */
c975f4c0c6a767050755e824d5a5ddeb1dbf7f6eMartti Rannanjärvi enum quota_get_result error_res;
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi if (quota_transaction_set_limits(qt, &error_res, &error) < 0) {
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi if (error_res == QUOTA_GET_RESULT_BACKGROUND_CALC)
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi i_warning("quota: %s - saving mail anyway", error);
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi else
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi i_error("quota: %s - saving mail anyway", error);
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi }
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen return qbox->module_ctx.super.save_begin(ctx, input);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainenstatic int quota_save_finish(struct mail_save_context *ctx)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_mailbox *qbox = QUOTA_CONTEXT_REQUIRE(ctx->transaction->box);
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen struct mailbox *src_box;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (qbox->module_ctx.super.save_finish(ctx) < 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen src_box = ctx->copy_src_mail == NULL ? NULL : ctx->copy_src_mail->box;
2ec4ab6f5a1172e86afc72c0f29f470d6fd2bd9aTimo Sirainen return quota_check(ctx, src_box);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainenstatic void quota_mailbox_sync_cleanup(struct quota_mailbox *qbox)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (array_is_created(&qbox->expunge_uids)) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen array_clear(&qbox->expunge_uids);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen array_clear(&qbox->expunge_sizes);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen if (qbox->expunge_qt != NULL && qbox->expunge_qt->tmp_mail != NULL) {
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen mail_free(&qbox->expunge_qt->tmp_mail);
93eb96e09c0fd00a77b3fba185b043cc2c011444Timo Sirainen (void)mailbox_transaction_commit(&qbox->expunge_trans);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen qbox->sync_transaction_expunge = FALSE;
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen}
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainenstatic void quota_mailbox_sync_commit(struct quota_mailbox *qbox)
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen{
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen quota_mailbox_sync_cleanup(qbox);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen if (qbox->expunge_qt != NULL)
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen (void)quota_transaction_commit(&qbox->expunge_qt);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen qbox->recalculate = FALSE;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen}
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenstatic void quota_mailbox_sync_notify(struct mailbox *box, uint32_t uid,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen enum mailbox_sync_type sync_type)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_mailbox *qbox = QUOTA_CONTEXT_REQUIRE(box);
9963bef626fd9ea227fb606e8b1694cdb1ab39aaTimo Sirainen struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box);
9390fe0c1b5b7495363f9194ff701470e242dd0eAki Tuomi struct quota_user *quser = QUOTA_USER_CONTEXT_REQUIRE(box->storage->user);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen const uint32_t *uids;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen const uoff_t *sizep;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen unsigned int i, count;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen uoff_t size;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (qbox->module_ctx.super.sync_notify != NULL)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen qbox->module_ctx.super.sync_notify(box, uid, sync_type);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
f8a63c14a4d2eed05019e5d8ed48fc3d8c976ed4Timo Sirainen if (sync_type != MAILBOX_SYNC_TYPE_EXPUNGE || qbox->recalculate ||
f8a63c14a4d2eed05019e5d8ed48fc3d8c976ed4Timo Sirainen (box->flags & MAILBOX_FLAG_DELETE_UNSAFE) != 0) {
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen if (uid == 0) {
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen /* free the transaction before view syncing begins,
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen otherwise it'll crash. */
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen quota_mailbox_sync_cleanup(qbox);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen return;
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen if (qbox->expunge_qt == NULL) {
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen qbox->expunge_qt = quota_transaction_begin(box);
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen qbox->expunge_qt->sync_transaction =
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen qbox->sync_transaction_expunge;
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen }
f9362223346d10a5866b376ed227b77b26ea4292Timo Sirainen if (qbox->expunge_qt->auto_updating) {
f9362223346d10a5866b376ed227b77b26ea4292Timo Sirainen /* even though backend doesn't care about size/count changes,
f9362223346d10a5866b376ed227b77b26ea4292Timo Sirainen make sure count_used changes so quota_warnings are
f9362223346d10a5866b376ed227b77b26ea4292Timo Sirainen executed */
f9362223346d10a5866b376ed227b77b26ea4292Timo Sirainen quota_free_bytes(qbox->expunge_qt, 0);
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen return;
f9362223346d10a5866b376ed227b77b26ea4292Timo Sirainen }
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen
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 the size. */
e7c85ff1029e1e01740613c2de2766e609195d84Timo Sirainen if (!array_is_created(&qbox->expunge_uids) ||
e7c85ff1029e1e01740613c2de2766e609195d84Timo Sirainen array_is_empty(&qbox->expunge_uids)) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i = count = 0;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen } else {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen uids = array_get(&qbox->expunge_uids, &count);
62f2f6bafd6253443a8ec9715278db60df660254Timo Sirainen for (i = qbox->prev_idx; i < count; i++) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (uids[i] == uid)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen break;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
62f2f6bafd6253443a8ec9715278db60df660254Timo Sirainen if (i >= count) {
62f2f6bafd6253443a8ec9715278db60df660254Timo Sirainen for (i = 0; i < qbox->prev_idx; i++) {
62f2f6bafd6253443a8ec9715278db60df660254Timo Sirainen if (uids[i] == uid)
62f2f6bafd6253443a8ec9715278db60df660254Timo Sirainen break;
62f2f6bafd6253443a8ec9715278db60df660254Timo Sirainen }
62f2f6bafd6253443a8ec9715278db60df660254Timo Sirainen if (i == qbox->prev_idx)
62f2f6bafd6253443a8ec9715278db60df660254Timo Sirainen i = count;
62f2f6bafd6253443a8ec9715278db60df660254Timo Sirainen }
62f2f6bafd6253443a8ec9715278db60df660254Timo Sirainen qbox->prev_idx = i;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (i != count) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* we already know the size */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen sizep = array_idx(&qbox->expunge_sizes, i);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen quota_free_bytes(qbox->expunge_qt, *sizep);
9963bef626fd9ea227fb606e8b1694cdb1ab39aaTimo Sirainen /* FIXME: it's not ideal that we do the vsize update here, but
9963bef626fd9ea227fb606e8b1694cdb1ab39aaTimo Sirainen this is the easiest place for it for now.. maybe the mail
9963bef626fd9ea227fb606e8b1694cdb1ab39aaTimo Sirainen size checking code could be moved to lib-storage */
9963bef626fd9ea227fb606e8b1694cdb1ab39aaTimo Sirainen if (ibox->vsize_update != NULL && quser->quota->set->vsizes)
9963bef626fd9ea227fb606e8b1694cdb1ab39aaTimo Sirainen index_mailbox_vsize_hdr_expunge(ibox->vsize_update, uid, *sizep);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen return;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* try to look up the size. this works only if it's cached. */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (qbox->expunge_qt->tmp_mail == NULL) {
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. */
6ae329de09afb7214c906d762320847e05469d53Timo Sirainen struct mail_index_view *box_view = box->view;
6ae329de09afb7214c906d762320847e05469d53Timo Sirainen if (box->tmp_sync_view != NULL)
6ae329de09afb7214c906d762320847e05469d53Timo Sirainen box->view = box->tmp_sync_view;
0dab9cb35a976c49b28a11e28d5570f5191f1a7aMartti Rannanjärvi qbox->expunge_trans = mailbox_transaction_begin(box, 0, "quota");
6ae329de09afb7214c906d762320847e05469d53Timo Sirainen box->view = box_view;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen qbox->expunge_qt->tmp_mail =
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen mail_alloc(qbox->expunge_trans,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen MAIL_FETCH_PHYSICAL_SIZE, NULL);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen if (!mail_set_uid(qbox->expunge_qt->tmp_mail, uid))
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen ;
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen else if (!quser->quota->set->vsizes) {
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen if (mail_get_physical_size(qbox->expunge_qt->tmp_mail, &size) == 0) {
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen quota_free_bytes(qbox->expunge_qt, size);
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen return;
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen }
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen } else if (mail_get_virtual_size(qbox->expunge_qt->tmp_mail, &size) == 0) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen quota_free_bytes(qbox->expunge_qt, size);
9963bef626fd9ea227fb606e8b1694cdb1ab39aaTimo Sirainen if (ibox->vsize_update != NULL)
9963bef626fd9ea227fb606e8b1694cdb1ab39aaTimo Sirainen index_mailbox_vsize_hdr_expunge(ibox->vsize_update, uid, size);
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen } else {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* there's no way to get the size. recalculate the quota. */
39dea5f2e78f6bfc3adc0655176f596ee211938fTimo Sirainen quota_recalculate(qbox->expunge_qt, QUOTA_RECALCULATE_MISSING_FREES);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen qbox->recalculate = TRUE;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen}
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenstatic int quota_mailbox_sync_deinit(struct mailbox_sync_context *ctx,
2584e86cc2d8c31ba30a4109cf4ba09d1e37e28aTimo Sirainen struct mailbox_sync_status *status_r)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_mailbox *qbox = QUOTA_CONTEXT_REQUIRE(ctx->box);
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen int ret;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
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. */
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen quota_mailbox_sync_commit(qbox);
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen return ret;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen}
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainenstatic void quota_roots_flush(struct quota *quota)
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen{
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen struct quota_root *const *roots;
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen unsigned int i, count;
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen roots = array_get(&quota->roots, &count);
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen for (i = 0; i < count; i++) {
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen if (roots[i]->backend.v.flush != NULL)
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen roots[i]->backend.v.flush(roots[i]);
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen }
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen}
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainenstatic void quota_mailbox_close(struct mailbox *box)
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen{
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_mailbox *qbox = QUOTA_CONTEXT_REQUIRE(box);
9390fe0c1b5b7495363f9194ff701470e242dd0eAki Tuomi struct quota_user *quser = QUOTA_USER_CONTEXT_REQUIRE(box->storage->user);
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen /* sync_notify() may be called outside sync_begin()..sync_deinit().
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen make sure we apply changes at close time at latest. */
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen quota_mailbox_sync_commit(qbox);
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen /* make sure quota backend flushes all data. this could also be done
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen somewhat later, but user.deinit() is too late, since the flushing
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen can trigger quota recalculation which isn't safe to do anymore
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen at user.deinit() when most of the loaded plugins have already been
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen deinitialized. */
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen quota_roots_flush(quser->quota);
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen qbox->module_ctx.super.close(box);
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen}
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen
e03d986a74128f5ba30fcfda9f6e36578f5d8decTimo Sirainenstatic void quota_mailbox_free(struct mailbox *box)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi struct quota_mailbox *qbox = QUOTA_CONTEXT_REQUIRE(box);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (array_is_created(&qbox->expunge_uids)) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen array_free(&qbox->expunge_uids);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen array_free(&qbox->expunge_sizes);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_assert(qbox->expunge_qt == NULL ||
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen qbox->expunge_qt->tmp_mail == NULL);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
e03d986a74128f5ba30fcfda9f6e36578f5d8decTimo Sirainen qbox->module_ctx.super.free(box);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen}
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
ea9d9d99948cff5f9b881f79b28fa3b80da0f2a7Timo Sirainenvoid quota_mailbox_allocated(struct mailbox *box)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen struct mailbox_vfuncs *v = box->vlast;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct quota_mailbox *qbox;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
ea9d9d99948cff5f9b881f79b28fa3b80da0f2a7Timo Sirainen if (QUOTA_LIST_CONTEXT(box->list) == NULL)
ea9d9d99948cff5f9b881f79b28fa3b80da0f2a7Timo Sirainen return;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) != 0)
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen return;
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen qbox = p_new(box->pool, struct quota_mailbox, 1);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen qbox->module_ctx.super = *v;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen box->vlast = &qbox->module_ctx.super;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen
9357960a30d4936de5e261bf522d83f7d3f49986Timo Sirainen v->get_status = quota_get_status;
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;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->save_begin = quota_save_begin;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->save_finish = quota_save_finish;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->copy = quota_copy;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->sync_notify = quota_mailbox_sync_notify;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->sync_deinit = quota_mailbox_sync_deinit;
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen v->close = quota_mailbox_close;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->free = quota_mailbox_free;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen MODULE_CONTEXT_SET(box, quota_storage_module, qbox);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic void quota_mailbox_list_deinit(struct mailbox_list *list)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(list);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
fe6356ecbb1c64802f121a37d2e4c6199fe67074Aki Tuomi i_assert(qlist != NULL);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen quota_remove_user_namespace(list->ns);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen qlist->module_ctx.super.deinit(list);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenstruct quota *quota_get_mail_user_quota(struct mail_user *user)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct quota_user *quser = QUOTA_USER_CONTEXT(user);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen return quser == NULL ? NULL : quser->quota;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen}
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenstatic void quota_user_deinit(struct mail_user *user)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen{
9390fe0c1b5b7495363f9194ff701470e242dd0eAki Tuomi struct quota_user *quser = QUOTA_USER_CONTEXT_REQUIRE(user);
bb34c6538784b564c05eefccc74af2bd7c9505f5Timo Sirainen struct quota_settings *quota_set = quser->quota->set;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen quota_deinit(&quser->quota);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen quser->module_ctx.super.deinit(user);
bb34c6538784b564c05eefccc74af2bd7c9505f5Timo Sirainen
bb34c6538784b564c05eefccc74af2bd7c9505f5Timo Sirainen quota_settings_deinit(&quota_set);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen}
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenvoid quota_mail_user_created(struct mail_user *user)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen{
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen struct mail_user_vfuncs *v = user->vlast;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct quota_user *quser;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct quota_settings *set;
4316355ca8b7698516272520a972291378698140Timo Sirainen struct quota *quota;
4316355ca8b7698516272520a972291378698140Timo Sirainen const char *error;
4316355ca8b7698516272520a972291378698140Timo Sirainen int ret;
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen if ((ret = quota_user_read_settings(user, &set, &error)) > 0) {
4316355ca8b7698516272520a972291378698140Timo Sirainen if (quota_init(set, user, &quota, &error) < 0) {
4316355ca8b7698516272520a972291378698140Timo Sirainen quota_settings_deinit(&set);
4316355ca8b7698516272520a972291378698140Timo Sirainen ret = -1;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen if (ret < 0) {
4316355ca8b7698516272520a972291378698140Timo Sirainen user->error = p_strdup_printf(user->pool,
4316355ca8b7698516272520a972291378698140Timo Sirainen "Failed to initialize quota: %s", error);
4316355ca8b7698516272520a972291378698140Timo Sirainen return;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen if (ret > 0) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen quser = p_new(user->pool, struct quota_user, 1);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen quser->module_ctx.super = *v;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen user->vlast = &quser->module_ctx.super;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->deinit = quota_user_deinit;
4316355ca8b7698516272520a972291378698140Timo Sirainen quser->quota = quota;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen MODULE_CONTEXT_SET(user, quota_user_module, quser);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen } else if (user->mail_debug) {
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk i_debug("quota: No quota setting - plugin disabled");
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen}
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainenstatic struct quota_root *
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainenquota_find_root_for_ns(struct quota *quota, struct mail_namespace *ns)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen{
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen struct quota_root *const *roots;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen unsigned int i, count;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen roots = array_get(&quota->roots, &count);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen for (i = 0; i < count; i++) {
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if (roots[i]->ns_prefix != NULL &&
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen strcmp(roots[i]->ns_prefix, ns->prefix) == 0)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen return roots[i];
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen }
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen return NULL;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen}
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainenvoid quota_mailbox_list_created(struct mailbox_list *list)
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen{
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen struct quota_mailbox_list *qlist;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct quota *quota = NULL;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen struct quota_root *root;
1f5670bce3e5ec0c1ddf8d95c57ebb428f1339f6Timo Sirainen struct mail_user *quota_user;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen bool add;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
805872b9f885c1cc4bddf0b6ff71ca61f6cdd7bfTimo Sirainen /* see if we have a quota explicitly defined for this namespace */
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen quota = quota_get_mail_user_quota(list->ns->user);
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen if (quota == NULL)
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen return;
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen root = quota_find_root_for_ns(quota, list->ns);
1f5670bce3e5ec0c1ddf8d95c57ebb428f1339f6Timo Sirainen if (root != NULL) {
1f5670bce3e5ec0c1ddf8d95c57ebb428f1339f6Timo Sirainen /* explicit quota root */
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen root->ns = list->ns;
1f5670bce3e5ec0c1ddf8d95c57ebb428f1339f6Timo Sirainen quota_user = list->ns->user;
1f5670bce3e5ec0c1ddf8d95c57ebb428f1339f6Timo Sirainen } else {
1f5670bce3e5ec0c1ddf8d95c57ebb428f1339f6Timo Sirainen quota_user = list->ns->owner != NULL ?
1f5670bce3e5ec0c1ddf8d95c57ebb428f1339f6Timo Sirainen list->ns->owner : list->ns->user;
1f5670bce3e5ec0c1ddf8d95c57ebb428f1339f6Timo Sirainen }
805872b9f885c1cc4bddf0b6ff71ca61f6cdd7bfTimo Sirainen
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen if ((list->ns->flags & NAMESPACE_FLAG_NOQUOTA) != 0)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen add = FALSE;
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen else if (list->ns->owner == NULL) {
805872b9f885c1cc4bddf0b6ff71ca61f6cdd7bfTimo Sirainen /* public namespace - add quota only if namespace is
805872b9f885c1cc4bddf0b6ff71ca61f6cdd7bfTimo Sirainen explicitly defined for it */
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen add = root != NULL;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen } else {
1f5670bce3e5ec0c1ddf8d95c57ebb428f1339f6Timo Sirainen /* for shared namespaces add only if the owner has quota
1f5670bce3e5ec0c1ddf8d95c57ebb428f1339f6Timo Sirainen enabled */
1f5670bce3e5ec0c1ddf8d95c57ebb428f1339f6Timo Sirainen add = QUOTA_USER_CONTEXT(quota_user) != NULL;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen }
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if (add) {
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen struct mailbox_list_vfuncs *v = list->vlast;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen
f7fcdca120f1b591f885408d08fc90cfd725bf94Timo Sirainen qlist = p_new(list->pool, struct quota_mailbox_list, 1);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen qlist->module_ctx.super = *v;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen list->vlast = &qlist->module_ctx.super;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen v->deinit = quota_mailbox_list_deinit;
f7fcdca120f1b591f885408d08fc90cfd725bf94Timo Sirainen MODULE_CONTEXT_SET(list, quota_mailbox_list_module, qlist);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
1f5670bce3e5ec0c1ddf8d95c57ebb428f1339f6Timo Sirainen quota = quota_get_mail_user_quota(quota_user);
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen i_assert(quota != NULL);
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen quota_add_user_namespace(quota, list->ns);
f7fcdca120f1b591f885408d08fc90cfd725bf94Timo Sirainen }
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen}
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainenstatic void quota_root_set_namespace(struct quota_root *root,
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen struct mail_namespace *namespaces)
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen{
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen const struct quota_rule *rule;
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen const char *name;
9b706b345064ce8e8a657f54633f009a101298eaTimo Sirainen struct mail_namespace *ns;
9a30df5ac82378e2706715eaf3ee6653d8b46161Timo Sirainen /* silence errors for autocreated (shared) users */
9a30df5ac82378e2706715eaf3ee6653d8b46161Timo Sirainen bool silent_errors = namespaces->user->autocreated;
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen if (root->ns_prefix != NULL && root->ns == NULL) {
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen root->ns = mail_namespace_find_prefix(namespaces,
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen root->ns_prefix);
9a30df5ac82378e2706715eaf3ee6653d8b46161Timo Sirainen if (root->ns == NULL && !silent_errors) {
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen i_error("quota: Unknown namespace: %s",
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen root->ns_prefix);
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen }
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen }
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen array_foreach(&root->set->rules, rule) {
81e4bda7d481c57cd049a0a68daab733b1ca9c44Timo Sirainen name = rule->mailbox_mask;
9b706b345064ce8e8a657f54633f009a101298eaTimo Sirainen ns = mail_namespace_find(namespaces, name);
9a30df5ac82378e2706715eaf3ee6653d8b46161Timo Sirainen if ((ns->flags & NAMESPACE_FLAG_UNUSABLE) != 0 &&
9a30df5ac82378e2706715eaf3ee6653d8b46161Timo Sirainen !silent_errors)
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen i_error("quota: Unknown namespace: %s", name);
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen }
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen}
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainenvoid quota_mail_namespaces_created(struct mail_namespace *namespaces)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen{
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen struct quota *quota;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen struct quota_root *const *roots;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen unsigned int i, count;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen quota = quota_get_mail_user_quota(namespaces->user);
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen if (quota == NULL)
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen return;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen roots = array_get(&quota->roots, &count);
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen for (i = 0; i < count; i++)
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen quota_root_set_namespace(roots[i], namespaces);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen quota_over_flag_check_startup(quota);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen}