quota-storage.c revision 0df9428baed48afaff90b4d4f03792d2fd756a43
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina MODULE_CONTEXT(obj, quota_mailbox_list_module)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct mailbox_transaction_context *expunge_trans;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina MODULE_CONTEXT_INIT(&mail_user_module_register);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic MODULE_CONTEXT_DEFINE_INIT(quota_storage_module,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic MODULE_CONTEXT_DEFINE_INIT(quota_mail_module, &mail_module_register);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic MODULE_CONTEXT_DEFINE_INIT(quota_mailbox_list_module,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void quota_mail_expunge(struct mail *_mail)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct mail_private *mail = (struct mail_private *)_mail;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_mailbox *qbox = QUOTA_CONTEXT(_mail->box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina union mail_module_context *qmail = QUOTA_MAIL_CONTEXT(mail);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* We need to handle the situation where multiple transactions expunged
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina the mail at the same time. In here we'll just save the message's
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina physical size and do the quota freeing later when the message was
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina known to be expunged. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (mail_get_physical_size(_mail, &size) == 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina array_append(&qbox->expunge_uids, &_mail->uid, 1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaquota_mailbox_transaction_begin(struct mailbox *box,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina t = qbox->module_ctx.super.transaction_begin(box, flags);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina MODULE_CONTEXT_SET(t, quota_storage_module, qt);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaquota_mailbox_transaction_commit(struct mailbox_transaction_context *ctx,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (qbox->module_ctx.super.transaction_commit(ctx,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaquota_mailbox_transaction_rollback(struct mailbox_transaction_context *ctx)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina qbox->module_ctx.super.transaction_rollback(ctx);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic struct mail *
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaquota_mail_alloc(struct mailbox_transaction_context *t,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct mailbox_header_lookup_ctx *wanted_headers)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina qmail = p_new(mail->pool, union mail_module_context, 1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina MODULE_CONTEXT_SET_SELF(mail, quota_mail_module, qmail);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int quota_check(struct mailbox_transaction_context *t, struct mail *mail)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina else if (ret == 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_storage_set_error(t->box->storage, MAIL_ERROR_NOSPACE,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "Internal quota calculation error");
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaquota_copy(struct mailbox_transaction_context *t, struct mail *mail,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina enum mail_flags flags, struct mail_keywords *keywords,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* we always want to know the mail size */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (qbox->module_ctx.super.copy(t, mail, flags, keywords,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* if copying used saving internally, we already checked the quota
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina and set qbox->save_hack = TRUE. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return qbox->save_hack ? 0 : quota_check(t, dest_mail);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaquota_save_begin(struct mail_save_context *ctx, struct istream *input)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct mailbox_transaction_context *t = ctx->transaction;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* Input size is known, check for quota immediately. This
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina check isn't perfect, especially because input stream's
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina linefeeds may contain CR+LFs while physical message would
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina only contain LFs. With mbox some headers might be skipped
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina I think these don't really matter though compared to the
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina benefit of giving "out of quota" error before sending the
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina full mail. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = quota_test_alloc(qt, st->st_size, &too_large);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } else if (ret < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "Internal quota calculation error");
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* we always want to know the mail size */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return qbox->module_ctx.super.save_begin(ctx, input);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int quota_save_finish(struct mail_save_context *ctx)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->transaction->box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (qbox->module_ctx.super.save_finish(ctx) < 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return quota_check(ctx->transaction, ctx->dest_mail);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void quota_mailbox_sync_cleanup(struct quota_mailbox *qbox)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (qbox->expunge_qt != NULL && qbox->expunge_qt->tmp_mail != NULL) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mailbox_transaction_rollback(&qbox->expunge_trans);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void quota_mailbox_sync_commit(struct quota_mailbox *qbox)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina (void)quota_transaction_commit(&qbox->expunge_qt);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void quota_mailbox_sync_notify(struct mailbox *box, uint32_t uid,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina unsigned int i, count;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (qbox->module_ctx.super.sync_notify != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina qbox->module_ctx.super.sync_notify(box, uid, sync_type);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (sync_type != MAILBOX_SYNC_TYPE_EXPUNGE || qbox->recalculate) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* free the transaction before view syncing begins,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina otherwise it'll crash. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* we're in the middle of syncing the mailbox, so it's a bad idea to
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina try and get the message sizes at this point. Rely on sizes that
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina we saved earlier, or recalculate the whole quota if we don't know
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina uids = array_get(&qbox->expunge_uids, &count);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina for (i = 0; i < count; i++) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina qbox->expunge_qt = quota_transaction_begin(box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* we already know the size */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* try to look up the size. this works only if it's cached. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina qbox->expunge_trans = mailbox_transaction_begin(box, 0);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (mail_set_uid(qbox->expunge_qt->tmp_mail, uid) &&
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mail_get_physical_size(qbox->expunge_qt->tmp_mail, &size) == 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* there's no way to get the size. recalculate the quota. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int quota_mailbox_sync_deinit(struct mailbox_sync_context *ctx,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = qbox->module_ctx.super.sync_deinit(ctx, status_items, status_r);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* update quota only after syncing is finished. the quota commit may
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina recalculate the quota and cause all mailboxes to be synced,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina including the one we're already syncing. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int quota_mailbox_close(struct mailbox *box)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic struct mailbox *
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaquota_mailbox_open(struct mail_storage *storage, const char *name,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct istream *input, enum mailbox_open_flags flags)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina union mail_storage_module_context *qstorage = QUOTA_CONTEXT(storage);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina box = qstorage->super.mailbox_open(storage, name, input, flags);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina qbox = p_new(box->pool, struct quota_mailbox, 1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina box->v.transaction_begin = quota_mailbox_transaction_begin;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina box->v.transaction_commit = quota_mailbox_transaction_commit;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina box->v.transaction_rollback = quota_mailbox_transaction_rollback;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina box->v.sync_notify = quota_mailbox_sync_notify;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina box->v.sync_deinit = quota_mailbox_sync_deinit;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina MODULE_CONTEXT_SET(box, quota_storage_module, qbox);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaquota_mailbox_delete_shrink_quota(struct mailbox *box)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ, 0, NULL) < 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ctx = mailbox_search_init(t, search_args, NULL);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaquota_mailbox_list_delete(struct mailbox_list *list, const char *name)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(list);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *str;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* This is a bit annoying to handle. We'll have to open the mailbox
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina and free the quota for all the messages existing in it. Open the
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina mailbox locked so that other processes can't mess up the quota
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina calculations by adding/removing mails while we're doing this. */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina box = mailbox_open(&storage, name, NULL, MAILBOX_OPEN_FAST |
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina MAILBOX_OPEN_KEEP_RECENT | MAILBOX_OPEN_KEEP_LOCKED);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina str = mail_storage_get_last_error(qlist->storage, &error);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* mailbox isn't selectable */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina str = mail_storage_get_last_error(qlist->storage, &error);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* FIXME: here's an unfortunate race condition */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina qlist->module_ctx.super.delete_mailbox(list, name);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void quota_storage_destroy(struct mail_storage *storage)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina union mail_storage_module_context *qstorage = QUOTA_CONTEXT(storage);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastruct quota *quota_get_mail_user_quota(struct mail_user *user)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_user *quser = QUOTA_USER_CONTEXT(user);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void quota_user_deinit(struct mail_user *user)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_user *quser = QUOTA_USER_CONTEXT(user);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinavoid quota_mail_user_created(struct mail_user *user)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina quser = p_new(user->pool, struct quota_user, 1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina MODULE_CONTEXT_SET(user, quota_user_module, quser);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_info("quota: No quota setting - plugin disabled");
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (quota_next_hook_mail_user_created != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinavoid quota_mail_storage_created(struct mail_storage *storage)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(storage->list);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina qstorage = p_new(storage->pool, union mail_storage_module_context, 1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina MODULE_CONTEXT_SET_SELF(storage, quota_storage_module, qstorage);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina (storage->ns->flags & NAMESPACE_FLAG_INTERNAL) == 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* register to owner's quota roots */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina quota = quota_get_mail_user_quota(storage->ns->owner);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (quota_next_hook_mail_storage_created != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina quota_next_hook_mail_storage_created(storage);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinavoid quota_mailbox_list_created(struct mailbox_list *list)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina qlist = p_new(list->pool, struct quota_mailbox_list, 1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina list->v.delete_mailbox = quota_mailbox_list_delete;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina MODULE_CONTEXT_SET(list, quota_mailbox_list_module, qlist);