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"
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen#include "hash.h"
76a99afe0914951d20d96e0bf5e6d8d3ea3fd503Timo Sirainen#include "str.h"
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen#include "ioloop.h"
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#include "net.h"
76a99afe0914951d20d96e0bf5e6d8d3ea3fd503Timo Sirainen#include "write-full.h"
596ec384269cad3b0f0661df89b9cf33cbd171b7Timo Sirainen#include "eacces-error.h"
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen#include "wildcard-match.h"
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen#include "dict.h"
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen#include "mailbox-list-private.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "quota-private.h"
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen#include "quota-fs.h"
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi#include "llist.h"
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi#include "program-client.h"
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi#include "settings-parser.h"
94e1adead9faddec88a623485b9999a87b1684faTimo Sirainen
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen#include <sys/wait.h>
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
cb2b5a7d6e7e651191bf9ee1eda94a6e207288b0Timo Sirainen#define DEFAULT_QUOTA_EXCEEDED_MSG \
cb2b5a7d6e7e651191bf9ee1eda94a6e207288b0Timo Sirainen "Quota exceeded (mailbox for user is full)"
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen#define QUOTA_LIMIT_SET_PATH DICT_PATH_PRIVATE"quota/limit/"
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen/* How many seconds after the userdb lookup do we still want to execute the
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen quota_over_script. This applies to quota_over_flag_lazy_check=yes and also
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen after unhibernating IMAP connections. */
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen#define QUOTA_OVER_FLAG_MAX_DELAY_SECS 10
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstruct quota_root_iter {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota *quota;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct mailbox *box;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int i;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen};
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenunsigned int quota_module_id = 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
c4db1218645ed8ec8b5ae67c05bc5d7a80c1b8aeTimo Sirainenextern struct quota_backend quota_backend_count;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenextern struct quota_backend quota_backend_dict;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenextern struct quota_backend quota_backend_dirsize;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenextern struct quota_backend quota_backend_fs;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenextern struct quota_backend quota_backend_imapc;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenextern struct quota_backend quota_backend_maildir;
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomistatic const struct quota_backend *quota_internal_backends[] = {
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen#ifdef HAVE_FS_QUOTA
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen &quota_backend_fs,
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen#endif
c4db1218645ed8ec8b5ae67c05bc5d7a80c1b8aeTimo Sirainen &quota_backend_count,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen &quota_backend_dict,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen &quota_backend_dirsize,
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen &quota_backend_imapc,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen &quota_backend_maildir
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen};
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomistatic ARRAY(const struct quota_backend*) quota_backends;
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void hidden_param_handler(struct quota_root *_root, const char *param_value);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void ignoreunlim_param_handler(struct quota_root *_root, const char *param_value);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void noenforcing_param_handler(struct quota_root *_root, const char *param_value);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void ns_param_handler(struct quota_root *_root, const char *param_value);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstruct quota_param_parser quota_param_hidden = {.param_name = "hidden", .param_handler = hidden_param_handler};
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstruct quota_param_parser quota_param_ignoreunlimited = {.param_name = "ignoreunlimited", .param_handler = ignoreunlim_param_handler};
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstruct quota_param_parser quota_param_noenforcing = {.param_name = "noenforcing", .param_handler = noenforcing_param_handler};
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstruct quota_param_parser quota_param_ns = {.param_name = "ns=", .param_handler = ns_param_handler};
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov
64f889c7c1707f9951755c997d750639be5dd41cMartti Rannanjärvistatic enum quota_alloc_result quota_default_test_alloc(
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi struct quota_transaction_context *ctx, uoff_t size,
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi const char **error_r);
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainenstatic void quota_over_flag_check_root(struct quota_root *root);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic const struct quota_backend *quota_backend_find(const char *name)
4316355ca8b7698516272520a972291378698140Timo Sirainen{
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi const struct quota_backend *const *backend;
4316355ca8b7698516272520a972291378698140Timo Sirainen
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi array_foreach(&quota_backends, backend) {
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi if (strcmp((*backend)->name, name) == 0)
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi return *backend;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen return NULL;
4316355ca8b7698516272520a972291378698140Timo Sirainen}
4316355ca8b7698516272520a972291378698140Timo Sirainen
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomivoid quota_backend_register(const struct quota_backend *backend)
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi{
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi i_assert(quota_backend_find(backend->name) == NULL);
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi array_append(&quota_backends, &backend, 1);
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi}
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomivoid quota_backend_unregister(const struct quota_backend *backend)
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi{
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi for(unsigned int i = 0; i < array_count(&quota_backends); i++) {
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi const struct quota_backend *const *be =
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi array_idx(&quota_backends, i);
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi if (strcmp((*be)->name, backend->name) == 0) {
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi array_delete(&quota_backends, i, 1);
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi return;
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi }
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi }
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi i_unreached();
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi}
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomivoid quota_backends_register(void);
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomivoid quota_backends_unregister(void);
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomivoid quota_backends_register(void)
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi{
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi i_array_init(&quota_backends, 8);
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi array_append(&quota_backends, quota_internal_backends,
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi N_ELEMENTS(quota_internal_backends));
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi}
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomivoid quota_backends_unregister(void)
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi{
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi for(size_t i = 0; i < N_ELEMENTS(quota_internal_backends); i++) {
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi quota_backend_unregister(quota_internal_backends[i]);
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi }
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi i_assert(array_count(&quota_backends) == 0);
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi array_free(&quota_backends);
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi}
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic int quota_root_add_rules(struct mail_user *user, const char *root_name,
4316355ca8b7698516272520a972291378698140Timo Sirainen struct quota_root_settings *root_set,
4316355ca8b7698516272520a972291378698140Timo Sirainen const char **error_r)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const char *rule_name, *rule, *error;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen unsigned int i;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen rule_name = t_strconcat(root_name, "_rule", NULL);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen for (i = 2;; i++) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen rule = mail_user_plugin_getenv(user, rule_name);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (rule == NULL)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen break;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (quota_root_add_rule(root_set, rule, &error) < 0) {
4316355ca8b7698516272520a972291378698140Timo Sirainen *error_r = t_strdup_printf("Invalid rule %s: %s",
4316355ca8b7698516272520a972291378698140Timo Sirainen rule, error);
4316355ca8b7698516272520a972291378698140Timo Sirainen return -1;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen rule_name = t_strdup_printf("%s_rule%d", root_name, i);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen return 0;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic int
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenquota_root_add_warning_rules(struct mail_user *user, const char *root_name,
4316355ca8b7698516272520a972291378698140Timo Sirainen struct quota_root_settings *root_set,
4316355ca8b7698516272520a972291378698140Timo Sirainen const char **error_r)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const char *rule_name, *rule, *error;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen unsigned int i;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen rule_name = t_strconcat(root_name, "_warning", NULL);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen for (i = 2;; i++) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen rule = mail_user_plugin_getenv(user, rule_name);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (rule == NULL)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen break;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (quota_root_add_warning_rule(root_set, rule, &error) < 0) {
4316355ca8b7698516272520a972291378698140Timo Sirainen *error_r = t_strdup_printf("Invalid warning rule: %s",
4316355ca8b7698516272520a972291378698140Timo Sirainen rule);
4316355ca8b7698516272520a972291378698140Timo Sirainen return -1;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen rule_name = t_strdup_printf("%s_warning%d", root_name, i);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen return 0;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainenstatic int
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainenquota_root_parse_set(struct mail_user *user, const char *root_name,
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen struct quota_root_settings *root_set,
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen const char **error_r)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen{
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen const char *name, *value;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen name = t_strconcat(root_name, "_set", NULL);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen value = mail_user_plugin_getenv(user, name);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (value == NULL)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return 0;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (strncmp(value, "dict:", 5) != 0) {
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen *error_r = t_strdup_printf("%s supports only dict backend", name);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return -1;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen }
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen root_set->limit_set = p_strdup(root_set->set->pool, value+5);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return 0;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen}
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic int
4316355ca8b7698516272520a972291378698140Timo Sirainenquota_root_settings_init(struct quota_settings *quota_set, const char *root_def,
4316355ca8b7698516272520a972291378698140Timo Sirainen struct quota_root_settings **set_r,
4316355ca8b7698516272520a972291378698140Timo Sirainen const char **error_r)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct quota_root_settings *root_set;
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen const struct quota_backend *backend;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen const char *p, *args, *backend_name;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* <backend>[:<quota root name>[:<backend args>]] */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen p = strchr(root_def, ':');
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (p == NULL) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen backend_name = root_def;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen args = NULL;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen backend_name = t_strdup_until(root_def, p);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen args = p + 1;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen backend = quota_backend_find(backend_name);
d9b8c65d0a0ffc709ba7d23c449dbf2f46b10674Timo Sirainen if (backend == NULL) {
4316355ca8b7698516272520a972291378698140Timo Sirainen *error_r = t_strdup_printf("Unknown quota backend: %s",
4316355ca8b7698516272520a972291378698140Timo Sirainen backend_name);
4316355ca8b7698516272520a972291378698140Timo Sirainen return -1;
d9b8c65d0a0ffc709ba7d23c449dbf2f46b10674Timo Sirainen }
73cdeeae4ccae9930973dfb6a5b4835c79bdba49Sergey Kitov
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root_set = p_new(quota_set->pool, struct quota_root_settings, 1);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root_set->set = quota_set;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root_set->backend = backend;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (args != NULL) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* save root's name */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen p = strchr(args, ':');
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (p == NULL) {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root_set->name = p_strdup(quota_set->pool, args);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen args = NULL;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root_set->name =
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen p_strdup_until(quota_set->pool, args, p);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen args = p + 1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root_set->name = "";
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root_set->args = p_strdup(quota_set->pool, args);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (quota_set->debug) {
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk i_debug("Quota root: name=%s backend=%s args=%s",
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk root_set->name, backend_name, args == NULL ? "" : args);
e3077468777f5d324224365e34d7bbc449168e52Timo Sirainen }
e3077468777f5d324224365e34d7bbc449168e52Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen p_array_init(&root_set->rules, quota_set->pool, 4);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen p_array_init(&root_set->warning_rules, quota_set->pool, 4);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen array_append(&quota_set->root_sets, &root_set, 1);
4316355ca8b7698516272520a972291378698140Timo Sirainen *set_r = root_set;
4316355ca8b7698516272520a972291378698140Timo Sirainen return 0;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic int
4316355ca8b7698516272520a972291378698140Timo Sirainenquota_root_add(struct quota_settings *quota_set, struct mail_user *user,
4316355ca8b7698516272520a972291378698140Timo Sirainen const char *env, const char *root_name, const char **error_r)
4316355ca8b7698516272520a972291378698140Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen struct quota_root_settings *root_set;
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen const char *set_name, *value;
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen if (quota_root_settings_init(quota_set, env, &root_set, error_r) < 0)
4316355ca8b7698516272520a972291378698140Timo Sirainen return -1;
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen root_set->set_name = p_strdup(quota_set->pool, root_name);
4316355ca8b7698516272520a972291378698140Timo Sirainen if (quota_root_add_rules(user, root_name, root_set, error_r) < 0)
4316355ca8b7698516272520a972291378698140Timo Sirainen return -1;
4316355ca8b7698516272520a972291378698140Timo Sirainen if (quota_root_add_warning_rules(user, root_name, root_set, error_r) < 0)
4316355ca8b7698516272520a972291378698140Timo Sirainen return -1;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (quota_root_parse_set(user, root_name, root_set, error_r) < 0)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return -1;
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen set_name = t_strconcat(root_name, "_grace", NULL);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen value = mail_user_plugin_getenv(user, set_name);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (quota_root_parse_grace(root_set, value, error_r) < 0) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen *error_r = t_strdup_printf("Invalid %s value '%s': %s",
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen set_name, value, *error_r);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen return -1;
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen return 0;
4316355ca8b7698516272520a972291378698140Timo Sirainen}
4316355ca8b7698516272520a972291378698140Timo Sirainen
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärviconst char *quota_alloc_result_errstr(enum quota_alloc_result res,
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi struct quota_transaction_context *qt)
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi{
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi switch (res) {
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi case QUOTA_ALLOC_RESULT_OK:
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return "OK";
b14f93ea4f7e9b78e5adbabcc78bd2f63d2bcd36Martti Rannanjärvi case QUOTA_ALLOC_RESULT_BACKGROUND_CALC:
b14f93ea4f7e9b78e5adbabcc78bd2f63d2bcd36Martti Rannanjärvi return "Blocked by an ongoing background quota calculation";
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi case QUOTA_ALLOC_RESULT_TEMPFAIL:
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return "Internal quota calculation error";
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi case QUOTA_ALLOC_RESULT_OVER_MAXSIZE:
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi return "Mail size is larger than the maximum size allowed by "
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi "server configuration";
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi case QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT:
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi case QUOTA_ALLOC_RESULT_OVER_QUOTA:
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return qt->quota->set->quota_exceeded_msg;
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi }
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi i_unreached();
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi}
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi
4316355ca8b7698516272520a972291378698140Timo Sirainenint quota_user_read_settings(struct mail_user *user,
4316355ca8b7698516272520a972291378698140Timo Sirainen struct quota_settings **set_r,
4316355ca8b7698516272520a972291378698140Timo Sirainen const char **error_r)
4316355ca8b7698516272520a972291378698140Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen struct quota_settings *quota_set;
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen char root_name[5 + MAX_INT_STRLEN + 1];
4316355ca8b7698516272520a972291378698140Timo Sirainen const char *env, *error;
4316355ca8b7698516272520a972291378698140Timo Sirainen unsigned int i;
4316355ca8b7698516272520a972291378698140Timo Sirainen pool_t pool;
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen pool = pool_alloconly_create("quota settings", 2048);
4316355ca8b7698516272520a972291378698140Timo Sirainen quota_set = p_new(pool, struct quota_settings, 1);
4316355ca8b7698516272520a972291378698140Timo Sirainen quota_set->pool = pool;
4316355ca8b7698516272520a972291378698140Timo Sirainen quota_set->test_alloc = quota_default_test_alloc;
4316355ca8b7698516272520a972291378698140Timo Sirainen quota_set->debug = user->mail_debug;
4316355ca8b7698516272520a972291378698140Timo Sirainen quota_set->quota_exceeded_msg =
4316355ca8b7698516272520a972291378698140Timo Sirainen mail_user_plugin_getenv(user, "quota_exceeded_message");
4316355ca8b7698516272520a972291378698140Timo Sirainen if (quota_set->quota_exceeded_msg == NULL)
4316355ca8b7698516272520a972291378698140Timo Sirainen quota_set->quota_exceeded_msg = DEFAULT_QUOTA_EXCEEDED_MSG;
8fc6dd0572450cc2896f8089d23319a64a50267aMartti Rannanjärvi quota_set->vsizes = mail_user_plugin_getenv_bool(user, "quota_vsizes");
4316355ca8b7698516272520a972291378698140Timo Sirainen
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi const char *max_size = mail_user_plugin_getenv(user,
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi "quota_max_mail_size");
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi if (max_size != NULL) {
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi const char *error = NULL;
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi if (settings_get_size(max_size, &quota_set->max_mail_size,
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi &error) < 0) {
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi *error_r = t_strdup_printf("quota_max_mail_size: %s",
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi error);
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi return -1;
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi }
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi }
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi
4316355ca8b7698516272520a972291378698140Timo Sirainen p_array_init(&quota_set->root_sets, pool, 4);
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (i_strocpy(root_name, "quota", sizeof(root_name)) < 0)
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen i_unreached();
4316355ca8b7698516272520a972291378698140Timo Sirainen for (i = 2;; i++) {
4316355ca8b7698516272520a972291378698140Timo Sirainen env = mail_user_plugin_getenv(user, root_name);
3cfc375f0d939c346b9b0e6f0ac78b9bc367dd95Timo Sirainen if (env == NULL || *env == '\0')
4316355ca8b7698516272520a972291378698140Timo Sirainen break;
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen if (quota_root_add(quota_set, user, env, root_name,
4316355ca8b7698516272520a972291378698140Timo Sirainen &error) < 0) {
4316355ca8b7698516272520a972291378698140Timo Sirainen *error_r = t_strdup_printf("Invalid quota root %s: %s",
4316355ca8b7698516272520a972291378698140Timo Sirainen root_name, error);
4316355ca8b7698516272520a972291378698140Timo Sirainen pool_unref(&pool);
4316355ca8b7698516272520a972291378698140Timo Sirainen return -1;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (i_snprintf(root_name, sizeof(root_name), "quota%d", i) < 0)
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen i_unreached();
4316355ca8b7698516272520a972291378698140Timo Sirainen }
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi if (quota_set->max_mail_size == 0 &&
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi array_count(&quota_set->root_sets) == 0) {
4316355ca8b7698516272520a972291378698140Timo Sirainen pool_unref(&pool);
4316355ca8b7698516272520a972291378698140Timo Sirainen return 0;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
b8a4aab1f117f6760184ad50b1af41ba810b51f9Timo Sirainen
b8a4aab1f117f6760184ad50b1af41ba810b51f9Timo Sirainen quota_set->initialized = TRUE;
4316355ca8b7698516272520a972291378698140Timo Sirainen *set_r = quota_set;
4316355ca8b7698516272520a972291378698140Timo Sirainen return 1;
4316355ca8b7698516272520a972291378698140Timo Sirainen}
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenvoid quota_settings_deinit(struct quota_settings **_quota_set)
4316355ca8b7698516272520a972291378698140Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen struct quota_settings *quota_set = *_quota_set;
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen *_quota_set = NULL;
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen pool_unref(&quota_set->pool);
4316355ca8b7698516272520a972291378698140Timo Sirainen}
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void quota_root_deinit(struct quota_root *root)
4316355ca8b7698516272520a972291378698140Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen pool_t pool = root->pool;
4316355ca8b7698516272520a972291378698140Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (root->limit_set_dict != NULL)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen dict_deinit(&root->limit_set_dict);
4316355ca8b7698516272520a972291378698140Timo Sirainen root->backend.v.deinit(root);
4316355ca8b7698516272520a972291378698140Timo Sirainen pool_unref(&pool);
4316355ca8b7698516272520a972291378698140Timo Sirainen}
4316355ca8b7698516272520a972291378698140Timo Sirainen
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainenint quota_root_default_init(struct quota_root *root, const char *args,
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen const char **error_r)
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen{
aa089dcef71e735dcb902cb709f0b37643fd9842Sergey Kitov const struct quota_param_parser default_params[] = {
aa089dcef71e735dcb902cb709f0b37643fd9842Sergey Kitov quota_param_hidden,
aa089dcef71e735dcb902cb709f0b37643fd9842Sergey Kitov quota_param_ignoreunlimited,
aa089dcef71e735dcb902cb709f0b37643fd9842Sergey Kitov quota_param_noenforcing,
aa089dcef71e735dcb902cb709f0b37643fd9842Sergey Kitov quota_param_ns,
aa089dcef71e735dcb902cb709f0b37643fd9842Sergey Kitov {.param_name = NULL}
aa089dcef71e735dcb902cb709f0b37643fd9842Sergey Kitov };
fde14422caabc3c4ac4a6c5e3e5cf176cedd90a6Timo Sirainen return quota_parse_parameters(root, &args, error_r, default_params, TRUE);
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen}
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic int
4316355ca8b7698516272520a972291378698140Timo Sirainenquota_root_init(struct quota_root_settings *root_set, struct quota *quota,
4316355ca8b7698516272520a972291378698140Timo Sirainen struct quota_root **root_r, const char **error_r)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct quota_root *root;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root = root_set->backend->v.alloc();
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root->pool = pool_alloconly_create("quota root", 512);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root->set = root_set;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root->quota = quota;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root->backend = *root_set->backend;
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen root->bytes_limit = root_set->default_rule.bytes_limit;
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen root->count_limit = root_set->default_rule.count_limit;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen array_create(&root->quota_module_contexts, root->pool,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen sizeof(void *), 10);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (root->backend.v.init != NULL) {
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen if (root->backend.v.init(root, root_set->args, error_r) < 0) {
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen *error_r = t_strdup_printf("%s quota init failed: %s",
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen root->backend.name, *error_r);
4316355ca8b7698516272520a972291378698140Timo Sirainen return -1;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen } else {
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen if (quota_root_default_init(root, root_set->args, error_r) < 0)
4316355ca8b7698516272520a972291378698140Timo Sirainen return -1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
d19d3aa4eafa34b48b7d7d311c9db31e1898576aTimo Sirainen if (root_set->default_rule.bytes_limit == 0 &&
d19d3aa4eafa34b48b7d7d311c9db31e1898576aTimo Sirainen root_set->default_rule.count_limit == 0 &&
4316355ca8b7698516272520a972291378698140Timo Sirainen root->disable_unlimited_tracking) {
4316355ca8b7698516272520a972291378698140Timo Sirainen quota_root_deinit(root);
4316355ca8b7698516272520a972291378698140Timo Sirainen return 0;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen *root_r = root;
4316355ca8b7698516272520a972291378698140Timo Sirainen return 1;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen}
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenint quota_init(struct quota_settings *quota_set, struct mail_user *user,
4316355ca8b7698516272520a972291378698140Timo Sirainen struct quota **quota_r, const char **error_r)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct quota *quota;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct quota_root *root;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct quota_root_settings *const *root_sets;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen unsigned int i, count;
4316355ca8b7698516272520a972291378698140Timo Sirainen const char *error;
4316355ca8b7698516272520a972291378698140Timo Sirainen int ret;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen quota = i_new(struct quota, 1);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen quota->user = user;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen quota->set = quota_set;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen i_array_init(&quota->roots, 8);
8a1c866a4c429f26c8746525f82024bc387f1407Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root_sets = array_get(&quota_set->root_sets, &count);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_array_init(&quota->namespaces, count);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen for (i = 0; i < count; i++) {
4316355ca8b7698516272520a972291378698140Timo Sirainen ret = quota_root_init(root_sets[i], quota, &root, &error);
4316355ca8b7698516272520a972291378698140Timo Sirainen if (ret < 0) {
4316355ca8b7698516272520a972291378698140Timo Sirainen *error_r = t_strdup_printf("Quota root %s: %s",
4316355ca8b7698516272520a972291378698140Timo Sirainen root_sets[i]->name, error);
4316355ca8b7698516272520a972291378698140Timo Sirainen quota_deinit(&quota);
4316355ca8b7698516272520a972291378698140Timo Sirainen return -1;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen if (ret > 0)
d19d3aa4eafa34b48b7d7d311c9db31e1898576aTimo Sirainen array_append(&quota->roots, &root, 1);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen *quota_r = quota;
4316355ca8b7698516272520a972291378698140Timo Sirainen return 0;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen}
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenvoid quota_deinit(struct quota **_quota)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct quota *quota = *_quota;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct quota_root *const *roots;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen unsigned int i, count;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen roots = array_get(&quota->roots, &count);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen for (i = 0; i < count; i++)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen quota_root_deinit(roots[i]);
6b43203878bd3c87f5ae690617a1cbc694e24c01Timo Sirainen
6b43203878bd3c87f5ae690617a1cbc694e24c01Timo Sirainen /* deinit quota roots before setting quser->quota=NULL */
6b43203878bd3c87f5ae690617a1cbc694e24c01Timo Sirainen *_quota = NULL;
6b43203878bd3c87f5ae690617a1cbc694e24c01Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen array_free(&quota->roots);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen array_free(&quota->namespaces);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen i_free(quota);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainenstatic int quota_root_get_rule_limits(struct quota_root *root,
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen const char *mailbox_name,
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen uint64_t *bytes_limit_r,
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen uint64_t *count_limit_r,
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi bool *ignored_r,
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi const char **error_r)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_rule *rule;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen int64_t bytes_limit, count_limit;
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek int ret;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen *ignored_r = FALSE;
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen if (!root->set->force_default_rule) {
faeaff460d763bb4b574d31d78773f91aaba5510Timo Sirainen if (root->backend.v.init_limits != NULL) {
e83a11eb1dc0dea31754396e1d07c5205d810104Martti Rannanjärvi const char *error;
e83a11eb1dc0dea31754396e1d07c5205d810104Martti Rannanjärvi if (root->backend.v.init_limits(root, &error) < 0) {
e83a11eb1dc0dea31754396e1d07c5205d810104Martti Rannanjärvi *error_r = t_strdup_printf(
e83a11eb1dc0dea31754396e1d07c5205d810104Martti Rannanjärvi "Initializing limits failed for quota backend: %s",
e83a11eb1dc0dea31754396e1d07c5205d810104Martti Rannanjärvi error);
faeaff460d763bb4b574d31d78773f91aaba5510Timo Sirainen return -1;
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi }
faeaff460d763bb4b574d31d78773f91aaba5510Timo Sirainen }
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen }
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen bytes_limit = root->bytes_limit;
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen count_limit = root->count_limit;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen /* if default rule limits are 0, user has unlimited quota.
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen ignore any specific quota rules */
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek if (bytes_limit != 0 || count_limit != 0) {
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek (void)mail_namespace_find_unalias(root->quota->user->namespaces,
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek &mailbox_name);
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek rule = quota_root_rule_find(root->set, mailbox_name);
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek ret = 1;
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek } else {
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek rule = NULL;
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek ret = 0;
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (rule != NULL) {
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen if (!rule->ignore) {
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen bytes_limit += rule->bytes_limit;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen count_limit += rule->count_limit;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen } else {
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen bytes_limit = 0;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen count_limit = 0;
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen *ignored_r = TRUE;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen *bytes_limit_r = bytes_limit <= 0 ? 0 : bytes_limit;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen *count_limit_r = count_limit <= 0 ? 0 : count_limit;
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek return ret;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen}
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainenstatic bool
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainenquota_is_duplicate_namespace(struct quota *quota, struct mail_namespace *ns)
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen{
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen struct mail_namespace *const *namespaces;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen unsigned int i, count;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen const char *path, *path2;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen if (!mailbox_list_get_root_path(ns->list,
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX, &path))
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen path = NULL;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen namespaces = array_get(&quota->namespaces, &count);
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen for (i = 0; i < count; i++) {
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen /* count namespace aliases only once. don't rely only on
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen alias_for != NULL, because the alias might have been
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen explicitly added as the wanted quota namespace. */
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen if (ns->alias_for == namespaces[i] ||
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen namespaces[i]->alias_for == ns)
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen continue;
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen if (path != NULL &&
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen mailbox_list_get_root_path(namespaces[i]->list,
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX, &path2) &&
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen strcmp(path, path2) == 0) {
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen /* duplicate path */
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0)
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen return TRUE;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen /* this is inbox=yes namespace, but the earlier one
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen that had the same location was inbox=no. we need to
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen include the INBOX also in quota calculations, so we
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen can't just ignore this namespace. but since we've
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen already called backend's namespace_added(), we can't
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen just remove it either. so just mark the old one as
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen unwanted namespace.
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen an alternative would be to do a bit larger change so
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen namespaces wouldn't be added until
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen mail_namespaces_created() hook is called */
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen i_assert(quota->unwanted_ns == NULL);
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen quota->unwanted_ns = namespaces[i];
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen return FALSE;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen }
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen }
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen return FALSE;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen}
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenvoid quota_add_user_namespace(struct quota *quota, struct mail_namespace *ns)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_root *const *roots;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_backend **backends;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int i, j, count;
ccd44abfe14f51cc1f6d8c0ec1aa6dc31242e2d3Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* first check if there already exists a namespace with the exact same
de5c7c99783cd86f3bdbc057345cbee923b51a20Timo Sirainen path. we don't want to count them twice. */
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen if (quota_is_duplicate_namespace(quota, ns))
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen return;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen array_append(&quota->namespaces, &ns, 1);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen roots = array_get(&quota->roots, &count);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* @UNSAFE: get different backends into one array */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen backends = t_new(struct quota_backend *, count + 1);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < count; i++) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (j = 0; backends[j] != NULL; j++) {
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen if (backends[j]->name == roots[i]->backend.name)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen break;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (backends[j] == NULL)
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen backends[j] = &roots[i]->backend;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; backends[i] != NULL; i++) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (backends[i]->v.namespace_added != NULL)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen backends[i]->v.namespace_added(quota, ns);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenvoid quota_remove_user_namespace(struct mail_namespace *ns)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct quota *quota;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_namespace *const *namespaces;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen unsigned int i, count;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen quota = ns->owner != NULL ?
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen quota_get_mail_user_quota(ns->owner) :
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen quota_get_mail_user_quota(ns->user);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (quota == NULL) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* no quota for this namespace */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen return;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen namespaces = array_get(&quota->namespaces, &count);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen for (i = 0; i < count; i++) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (namespaces[i] == ns) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen array_delete(&quota->namespaces, i, 1);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen break;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstruct quota_root_iter *
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenquota_root_iter_init(struct mailbox *box)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_root_iter *iter;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen iter = i_new(struct quota_root_iter, 1);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen iter->quota = box->list->ns->owner != NULL ?
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen quota_get_mail_user_quota(box->list->ns->owner) :
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen quota_get_mail_user_quota(box->list->ns->user);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen iter->box = box;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return iter;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenbool quota_root_is_namespace_visible(struct quota_root *root,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_namespace *ns)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen{
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen struct mailbox_list *list = ns->list;
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen struct mail_storage *storage;
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen /* this check works as long as there is only one storage per list */
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen if (mailbox_list_get_storage(&list, "", &storage) == 0 &&
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen (storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) != 0)
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen return FALSE;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen if (root->quota->unwanted_ns == ns)
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen return FALSE;
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen
766115d2b2e6dbcf59f90d3b3866851cf6f740feTimo Sirainen if (root->ns_prefix != NULL) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (root->ns != ns)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen return FALSE;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen } else {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (ns->owner == NULL)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen return FALSE;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen }
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen return TRUE;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen}
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainenstatic bool
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainenquota_root_is_visible(struct quota_root *root, struct mailbox *box,
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen bool enforce)
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen{
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen if (root->no_enforcing && enforce) {
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen /* we don't want to include this root in quota enforcing */
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen return FALSE;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (!quota_root_is_namespace_visible(root, box->list->ns))
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen return FALSE;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen if (array_count(&root->quota->roots) == 1) {
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen /* a single quota root: don't bother checking further */
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen return TRUE;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen }
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen return root->backend.v.match_box == NULL ? TRUE :
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen root->backend.v.match_box(root, box);
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen}
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstruct quota_root *quota_root_iter_next(struct quota_root_iter *iter)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_root *const *roots, *root = NULL;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int count;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen if (iter->quota == NULL)
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen return NULL;
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen roots = array_get(&iter->quota->roots, &count);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (iter->i >= count)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return NULL;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (; iter->i < count; iter->i++) {
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen if (!quota_root_is_visible(roots[iter->i], iter->box, FALSE))
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen continue;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen
bd389ead3c13c43f85bcd7cdcb1ba3cf304b85a3Timo Sirainen root = roots[iter->i];
bd389ead3c13c43f85bcd7cdcb1ba3cf304b85a3Timo Sirainen break;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen iter->i++;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return root;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenvoid quota_root_iter_deinit(struct quota_root_iter **_iter)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct quota_root_iter *iter = *_iter;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen *_iter = NULL;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_free(iter);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenstruct quota_root *quota_root_lookup(struct mail_user *user, const char *name)
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct quota *quota;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_root *const *roots;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen unsigned int i, count;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen quota = quota_get_mail_user_quota(user);
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen if (quota == NULL)
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen return NULL;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen roots = array_get(&quota->roots, &count);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen for (i = 0; i < count; i++) {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (strcmp(roots[i]->set->name, name) == 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return roots[i];
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return NULL;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen}
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenconst char *quota_root_get_name(struct quota_root *root)
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen return root->set->name;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenconst char *const *quota_root_get_resources(struct quota_root *root)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen /* if we haven't checked the quota_over_flag yet, do it now */
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen quota_over_flag_check_root(root);
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen return root->backend.v.get_resources(root);
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen}
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainenbool quota_root_is_hidden(struct quota_root *root)
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainen{
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainen return root->hidden;
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainen}
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainen
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainenenum quota_get_result
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainenquota_get_resource(struct quota_root *root, const char *mailbox_name,
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi const char *name, uint64_t *value_r, uint64_t *limit_r,
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi const char **error_r)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
d81702ddd148abde32cbe2a45f6239e1baee6907Timo Sirainen const char *error;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen uint64_t bytes_limit, count_limit;
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen bool ignored, kilobytes = FALSE;
1379bb74c2855aaf3415fdfe965164a44ac3c001Martti Rannanjärvi enum quota_get_result ret;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
68d7af0bb1d5f9da9dffda7d0616e99624d784e6Timo Sirainen *value_r = *limit_r = 0;
68d7af0bb1d5f9da9dffda7d0616e99624d784e6Timo Sirainen
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (strcmp(name, QUOTA_NAME_STORAGE_KILOBYTES) == 0) {
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen name = QUOTA_NAME_STORAGE_BYTES;
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen kilobytes = TRUE;
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen }
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen /* Get the value first. This call may also update quota limits if
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen they're defined externally. */
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi ret = root->backend.v.get_resource(root, name, value_r, &error);
1379bb74c2855aaf3415fdfe965164a44ac3c001Martti Rannanjärvi if (ret == QUOTA_GET_RESULT_UNLIMITED)
1379bb74c2855aaf3415fdfe965164a44ac3c001Martti Rannanjärvi i_panic("Quota backend %s returned QUOTA_GET_RESULT_UNLIMITED "
1379bb74c2855aaf3415fdfe965164a44ac3c001Martti Rannanjärvi "while getting resource %s from box %s",
1379bb74c2855aaf3415fdfe965164a44ac3c001Martti Rannanjärvi root->backend.name, name, mailbox_name);
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi else if (ret != QUOTA_GET_RESULT_LIMITED) {
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi *error_r = t_strdup_printf(
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi "quota-%s: %s", root->set->backend->name, error);
1379bb74c2855aaf3415fdfe965164a44ac3c001Martti Rannanjärvi return ret;
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi }
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen if (quota_root_get_rule_limits(root, mailbox_name,
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen &bytes_limit, &count_limit,
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi &ignored, &error) < 0) {
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi *error_r = t_strdup_printf(
d81702ddd148abde32cbe2a45f6239e1baee6907Timo Sirainen "Failed to get quota root rule limits for mailbox %s: %s",
d81702ddd148abde32cbe2a45f6239e1baee6907Timo Sirainen mailbox_name, error);
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainen return QUOTA_GET_RESULT_INTERNAL_ERROR;
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi }
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen *limit_r = bytes_limit;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen *limit_r = count_limit;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen else
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen *limit_r = 0;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen if (kilobytes) {
e265563be2031bc1f9b4ef15888e31a94a344e5eTimo Sirainen *value_r = (*value_r + 1023) / 1024;
e265563be2031bc1f9b4ef15888e31a94a344e5eTimo Sirainen *limit_r = (*limit_r + 1023) / 1024;
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen }
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainen return *limit_r == 0 ? QUOTA_GET_RESULT_UNLIMITED : QUOTA_GET_RESULT_LIMITED;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainenint quota_set_resource(struct quota_root *root, const char *name,
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen uint64_t value, const char **error_r)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen struct dict_transaction_context *trans;
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen const char *key, *error;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (root->set->limit_set == NULL) {
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen *error_r = MAIL_ERRSTR_NO_PERMISSION;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return -1;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen }
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (strcasecmp(name, QUOTA_NAME_STORAGE_KILOBYTES) == 0)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen key = "storage";
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen else if (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) == 0)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen key = "bytes";
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen else if (strcasecmp(name, QUOTA_NAME_MESSAGES) == 0)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen key = "messages";
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen else {
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen *error_r = t_strdup_printf("Unsupported resource name: %s", name);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return -1;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen }
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (root->limit_set_dict == NULL) {
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen struct dict_settings set;
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&set);
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen set.username = root->quota->user->username;
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen set.base_dir = root->quota->user->set->base_dir;
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen if (mail_user_get_home(root->quota->user, &set.home_dir) <= 0)
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen set.home_dir = NULL;
20e04227229970d148801c507946666e2a9bd838Timo Sirainen if (dict_init(root->set->limit_set, &set,
20e04227229970d148801c507946666e2a9bd838Timo Sirainen &root->limit_set_dict, error_r) < 0)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return -1;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen }
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen trans = dict_transaction_begin(root->limit_set_dict);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen key = t_strdup_printf(QUOTA_LIMIT_SET_PATH"%s", key);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen dict_set(trans, key, dec2str(value));
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen if (dict_transaction_commit(&trans, &error) < 0) {
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen i_error("dict_transaction_commit() failed: %s", error);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen *error_r = "Internal quota limit update error";
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return -1;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen }
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenstruct quota_transaction_context *quota_transaction_begin(struct mailbox *box)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_transaction_context *ctx;
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen struct quota_root *const *rootp;
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen const struct quota_rule *rule;
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen const char *mailbox_name;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ctx = i_new(struct quota_transaction_context, 1);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ctx->quota = box->list->ns->owner != NULL ?
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen quota_get_mail_user_quota(box->list->ns->owner) :
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen quota_get_mail_user_quota(box->list->ns->user);
6b43203878bd3c87f5ae690617a1cbc694e24c01Timo Sirainen i_assert(ctx->quota != NULL);
6b43203878bd3c87f5ae690617a1cbc694e24c01Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ctx->box = box;
30ad2b0309119501efad06c72ec9b1561b90d4afTimo Sirainen ctx->bytes_ceil = (uint64_t)-1;
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen ctx->bytes_ceil2 = (uint64_t)-1;
30ad2b0309119501efad06c72ec9b1561b90d4afTimo Sirainen ctx->count_ceil = (uint64_t)-1;
97daba82224dd757b7b7526ab3fd5d574a5f35d8Timo Sirainen
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen mailbox_name = mailbox_get_vname(box);
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen (void)mail_namespace_find_unalias(box->storage->user->namespaces,
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen &mailbox_name);
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen ctx->auto_updating = TRUE;
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen array_foreach(&ctx->quota->roots, rootp) {
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen if (!quota_root_is_visible(*rootp, ctx->box, FALSE))
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen continue;
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen rule = quota_root_rule_find((*rootp)->set, mailbox_name);
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen if (rule != NULL && rule->ignore) {
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen /* This mailbox isn't included in quota. This means
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen it's also not included in quota_warnings, so make
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen sure it's fully ignored. */
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen continue;
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen }
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen
10207755f33a4cf1a1e901968137ae4f2bc93bbfTimo Sirainen /* If there are reverse quota_warnings, we'll need to track
10207755f33a4cf1a1e901968137ae4f2bc93bbfTimo Sirainen how many bytes were expunged even with auto_updating roots.
10207755f33a4cf1a1e901968137ae4f2bc93bbfTimo Sirainen (An alternative could be to get the current quota usage
10207755f33a4cf1a1e901968137ae4f2bc93bbfTimo Sirainen before and after the expunges, but that's more complicated
10207755f33a4cf1a1e901968137ae4f2bc93bbfTimo Sirainen and probably isn't any better.) */
10207755f33a4cf1a1e901968137ae4f2bc93bbfTimo Sirainen if (!(*rootp)->auto_updating ||
10207755f33a4cf1a1e901968137ae4f2bc93bbfTimo Sirainen (*rootp)->set->have_reverse_warnings)
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen ctx->auto_updating = FALSE;
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen }
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen
10ccd0e45768923d69be459e87ef6cd2574cec60Timo Sirainen if (box->storage->user->dsyncing) {
10ccd0e45768923d69be459e87ef6cd2574cec60Timo Sirainen /* ignore quota for dsync */
97daba82224dd757b7b7526ab3fd5d574a5f35d8Timo Sirainen ctx->limits_set = TRUE;
97daba82224dd757b7b7526ab3fd5d574a5f35d8Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return ctx;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärviint quota_transaction_set_limits(struct quota_transaction_context *ctx,
c975f4c0c6a767050755e824d5a5ddeb1dbf7f6eMartti Rannanjärvi enum quota_get_result *error_result_r,
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi const char **error_r)
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen{
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen struct quota_root *const *roots;
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi const char *mailbox_name, *error;
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen unsigned int i, count;
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen uint64_t bytes_limit, count_limit, current, limit, diff;
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen bool use_grace, ignored;
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainen enum quota_get_result ret;
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen
5b5da56e477dd55fbac23be9c96d8baa00482069Timo Sirainen if (ctx->limits_set)
5b5da56e477dd55fbac23be9c96d8baa00482069Timo Sirainen return 0;
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen ctx->limits_set = TRUE;
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen mailbox_name = mailbox_get_vname(ctx->box);
e66cd209fcab4817d2234d0121b404925dc60034Timo Sirainen /* use quota_grace only for LDA/LMTP */
50ecf65993bff429af04deef6c832deb019c76a5Timo Sirainen use_grace = (ctx->box->flags & MAILBOX_FLAG_POST_SESSION) != 0;
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen ctx->no_quota_updates = TRUE;
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen /* find the lowest quota limits from all roots and use them */
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen roots = array_get(&ctx->quota->roots, &count);
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen for (i = 0; i < count; i++) {
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen if (!quota_root_is_visible(roots[i], ctx->box, TRUE))
d5d23d5ff8b7a06d2ead489ddcf55ee8fb5ca7b6Timo Sirainen continue;
d5d23d5ff8b7a06d2ead489ddcf55ee8fb5ca7b6Timo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen if (quota_root_get_rule_limits(roots[i], mailbox_name,
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen &bytes_limit, &count_limit,
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi &ignored, &error) < 0) {
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen ctx->failed = TRUE;
c975f4c0c6a767050755e824d5a5ddeb1dbf7f6eMartti Rannanjärvi *error_result_r = QUOTA_GET_RESULT_INTERNAL_ERROR;
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi *error_r = t_strdup_printf(
93b8808c974a4d6dfe97ba6006ca2f0f1df76cb5Martti Rannanjärvi "Failed to get quota root rule limits for %s: %s",
93b8808c974a4d6dfe97ba6006ca2f0f1df76cb5Martti Rannanjärvi mailbox_name, error);
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen return -1;
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen }
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen if (!ignored)
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen ctx->no_quota_updates = FALSE;
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen if (bytes_limit > 0) {
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen ret = quota_get_resource(roots[i], mailbox_name,
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen QUOTA_NAME_STORAGE_BYTES,
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi &current, &limit, &error);
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainen if (ret == QUOTA_GET_RESULT_LIMITED) {
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen if (limit <= current) {
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen /* over quota */
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen ctx->bytes_ceil = 0;
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen ctx->bytes_ceil2 = 0;
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen diff = current - limit;
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen if (ctx->bytes_over < diff)
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen ctx->bytes_over = diff;
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen } else {
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen diff = limit - current;
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen if (ctx->bytes_ceil2 > diff)
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen ctx->bytes_ceil2 = diff;
50ecf65993bff429af04deef6c832deb019c76a5Timo Sirainen diff += !use_grace ? 0 :
a2f5f69de816fd9680ded4d1150e103f5dcda1beTimo Sirainen roots[i]->set->last_mail_max_extra_bytes;
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen if (ctx->bytes_ceil > diff)
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen ctx->bytes_ceil = diff;
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen }
c975f4c0c6a767050755e824d5a5ddeb1dbf7f6eMartti Rannanjärvi } else if (ret <= QUOTA_GET_RESULT_INTERNAL_ERROR) {
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen ctx->failed = TRUE;
c975f4c0c6a767050755e824d5a5ddeb1dbf7f6eMartti Rannanjärvi *error_result_r = ret;
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi *error_r = t_strdup_printf(
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi "Failed to get quota resource "
93b8808c974a4d6dfe97ba6006ca2f0f1df76cb5Martti Rannanjärvi QUOTA_NAME_STORAGE_BYTES" for %s: %s",
93b8808c974a4d6dfe97ba6006ca2f0f1df76cb5Martti Rannanjärvi mailbox_name, error);
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen return -1;
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen }
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen }
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen if (count_limit > 0) {
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen ret = quota_get_resource(roots[i], mailbox_name,
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen QUOTA_NAME_MESSAGES,
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi &current, &limit, &error);
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainen if (ret == QUOTA_GET_RESULT_LIMITED) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (limit <= current) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen /* over quota */
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen ctx->count_ceil = 0;
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen diff = current - limit;
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen if (ctx->count_over < diff)
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen ctx->count_over = diff;
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen } else {
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen diff = limit - current;
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen if (ctx->count_ceil > diff)
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen ctx->count_ceil = diff;
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen }
c975f4c0c6a767050755e824d5a5ddeb1dbf7f6eMartti Rannanjärvi } else if (ret <= QUOTA_GET_RESULT_INTERNAL_ERROR) {
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen ctx->failed = TRUE;
c975f4c0c6a767050755e824d5a5ddeb1dbf7f6eMartti Rannanjärvi *error_result_r = ret;
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi *error_r = t_strdup_printf(
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi "Failed to get quota resource "
93b8808c974a4d6dfe97ba6006ca2f0f1df76cb5Martti Rannanjärvi QUOTA_NAME_MESSAGES" for %s: %s",
93b8808c974a4d6dfe97ba6006ca2f0f1df76cb5Martti Rannanjärvi mailbox_name, error);
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen return -1;
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen }
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen }
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen }
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen return 0;
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen}
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainenstatic void quota_warning_execute(struct quota_root *root, const char *cmd,
f97866381675fe12cdab0f2d56a059fe7b88457aTimo Sirainen const char *last_arg, const char *reason)
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen{
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi const char *socket_path, *const *args, *error, *scheme, *ptr;
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi struct program_client_settings set = {
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi .client_connect_timeout_msecs = 1000,
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi };
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi struct program_client *pc;
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi restrict_access_init(&set.restrict_set);
b40d1ef77181fda648e2ba22188c91a124f5bee6Timo Sirainen
b40d1ef77181fda648e2ba22188c91a124f5bee6Timo Sirainen if (root->quota->set->debug)
f97866381675fe12cdab0f2d56a059fe7b88457aTimo Sirainen i_debug("quota: Executing warning: %s (because %s)", cmd, reason);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen
4f68fa8a117642e2c134a29d8e35569bce6c3158Timo Sirainen args = t_strsplit_spaces(cmd, " ");
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen if (last_arg != NULL) {
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen unsigned int count = str_array_length(args);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen const char **new_args = t_new(const char *, count + 2);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen memcpy(new_args, args, sizeof(const char *) * count);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen new_args[count] = last_arg;
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen args = new_args;
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen }
76a99afe0914951d20d96e0bf5e6d8d3ea3fd503Timo Sirainen socket_path = args[0];
76a99afe0914951d20d96e0bf5e6d8d3ea3fd503Timo Sirainen
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi if ((ptr = strchr(socket_path, ':')) != NULL) {
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi scheme = t_strcut(socket_path, ':');
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi socket_path = ptr+1;
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi } else {
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi scheme = "unix";
76a99afe0914951d20d96e0bf5e6d8d3ea3fd503Timo Sirainen }
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi if (*socket_path != '/' &&
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi strcmp(scheme, "unix") == 0)
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi socket_path =
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi t_strconcat(root->quota->user->set->base_dir,
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi "/", socket_path, NULL);
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi socket_path = t_strdup_printf("%s:%s", scheme, socket_path);
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi args++;
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi if (program_client_create(socket_path, args, &set, TRUE,
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi &pc, &error) < 0) {
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi i_error("program_client_create(%s) failed: %s", socket_path,
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi error);
76a99afe0914951d20d96e0bf5e6d8d3ea3fd503Timo Sirainen return;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen }
76a99afe0914951d20d96e0bf5e6d8d3ea3fd503Timo Sirainen
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi (void)program_client_run(pc);
76a99afe0914951d20d96e0bf5e6d8d3ea3fd503Timo Sirainen
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi program_client_destroy(&pc);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen}
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen
20dd6f6726db692904794d7daafe5b6e7238b720Timo Sirainenstatic void quota_warnings_execute(struct quota_transaction_context *ctx,
20dd6f6726db692904794d7daafe5b6e7238b720Timo Sirainen struct quota_root *root)
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen{
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen struct quota_warning_rule *warnings;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen unsigned int i, count;
20dd6f6726db692904794d7daafe5b6e7238b720Timo Sirainen uint64_t bytes_current, bytes_before, bytes_limit;
20dd6f6726db692904794d7daafe5b6e7238b720Timo Sirainen uint64_t count_current, count_before, count_limit;
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi const char *reason, *error;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen warnings = array_get_modifiable(&root->set->warning_rules, &count);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen if (count == 0)
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen return;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen if (quota_get_resource(root, "", QUOTA_NAME_STORAGE_BYTES,
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi &bytes_current, &bytes_limit, &error) == QUOTA_GET_RESULT_INTERNAL_ERROR) {
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi i_error("Failed to get quota resource "QUOTA_NAME_STORAGE_BYTES
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi ": %s", error);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen return;
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi }
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen if (quota_get_resource(root, "", QUOTA_NAME_MESSAGES,
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi &count_current, &count_limit, &error) == QUOTA_GET_RESULT_INTERNAL_ERROR) {
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi i_error("Failed to get quota resource "QUOTA_NAME_MESSAGES
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi ": %s", error);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen return;
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi }
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen
cd337e465bf8bf33d556fc397787d1519d1cef07Timo Sirainen if (ctx->bytes_used > 0 && bytes_current < (uint64_t)ctx->bytes_used)
cd337e465bf8bf33d556fc397787d1519d1cef07Timo Sirainen bytes_before = 0;
cd337e465bf8bf33d556fc397787d1519d1cef07Timo Sirainen else
cd337e465bf8bf33d556fc397787d1519d1cef07Timo Sirainen bytes_before = bytes_current - ctx->bytes_used;
cd337e465bf8bf33d556fc397787d1519d1cef07Timo Sirainen
cd337e465bf8bf33d556fc397787d1519d1cef07Timo Sirainen if (ctx->count_used > 0 && count_current < (uint64_t)ctx->count_used)
cd337e465bf8bf33d556fc397787d1519d1cef07Timo Sirainen count_before = 0;
cd337e465bf8bf33d556fc397787d1519d1cef07Timo Sirainen else
cd337e465bf8bf33d556fc397787d1519d1cef07Timo Sirainen count_before = count_current - ctx->count_used;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen for (i = 0; i < count; i++) {
ad4f1b0666975c57dd2d8d3492b223ec814791cdTimo Sirainen if (quota_warning_match(&warnings[i],
ad4f1b0666975c57dd2d8d3492b223ec814791cdTimo Sirainen bytes_before, bytes_current,
f97866381675fe12cdab0f2d56a059fe7b88457aTimo Sirainen count_before, count_current,
f97866381675fe12cdab0f2d56a059fe7b88457aTimo Sirainen &reason)) {
f97866381675fe12cdab0f2d56a059fe7b88457aTimo Sirainen quota_warning_execute(root, warnings[i].command,
f97866381675fe12cdab0f2d56a059fe7b88457aTimo Sirainen NULL, reason);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen break;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen }
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen }
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen}
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainenint quota_transaction_commit(struct quota_transaction_context **_ctx)
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen{
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen struct quota_transaction_context *ctx = *_ctx;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen struct quota_rule *rule;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen struct quota_root *const *roots;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen unsigned int i, count;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen const char *mailbox_name;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen int ret = 0;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen *_ctx = NULL;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen if (ctx->failed)
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen ret = -1;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen else if (ctx->bytes_used != 0 || ctx->count_used != 0 ||
39dea5f2e78f6bfc3adc0655176f596ee211938fTimo Sirainen ctx->recalculate != QUOTA_RECALCULATE_DONT) T_BEGIN {
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen ARRAY(struct quota_root *) warn_roots;
968aab2a00d1b53a53854fc2e97d1c632171a54eTimo Sirainen
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen mailbox_name = mailbox_get_vname(ctx->box);
b089505afb8b5c5dfb43ef88af0ea7e0920db587Timo Sirainen (void)mail_namespace_find_unalias(
b089505afb8b5c5dfb43ef88af0ea7e0920db587Timo Sirainen ctx->box->storage->user->namespaces, &mailbox_name);
b089505afb8b5c5dfb43ef88af0ea7e0920db587Timo Sirainen
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen roots = array_get(&ctx->quota->roots, &count);
968aab2a00d1b53a53854fc2e97d1c632171a54eTimo Sirainen t_array_init(&warn_roots, count);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen for (i = 0; i < count; i++) {
89adb28d8f041e8c0b9c5156f089bb2f8f478ee3Timo Sirainen if (!quota_root_is_visible(roots[i], ctx->box, FALSE))
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen continue;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen rule = quota_root_rule_find(roots[i]->set,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen mailbox_name);
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen if (rule != NULL && rule->ignore) {
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen /* mailbox not included in quota */
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen continue;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen }
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen
7b3b617e946d5b32078baa821f5fc05f775e1dfeMartti Rannanjärvi const char *error;
7b3b617e946d5b32078baa821f5fc05f775e1dfeMartti Rannanjärvi if (roots[i]->backend.v.update(roots[i], ctx, &error) < 0) {
7b3b617e946d5b32078baa821f5fc05f775e1dfeMartti Rannanjärvi i_error("Failed to update quota for %s: %s",
7b3b617e946d5b32078baa821f5fc05f775e1dfeMartti Rannanjärvi mailbox_name, error);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen ret = -1;
7b3b617e946d5b32078baa821f5fc05f775e1dfeMartti Rannanjärvi }
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen else if (!ctx->sync_transaction)
968aab2a00d1b53a53854fc2e97d1c632171a54eTimo Sirainen array_append(&warn_roots, &roots[i], 1);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen }
d8548e14925a06e44c4f1983d7b118b0180939e0Timo Sirainen /* execute quota warnings after all updates. this makes it
d8548e14925a06e44c4f1983d7b118b0180939e0Timo Sirainen work correctly regardless of whether backend.get_resource()
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen returns updated values before backend.update() or not.
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen warnings aren't executed when dsync bring the user over,
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen because the user probably already got the warning on the
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen other replica. */
968aab2a00d1b53a53854fc2e97d1c632171a54eTimo Sirainen array_foreach(&warn_roots, roots)
968aab2a00d1b53a53854fc2e97d1c632171a54eTimo Sirainen quota_warnings_execute(ctx, *roots);
968aab2a00d1b53a53854fc2e97d1c632171a54eTimo Sirainen } T_END;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen i_free(ctx);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen return ret;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen}
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen
b711a7fc74bb2d21e1e951d7ae2e8517c53bb24bTimo Sirainenstatic bool quota_over_flag_init_root(struct quota_root *root,
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen const char **quota_over_script_r,
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen const char **quota_over_flag_r,
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen bool *status_r)
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen{
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen const char *name, *flag_mask;
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen *quota_over_flag_r = NULL;
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen *status_r = FALSE;
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen name = t_strconcat(root->set->set_name, "_over_script", NULL);
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen *quota_over_script_r = mail_user_plugin_getenv(root->quota->user, name);
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen if (*quota_over_script_r == NULL) {
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen if (root->quota->set->debug) {
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen i_debug("quota: quota_over_flag check: "
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen "%s unset - skipping", name);
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen }
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen return FALSE;
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen }
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen /* e.g.: quota_over_flag_value=TRUE or quota_over_flag_value=* */
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen name = t_strconcat(root->set->set_name, "_over_flag_value", NULL);
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen flag_mask = mail_user_plugin_getenv(root->quota->user, name);
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen if (flag_mask == NULL) {
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen if (root->quota->set->debug) {
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen i_debug("quota: quota_over_flag check: "
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen "%s unset - skipping", name);
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen }
b711a7fc74bb2d21e1e951d7ae2e8517c53bb24bTimo Sirainen return FALSE;
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen }
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen /* compare quota_over_flag's value (that comes from userdb) to
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen quota_over_flag_value and save the result. */
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen name = t_strconcat(root->set->set_name, "_over_flag", NULL);
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen *quota_over_flag_r = mail_user_plugin_getenv(root->quota->user, name);
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen *status_r = *quota_over_flag_r != NULL &&
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen wildcard_match_icase(*quota_over_flag_r, flag_mask);
b711a7fc74bb2d21e1e951d7ae2e8517c53bb24bTimo Sirainen return TRUE;
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen}
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainenstatic void quota_over_flag_check_root(struct quota_root *root)
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen{
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi const char *quota_over_script, *quota_over_flag, *error;
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen const char *const *resources;
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen unsigned int i;
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen uint64_t value, limit;
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen bool cur_overquota = FALSE;
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen bool quota_over_status;
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainen enum quota_get_result ret;
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen if (root->quota_over_flag_checked)
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen return;
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen if (root->quota->user->session_create_time +
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen QUOTA_OVER_FLAG_MAX_DELAY_SECS < ioloop_time) {
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen /* userdb's quota_over_flag lookup is too old. */
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen if (root->quota->set->debug) {
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen i_debug("quota: quota_over_flag check: "
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen "Flag lookup time is too old - skipping");
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen }
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen return;
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen }
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen if (root->quota->user->session_restored) {
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen /* we don't know whether the quota_over_script was executed
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen before hibernation. just assume that it was, so we don't
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen unnecessarily call it too often. */
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen if (root->quota->set->debug) {
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen i_debug("quota: quota_over_flag check: "
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen "Session was already hibernated - skipping");
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen }
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen return;
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen }
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen root->quota_over_flag_checked = TRUE;
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen if (!quota_over_flag_init_root(root, &quota_over_script,
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen &quota_over_flag, &quota_over_status))
b711a7fc74bb2d21e1e951d7ae2e8517c53bb24bTimo Sirainen return;
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen resources = quota_root_get_resources(root);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen for (i = 0; resources[i] != NULL; i++) {
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi ret = quota_get_resource(root, "", resources[i], &value,
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi &limit, &error);
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainen if (ret == QUOTA_GET_RESULT_INTERNAL_ERROR) {
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen /* can't reliably verify this */
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi i_error("quota: Quota %s lookup failed - can't verify quota_over_flag: %s",
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi resources[i], error);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen return;
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen }
9871c4b8a06fc26de2101b825fedd7c80e8195b3Timo Sirainen if (root->quota->set->debug) {
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi i_debug("quota: quota_over_flag check: %s ret=%d value=%"PRIu64" limit=%"PRIu64,
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi resources[i], ret, value, limit);
9871c4b8a06fc26de2101b825fedd7c80e8195b3Timo Sirainen }
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainen if (ret == QUOTA_GET_RESULT_LIMITED && value >= limit)
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen cur_overquota = TRUE;
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen }
9871c4b8a06fc26de2101b825fedd7c80e8195b3Timo Sirainen if (root->quota->set->debug) {
9871c4b8a06fc26de2101b825fedd7c80e8195b3Timo Sirainen i_debug("quota: quota_over_flag=%d(%s) vs currently overquota=%d",
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen quota_over_status ? 1 : 0,
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen quota_over_flag == NULL ? "(null)" : quota_over_flag,
f0339f522dc9c8e2e8a29ef9a3f937c431c6bd1bTimo Sirainen cur_overquota ? 1 : 0);
9871c4b8a06fc26de2101b825fedd7c80e8195b3Timo Sirainen }
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen if (cur_overquota != quota_over_status) {
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen quota_warning_execute(root, quota_over_script, quota_over_flag,
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen "quota_over_flag mismatch");
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen }
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen}
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainenvoid quota_over_flag_check_startup(struct quota *quota)
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen{
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen struct quota_root *const *roots;
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen unsigned int i, count;
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen const char *name;
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen roots = array_get(&quota->roots, &count);
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen for (i = 0; i < count; i++) {
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen name = t_strconcat(roots[i]->set->set_name, "_over_flag_lazy_check", NULL);
b1e097e04ceff5800101ec3615f815f2d0f2fd12Timo Sirainen if (!mail_user_plugin_getenv_bool(roots[i]->quota->user, name))
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen quota_over_flag_check_root(roots[i]);
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen }
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen}
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainenvoid quota_transaction_rollback(struct quota_transaction_context **_ctx)
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen{
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen struct quota_transaction_context *ctx = *_ctx;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen *_ctx = NULL;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen i_free(ctx);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen}
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen
27569c07b5df5c4f079ca6cd329a5abb1b770d05Timo Sirainenstatic int quota_get_mail_size(struct quota_transaction_context *ctx,
27569c07b5df5c4f079ca6cd329a5abb1b770d05Timo Sirainen struct mail *mail, uoff_t *size_r)
27569c07b5df5c4f079ca6cd329a5abb1b770d05Timo Sirainen{
27569c07b5df5c4f079ca6cd329a5abb1b770d05Timo Sirainen if (ctx->quota->set->vsizes)
27569c07b5df5c4f079ca6cd329a5abb1b770d05Timo Sirainen return mail_get_virtual_size(mail, size_r);
27569c07b5df5c4f079ca6cd329a5abb1b770d05Timo Sirainen else
27569c07b5df5c4f079ca6cd329a5abb1b770d05Timo Sirainen return mail_get_physical_size(mail, size_r);
27569c07b5df5c4f079ca6cd329a5abb1b770d05Timo Sirainen}
27569c07b5df5c4f079ca6cd329a5abb1b770d05Timo Sirainen
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvienum quota_alloc_result quota_try_alloc(struct quota_transaction_context *ctx,
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi struct mail *mail, const char **error_r)
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen{
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen uoff_t size;
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi const char *error;
c975f4c0c6a767050755e824d5a5ddeb1dbf7f6eMartti Rannanjärvi enum quota_get_result error_res;
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi if (quota_transaction_set_limits(ctx, &error_res, error_r) < 0) {
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi if (error_res == QUOTA_GET_RESULT_BACKGROUND_CALC)
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi return QUOTA_ALLOC_RESULT_BACKGROUND_CALC;
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return QUOTA_ALLOC_RESULT_TEMPFAIL;
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi }
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen if (ctx->no_quota_updates)
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return QUOTA_ALLOC_RESULT_OK;
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen
27569c07b5df5c4f079ca6cd329a5abb1b770d05Timo Sirainen if (quota_get_mail_size(ctx, mail, &size) < 0) {
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi enum mail_error err;
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi error = mailbox_get_last_internal_error(mail->box, &err);
d6b606142e1349f4002a6bd83874399e0edd28bdTimo Sirainen
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi if (err == MAIL_ERROR_EXPUNGED) {
d6b606142e1349f4002a6bd83874399e0edd28bdTimo Sirainen /* mail being copied was already expunged. it'll fail,
d6b606142e1349f4002a6bd83874399e0edd28bdTimo Sirainen so just return success for the quota allocated. */
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return QUOTA_ALLOC_RESULT_OK;
d6b606142e1349f4002a6bd83874399e0edd28bdTimo Sirainen }
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi *error_r = t_strdup_printf(
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi "Failed to get mail size (box=%s, uid=%u): %s",
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi mail->box->vname, mail->uid, error);
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return QUOTA_ALLOC_RESULT_TEMPFAIL;
13a2b020c7ae44b1433a6328f7c79cc3ad8306feTimo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi enum quota_alloc_result ret = quota_test_alloc(ctx, size, error_r);
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi if (ret != QUOTA_ALLOC_RESULT_OK)
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen return ret;
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen /* with quota_try_alloc() we want to keep track of how many bytes
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen we've been adding/removing, so disable auto_updating=TRUE
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen optimization. this of course doesn't work perfectly if
7076aac11069d8400785373f6317c3c7f07d0883Timo Sirainen quota_alloc() or quota_free_bytes() was already used within the same
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen transaction, but that doesn't normally happen. */
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen ctx->auto_updating = FALSE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen quota_alloc(ctx, mail);
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return QUOTA_ALLOC_RESULT_OK;
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen}
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvienum quota_alloc_result quota_test_alloc(struct quota_transaction_context *ctx,
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi uoff_t size, const char **error_r)
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen{
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi if (ctx->failed) {
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi *error_r = "Quota transaction has failed earlier";
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return QUOTA_ALLOC_RESULT_TEMPFAIL;
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi }
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen
c975f4c0c6a767050755e824d5a5ddeb1dbf7f6eMartti Rannanjärvi enum quota_get_result error_res;
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi if (quota_transaction_set_limits(ctx, &error_res, error_r) < 0) {
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi if (error_res == QUOTA_GET_RESULT_BACKGROUND_CALC)
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi return QUOTA_ALLOC_RESULT_BACKGROUND_CALC;
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return QUOTA_ALLOC_RESULT_TEMPFAIL;
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi }
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi uoff_t max_size = ctx->quota->set->max_mail_size;
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi if (max_size > 0 && size > max_size) {
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi *error_r = t_strdup_printf(
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi "Requested allocation size %"PRIuUOFF_T" exceeds max "
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi "mail size %"PRIuUOFF_T, size, max_size);
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi return QUOTA_ALLOC_RESULT_OVER_MAXSIZE;
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi }
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen if (ctx->no_quota_updates)
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return QUOTA_ALLOC_RESULT_OK;
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen /* this is a virtual function mainly for trash plugin and similar,
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen which may automatically delete mails to stay under quota. */
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi return ctx->quota->set->test_alloc(ctx, size, error_r);
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen}
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen
64f889c7c1707f9951755c997d750639be5dd41cMartti Rannanjärvistatic enum quota_alloc_result quota_default_test_alloc(
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi struct quota_transaction_context *ctx, uoff_t size,
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi const char **error_r)
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_root *const *roots;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int i, count;
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen bool ignore;
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen int ret;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (!quota_transaction_is_over(ctx, size))
64f889c7c1707f9951755c997d750639be5dd41cMartti Rannanjärvi return QUOTA_ALLOC_RESULT_OK;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen
64f889c7c1707f9951755c997d750639be5dd41cMartti Rannanjärvi /* limit reached. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen roots = array_get(&ctx->quota->roots, &count);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < count; i++) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen uint64_t bytes_limit, count_limit;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen if (!quota_root_is_visible(roots[i], ctx->box, TRUE))
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen continue;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi const char *error;
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen ret = quota_root_get_rule_limits(roots[i],
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen mailbox_get_vname(ctx->box),
ad759d5e8971dd3dae17f40087bcb50f7bc4599eTimo Sirainen &bytes_limit, &count_limit,
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi &ignore, &error);
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi if (ret < 0) {
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi *error_r = t_strdup_printf(
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi "Failed to get quota root rule limits: %s",
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi error);
64f889c7c1707f9951755c997d750639be5dd41cMartti Rannanjärvi return QUOTA_ALLOC_RESULT_TEMPFAIL;
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi }
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* if size is bigger than any limit, then
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen it is bigger than the lowest limit */
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi if (bytes_limit > 0 && size > bytes_limit) {
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi *error_r = t_strdup_printf(
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi "Allocating %"PRIuUOFF_T" bytes would exceed quota limit",
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi size);
64f889c7c1707f9951755c997d750639be5dd41cMartti Rannanjärvi return QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT;
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi }
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen }
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi *error_r = t_strdup_printf(
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi "Allocating %"PRIuUOFF_T" bytes would exceed quota", size);
64f889c7c1707f9951755c997d750639be5dd41cMartti Rannanjärvi return QUOTA_ALLOC_RESULT_OVER_QUOTA;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen}
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenvoid quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen{
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen uoff_t size;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen
f9362223346d10a5866b376ed227b77b26ea4292Timo Sirainen if (!ctx->auto_updating) {
f9362223346d10a5866b376ed227b77b26ea4292Timo Sirainen if (quota_get_mail_size(ctx, mail, &size) == 0)
f9362223346d10a5866b376ed227b77b26ea4292Timo Sirainen ctx->bytes_used += size;
f9362223346d10a5866b376ed227b77b26ea4292Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen ctx->bytes_ceil = ctx->bytes_ceil2;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ctx->count_used++;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen}
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenvoid quota_free_bytes(struct quota_transaction_context *ctx,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen uoff_t physical_size)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ctx->bytes_used -= physical_size;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ctx->count_used--;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen}
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
39dea5f2e78f6bfc3adc0655176f596ee211938fTimo Sirainenvoid quota_recalculate(struct quota_transaction_context *ctx,
39dea5f2e78f6bfc3adc0655176f596ee211938fTimo Sirainen enum quota_recalculate recalculate)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
39dea5f2e78f6bfc3adc0655176f596ee211938fTimo Sirainen ctx->recalculate = recalculate;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen}
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void hidden_param_handler(struct quota_root *_root, const char *param_value ATTR_UNUSED)
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov{
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov _root->hidden = TRUE;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov}
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void ignoreunlim_param_handler(struct quota_root *_root, const char *param_value ATTR_UNUSED)
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov{
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov _root->disable_unlimited_tracking = TRUE;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov}
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void noenforcing_param_handler(struct quota_root *_root, const char *param_value ATTR_UNUSED)
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov{
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov _root->no_enforcing = TRUE;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov}
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void ns_param_handler(struct quota_root *_root, const char *param_value)
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov{
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov _root->ns_prefix = p_strdup(_root->pool, param_value);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov}
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovint quota_parse_parameters(struct quota_root *root, const char **args, const char **error_r,
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov const struct quota_param_parser *valid_params, bool fail_on_unknown)
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov{
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov const char *tmp_param_name, *tmp_param_val;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov size_t tmp_param_len;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov while (*args != NULL && (*args)[0] != '\0') {
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov for (; valid_params->param_name != NULL; ++valid_params) {
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov tmp_param_name = valid_params->param_name;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov tmp_param_len = strlen(valid_params->param_name);
e8760ad579cae936b32308088e3ed78d9aeb273eSergey Kitov i_assert(*args != NULL);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov if (strncmp(*args, tmp_param_name, tmp_param_len) == 0) {
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov tmp_param_val = NULL;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov *args += tmp_param_len;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov if (tmp_param_name[tmp_param_len - 1] == '=') {
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov const char *next_colon = strchr(*args, ':');
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov tmp_param_val = (next_colon == NULL)?
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov t_strdup(*args):
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov t_strdup_until(*args, next_colon);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov *args = (next_colon == NULL) ? NULL : next_colon + 1;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov }
e8760ad579cae936b32308088e3ed78d9aeb273eSergey Kitov else if ((*args)[0] == '\0' ||
e8760ad579cae936b32308088e3ed78d9aeb273eSergey Kitov (*args)[0] == ':') {
e8760ad579cae936b32308088e3ed78d9aeb273eSergey Kitov *args = ((*args)[0] == ':') ? *args + 1 : NULL;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov /* in case parameter is a boolean second parameter
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov * string parameter value will be ignored by param_handler
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov * we just need some non-NULL value
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov * to indicate that argument is to be processed */
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov tmp_param_val = "";
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov }
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov if (tmp_param_val != NULL) {
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov valid_params->param_handler(root, tmp_param_val);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov break;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov }
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov }
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov }
6e43afe039cf90e7755ba470070b3bec67d88b3fTimo Sirainen if (valid_params->param_name == NULL) {
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov if (fail_on_unknown) {
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov *error_r = t_strdup_printf(
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov "Unknown parameter for backend %s: %s",
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov root->backend.name, *args);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov return -1;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov }
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov else {
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov break;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov }
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov }
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov }
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov return 0;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov}