quota-storage.c revision 5f5870385cff47efd2f58e7892f251cf13761528
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2005-2012 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "lib.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "array.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "istream.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "mail-search-build.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "mail-storage-private.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "mailbox-list-private.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "maildir-storage.h"
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen#include "quota-private.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "quota-plugin.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <sys/stat.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define QUOTA_CONTEXT(obj) \
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen MODULE_CONTEXT(obj, quota_storage_module)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define QUOTA_MAIL_CONTEXT(obj) \
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen MODULE_CONTEXT(obj, quota_mail_module)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define QUOTA_LIST_CONTEXT(obj) \
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen MODULE_CONTEXT(obj, quota_mailbox_list_module)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct quota_mailbox_list {
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen union mailbox_list_module_context module_ctx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen};
03010dbaa74ec70f062994dfe3cd39bedc99a28bTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct quota_mailbox {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen union mailbox_module_context module_ctx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi struct mailbox_transaction_context *expunge_trans;
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi struct quota_transaction_context *expunge_qt;
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen ARRAY_DEFINE(expunge_uids, uint32_t);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ARRAY_DEFINE(expunge_sizes, uoff_t);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int recalculate:1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen};
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct quota_user_module quota_user_module =
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen MODULE_CONTEXT_INIT(&mail_user_module_register);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(quota_storage_module,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen &mail_storage_module_register);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(quota_mail_module, &mail_module_register);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(quota_mailbox_list_module,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen &mailbox_list_module_register);
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvistatic void quota_mail_expunge(struct mail *_mail)
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi{
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(_mail->box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen union mail_module_context *qmail = QUOTA_MAIL_CONTEXT(mail);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen uoff_t size;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* We need to handle the situation where multiple transactions expunged
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen the mail at the same time. In here we'll just save the message's
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen physical size and do the quota freeing later when the message was
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen known to be expunged. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mail_get_physical_size(_mail, &size) == 0) {
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek if (!array_is_created(&qbox->expunge_uids)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_array_init(&qbox->expunge_uids, 64);
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen i_array_init(&qbox->expunge_sizes, 64);
767ff4367960efd5fa868f3b56f850fd4c205c8bTimo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_append(&qbox->expunge_uids, &_mail->uid, 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_append(&qbox->expunge_sizes, &size, 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen qmail->super.expunge(_mail);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen}
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainenstatic struct mailbox_transaction_context *
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenquota_mailbox_transaction_begin(struct mailbox *box,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen enum mailbox_transaction_flags flags)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek struct mailbox_transaction_context *t;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct quota_transaction_context *qt;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen t = qbox->module_ctx.super.transaction_begin(box, flags);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen qt = quota_transaction_begin(box);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen MODULE_CONTEXT_SET(t, quota_storage_module, qt);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return t;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenquota_mailbox_transaction_commit(struct mailbox_transaction_context *ctx,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_transaction_commit_changes *changes_r)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (qt->tmp_mail != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_free(&qt->tmp_mail);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (qbox->module_ctx.super.transaction_commit(ctx, changes_r) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen quota_transaction_rollback(&qt);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek (void)quota_transaction_commit(&qt);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenquota_mailbox_transaction_rollback(struct mailbox_transaction_context *ctx)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
7204b8112e005ff81dcf628f7880ef1feed1effeTimo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (qt->tmp_mail != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_free(&qt->tmp_mail);
fc40a9a002458e372ff4b9f6f4e15239520c0bcdTimo Sirainen
767ff4367960efd5fa868f3b56f850fd4c205c8bTimo Sirainen qbox->module_ctx.super.transaction_rollback(ctx);
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen quota_transaction_rollback(&qt);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid quota_mail_allocated(struct mail *_mail)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(_mail->box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_vfuncs *v = mail->vlast;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen union mail_module_context *qmail;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (qbox == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen qmail = p_new(mail->pool, union mail_module_context, 1);
4ca83616715c3bd417e34ced2c1d61852513e427Timo Sirainen qmail->super = *v;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail->vlast = &qmail->super;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen v->expunge = quota_mail_expunge;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen MODULE_CONTEXT_SET_SELF(mail, quota_mail_module, qmail);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int quota_check(struct mailbox_transaction_context *t, struct mail *mail)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bool too_large;
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = quota_try_alloc(qt, mail, &too_large);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret > 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (ret == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_storage_set_error(t->box->storage, MAIL_ERROR_NOSPACE,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen qt->quota->set->quota_exceeded_msg);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_storage_set_critical(t->box->storage,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Internal quota calculation error");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenquota_copy(struct mail_save_context *ctx, struct mail *mail)
767ff4367960efd5fa868f3b56f850fd4c205c8bTimo Sirainen{
03010dbaa74ec70f062994dfe3cd39bedc99a28bTimo Sirainen struct mailbox_transaction_context *t = ctx->transaction;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ctx->dest_mail == NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* we always want to know the mail size */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (qt->tmp_mail == NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
5096d157aa1acd28a93f45b058ca5bf26a4fddbeTimo Sirainen NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ctx->dest_mail = qt->tmp_mail;
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen }
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen if (qbox->module_ctx.super.copy(ctx, mail) < 0)
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen return -1;
804fa3f03bd9170272168a5ad214053bbe3160c7Josef 'Jeff' Sipek
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek /* if copying used saving internally, we already checked the quota */
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen return ctx->copying_via_save ? 0 : quota_check(t, ctx->dest_mail);
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen}
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainenstatic int
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainenquota_save_begin(struct mail_save_context *ctx, struct istream *input)
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen{
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen struct mailbox_transaction_context *t = ctx->transaction;
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen uoff_t size;
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen int ret;
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen if (i_stream_get_size(input, TRUE, &size) > 0) {
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen /* Input size is known, check for quota immediately. This
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen check isn't perfect, especially because input stream's
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen linefeeds may contain CR+LFs while physical message would
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen only contain LFs. With mbox some headers might be skipped
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen entirely.
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen I think these don't really matter though compared to the
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen benefit of giving "out of quota" error before sending the
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen full mail. */
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen bool too_large;
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen ret = quota_test_alloc(qt, size, &too_large);
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen if (ret == 0) {
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen mail_storage_set_error(t->box->storage,
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen MAIL_ERROR_NOSPACE,
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen qt->quota->set->quota_exceeded_msg);
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen return -1;
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen } else if (ret < 0) {
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen mail_storage_set_critical(t->box->storage,
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen "Internal quota calculation error");
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen return -1;
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen }
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen }
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen if (ctx->dest_mail == NULL) {
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen /* we always want to know the mail size */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen if (qt->tmp_mail == NULL) {
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen NULL);
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen }
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen ctx->dest_mail = qt->tmp_mail;
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen }
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen return qbox->module_ctx.super.save_begin(ctx, input);
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen}
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainenstatic int quota_save_finish(struct mail_save_context *ctx)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->transaction->box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (qbox->module_ctx.super.save_finish(ctx) < 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return quota_check(ctx->transaction, ctx->dest_mail);
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen}
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainenstatic void quota_mailbox_sync_cleanup(struct quota_mailbox *qbox)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (array_is_created(&qbox->expunge_uids)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_clear(&qbox->expunge_uids);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_clear(&qbox->expunge_sizes);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
f46885a5b78b15a8d2419f6e5d13b643bd85e41fTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (qbox->expunge_qt != NULL && qbox->expunge_qt->tmp_mail != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_free(&qbox->expunge_qt->tmp_mail);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mailbox_transaction_rollback(&qbox->expunge_trans);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void quota_mailbox_sync_commit(struct quota_mailbox *qbox)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen quota_mailbox_sync_cleanup(qbox);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (qbox->expunge_qt != NULL)
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen (void)quota_transaction_commit(&qbox->expunge_qt);
03de962febfc2ac572f9e4029463c16d29c1ed55Timo Sirainen qbox->recalculate = FALSE;
21aaa6affb9f134112b75b5db737309fc35ef1cfMartti Rannanjärvi}
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void quota_mailbox_sync_notify(struct mailbox *box, uint32_t uid,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen enum mailbox_sync_type sync_type)
f46885a5b78b15a8d2419f6e5d13b643bd85e41fTimo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const uint32_t *uids;
6564208826b0f46a00f010d1b5711d85944c3c88Timo Sirainen const uoff_t *sizep;
6de6ec228a41275ddda972d4a554699ea75cd06dTimo Sirainen unsigned int i, count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen 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);
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) {
qbox->expunge_trans = mailbox_transaction_begin(box, 0);
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_mailbox_close(struct mailbox *box)
{
struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
/* 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);
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->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;
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)
root->ns = list->ns;
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 {
add = TRUE;
}
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);
/* register to owner's quota roots */
quota = list->ns->owner != NULL ?
quota_get_mail_user_quota(list->ns->owner) :
quota_get_mail_user_quota(list->ns->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;
if (root->ns_prefix != NULL && root->ns == NULL) {
root->ns = mail_namespace_find_prefix(namespaces,
root->ns_prefix);
if (root->ns == NULL) {
i_error("quota: Unknown namespace: %s",
root->ns_prefix);
}
}
array_foreach(&root->set->rules, rule) {
name = rule->mailbox_name;
if (mail_namespace_find(namespaces, name) == NULL)
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);
}