quota.c revision 12d7f667b636405ae54dcc4cb3031f4ba52aff04
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen "Quota exceeded (mailbox for user is full)"
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen#define QUOTA_LIMIT_SET_PATH DICT_PATH_PRIVATE"quota/limit/"
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen/* How many seconds after the userdb lookup do we still want to execute the
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen quota_over_script. This applies to quota_over_flag_lazy_check=yes and also
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen after unhibernating IMAP connections. */
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen unsigned int i;
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenunsigned int quota_module_id = 0;
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenextern struct quota_backend quota_backend_count;
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenextern struct quota_backend quota_backend_dict;
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenextern struct quota_backend quota_backend_dirsize;
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenextern struct quota_backend quota_backend_imapc;
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenextern struct quota_backend quota_backend_maildir;
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenstatic const struct quota_backend *quota_internal_backends[] = {
1b75b342eca820e52ca27e6bc33e0062d63eece3Timo Sirainenstatic ARRAY(const struct quota_backend*) quota_backends;
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenstatic void hidden_param_handler(struct quota_root *_root, const char *param_value);
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenstatic void ignoreunlim_param_handler(struct quota_root *_root, const char *param_value);
0d1b8b6bec79746c5d89d57dd8c1688946bd9237Josef 'Jeff' Sipekstatic void noenforcing_param_handler(struct quota_root *_root, const char *param_value);
5f1d689131a75c39f064cbd4202373e7edf78f18Josef 'Jeff' Sipekstatic void ns_param_handler(struct quota_root *_root, const char *param_value);
a943ed0f901e312445fd393249b91932797bba79Josef 'Jeff' Sipekstruct quota_param_parser quota_param_hidden = {.param_name = "hidden", .param_handler = hidden_param_handler};
f1b04cf99c9e45c318f31e6c5ade595ee7e595a5Stephan Boschstruct quota_param_parser quota_param_ignoreunlimited = {.param_name = "ignoreunlimited", .param_handler = ignoreunlim_param_handler};
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Boschstruct quota_param_parser quota_param_noenforcing = {.param_name = "noenforcing", .param_handler = noenforcing_param_handler};
f1b04cf99c9e45c318f31e6c5ade595ee7e595a5Stephan Boschstruct quota_param_parser quota_param_ns = {.param_name = "ns=", .param_handler = ns_param_handler};
f1b04cf99c9e45c318f31e6c5ade595ee7e595a5Stephan Boschstatic enum quota_alloc_result quota_default_test_alloc(
f1b04cf99c9e45c318f31e6c5ade595ee7e595a5Stephan Bosch struct quota_transaction_context *ctx, uoff_t size,
f1b04cf99c9e45c318f31e6c5ade595ee7e595a5Stephan Bosch const char **error_r);
f1b04cf99c9e45c318f31e6c5ade595ee7e595a5Stephan Boschstatic void quota_over_flag_check_root(struct quota_root *root);
f1b04cf99c9e45c318f31e6c5ade595ee7e595a5Stephan Boschstatic const struct quota_backend *quota_backend_find(const char *name)
d29c3ac4da9857ffcce57eec726d042c292e2becTimo Sirainenvoid quota_backend_register(const struct quota_backend *backend)
d29c3ac4da9857ffcce57eec726d042c292e2becTimo Sirainen i_assert(quota_backend_find(backend->name) == NULL);
d29c3ac4da9857ffcce57eec726d042c292e2becTimo Sirainenvoid quota_backend_unregister(const struct quota_backend *backend)
d29c3ac4da9857ffcce57eec726d042c292e2becTimo Sirainen for(unsigned int i = 0; i < array_count("a_backends); i++) {
d29c3ac4da9857ffcce57eec726d042c292e2becTimo Sirainen if (strcmp((*be)->name, backend->name) == 0) {
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen array_append("a_backends, quota_internal_backends,
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen for(size_t i = 0; i < N_ELEMENTS(quota_internal_backends); i++) {
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen quota_backend_unregister(quota_internal_backends[i]);
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainenstatic int quota_root_add_rules(struct mail_user *user, const char *root_name,
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen const char **error_r)
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen unsigned int i;
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen rule_name = t_strconcat(root_name, "_rule", NULL);
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen for (i = 2;; i++) {
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen rule = mail_user_plugin_getenv(user, rule_name);
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen if (quota_root_add_rule(root_set, rule, &error) < 0) {
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen *error_r = t_strdup_printf("Invalid rule %s: %s",
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen rule_name = t_strdup_printf("%s_rule%d", root_name, i);
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainenquota_root_add_warning_rules(struct mail_user *user, const char *root_name,
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen const char **error_r)
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen unsigned int i;
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen rule_name = t_strconcat(root_name, "_warning", NULL);
28715adb0aa53197a25aac72f1bdd44f44df3cd1Timo Sirainen for (i = 2;; i++) {
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen rule = mail_user_plugin_getenv(user, rule_name);
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen if (quota_root_add_warning_rule(root_set, rule, &error) < 0) {
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen *error_r = t_strdup_printf("Invalid warning rule: %s",
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen rule_name = t_strdup_printf("%s_warning%d", root_name, i);
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainenquota_root_parse_set(struct mail_user *user, const char *root_name,
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen const char **error_r)
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen *error_r = t_strdup_printf("%s supports only dict backend", name);
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen root_set->limit_set = p_strdup(root_set->set->pool, value+5);
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenquota_root_settings_init(struct quota_settings *quota_set, const char *root_def,
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen const char **error_r)
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen /* <backend>[:<quota root name>[:<backend args>]] */
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen *error_r = t_strdup_printf("Unknown quota backend: %s",
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen root_set = p_new(quota_set->pool, struct quota_root_settings, 1);
d29c3ac4da9857ffcce57eec726d042c292e2becTimo Sirainen /* save root's name */
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen root_set->name = p_strdup(quota_set->pool, args);
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen root_set->args = p_strdup(quota_set->pool, args);
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen i_debug("Quota root: name=%s backend=%s args=%s",
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen root_set->name, backend_name, args == NULL ? "" : args);
17fc2a887a5683b2e1bbd6bd9fdf0cdb97b509fbTimo Sirainen p_array_init(&root_set->rules, quota_set->pool, 4);
7d102c66eb1755e1894cf56e3594cd744e855238Timo Sirainen p_array_init(&root_set->warning_rules, quota_set->pool, 4);
7d102c66eb1755e1894cf56e3594cd744e855238Timo Sirainen array_append("a_set->root_sets, &root_set, 1);
96e3a90451b495d8bedbe5dd731539269cb8c08dStephan Boschquota_root_add(struct quota_settings *quota_set, struct mail_user *user,
96e3a90451b495d8bedbe5dd731539269cb8c08dStephan Bosch const char *env, const char *root_name, const char **error_r)
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen if (quota_root_settings_init(quota_set, env, &root_set, error_r) < 0)
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen root_set->set_name = p_strdup(quota_set->pool, root_name);
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen if (quota_root_add_rules(user, root_name, root_set, error_r) < 0)
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen if (quota_root_add_warning_rules(user, root_name, root_set, error_r) < 0)
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen if (quota_root_parse_set(user, root_name, root_set, error_r) < 0)
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen set_name = t_strconcat(root_name, "_grace", NULL);
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen value = mail_user_plugin_getenv(user, set_name);
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen if (quota_root_parse_grace(root_set, value, error_r) < 0) {
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen *error_r = t_strdup_printf("Invalid %s value '%s': %s",
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenconst char *quota_alloc_result_errstr(enum quota_alloc_result res,
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen return "Blocked by an ongoing background quota calculation";
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen return "Internal quota calculation error";
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen return "Mail size is larger than the maximum size allowed by "
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen "server configuration";
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenint quota_user_read_settings(struct mail_user *user,
30f128cc663b8aeb68bb3bfb9ed49ab2e48029e1Timo Sirainen const char **error_r)
30f128cc663b8aeb68bb3bfb9ed49ab2e48029e1Timo Sirainen unsigned int i;
30f128cc663b8aeb68bb3bfb9ed49ab2e48029e1Timo Sirainen pool = pool_alloconly_create("quota settings", 2048);
30f128cc663b8aeb68bb3bfb9ed49ab2e48029e1Timo Sirainen quota_set = p_new(pool, struct quota_settings, 1);
30f128cc663b8aeb68bb3bfb9ed49ab2e48029e1Timo Sirainen quota_set->test_alloc = quota_default_test_alloc;
30f128cc663b8aeb68bb3bfb9ed49ab2e48029e1Timo Sirainen mail_user_plugin_getenv(user, "quota_exceeded_message");
30f128cc663b8aeb68bb3bfb9ed49ab2e48029e1Timo Sirainen quota_set->quota_exceeded_msg = DEFAULT_QUOTA_EXCEEDED_MSG;
30f128cc663b8aeb68bb3bfb9ed49ab2e48029e1Timo Sirainen quota_set->vsizes = mail_user_plugin_getenv_bool(user, "quota_vsizes");
30f128cc663b8aeb68bb3bfb9ed49ab2e48029e1Timo Sirainen const char *max_size = mail_user_plugin_getenv(user,
30f128cc663b8aeb68bb3bfb9ed49ab2e48029e1Timo Sirainen "quota_max_mail_size");
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen if (settings_get_size(max_size, "a_set->max_mail_size,
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen *error_r = t_strdup_printf("quota_max_mail_size: %s",
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch if (i_strocpy(root_name, "quota", sizeof(root_name)) < 0)
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen for (i = 2;; i++) {
30f128cc663b8aeb68bb3bfb9ed49ab2e48029e1Timo Sirainen env = mail_user_plugin_getenv(user, root_name);
30f128cc663b8aeb68bb3bfb9ed49ab2e48029e1Timo Sirainen if (quota_root_add(quota_set, user, env, root_name,
30f128cc663b8aeb68bb3bfb9ed49ab2e48029e1Timo Sirainen *error_r = t_strdup_printf("Invalid quota root %s: %s",
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen if (i_snprintf(root_name, sizeof(root_name), "quota%d", i) < 0)
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenvoid quota_settings_deinit(struct quota_settings **_quota_set)
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen struct quota_settings *quota_set = *_quota_set;
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenstatic void quota_root_deinit(struct quota_root *root)
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainenint quota_root_default_init(struct quota_root *root, const char *args,
ba54c712141b9764a2e06ed8dfb35bc3154b53c7Timo Sirainen const char **error_r)
994a2b017b5b7da97914baa1ef711b124c8d15a7Timo Sirainen const struct quota_param_parser default_params[] = {
994a2b017b5b7da97914baa1ef711b124c8d15a7Timo Sirainen return quota_parse_parameters(root, &args, error_r, default_params, TRUE);
994a2b017b5b7da97914baa1ef711b124c8d15a7Timo Sirainenquota_root_init(struct quota_root_settings *root_set, struct quota *quota,
unsigned int i, count;
const char *error;
int ret;
for (i = 0; i < count; i++) {
if (ret < 0) {
if (ret > 0)
unsigned int i, count;
for (i = 0; i < count; i++)
const char *mailbox_name,
bool *ignored_r,
const char **error_r)
int ret;
const char *error;
error);
&mailbox_name);
ret = 0;
bytes_limit = 0;
count_limit = 0;
return ret;
unsigned int i, count;
for (i = 0; i < count; i++) {
return TRUE;
return FALSE;
return FALSE;
unsigned int i, j, count;
for (i = 0; i < count; i++) {
unsigned int i, count;
for (i = 0; i < count; i++) {
struct quota_root_iter *
return iter;
return FALSE;
return FALSE;
return FALSE;
return FALSE;
return TRUE;
bool enforce)
return FALSE;
return FALSE;
return TRUE;
unsigned int count;
return NULL;
return NULL;
iter->i++;
return root;
unsigned int i, count;
return NULL;
for (i = 0; i < count; i++) {
return roots[i];
return NULL;
enum quota_get_result
const char **error_r)
const char *error;
return ret;
return QUOTA_GET_RESULT_INTERNAL_ERROR;
*limit_r = 0;
if (kilobytes) {
const char *mailbox_name;
&mailbox_name);
return ctx;
const char **error_r)
unsigned int i, count;
for (i = 0; i < count; i++) {
error);
if (!ignored)
if (bytes_limit > 0) {
error);
if (count_limit > 0) {
error);
args++;
error);
unsigned int i, count;
if (count == 0)
bytes_before = 0;
count_before = 0;
for (i = 0; i < count; i++) {
&reason)) {
unsigned int i, count;
const char *mailbox_name;
int ret = 0;
(void)mail_namespace_find_unalias(
for (i = 0; i < count; i++) {
const char *error;
} T_END;
return ret;
const char **quota_over_script_r,
const char **quota_over_flag_r,
bool *status_r)
return FALSE;
return FALSE;
return TRUE;
const char *const *resources;
bool quota_over_status;
unsigned int i, count;
const char *name;
for (i = 0; i < count; i++) {
const char *error;
return QUOTA_ALLOC_RESULT_TEMPFAIL;
return QUOTA_ALLOC_RESULT_OK;
return QUOTA_ALLOC_RESULT_OK;
return QUOTA_ALLOC_RESULT_TEMPFAIL;
return ret;
return QUOTA_ALLOC_RESULT_OK;
return QUOTA_ALLOC_RESULT_TEMPFAIL;
const char *error;
return QUOTA_ALLOC_RESULT_TEMPFAIL;
return QUOTA_ALLOC_RESULT_OVER_MAXSIZE;
return QUOTA_ALLOC_RESULT_OK;
const char **error_r)
unsigned int i, count;
bool ignore;
int ret;
return QUOTA_ALLOC_RESULT_OK;
for (i = 0; i < count; i++) {
const char *error;
if (ret < 0) {
error);
return QUOTA_ALLOC_RESULT_TEMPFAIL;
size);
return QUOTA_ALLOC_RESULT_OVER_QUOTA;
static void ignoreunlim_param_handler(struct quota_root *_root, const char *param_value ATTR_UNUSED)
static void noenforcing_param_handler(struct quota_root *_root, const char *param_value ATTR_UNUSED)
if (fail_on_unknown) {