quota-storage.c revision 2ed2459dbd183bb371da4a0aecb2d2b74ae7c815
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov/* Copyright (c) 2005-2014 Dovecot authors, see the included COPYING file */
d82741b1a8ada493ca74efa5d5c8b731412d035cLukas Slebodnik
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include "lib.h"
4e17c050dac8f2c6e2d278c4c4a27001c8d7d164Jakub Hrozek#include "array.h"
1921d739ff7b028baa591272cc8969e330c8f872Jakub Hrozek#include "istream.h"
38b07019861240cf5107f5d51fc0027519e21619Lukas Slebodnik#include "mail-search-build.h"
b8946a5dbde01a87465de707092716349a35248bJakub Hrozek#include "mail-storage-private.h"
b4633e73067d7bf3b0dbaf212569c123de88f306Lukas Slebodnik#include "mailbox-list-private.h"
8578fba1500d43ad9632784462c255bf8bb360feJakub Hrozek#include "maildir-storage.h"
3728db53ac32da51fcaae96b132e8e56ebbaebfaJakub Hrozek#include "quota-private.h"
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include "quota-plugin.h"
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include <sys/stat.h>
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#define QUOTA_CONTEXT(obj) \
8d1dcb6af723f2968410c4b088d06d63d02b4feaPavel Reichl MODULE_CONTEXT(obj, quota_storage_module)
586f512ab8b6e5a03349598846141f43c1d505b8Michal Židek#define QUOTA_MAIL_CONTEXT(obj) \
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov MODULE_CONTEXT(obj, quota_mail_module)
1f4dc2971bac4ceb0803b18f86a746656a0f1990Lukas Slebodnik#define QUOTA_LIST_CONTEXT(obj) \
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik MODULE_CONTEXT(obj, quota_mailbox_list_module)
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik
53a4219e2f51cd0443931aa931505bf0b4bf5a45Nikolai Kondrashovstruct quota_mailbox_list {
b8946a5dbde01a87465de707092716349a35248bJakub Hrozek union mailbox_list_module_context module_ctx;
05457ed0e399aaacc919b7aacee5d8210e1c1072Petr Cech};
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekstruct quota_mailbox {
35ecfab87a24031e55798b22975e02832ee0f2adMichal Židek union mailbox_module_context module_ctx;
8bdb8c0970dc9acb5b0a54dab0bae306ca964944Jakub Hrozek
8bdb8c0970dc9acb5b0a54dab0bae306ca964944Jakub Hrozek struct mailbox_transaction_context *expunge_trans;
8bdb8c0970dc9acb5b0a54dab0bae306ca964944Jakub Hrozek struct quota_transaction_context *expunge_qt;
0700118d8388c38b8cb28279510b206b76a3a411Jakub Hrozek ARRAY(uint32_t) expunge_uids;
0700118d8388c38b8cb28279510b206b76a3a411Jakub Hrozek ARRAY(uoff_t) expunge_sizes;
0700118d8388c38b8cb28279510b206b76a3a411Jakub Hrozek
1f331476e7d33bb03cc35a2a9064ee1cc5bed6cfSumit Bose unsigned int recalculate:1;
36df33cd44774a5b5eab52ab222bcd3240b3ca5aLukas Slebodnik unsigned int sync_transaction_expunge:1;
36df33cd44774a5b5eab52ab222bcd3240b3ca5aLukas Slebodnik};
da7a3c347dd630085839afa7ec245ee9d36f6ce2Lukas Slebodnik
0df79781d78973e5462dbef1e89d8fd6001da05cLukas Slebodnikstruct quota_user_module quota_user_module =
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov MODULE_CONTEXT_INIT(&mail_user_module_register);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnikstatic MODULE_CONTEXT_DEFINE_INIT(quota_storage_module,
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik &mail_storage_module_register);
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnikstatic MODULE_CONTEXT_DEFINE_INIT(quota_mail_module, &mail_module_register);
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnikstatic MODULE_CONTEXT_DEFINE_INIT(quota_mailbox_list_module,
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik &mailbox_list_module_register);
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnikstatic void quota_mail_expunge(struct mail *_mail)
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik{
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik struct mail_private *mail = (struct mail_private *)_mail;
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik struct quota_mailbox *qbox = QUOTA_CONTEXT(_mail->box);
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik union mail_module_context *qmail = QUOTA_MAIL_CONTEXT(mail);
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik uoff_t size;
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik /* We need to handle the situation where multiple transactions expunged
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik the mail at the same time. In here we'll just save the message's
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik physical size and do the quota freeing later when the message was
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik known to be expunged. */
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik if (mail_get_physical_size(_mail, &size) == 0) {
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik if (!array_is_created(&qbox->expunge_uids)) {
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik i_array_init(&qbox->expunge_uids, 64);
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik i_array_init(&qbox->expunge_sizes, 64);
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik }
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik array_append(&qbox->expunge_uids, &_mail->uid, 1);
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik array_append(&qbox->expunge_sizes, &size, 1);
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik if ((_mail->transaction->flags & MAILBOX_TRANSACTION_FLAG_SYNC) != 0) {
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik /* we're running dsync. if this brings the quota below
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik a negative quota warning, don't execute it, because
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik it probably was already executed by the replica. */
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik qbox->sync_transaction_expunge = TRUE;
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik } else {
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik qbox->sync_transaction_expunge = FALSE;
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik }
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov }
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov qmail->super.expunge(_mail);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov}
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashovstatic int
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashovquota_get_status(struct mailbox *box, enum mailbox_status_items items,
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek struct mailbox_status *status_r)
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek{
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
91b0592cdab22915dff27ceae6d8e49c608aea4aJakub Hrozek struct quota_transaction_context *qt;
53a4219e2f51cd0443931aa931505bf0b4bf5a45Nikolai Kondrashov bool too_large;
a3bed9df5a47bfc84b82341f0f7e693e2b14a67aLukas Slebodnik int ret = 0;
a3bed9df5a47bfc84b82341f0f7e693e2b14a67aLukas Slebodnik
a3bed9df5a47bfc84b82341f0f7e693e2b14a67aLukas Slebodnik if ((items & STATUS_CHECK_OVER_QUOTA) != 0) {
a3bed9df5a47bfc84b82341f0f7e693e2b14a67aLukas Slebodnik qt = quota_transaction_begin(box);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if ((ret = quota_test_alloc(qt, 0, &too_large)) == 0) {
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov mail_storage_set_error(box->storage, MAIL_ERROR_NOQUOTA,
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov qt->quota->set->quota_exceeded_msg);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov ret = -1;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov }
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov quota_transaction_rollback(&qt);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if ((items & ~STATUS_CHECK_OVER_QUOTA) == 0) {
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov /* don't bother calling parent, it may unnecessarily
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov try to open the mailbox */
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov return ret < 0 ? -1 : 0;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov }
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov }
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if (qbox->module_ctx.super.get_status(box, items, status_r) < 0)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov ret = -1;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov return ret < 0 ? -1 : 0;
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik}
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashovstatic struct mailbox_transaction_context *
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashovquota_mailbox_transaction_begin(struct mailbox *box,
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov enum mailbox_transaction_flags flags)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov{
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov struct mailbox_transaction_context *t;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov struct quota_transaction_context *qt;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov t = qbox->module_ctx.super.transaction_begin(box, flags);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov qt = quota_transaction_begin(box);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov qt->sync_transaction = (flags & MAILBOX_TRANSACTION_FLAG_SYNC) != 0;
82c36227e36de155b13e6eb7cfa3e80a25774157Lukas Slebodnik
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov MODULE_CONTEXT_SET(t, quota_storage_module, qt);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov return t;
841bcb5e1f66bb9c41e1884a2ab1dae654def13eLukas Slebodnik}
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashovstatic int
f75ba99fc8dd64e45af2f642d9fb7660860fd28fLukas Slebodnikquota_mailbox_transaction_commit(struct mailbox_transaction_context *ctx,
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik struct mail_transaction_commit_changes *changes_r)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov{
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if (qt->tmp_mail != NULL)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov mail_free(&qt->tmp_mail);
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik if (qbox->module_ctx.super.transaction_commit(ctx, changes_r) < 0) {
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik quota_transaction_rollback(&qt);
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik return -1;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov } else {
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov (void)quota_transaction_commit(&qt);
return 0;
}
}
static void
quota_mailbox_transaction_rollback(struct mailbox_transaction_context *ctx)
{
struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);
if (qt->tmp_mail != NULL)
mail_free(&qt->tmp_mail);
qbox->module_ctx.super.transaction_rollback(ctx);
quota_transaction_rollback(&qt);
}
void quota_mail_allocated(struct mail *_mail)
{
struct quota_mailbox *qbox = QUOTA_CONTEXT(_mail->box);
struct mail_private *mail = (struct mail_private *)_mail;
struct mail_vfuncs *v = mail->vlast;
union mail_module_context *qmail;
if (qbox == NULL)
return;
qmail = p_new(mail->pool, union mail_module_context, 1);
qmail->super = *v;
mail->vlast = &qmail->super;
v->expunge = quota_mail_expunge;
MODULE_CONTEXT_SET_SELF(mail, quota_mail_module, qmail);
}
static int quota_check(struct mail_save_context *ctx)
{
struct mailbox_transaction_context *t = ctx->transaction;
struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
int ret;
bool too_large;
if (ctx->moving) {
/* the mail is being moved. the quota won't increase (after
the following expunge), so allow this even if user is
currently over quota */
quota_alloc(qt, ctx->dest_mail);
return 0;
}
ret = quota_try_alloc(qt, ctx->dest_mail, &too_large);
if (ret > 0)
return 0;
else if (ret == 0) {
mail_storage_set_error(t->box->storage, MAIL_ERROR_NOQUOTA,
qt->quota->set->quota_exceeded_msg);
return -1;
} else {
mail_storage_set_critical(t->box->storage,
"Internal quota calculation error");
/* allow saving anyway */
return 0;
}
}
static int
quota_copy(struct mail_save_context *ctx, struct mail *mail)
{
struct mailbox_transaction_context *t = ctx->transaction;
struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
if (ctx->dest_mail == NULL) {
/* we always want to know the mail size */
if (qt->tmp_mail == NULL) {
qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
NULL);
}
ctx->dest_mail = qt->tmp_mail;
}
if (qbox->module_ctx.super.copy(ctx, mail) < 0)
return -1;
if (ctx->copying_via_save) {
/* copying used saving internally, we already checked the
quota */
return 0;
}
return quota_check(ctx);
}
static int
quota_save_begin(struct mail_save_context *ctx, struct istream *input)
{
struct mailbox_transaction_context *t = ctx->transaction;
struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
uoff_t size;
int ret;
if (!ctx->moving && i_stream_get_size(input, TRUE, &size) > 0) {
/* Input size is known, check for quota immediately. This
check isn't perfect, especially because input stream's
linefeeds may contain CR+LFs while physical message would
only contain LFs. With mbox some headers might be skipped
entirely.
I think these don't really matter though compared to the
benefit of giving "out of quota" error before sending the
full mail. */
bool too_large;
ret = quota_test_alloc(qt, size, &too_large);
if (ret == 0) {
mail_storage_set_error(t->box->storage,
MAIL_ERROR_NOQUOTA,
qt->quota->set->quota_exceeded_msg);
return -1;
} else if (ret < 0) {
mail_storage_set_critical(t->box->storage,
"Internal quota calculation error");
/* allow saving anyway */
}
}
if (ctx->dest_mail == NULL) {
/* we always want to know the mail size */
if (qt->tmp_mail == NULL) {
qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
NULL);
}
ctx->dest_mail = qt->tmp_mail;
}
return qbox->module_ctx.super.save_begin(ctx, input);
}
static int quota_save_finish(struct mail_save_context *ctx)
{
struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->transaction->box);
if (qbox->module_ctx.super.save_finish(ctx) < 0)
return -1;
return quota_check(ctx);
}
static void quota_mailbox_sync_cleanup(struct quota_mailbox *qbox)
{
if (array_is_created(&qbox->expunge_uids)) {
array_clear(&qbox->expunge_uids);
array_clear(&qbox->expunge_sizes);
}
if (qbox->expunge_qt != NULL && qbox->expunge_qt->tmp_mail != NULL) {
mail_free(&qbox->expunge_qt->tmp_mail);
mailbox_transaction_rollback(&qbox->expunge_trans);
}
qbox->sync_transaction_expunge = FALSE;
}
static void quota_mailbox_sync_commit(struct quota_mailbox *qbox)
{
quota_mailbox_sync_cleanup(qbox);
if (qbox->expunge_qt != NULL)
(void)quota_transaction_commit(&qbox->expunge_qt);
qbox->recalculate = FALSE;
}
static void quota_mailbox_sync_notify(struct mailbox *box, uint32_t uid,
enum mailbox_sync_type sync_type)
{
struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
const uint32_t *uids;
const uoff_t *sizep;
unsigned int i, count;
uoff_t size;
if (qbox->module_ctx.super.sync_notify != NULL)
qbox->module_ctx.super.sync_notify(box, uid, sync_type);
if (sync_type != MAILBOX_SYNC_TYPE_EXPUNGE || qbox->recalculate) {
if (uid == 0) {
/* free the transaction before view syncing begins,
otherwise it'll crash. */
quota_mailbox_sync_cleanup(qbox);
}
return;
}
/* we're in the middle of syncing the mailbox, so it's a bad idea to
try and get the message sizes at this point. Rely on sizes that
we saved earlier, or recalculate the whole quota if we don't know
the size. */
if (!array_is_created(&qbox->expunge_uids)) {
i = count = 0;
} else {
uids = array_get(&qbox->expunge_uids, &count);
for (i = 0; i < count; i++) {
if (uids[i] == uid)
break;
}
}
if (qbox->expunge_qt == NULL) {
qbox->expunge_qt = quota_transaction_begin(box);
qbox->expunge_qt->sync_transaction =
qbox->sync_transaction_expunge;
}
if (i != count) {
/* we already know the size */
sizep = array_idx(&qbox->expunge_sizes, i);
quota_free_bytes(qbox->expunge_qt, *sizep);
return;
}
/* try to look up the size. this works only if it's cached. */
if (qbox->expunge_qt->tmp_mail == NULL) {
/* FIXME: ugly kludge to open the transaction for sync_view.
box->view may not have all the new messages that
sync_notify() notifies about, and those messages would
cause a quota recalculation. */
struct mail_index_view *box_view = box->view;
if (box->tmp_sync_view != NULL)
box->view = box->tmp_sync_view;
qbox->expunge_trans = mailbox_transaction_begin(box, 0);
box->view = box_view;
qbox->expunge_qt->tmp_mail =
mail_alloc(qbox->expunge_trans,
MAIL_FETCH_PHYSICAL_SIZE, NULL);
}
if (mail_set_uid(qbox->expunge_qt->tmp_mail, uid) &&
mail_get_physical_size(qbox->expunge_qt->tmp_mail, &size) == 0)
quota_free_bytes(qbox->expunge_qt, size);
else {
/* there's no way to get the size. recalculate the quota. */
quota_recalculate(qbox->expunge_qt);
qbox->recalculate = TRUE;
}
}
static int quota_mailbox_sync_deinit(struct mailbox_sync_context *ctx,
struct mailbox_sync_status *status_r)
{
struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
int ret;
ret = qbox->module_ctx.super.sync_deinit(ctx, status_r);
/* update quota only after syncing is finished. the quota commit may
recalculate the quota and cause all mailboxes to be synced,
including the one we're already syncing. */
quota_mailbox_sync_commit(qbox);
return ret;
}
static void quota_roots_flush(struct quota *quota)
{
struct quota_root *const *roots;
unsigned int i, count;
roots = array_get(&quota->roots, &count);
for (i = 0; i < count; i++) {
if (roots[i]->backend.v.flush != NULL)
roots[i]->backend.v.flush(roots[i]);
}
}
static void quota_mailbox_close(struct mailbox *box)
{
struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
struct quota_user *quser = QUOTA_USER_CONTEXT(box->storage->user);
/* sync_notify() may be called outside sync_begin()..sync_deinit().
make sure we apply changes at close time at latest. */
quota_mailbox_sync_commit(qbox);
/* make sure quota backend flushes all data. this could also be done
somewhat later, but user.deinit() is too late, since the flushing
can trigger quota recalculation which isn't safe to do anymore
at user.deinit() when most of the loaded plugins have already been
deinitialized. */
quota_roots_flush(quser->quota);
qbox->module_ctx.super.close(box);
}
static void quota_mailbox_free(struct mailbox *box)
{
struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
if (array_is_created(&qbox->expunge_uids)) {
array_free(&qbox->expunge_uids);
array_free(&qbox->expunge_sizes);
}
i_assert(qbox->expunge_qt == NULL ||
qbox->expunge_qt->tmp_mail == NULL);
qbox->module_ctx.super.free(box);
}
void quota_mailbox_allocated(struct mailbox *box)
{
struct mailbox_vfuncs *v = box->vlast;
struct quota_mailbox *qbox;
if (QUOTA_LIST_CONTEXT(box->list) == NULL)
return;
if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) != 0)
return;
qbox = p_new(box->pool, struct quota_mailbox, 1);
qbox->module_ctx.super = *v;
box->vlast = &qbox->module_ctx.super;
v->get_status = quota_get_status;
v->transaction_begin = quota_mailbox_transaction_begin;
v->transaction_commit = quota_mailbox_transaction_commit;
v->transaction_rollback = quota_mailbox_transaction_rollback;
v->save_begin = quota_save_begin;
v->save_finish = quota_save_finish;
v->copy = quota_copy;
v->sync_notify = quota_mailbox_sync_notify;
v->sync_deinit = quota_mailbox_sync_deinit;
v->close = quota_mailbox_close;
v->free = quota_mailbox_free;
MODULE_CONTEXT_SET(box, quota_storage_module, qbox);
}
static void quota_mailbox_list_deinit(struct mailbox_list *list)
{
struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(list);
quota_remove_user_namespace(list->ns);
qlist->module_ctx.super.deinit(list);
}
struct quota *quota_get_mail_user_quota(struct mail_user *user)
{
struct quota_user *quser = QUOTA_USER_CONTEXT(user);
return quser->quota;
}
static void quota_user_deinit(struct mail_user *user)
{
struct quota_user *quser = QUOTA_USER_CONTEXT(user);
struct quota_settings *quota_set = quser->quota->set;
quota_deinit(&quser->quota);
quser->module_ctx.super.deinit(user);
quota_settings_deinit(&quota_set);
}
void quota_mail_user_created(struct mail_user *user)
{
struct mail_user_vfuncs *v = user->vlast;
struct quota_user *quser;
struct quota_settings *set;
struct quota *quota;
const char *error;
int ret;
if ((ret = quota_user_read_settings(user, &set, &error)) > 0) {
if (quota_init(set, user, &quota, &error) < 0) {
quota_settings_deinit(&set);
ret = -1;
}
}
if (ret < 0) {
user->error = p_strdup_printf(user->pool,
"Failed to initialize quota: %s", error);
return;
}
if (ret > 0) {
quser = p_new(user->pool, struct quota_user, 1);
quser->module_ctx.super = *v;
user->vlast = &quser->module_ctx.super;
v->deinit = quota_user_deinit;
quser->quota = quota;
MODULE_CONTEXT_SET(user, quota_user_module, quser);
} else if (user->mail_debug) {
i_debug("quota: No quota setting - plugin disabled");
}
}
static struct quota_root *
quota_find_root_for_ns(struct quota *quota, struct mail_namespace *ns)
{
struct quota_root *const *roots;
unsigned int i, count;
roots = array_get(&quota->roots, &count);
for (i = 0; i < count; i++) {
if (roots[i]->ns_prefix != NULL &&
strcmp(roots[i]->ns_prefix, ns->prefix) == 0)
return roots[i];
}
return NULL;
}
void quota_mailbox_list_created(struct mailbox_list *list)
{
struct quota_mailbox_list *qlist;
struct quota *quota = NULL;
struct quota_root *root;
struct mail_user *quota_user;
bool add;
if (QUOTA_USER_CONTEXT(list->ns->user) == NULL)
return;
/* see if we have a quota explicitly defined for this namespace */
quota = quota_get_mail_user_quota(list->ns->user);
root = quota_find_root_for_ns(quota, list->ns);
if (root != NULL) {
/* explicit quota root */
root->ns = list->ns;
quota_user = list->ns->user;
} else {
quota_user = list->ns->owner != NULL ?
list->ns->owner : list->ns->user;
}
if ((list->ns->flags & NAMESPACE_FLAG_NOQUOTA) != 0)
add = FALSE;
else if (list->ns->owner == NULL) {
/* public namespace - add quota only if namespace is
explicitly defined for it */
add = root != NULL;
} else {
/* for shared namespaces add only if the owner has quota
enabled */
add = QUOTA_USER_CONTEXT(quota_user) != NULL;
}
if (add) {
struct mailbox_list_vfuncs *v = list->vlast;
qlist = p_new(list->pool, struct quota_mailbox_list, 1);
qlist->module_ctx.super = *v;
list->vlast = &qlist->module_ctx.super;
v->deinit = quota_mailbox_list_deinit;
MODULE_CONTEXT_SET(list, quota_mailbox_list_module, qlist);
quota = quota_get_mail_user_quota(quota_user);
quota_add_user_namespace(quota, list->ns);
}
}
static void quota_root_set_namespace(struct quota_root *root,
struct mail_namespace *namespaces)
{
const struct quota_rule *rule;
const char *name;
struct mail_namespace *ns;
/* silence errors for autocreated (shared) users */
bool silent_errors = namespaces->user->autocreated;
if (root->ns_prefix != NULL && root->ns == NULL) {
root->ns = mail_namespace_find_prefix(namespaces,
root->ns_prefix);
if (root->ns == NULL && !silent_errors) {
i_error("quota: Unknown namespace: %s",
root->ns_prefix);
}
}
array_foreach(&root->set->rules, rule) {
name = rule->mailbox_mask;
ns = mail_namespace_find(namespaces, name);
if ((ns->flags & NAMESPACE_FLAG_UNUSABLE) != 0 &&
!silent_errors)
i_error("quota: Unknown namespace: %s", name);
}
}
void quota_mail_namespaces_created(struct mail_namespace *namespaces)
{
struct quota *quota;
struct quota_root *const *roots;
unsigned int i, count;
if (QUOTA_USER_CONTEXT(namespaces->user) == NULL)
return;
quota = quota_get_mail_user_quota(namespaces->user);
roots = array_get(&quota->roots, &count);
for (i = 0; i < count; i++)
quota_root_set_namespace(roots[i], namespaces);
}