From: Timo Sirainen Date: Thu, 15 Aug 2024 21:18:19 +0000 (+0300) Subject: quota: Initial conversion to use global settings X-Git-Tag: 2.4.1~665 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c7838108a43ee99534b3754acbe51b66b4fd3569;p=thirdparty%2Fdovecot%2Fcore.git quota: Initial conversion to use global settings --- diff --git a/src/plugins/quota-clone/quota-clone-plugin.c b/src/plugins/quota-clone/quota-clone-plugin.c index 81f1c2c9e6..dfbbdcd88f 100644 --- a/src/plugins/quota-clone/quota-clone-plugin.c +++ b/src/plugins/quota-clone/quota-clone-plugin.c @@ -89,7 +89,7 @@ static bool quota_clone_flush_real(struct mail_user *user) } /* get new values first */ - bytes_res = quota_get_resource(root, "", QUOTA_NAME_STORAGE_BYTES, + bytes_res = quota_get_resource(root, NULL, QUOTA_NAME_STORAGE_BYTES, &bytes_value, &limit, &error); if (bytes_res == QUOTA_GET_RESULT_INTERNAL_ERROR) { e_error(user->event, "quota_clone_plugin: " @@ -97,7 +97,7 @@ static bool quota_clone_flush_real(struct mail_user *user) error); return TRUE; } - count_res = quota_get_resource(root, "", QUOTA_NAME_MESSAGES, + count_res = quota_get_resource(root, NULL, QUOTA_NAME_MESSAGES, &count_value, &limit, &error); if (count_res == QUOTA_GET_RESULT_INTERNAL_ERROR) { e_error(user->event, "quota_clone_plugin: " diff --git a/src/plugins/quota/doveadm-quota.c b/src/plugins/quota/doveadm-quota.c index ff544fc9bb..7eb0e67b8a 100644 --- a/src/plugins/quota/doveadm-quota.c +++ b/src/plugins/quota/doveadm-quota.c @@ -24,7 +24,7 @@ static int cmd_quota_get_root(struct quota_root *root, struct mail_user *user) res = quota_root_get_resources(root); for (; *res != NULL; res++) { qret = quota_get_resource(root, NULL, *res, &value, &limit, &error); - doveadm_print(root->set->name); + doveadm_print(root->set->quota_name); doveadm_print(*res); if (qret == QUOTA_GET_RESULT_LIMITED) { doveadm_print_num(value); @@ -64,7 +64,7 @@ cmd_quota_get_run(struct doveadm_mail_cmd_context *ctx, } int ret = 0; - array_foreach(&quser->quota->roots, root) + array_foreach(&quser->quota->all_roots, root) if (cmd_quota_get_root(*root, user) < 0) ret = -1; if (ret < 0) @@ -114,7 +114,7 @@ cmd_quota_recalc_run(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED, trans.quota = quser->quota; trans.recalculate = QUOTA_RECALCULATE_FORCED; - array_foreach(&quser->quota->roots, root) { + array_foreach(&quser->quota->all_roots, root) { const char *error; if ((*root)->backend.v.update(*root, &trans, &error) < 0) e_error(user->event, diff --git a/src/plugins/quota/quota-count.c b/src/plugins/quota/quota-count.c index 8045b29126..80633815bd 100644 --- a/src/plugins/quota/quota-count.c +++ b/src/plugins/quota/quota-count.c @@ -2,6 +2,7 @@ #include "lib.h" #include "ioloop.h" +#include "settings.h" #include "mailbox-list-iter.h" #include "quota-private.h" @@ -29,7 +30,7 @@ quota_count_mailbox(struct quota_root *root, struct mail_namespace *ns, enum quota_get_result *error_result_r, const char **error_r) { - struct quota_rule *rule; + const struct quota_settings *set = NULL; struct mailbox *box; struct mailbox_metadata metadata; struct mailbox_status status; @@ -37,14 +38,15 @@ quota_count_mailbox(struct quota_root *root, struct mail_namespace *ns, const char *errstr; int ret; - rule = quota_root_rule_find(root->set, vname); - if (rule != NULL && rule->ignore) { - /* mailbox not included in quota */ - return 0; - } - box = mailbox_alloc(ns->list, vname, MAILBOX_FLAG_READONLY); - if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) != 0) { + struct event *event = event_create(box->event); + event_add_str(event, "quota", root->set->quota_name); + if (settings_get(event, "a_setting_parser_info, 0, + &set, error_r) < 0) + ret = -1; + else if (set->quota_ignore) + ret = 0; + else if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) != 0) { /* quota doesn't exist for this mailbox/storage */ ret = 0; } else if (mailbox_get_metadata(box, MAILBOX_METADATA_VIRTUAL_SIZE, @@ -73,6 +75,8 @@ quota_count_mailbox(struct quota_root *root, struct mail_namespace *ns, *bytes += metadata.virtual_size; *count += status.messages; } + settings_free(set); + event_unref(&event); mailbox_free(&box); return ret; } diff --git a/src/plugins/quota/quota-fs.c b/src/plugins/quota/quota-fs.c index b2005902a5..1d61f82a0c 100644 --- a/src/plugins/quota/quota-fs.c +++ b/src/plugins/quota/quota-fs.c @@ -140,7 +140,6 @@ static int fs_quota_init(struct quota_root *_root, const char *args, {.param_name = "group", .param_handler = handle_group_param}, {.param_name = "mount=", .param_handler = handle_mount_param}, {.param_name = "inode_per_mail", .param_handler = handle_inode_param}, - quota_param_hidden, quota_param_noenforcing, quota_param_ns, {.param_name = NULL} }; diff --git a/src/plugins/quota/quota-imapc.c b/src/plugins/quota/quota-imapc.c index 10950b044c..3d9678d3ab 100644 --- a/src/plugins/quota/quota-imapc.c +++ b/src/plugins/quota/quota-imapc.c @@ -63,7 +63,6 @@ static int imapc_quota_init(struct quota_root *_root, const char *args, const struct quota_param_parser imapc_params[] = { {.param_name = "box=", .param_handler = handle_box_param}, {.param_name = "root=", .param_handler = handle_root_param}, - quota_param_ns, {.param_name = NULL} }; @@ -98,7 +97,7 @@ imapc_quota_root_refresh_find(struct imapc_storage_client *client) i_assert(quota != NULL); /* find the quota root that is being refreshed */ - array_foreach("a->roots, rootp) { + array_foreach("a->all_roots, rootp) { if ((*rootp)->backend.name == quota_backend_imapc.name) { struct imapc_quota_root *root = (struct imapc_quota_root *)*rootp; @@ -253,7 +252,7 @@ imapc_quota_refresh_update(struct quota *quota, /* use the first quota root for everything */ refresh_root = array_front(&refresh->roots); - array_foreach("a->roots, rootp) { + array_foreach("a->all_roots, rootp) { if ((*rootp)->backend.name == quota_backend_imapc.name) { struct imapc_quota_root *root = (struct imapc_quota_root *)*rootp; diff --git a/src/plugins/quota/quota-maildir.c b/src/plugins/quota/quota-maildir.c index 8f92c8884d..dbb5af7cee 100644 --- a/src/plugins/quota/quota-maildir.c +++ b/src/plugins/quota/quota-maildir.c @@ -9,6 +9,7 @@ #include "read-full.h" #include "write-full.h" #include "str.h" +#include "settings.h" #include "maildir-storage.h" #include "mailbox-list-private.h" #include "quota-private.h" @@ -158,7 +159,6 @@ static bool maildir_set_next_path(struct maildir_list_context *ctx) static const char * maildir_list_next(struct maildir_list_context *ctx, time_t *mtime_r) { - struct quota_rule *rule; struct stat st; for (;;) { @@ -167,9 +167,23 @@ maildir_list_next(struct maildir_list_context *ctx, time_t *mtime_r) if (ctx->info == NULL) return NULL; - rule = quota_root_rule_find(ctx->root->root.set, - ctx->info->vname); - if (rule != NULL && rule->ignore) { + const struct quota_settings *set; + bool quota_ignore = FALSE; + const char *error; + struct event *event = + mail_storage_mailbox_create_event( + ctx->root->root.backend.event, + ctx->info->ns->list, ctx->info->vname); + if (settings_get(event, "a_setting_parser_info, 0, + &set, &error) < 0) + e_error(event, "%s", error); + else { + quota_ignore = set->quota_ignore; + settings_free(set); + } + event_unref(&event); + + if (quota_ignore) { /* mailbox not included in quota */ continue; } diff --git a/src/plugins/quota/quota-private.h b/src/plugins/quota/quota-private.h index 738c3230fd..80cf943eef 100644 --- a/src/plugins/quota/quota-private.h +++ b/src/plugins/quota/quota-private.h @@ -12,10 +12,16 @@ extern unsigned int quota_module_id; struct quota { struct mail_user *user; - struct quota_legacy_settings *set; struct event *event; - ARRAY(struct quota_root *) roots; + /* Global quota roots. These are filled when initializing the user. + These quota roots will be used only for private namespaces. */ + ARRAY(struct quota_root *) global_private_roots; + /* All seen quota roots, which may be specific to only some namespaces. + Quota roots are added lazily when a new quota_name is seen for a + namespace. It's assumed that the relevant quota backend settings + don't change for the same quota_name. */ + ARRAY(struct quota_root *) all_roots; enum quota_alloc_result (*test_alloc)( struct quota_transaction_context *ctx, uoff_t size, @@ -23,29 +29,6 @@ struct quota { bool vsizes:1; }; -struct quota_legacy_settings { - pool_t pool; - - ARRAY(struct quota_root_legacy_settings *) root_sets; -}; - -struct quota_rule { - const char *mailbox_mask; - - int64_t bytes_limit, count_limit; - /* relative to default_rule */ - int bytes_percent, count_percent; - - /* Don't include this mailbox in quota */ - bool ignore:1; -}; - -struct quota_warning_rule { - struct quota_rule rule; - const char *command; - bool reverse:1; -}; - struct quota_backend_vfuncs { struct quota_root *(*alloc)(void); int (*init)(struct quota_root *root, const char *args, @@ -80,46 +63,20 @@ struct quota_backend { struct quota_backend_vfuncs v; }; -struct quota_root_legacy_settings { - /* Unique quota root name. */ - const char *name; - /* Name in settings, e.g. "quota", "quota2", .. */ - const char *set_name; - - const char *args; - - const struct quota_backend *backend; - struct quota_rule default_rule; - ARRAY(struct quota_rule) rules; - ARRAY(struct quota_warning_rule) warning_rules; - - /* If user is under quota before saving a mail, allow the last mail to - bring the user over quota by this many bytes. */ - uint64_t quota_storage_grace; -}; - struct quota_root { pool_t pool; - struct quota_root_legacy_settings *set; + const struct quota_settings *set; + struct quota *quota; struct quota_backend backend; - /* this quota root applies only to this namespace. it may also be - a public namespace without an owner. */ - struct mail_namespace *ns; - /* this is set in quota init(), because namespaces aren't known yet. - when accessing shared users the ns_prefix may be non-NULL but - ns=NULL, so when checking if quota root applies only to a specific - namespace use the ns_prefix!=NULL check. */ - const char *ns_prefix; /* All namespaces using this quota root */ ARRAY(struct mail_namespace *) namespaces; - struct mail_namespace *unwanted_ns; - /* initially the same as set->default_rule.*_limit, but some backends - may change these by reading the limits elsewhere (e.g. imapc, - FS quota) */ + /* initially the same as set->quota_storage_size and + set->quota_message_count, but some backends may change these by + reading the limits elsewhere (e.g. imapc, FS quota) */ int64_t bytes_limit, count_limit; /* Module-specific contexts. See quota_module_id. */ @@ -140,6 +97,8 @@ struct quota_root { bool hidden:1; /* Did we already check quota_over_status correctness? */ bool quota_over_status_checked:1; + /* Are there any quota warnings with threshold=under? */ + bool have_under_warnings:1; }; struct quota_transaction_context { @@ -176,29 +135,20 @@ struct quota_transaction_context { bool no_quota_updates:1; }; -/* Register storage to all user's quota roots. */ -void quota_add_user_namespace(struct quota *quota, struct mail_namespace *ns); +void quota_add_user_namespace(struct quota *quota, const char *root_name, + struct mail_namespace *ns); void quota_remove_user_namespace(struct mail_namespace *ns); int quota_root_default_init(struct quota_root *root, const char *args, const char **error_r); struct quota *quota_get_mail_user_quota(struct mail_user *user); -bool quota_root_is_namespace_visible(struct quota_root *root, - struct mail_namespace *ns); -struct quota_rule * -quota_root_rule_find(struct quota_root_legacy_settings *root_set, const char *name); - /* Returns 1 if values were returned successfully, 0 if we're recursing into the same function, -1 if error. */ int quota_count(struct quota_root *root, uint64_t *bytes_r, uint64_t *count_r, enum quota_get_result *error_result_r, const char **error_r); -int quota_root_parse_grace(struct event *event, - struct quota_root_legacy_settings *root_set, - const char *value, const char **error_r); -bool quota_warning_match(struct quota_root *root, - const struct quota_warning_rule *w, +bool quota_warning_match(const struct quota_settings *w, uint64_t bytes_before, uint64_t bytes_current, uint64_t count_before, uint64_t count_current, const char **reason_r); @@ -207,6 +157,7 @@ int quota_transaction_set_limits(struct quota_transaction_context *ctx, enum quota_get_result *error_result_r, const char **error_r); +const struct quota_backend *quota_backend_find(const char *name); void quota_backend_register(const struct quota_backend *backend); void quota_backend_unregister(const struct quota_backend *backend); diff --git a/src/plugins/quota/quota-settings.c b/src/plugins/quota/quota-settings.c index 79c7aad3b7..1f6990a292 100644 --- a/src/plugins/quota/quota-settings.c +++ b/src/plugins/quota/quota-settings.c @@ -2,12 +2,44 @@ #include "lib.h" #include "settings-parser.h" +#include "quota-private.h" #include "quota-settings.h" +static bool quota_settings_check(void *_set, pool_t pool, const char **error_r); + #undef DEF #define DEF(type, name) \ SETTING_DEFINE_STRUCT_##type(#name, name, struct quota_settings) static const struct setting_define quota_setting_defines[] = { + { .type = SET_FILTER_ARRAY, .key = "quota", + .offset = offsetof(struct quota_settings, quota_roots), + .filter_array_field_name = "quota_name", }, + { .type = SET_FILTER_ARRAY, .key = "quota_warning", + .offset = offsetof(struct quota_settings, quota_warnings), + .filter_array_field_name = "quota_warning_name", + .required_setting = "execute", }, + + DEF(STR, quota_name), + DEF(STR, quota_driver), + DEF(STR, quota_args), + DEF(BOOL, quota_ignore), + DEF(SIZE, quota_storage_size), + DEF(UINT, quota_storage_percentage), + DEF(SIZE, quota_storage_extra), + DEF(SIZE, quota_storage_grace), + DEF(UINT, quota_message_count), + DEF(UINT, quota_message_percentage), + + DEF(STR, quota_warning_name), + DEF(ENUM, quota_warning_resource), + DEF(ENUM, quota_warning_threshold), + DEF(STR, quota_warning_command), + + DEF(BOOL, quota_over_status_lazy_check), + DEF(STR, quota_over_status_current), + DEF(STR, quota_over_status_mask), + DEF(STR, quota_over_status_script), + DEF(UINT, quota_mailbox_count), DEF(UINT, quota_mailbox_message_count), DEF(SIZE, quota_mail_size), @@ -17,6 +49,32 @@ static const struct setting_define quota_setting_defines[] = { }; static const struct quota_settings quota_default_settings = { + .quota_roots = ARRAY_INIT, + .quota_warnings = ARRAY_INIT, + + .quota_name = "", + .quota_driver = "count", + .quota_args = "", + .quota_ignore = FALSE, + .quota_storage_size = SET_SIZE_UNLIMITED, + .quota_storage_percentage = 100, + .quota_storage_extra = 0, + .quota_storage_grace = 1024 * 1024 * 10, + .quota_message_count = SET_UINT_UNLIMITED, + .quota_message_percentage = 100, + + .quota_warning_name = "", + .quota_warning_resource = QUOTA_WARNING_RESOURCE_STORAGE":" + QUOTA_WARNING_RESOURCE_MESSAGE, + .quota_warning_threshold = QUOTA_WARNING_THRESHOLD_OVER":" + QUOTA_WARNING_THRESHOLD_UNDER, + .quota_warning_command = "", + + .quota_over_status_lazy_check = FALSE, + .quota_over_status_current = "", + .quota_over_status_mask = "", + .quota_over_status_script = "", + .quota_mailbox_count = SET_UINT_UNLIMITED, .quota_mail_size = SET_SIZE_UNLIMITED, .quota_mailbox_message_count = SET_UINT_UNLIMITED, @@ -28,9 +86,42 @@ const struct setting_parser_info quota_setting_parser_info = { .defines = quota_setting_defines, .defaults = "a_default_settings, .struct_size = sizeof(struct quota_settings), +#ifndef CONFIG_BINARY + .check_func = quota_settings_check, +#endif .pool_offset1 = 1 + offsetof(struct quota_settings, pool), }; +#ifndef CONFIG_BINARY +static bool quota_settings_check(void *_set, pool_t pool ATTR_UNUSED, + const char **error_r) +{ + struct quota_settings *set = _set; + + set->backend = quota_backend_find(set->quota_driver); + if (set->backend == NULL) { + *error_r = t_strdup_printf("Unknown quota_driver: %s", + set->quota_driver); + return FALSE; + } + if (set->quota_storage_percentage == 0) { + *error_r = "quota_storage_percentage must not be 0"; + return FALSE; + } + if (set->quota_message_percentage == 0) { + *error_r = "quota_message_percentage must not be 0"; + return FALSE; + } + /* Change "unlimited" settings to 0 for easier handling. We accept 0 + as unlimited anyway because that's commonly used in userdbs. */ + if (set->quota_message_count == SET_UINT_UNLIMITED) + set->quota_message_count = 0; + if (set->quota_storage_size == SET_SIZE_UNLIMITED) + set->quota_storage_size = 0; + return TRUE; +} +#endif + struct quota_settings *quota_get_unlimited_set(void) { static struct quota_settings set; diff --git a/src/plugins/quota/quota-settings.h b/src/plugins/quota/quota-settings.h index 72e66ec273..3af4e9454d 100644 --- a/src/plugins/quota/quota-settings.h +++ b/src/plugins/quota/quota-settings.h @@ -1,13 +1,75 @@ #ifndef QUOTA_SETTINGS_H #define QUOTA_SETTINGS_H +/* */ +#define QUOTA_WARNING_RESOURCE_STORAGE "storage" +#define QUOTA_WARNING_RESOURCE_MESSAGE "message" + +#define QUOTA_WARNING_THRESHOLD_OVER "over" +#define QUOTA_WARNING_THRESHOLD_UNDER "under" +/* */ + struct quota_settings { pool_t pool; + ARRAY_TYPE(const_string) quota_roots; + ARRAY_TYPE(const_string) quota_warnings; + + /* For quota roots: */ + + /* Client-visible name of the quota root */ + const char *quota_name; + const char *quota_driver; + const char *quota_args; + /* If TRUE, quota is not tracked at all (for this mailbox). This is + typically set only for specific mailboxes or namespaces. Note that + this differs from unlimited quota, which still tracks the quota, + even if it is not enforced. */ + bool quota_ignore; + /* Quota storage size is counted as: + quota_storage_size * quota_storage_percentage / 100 + + quota_storage_extra. */ + uoff_t quota_storage_size; + unsigned int quota_storage_percentage; + uoff_t quota_storage_extra; + /* If user is under quota before saving a mail, allow the last mail to + bring the user over quota by this many bytes. This is only used for + mail delivery sessions (lda, lmtp). */ + uoff_t quota_storage_grace; + /* Quota messages count is counted as: + quota_message_count * quota_message_percentage / 100. */ + unsigned int quota_message_count; + unsigned int quota_message_percentage; + + /* For quota warnings: */ + + /* Name for the warning. This is only for identification in the + configuration. */ + const char *quota_warning_name; + /* Specifies the quota resource the warning tracks + (storage / message) */ + const char *quota_warning_resource; + /* Specifies whether the warning is executed when going over the limit + or back under the limit. */ + const char *quota_warning_threshold; + const char *quota_warning_command; + + /* For quota_over_status: */ + bool quota_over_status_lazy_check; + const char *quota_over_status_current; + const char *quota_over_status_mask; + const char *quota_over_status_script; + + /* Globals: */ + unsigned int quota_mailbox_count; uoff_t quota_mail_size; unsigned int quota_mailbox_message_count; const char *quota_exceeded_message; + + /* Generated: */ + + const struct quota_backend *backend; }; struct quota_settings *quota_get_unlimited_set(void); diff --git a/src/plugins/quota/quota-storage.c b/src/plugins/quota/quota-storage.c index 8fc9a22496..c6bd685caa 100644 --- a/src/plugins/quota/quota-storage.c +++ b/src/plugins/quota/quota-storage.c @@ -253,11 +253,11 @@ quota_move_requires_check(struct mailbox *dest_box, struct mailbox *src_box) struct quota_user *quser = QUOTA_USER_CONTEXT_REQUIRE(src_ns->user); struct quota_root *const *rootp; - array_foreach(&quser->quota->roots, rootp) { + array_foreach(&quser->quota->all_roots, rootp) { bool have_src_quota, have_dest_quota; - have_src_quota = quota_root_is_namespace_visible(*rootp, src_ns); - have_dest_quota = quota_root_is_namespace_visible(*rootp, dest_ns); + have_src_quota = array_lsearch_ptr(&(*rootp)->namespaces, src_ns) != NULL; + have_dest_quota = array_lsearch_ptr(&(*rootp)->namespaces, dest_ns) != NULL; if (have_src_quota == have_dest_quota) { /* Both/neither have this quota */ } else if (have_dest_quota) { @@ -575,7 +575,7 @@ static void quota_roots_flush(struct quota *quota) struct quota_root *const *roots; unsigned int i, count; - roots = array_get("a->roots, &count); + roots = array_get("a->all_roots, &count); for (i = 0; i < count; i++) { if (roots[i]->backend.v.flush != NULL) roots[i]->backend.v.flush(roots[i]); @@ -664,31 +664,19 @@ struct quota *quota_get_mail_user_quota(struct mail_user *user) static void quota_user_deinit(struct mail_user *user) { struct quota_user *quser = QUOTA_USER_CONTEXT_REQUIRE(user); - struct quota_legacy_settings *quota_set = quser->quota->set; quota_deinit(&quser->quota); quser->module_ctx.super.deinit(user); - - quota_settings_deinit("a_set); } void quota_mail_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct quota_user *quser; - struct quota_legacy_settings *set; struct quota *quota; const char *error; - int ret; - if ((ret = quota_user_read_settings(user, &set, &error)) == 0) { - if (quota_init(set, user, "a, &error) < 0) { - quota_settings_deinit(&set); - ret = -1; - } - } - - if (ret < 0) { + if (quota_init(user, "a, &error) < 0) { user->error = p_strdup_printf(user->pool, "Failed to initialize quota: %s", error); return; @@ -702,57 +690,39 @@ void quota_mail_user_created(struct mail_user *user) MODULE_CONTEXT_SET(user, quota_user_module, quser); } -static struct quota_root * -quota_find_root_for_ns(struct quota *quota, struct mail_namespace *ns) +static bool +quota_has_global_private_root(struct quota *quota, const char *root_name) { - struct quota_root *const *roots; - unsigned int i, count; - - roots = array_get("a->roots, &count); - for (i = 0; i < count; i++) { - if (roots[i]->ns_prefix != NULL && - strcmp(roots[i]->ns_prefix, ns->prefix) == 0) - return roots[i]; + struct quota_root *root; + array_foreach_elem("a->global_private_roots, root) { + if (strcmp(root->set->quota_name, root_name) == 0) + return TRUE; } - return NULL; + return FALSE; } void quota_mailbox_list_created(struct mailbox_list *list) { struct quota_mailbox_list *qlist; struct quota *quota = NULL; - struct quota_root *root; struct mail_user *quota_user; - bool add; + const struct quota_settings *set; + const char *root_name, *error; - /* see if we have a quota explicitly defined for this namespace */ - quota = quota_get_mail_user_quota(list->ns->user); + quota_user = list->ns->owner != NULL ? + list->ns->owner : list->ns->user; + quota = quota_get_mail_user_quota(quota_user); if (quota == NULL) return; - root = quota_find_root_for_ns(quota, list->ns); - if (root != NULL) { - /* explicit quota root */ - root->ns = list->ns; - quota_user = list->ns->user; - } else { - quota_user = list->ns->owner != NULL ? - list->ns->owner : list->ns->user; - } if ((list->ns->flags & NAMESPACE_FLAG_NOQUOTA) != 0) - add = FALSE; - else if (list->ns->owner == NULL) { - /* public namespace - add quota only if namespace is - explicitly defined for it */ - add = root != NULL; - } else { - /* for shared namespaces add only if the owner has quota - enabled */ - add = QUOTA_USER_CONTEXT(quota_user) != NULL; - } + return; - if (!add) + if (settings_get(list->event, "a_setting_parser_info, 0, + &set, &error) < 0) { + e_error(list->event, "%s", error); return; + } struct mailbox_list_vfuncs *v = list->vlast; @@ -762,52 +732,35 @@ void quota_mailbox_list_created(struct mailbox_list *list) v->deinit = quota_mailbox_list_deinit; MODULE_CONTEXT_SET(list, quota_mailbox_list_module, qlist); - quota = quota_get_mail_user_quota(quota_user); - i_assert(quota != NULL); - quota_add_user_namespace(quota, list->ns); -} - -static void quota_root_set_namespace(struct quota_root *root, - struct mail_namespace *namespaces) -{ - const struct quota_rule *rule; - const char *name; - struct mail_namespace *ns; - /* silence errors for autocreated (shared) users */ - bool silent_errors = namespaces->user->autocreated; - - if (root->ns_prefix != NULL && root->ns == NULL) { - root->ns = mail_namespace_find_prefix(namespaces, - root->ns_prefix); - if (root->ns == NULL && !silent_errors) { - e_error(root->quota->event, - "Unknown namespace: %s", - root->ns_prefix); - } + if (!array_is_created(&set->quota_roots)) { + /* even without quota roots, the quota plugin still enforces + e.g. quota_mail_size, so the quota_mailbox_list must be + set. */ + settings_free(set); + return; } - array_foreach(&root->set->rules, rule) { - name = rule->mailbox_mask; - ns = mail_namespace_find(namespaces, name); - if ((ns->flags & NAMESPACE_FLAG_UNUSABLE) != 0 && - !silent_errors) - e_error(root->quota->event, - "Unknown namespace: %s", name); + quota = quota_get_mail_user_quota(quota_user); + i_assert(quota != NULL); + array_foreach_elem(&set->quota_roots, root_name) { + /* All visible quota roots are used for private namespaces. + For shared/public namespaces use only quota roots explicitly + defined inside the namespace { .. } This means the quota root + name is not in the global_private_roots array. */ + if (list->ns->type == MAIL_NAMESPACE_TYPE_PRIVATE || + !quota_has_global_private_root(quota, root_name)) + quota_add_user_namespace(quota, root_name, list->ns); } + settings_free(set); } void quota_mail_namespaces_created(struct mail_namespace *namespaces) { struct quota *quota; - struct quota_root *const *roots; - unsigned int i, count; quota = quota_get_mail_user_quota(namespaces->user); if (quota == NULL) return; - roots = array_get("a->roots, &count); - for (i = 0; i < count; i++) - quota_root_set_namespace(roots[i], namespaces); quota_over_status_check_startup(quota); } diff --git a/src/plugins/quota/quota-util.c b/src/plugins/quota/quota-util.c index 63b77b4158..74b2e3ce99 100644 --- a/src/plugins/quota/quota-util.c +++ b/src/plugins/quota/quota-util.c @@ -1,350 +1,42 @@ /* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */ #include "lib.h" -#include "str-parse.h" -#include "wildcard-match.h" #include "quota-private.h" -#include - -#define QUOTA_DEFAULT_GRACE "10M" - -#define RULE_NAME_DEFAULT_FORCE "*" - -struct quota_rule * -quota_root_rule_find(struct quota_root_legacy_settings *root_set, const char *name) -{ - struct quota_rule *rule; - - array_foreach_modifiable(&root_set->rules, rule) { - if (wildcard_match(name, rule->mailbox_mask)) - return rule; - } - return NULL; -} - -static struct quota_rule * -quota_root_rule_find_exact(struct quota_root_legacy_settings *root_set, - const char *name) -{ - struct quota_rule *rule; - - array_foreach_modifiable(&root_set->rules, rule) { - if (strcmp(rule->mailbox_mask, name) == 0) - return rule; - } - return NULL; -} - -static int -quota_rule_parse_percentage(struct quota_root_legacy_settings *root_set, - struct quota_rule *rule, - int64_t *limit, const char **error_r) -{ - int64_t percentage = *limit; - - if (percentage <= -100 || percentage >= UINT_MAX) { - *error_r = "Invalid percentage"; - return -1; - } - - if (rule == &root_set->default_rule) { - *error_r = "Default rule can't be a percentage"; - return -1; - } - - if (limit == &rule->bytes_limit) - rule->bytes_percent = percentage; - else if (limit == &rule->count_limit) - rule->count_percent = percentage; - else - i_unreached(); - return 0; -} - -static int quota_limit_parse(struct quota_root_legacy_settings *root_set, - struct quota_rule *rule, const char *unit, - uint64_t multiply, int64_t *limit, - const char **error_r) -{ - switch (i_toupper(*unit)) { - case '\0': - /* default */ - break; - case 'B': - multiply = 1; - break; - case 'K': - multiply = 1024; - break; - case 'M': - multiply = 1024*1024; - break; - case 'G': - multiply = 1024*1024*1024; - break; - case 'T': - multiply = 1024ULL*1024*1024*1024; - break; - case '%': - multiply = 0; - if (quota_rule_parse_percentage(root_set, rule, limit, - error_r) < 0) - return -1; - break; - default: - *error_r = t_strdup_printf("Unknown unit: %s", unit); - return -1; - } - *limit *= multiply; - return 0; -} - -static int -quota_rule_parse_limits(struct event *event, - struct quota_root_legacy_settings *root_set, - struct quota_rule *rule, const char *limits, - const char *full_rule_def, - bool relative_rule, const char **error_r) -{ - const char **args, *key, *value, *error, *p; - uint64_t multiply; - int64_t *limit; - - args = t_strsplit(limits, ":"); - for (; *args != NULL; args++) { - multiply = 1; - limit = NULL; - - key = *args; - value = strchr(key, '='); - if (value == NULL) - value = ""; - else - key = t_strdup_until(key, value++); - - if (*value == '+') { - if (!relative_rule) { - *error_r = "Rule limit cannot have '+'"; - return -1; - } - value++; - } else if (*value != '-' && relative_rule) { - e_warning(event, "quota root %s rule %s: " - "obsolete configuration for rule '%s' " - "should be changed to '%s=+%s'", - root_set->name, full_rule_def, - *args, key, value); - } - - if (strcmp(key, "storage") == 0) { - multiply = 1024; - limit = &rule->bytes_limit; - if (str_parse_int64(value, limit, &p) < 0) { - *error_r = t_strdup_printf( - "Invalid storage limit: %s", value); - return -1; - } - } else if (strcmp(key, "bytes") == 0) { - limit = &rule->bytes_limit; - if (str_parse_int64(value, limit, &p) < 0) { - *error_r = t_strdup_printf( - "Invalid bytes limit: %s", value); - return -1; - } - } else if (strcmp(key, "messages") == 0) { - limit = &rule->count_limit; - if (str_parse_int64(value, limit, &p) < 0) { - *error_r = t_strdup_printf( - "Invalid bytes messages: %s", value); - return -1; - } - } else { - *error_r = t_strdup_printf( - "Unknown rule limit name: %s", key); - return -1; - } - - if (quota_limit_parse(root_set, rule, p, multiply, - limit, &error) < 0) { - *error_r = t_strdup_printf( - "Invalid rule limit value '%s': %s", - *args, error); - return -1; - } - } - if (!relative_rule) { - if (rule->bytes_limit < 0) { - *error_r = "Bytes limit can't be negative"; - return -1; - } - if (rule->count_limit < 0) { - *error_r = "Count limit can't be negative"; - return -1; - } - } - return 0; -} - -int quota_root_add_rule(struct event *event, pool_t pool, - struct quota_root_legacy_settings *root_set, - const char *rule_def, const char **error_r) -{ - struct quota_rule *rule; - const char *p, *mailbox_mask; - int ret = 0; - - p = strchr(rule_def, ':'); - if (p == NULL) { - *error_r = "Invalid rule"; - return -1; - } - - /* : */ - mailbox_mask = t_strdup_until(rule_def, p++); - - rule = quota_root_rule_find_exact(root_set, mailbox_mask); - if (rule == NULL) { - if (strcmp(mailbox_mask, RULE_NAME_DEFAULT_FORCE) == 0) - rule = &root_set->default_rule; - else { - rule = array_append_space(&root_set->rules); - rule->mailbox_mask = strcasecmp(mailbox_mask, "INBOX") == 0 ? "INBOX" : - p_strdup(pool, mailbox_mask); - } - } - - if (strcmp(p, "ignore") == 0) { - rule->ignore = TRUE; - e_debug(event, "Quota rule: root=%s mailbox=%s ignored", - root_set->name, mailbox_mask); - return 0; - } - - bool relative_rule = rule != &root_set->default_rule; - if (quota_rule_parse_limits(event, root_set, rule, p, rule_def, - relative_rule, error_r) < 0) - ret = -1; - - const char *rule_plus = - rule == &root_set->default_rule ? "" : "+"; - - e_debug(event, "Quota rule: root=%s mailbox=%s " - "bytes=%s%lld%s messages=%s%lld%s", - root_set->name, mailbox_mask, - rule->bytes_limit > 0 ? rule_plus : "", - (long long)rule->bytes_limit, - rule->bytes_percent == 0 ? "" : - t_strdup_printf(" (%u%%)", rule->bytes_percent), - rule->count_limit > 0 ? rule_plus : "", - (long long)rule->count_limit, - rule->count_percent == 0 ? "" : - t_strdup_printf(" (%u%%)", rule->count_percent)); - return ret; -} - -int quota_root_add_warning_rule(struct event *event, pool_t pool, - struct quota_root_legacy_settings *root_set, - const char *rule_def, const char **error_r) -{ - struct quota_warning_rule *warning; - struct quota_rule rule; - const char *p, *q; - int ret; - bool reverse = FALSE; - - p = strchr(rule_def, ' '); - if (p == NULL || p[1] == '\0') { - *error_r = "No command specified"; - return -1; - } - - if (*rule_def == '+') { - /* warn when exceeding quota */ - q = rule_def+1; - } else if (*rule_def == '-') { - /* warn when going below quota */ - q = rule_def+1; - reverse = TRUE; - } else { - /* default: same as '+' */ - q = rule_def; - } - - i_zero(&rule); - ret = quota_rule_parse_limits(event, root_set, &rule, t_strdup_until(q, p), - rule_def, FALSE, error_r); - if (ret < 0) - return -1; - - warning = array_append_space(&root_set->warning_rules); - warning->command = p_strdup(pool, p+1); - warning->rule = rule; - warning->reverse = reverse; - - e_debug(event, "Quota warning: bytes=%"PRId64"%s " - "messages=%"PRId64"%s reverse=%s command=%s", - warning->rule.bytes_limit, - warning->rule.bytes_percent == 0 ? "" : - t_strdup_printf(" (%u%%)", warning->rule.bytes_percent), - warning->rule.count_limit, - warning->rule.count_percent == 0 ? "" : - t_strdup_printf(" (%u%%)", warning->rule.count_percent), - warning->reverse ? "yes" : "no", - warning->command); - return 0; -} - -int quota_root_parse_grace(struct event *event, - struct quota_root_legacy_settings *root_set, - const char *value, const char **error_r) -{ - if (value == NULL) { - /* default */ - value = QUOTA_DEFAULT_GRACE; - } - - if (str_parse_get_size(value, &root_set->quota_storage_grace, - error_r) < 0) - return -1; - e_debug(event, "Quota grace: root=%s bytes=%"PRIu64, - root_set->name, root_set->quota_storage_grace); - return 0; -} - -bool quota_warning_match(struct quota_root *root, - const struct quota_warning_rule *w, +bool quota_warning_match(const struct quota_settings *w, uint64_t bytes_before, uint64_t bytes_current, uint64_t count_before, uint64_t count_current, const char **reason_r) { #define QUOTA_EXCEEDED(before, current, limit) \ ((before) < (uint64_t)(limit) && (current) >= (uint64_t)(limit)) - uint64_t bytes_limit = w->rule.bytes_percent == 0 ? - w->rule.bytes_limit : - root->bytes_limit * w->rule.bytes_percent / 100; - uint64_t count_limit = w->rule.count_percent == 0 ? - w->rule.count_limit : - root->count_limit * w->rule.count_percent / 100; - if (!w->reverse) { + uint64_t bytes_limit = w->quota_storage_size * + w->quota_storage_percentage / 100; + uint64_t count_limit = w->quota_message_count * + w->quota_message_percentage / 100; + if (strcmp(w->quota_warning_threshold, QUOTA_WARNING_THRESHOLD_OVER) == 0) { /* over quota (default) */ - if (QUOTA_EXCEEDED(bytes_before, bytes_current, bytes_limit)) { + if (strcmp(w->quota_warning_resource, QUOTA_WARNING_RESOURCE_STORAGE) == 0 && + QUOTA_EXCEEDED(bytes_before, bytes_current, bytes_limit)) { *reason_r = t_strdup_printf("bytes=%"PRIu64" -> %"PRIu64" over limit %"PRId64, bytes_before, bytes_current, bytes_limit); return TRUE; } - if (QUOTA_EXCEEDED(count_before, count_current, count_limit)) { + if (strcmp(w->quota_warning_resource, QUOTA_WARNING_RESOURCE_MESSAGE) == 0 && + QUOTA_EXCEEDED(count_before, count_current, count_limit)) { *reason_r = t_strdup_printf("count=%"PRIu64" -> %"PRIu64" over limit %"PRId64, count_before, count_current, count_limit); return TRUE; } } else { - if (QUOTA_EXCEEDED(bytes_current, bytes_before, bytes_limit)) { + if (strcmp(w->quota_warning_resource, QUOTA_WARNING_RESOURCE_STORAGE) == 0 && + QUOTA_EXCEEDED(bytes_current, bytes_before, bytes_limit)) { *reason_r = t_strdup_printf("bytes=%"PRIu64" -> %"PRIu64" below limit %"PRId64, bytes_before, bytes_current, bytes_limit); return TRUE; } - if (QUOTA_EXCEEDED(count_current, count_before, count_limit)) { + if (strcmp(w->quota_warning_resource, QUOTA_WARNING_RESOURCE_MESSAGE) == 0 && + QUOTA_EXCEEDED(count_current, count_before, count_limit)) { *reason_r = t_strdup_printf("count=%"PRIu64" -> %"PRIu64" below limit %"PRId64, count_before, count_current, count_limit); return TRUE; diff --git a/src/plugins/quota/quota.c b/src/plugins/quota/quota.c index 7c7818b650..4a7129d055 100644 --- a/src/plugins/quota/quota.c +++ b/src/plugins/quota/quota.c @@ -52,19 +52,19 @@ static ARRAY(const struct quota_backend*) quota_backends; static void hidden_param_handler(struct quota_root *_root, const char *param_value); static void ignoreunlim_param_handler(struct quota_root *_root, const char *param_value); static void noenforcing_param_handler(struct quota_root *_root, const char *param_value); -static void ns_param_handler(struct quota_root *_root, const char *param_value); struct quota_param_parser quota_param_hidden = {.param_name = "hidden", .param_handler = hidden_param_handler}; struct quota_param_parser quota_param_ignoreunlimited = {.param_name = "ignoreunlimited", .param_handler = ignoreunlim_param_handler}; struct quota_param_parser quota_param_noenforcing = {.param_name = "noenforcing", .param_handler = noenforcing_param_handler}; -struct quota_param_parser quota_param_ns = {.param_name = "ns=", .param_handler = ns_param_handler}; static enum quota_alloc_result quota_default_test_alloc( struct quota_transaction_context *ctx, uoff_t size, const char **error_r); static void quota_over_status_check_root(struct quota_root *root); +static struct quota_root * +quota_root_find(struct quota *quota, const char *name); -static const struct quota_backend *quota_backend_find(const char *name) +const struct quota_backend *quota_backend_find(const char *name) { const struct quota_backend *const *backend; @@ -117,139 +117,6 @@ void quota_backends_unregister(void) } -static int quota_root_add_rules(struct mail_user *user, const char *root_name, - pool_t pool, struct quota_root_legacy_settings *root_set, - const char **error_r) -{ - const char *rule_name, *rule, *error; - unsigned int i; - - rule_name = t_strconcat(root_name, "_rule", NULL); - for (i = 2;; i++) { - rule = mail_user_plugin_getenv(user, rule_name); - if (rule == NULL) - break; - - if (quota_root_add_rule(user->event, pool, root_set, - rule, &error) < 0) { - *error_r = t_strdup_printf("Invalid rule %s: %s", - rule, error); - return -1; - } - rule_name = t_strdup_printf("%s_rule%d", root_name, i); - } - return 0; -} - -static int -quota_root_add_warning_rules(struct mail_user *user, const char *root_name, - pool_t pool, struct quota_root_legacy_settings *root_set, - const char **error_r) -{ - const char *rule_name, *rule, *error; - unsigned int i; - - rule_name = t_strconcat(root_name, "_warning", NULL); - for (i = 2;; i++) { - rule = mail_user_plugin_getenv(user, rule_name); - if (rule == NULL) - break; - - if (quota_root_add_warning_rule(user->event, pool, root_set, - rule, &error) < 0) { - *error_r = t_strdup_printf("Invalid warning rule: %s", - rule); - return -1; - } - rule_name = t_strdup_printf("%s_warning%d", root_name, i); - } - return 0; -} - -static int -quota_root_settings_init(struct event *event, - struct quota_legacy_settings *quota_set, const char *root_def, - struct quota_root_legacy_settings **set_r, - const char **error_r) -{ - struct quota_root_legacy_settings *root_set; - const struct quota_backend *backend; - const char *p, *args, *backend_name; - - /* [:[:]] */ - p = strchr(root_def, ':'); - if (p == NULL) { - backend_name = root_def; - args = NULL; - } else { - backend_name = t_strdup_until(root_def, p); - args = p + 1; - } - - backend = quota_backend_find(backend_name); - if (backend == NULL) { - *error_r = t_strdup_printf("Unknown quota backend: %s", - backend_name); - return -1; - } - - root_set = p_new(quota_set->pool, struct quota_root_legacy_settings, 1); - root_set->backend = backend; - - if (args != NULL) { - /* save root's name */ - p = strchr(args, ':'); - if (p == NULL) { - root_set->name = p_strdup(quota_set->pool, args); - args = NULL; - } else { - root_set->name = - p_strdup_until(quota_set->pool, args, p); - args = p + 1; - } - } else { - root_set->name = ""; - } - root_set->args = p_strdup(quota_set->pool, args); - - e_debug(event, "Quota root: name=%s backend=%s args=%s", - root_set->name, backend_name, args == NULL ? "" : args); - - p_array_init(&root_set->rules, quota_set->pool, 4); - p_array_init(&root_set->warning_rules, quota_set->pool, 4); - array_push_back("a_set->root_sets, &root_set); - *set_r = root_set; - return 0; -} - -static int -quota_root_add(struct quota_legacy_settings *quota_set, struct mail_user *user, - pool_t pool, const char *env, const char *root_name, - const char **error_r) -{ - struct quota_root_legacy_settings *root_set; - const char *set_name, *value; - - if (quota_root_settings_init(user->event, quota_set, env, - &root_set, error_r) < 0) - return -1; - root_set->set_name = p_strdup(quota_set->pool, root_name); - if (quota_root_add_rules(user, root_name, pool, root_set, error_r) < 0) - return -1; - if (quota_root_add_warning_rules(user, root_name, quota_set->pool, - root_set, error_r) < 0) - return -1; - - set_name = t_strconcat(root_name, "_grace", NULL); - value = mail_user_plugin_getenv(user, set_name); - if (quota_root_parse_grace(user->event, root_set, value, error_r) < 0) { - *error_r = t_strdup_printf("Invalid %s value '%s': %s", - set_name, value, *error_r); - return -1; - } - return 0; -} - const char *quota_alloc_result_errstr(enum quota_alloc_result res, struct quota_transaction_context *qt) { @@ -272,57 +139,12 @@ const char *quota_alloc_result_errstr(enum quota_alloc_result res, i_unreached(); } -int quota_user_read_settings(struct mail_user *user, - struct quota_legacy_settings **set_r, - const char **error_r) -{ - struct quota_legacy_settings *quota_set; - char root_name[5 + MAX_INT_STRLEN]; - const char *env, *error; - unsigned int i; - pool_t pool; - - pool = pool_alloconly_create("quota settings", 2048); - quota_set = p_new(pool, struct quota_legacy_settings, 1); - quota_set->pool = pool; - - p_array_init("a_set->root_sets, pool, 4); - if (i_strocpy(root_name, "quota", sizeof(root_name)) < 0) - i_unreached(); - for (i = 2;; i++) { - env = mail_user_plugin_getenv(user, root_name); - if (env == NULL || *env == '\0') - break; - - if (quota_root_add(quota_set, user, pool, env, root_name, - &error) < 0) { - *error_r = t_strdup_printf("Invalid quota root %s: %s", - root_name, error); - pool_unref(&pool); - return -1; - } - if (i_snprintf(root_name, sizeof(root_name), "quota%d", i) < 0) - i_unreached(); - } - - *set_r = quota_set; - return 0; -} - -void quota_settings_deinit(struct quota_legacy_settings **_quota_set) -{ - struct quota_legacy_settings *quota_set = *_quota_set; - - *_quota_set = NULL; - - pool_unref("a_set->pool); -} - static void quota_root_deinit(struct quota_root *root) { pool_t pool = root->pool; event_unref(&root->backend.event); + settings_free(root->set); root->backend.v.deinit(root); pool_unref(&pool); } @@ -334,87 +156,159 @@ int quota_root_default_init(struct quota_root *root, const char *args, quota_param_hidden, quota_param_ignoreunlimited, quota_param_noenforcing, - quota_param_ns, {.param_name = NULL} }; return quota_parse_parameters(root, &args, error_r, default_params, TRUE); } static int -quota_root_init(struct quota_root_legacy_settings *root_set, struct quota *quota, +quota_root_settings_get(struct quota_root *root, struct event *set_event, + const struct quota_settings **set_r, + const char **error_r) +{ + struct event *event; + + if (set_event == NULL) + event = root->backend.event; + else { + event = event_create(set_event); + event_add_str(event, "quota", root->set->quota_name); + } + int ret = settings_get(event, "a_setting_parser_info, 0, + set_r, error_r); + if (set_event != NULL) + event_unref(&event); + return ret; +} + +static int quota_root_has_under_warnings(struct quota_root *root) +{ + const struct quota_settings *set; + const char *warn_name, *error; + + if (!array_is_created(&root->set->quota_warnings)) + return 0; + array_foreach_elem(&root->set->quota_warnings, warn_name) { + if (settings_get_filter(root->backend.event, + "quota_warning", warn_name, + "a_setting_parser_info, 0, + &set, &error) < 0) { + e_error(root->backend.event, "%s", error); + quota_root_deinit(root); + return -1; + } + bool under = strcmp(set->quota_warning_threshold, + QUOTA_WARNING_THRESHOLD_UNDER) == 0; + settings_free(set); + if (under) + return 1; + } + return 0; +} + +static int +quota_root_init(struct quota *quota, struct event *set_event, const char *root_name, struct quota_root **root_r, const char **error_r) { + const struct quota_settings *root_set; struct quota_root *root; + if (settings_get_filter(set_event, "quota", root_name, + "a_setting_parser_info, 0, + &root_set, error_r) < 0) + return -1; + + /* If a quota root exists already with the same name, assume it's the + same as this one. Don't add duplicates. */ + root = quota_root_find(quota, root_set->quota_name); + if (root != NULL) { + settings_free(root_set); + *root_r = root; + return 1; + } + root = root_set->backend->v.alloc(); root->pool = pool_alloconly_create("quota root", 512); root->set = root_set; root->quota = quota; root->backend = *root_set->backend; - root->bytes_limit = root_set->default_rule.bytes_limit; - root->count_limit = root_set->default_rule.count_limit; p_array_init(&root->namespaces, root->pool, 4); array_create(&root->quota_module_contexts, root->pool, sizeof(void *), 10); root->backend.event = event_create(quota->event); + event_add_str(root->backend.event, "quota", root_name); event_drop_parent_log_prefixes(root->backend.event, 1); - if (root->backend.v.init(root, root_set->args, error_r) < 0) { + root->bytes_limit = root->set->quota_storage_size > INT64_MAX ? 0 : + root->set->quota_storage_size; + root->count_limit = root->set->quota_message_count; + + if (root->backend.v.init(root, root->set->quota_args, error_r) < 0) { *error_r = t_strdup_printf("%s quota init failed: %s", root->backend.name, *error_r); event_unref(&root->backend.event); + settings_free(root->set); pool_unref(&root->pool); return -1; } - if (root_set->default_rule.bytes_limit == 0 && - root_set->default_rule.count_limit == 0 && + if (root->set->quota_storage_size == 0 && + root->set->quota_message_count == 0 && root->disable_unlimited_tracking) { quota_root_deinit(root); return 0; } + + /* If a quota backend needs virtual size instead of physical size, + use this for all backends. This is not ideal, but works. */ + if (root->set->backend->use_vsize) + quota->vsizes = TRUE; + + array_push_back("a->all_roots, &root); *root_r = root; return 1; } -int quota_init(struct quota_legacy_settings *quota_set, struct mail_user *user, - struct quota **quota_r, const char **error_r) +int quota_init(struct mail_user *user, struct quota **quota_r, + const char **error_r) { struct quota *quota; struct quota_root *root; - struct quota_root_legacy_settings *const *root_sets; - unsigned int i, count; + const struct quota_settings *set; + const char *root_name; const char *error; int ret; + if (settings_get(user->event, "a_setting_parser_info, 0, + &set, error_r) < 0) + return -1; + quota = i_new(struct quota, 1); quota->event = event_create(user->event); event_set_append_log_prefix(quota->event, "quota: "); quota->user = user; - quota->set = quota_set; quota->test_alloc = quota_default_test_alloc; - i_array_init("a->roots, 8); - - root_sets = array_get("a_set->root_sets, &count); - for (i = 0; i < count; i++) { - ret = quota_root_init(root_sets[i], quota, &root, &error); - if (ret < 0) { - *error_r = t_strdup_printf("Quota root %s: %s", - root_sets[i]->name, error); - quota_deinit("a); - return -1; - } - if (ret > 0) { - array_push_back("a->roots, &root); - /* If a quota backend needs virtual size instead of physical - size, use this for all backends. This is not ideal, but - works. */ - if (root->set->backend->use_vsize) - quota->vsizes = TRUE; + i_array_init("a->global_private_roots, 8); + i_array_init("a->all_roots, 8); + + if (array_is_created(&set->quota_roots)) { + array_foreach_elem(&set->quota_roots, root_name) { + ret = quota_root_init(quota, quota->event, root_name, + &root, &error); + if (ret < 0) { + *error_r = t_strdup_printf("Quota root %s: %s", + root_name, error); + settings_free(set); + quota_deinit("a); + return -1; + } + if (ret > 0) + array_push_back("a->global_private_roots, &root); } } + settings_free(set); *quota_r = quota; return 0; } @@ -425,61 +319,75 @@ void quota_deinit(struct quota **_quota) struct quota_root *const *roots; unsigned int i, count; - roots = array_get("a->roots, &count); + roots = array_get("a->all_roots, &count); for (i = 0; i < count; i++) quota_root_deinit(roots[i]); /* deinit quota roots before setting quser->quota=NULL */ *_quota = NULL; - array_free("a->roots); + array_free("a->global_private_roots); + array_free("a->all_roots); event_unref("a->event); i_free(quota); } -static void -quota_root_get_rule_limits(struct quota_root *root, struct mailbox *box, +static int +quota_root_get_rule_limits(struct quota_root *root, struct event *set_event, uint64_t *bytes_limit_r, uint64_t *count_limit_r, - bool *ignored_r) + bool *ignored_r, const char **error_r) { - struct quota_rule *rule; - int64_t bytes_limit, count_limit; + const struct quota_settings *set; - *ignored_r = FALSE; - - bytes_limit = root->bytes_limit; - count_limit = root->count_limit; + if (quota_root_settings_get(root, set_event, &set, error_r) < 0) + return -1; - /* if default rule limits are 0, user has unlimited quota. - ignore any specific quota rules */ - if (bytes_limit != 0 || count_limit != 0) { - const char *mailbox_name = mailbox_get_vname(box); - (void)mail_namespace_find_unalias(root->quota->user->namespaces, - &mailbox_name); - rule = quota_root_rule_find(root->set, mailbox_name); + if (set->quota_ignore) { + *bytes_limit_r = 0; + *count_limit_r = 0; + *ignored_r = TRUE; } else { - rule = NULL; - } + uint64_t bytes_limit, count_limit; - if (rule != NULL) { - if (!rule->ignore) { - if (rule->bytes_percent != 0) - bytes_limit += bytes_limit * rule->bytes_percent / 100; - else - bytes_limit += rule->bytes_limit; - if (rule->count_percent != 0) - count_limit += count_limit * rule->count_percent / 100; + if (set->quota_storage_size != 0) + bytes_limit = set->quota_storage_size; + else { + /* unlimited quota in configuration - see if the quota + backend has a limit set. */ + bytes_limit = root->bytes_limit; + } + if (bytes_limit == 0 || bytes_limit > INT64_MAX) + *bytes_limit_r = 0; + else { + if (set->quota_storage_percentage == 100) + *bytes_limit_r = bytes_limit; + else { + *bytes_limit_r = bytes_limit / 100.0 * + set->quota_storage_percentage; + } + if (*bytes_limit_r <= INT64_MAX && + set->quota_storage_extra <= INT64_MAX - *bytes_limit_r) + *bytes_limit_r += set->quota_storage_extra; else - count_limit += rule->count_limit; - } else { - bytes_limit = 0; - count_limit = 0; - *ignored_r = TRUE; + *bytes_limit_r = 0; } + + if (set->quota_message_count != 0) + count_limit = set->quota_message_count; + else + count_limit = root->count_limit; + + if (count_limit == 0 || set->quota_message_percentage == 100) + *count_limit_r = count_limit; + else { + *count_limit_r = count_limit / 100.0 * + set->quota_message_percentage; + } + *ignored_r = FALSE; } - *bytes_limit_r = bytes_limit <= 0 ? 0 : bytes_limit; - *count_limit_r = count_limit <= 0 ? 0 : count_limit; + settings_free(set); + return 0; } static bool @@ -517,37 +425,56 @@ quota_is_duplicate_namespace(struct quota_root *root, struct mail_namespace *ns) include the INBOX also in quota calculations, so we can't just ignore this namespace. but since we've already called backend's namespace_added(), we can't - just remove it either. so just mark the old one as - unwanted namespace. + entirely remove it either. an alternative would be to do a bit larger change so namespaces wouldn't be added until mail_namespaces_created() hook is called */ - i_assert(root->unwanted_ns == NULL); - root->unwanted_ns = namespaces[i]; + array_delete(&root->namespaces, i, 1); return FALSE; } } return FALSE; } -void quota_add_user_namespace(struct quota *quota, struct mail_namespace *ns) +static bool quota_root_is_namespace_visible(struct mail_namespace *ns) { - struct quota_root *root; + struct mailbox_list *list = ns->list; + struct mail_storage *storage; - array_foreach_elem("a->roots, root) { - if (!quota_root_is_namespace_visible(root, ns)) - continue; - /* first check if there already exists a namespace with the - exact same path. we don't want to count them twice. */ - if (quota_is_duplicate_namespace(root, ns)) - continue; + /* this check works as long as there is only one storage per list */ + const char *vname = ""; + if (mailbox_list_get_storage(&list, &vname, 0, &storage) == 0 && + (storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) != 0) + return FALSE; + return TRUE; +} - array_push_back(&root->namespaces, &ns); +void quota_add_user_namespace(struct quota *quota, const char *root_name, + struct mail_namespace *ns) +{ + struct quota_root *root; + const char *error; - if (root->backend.v.namespace_added != NULL) - root->backend.v.namespace_added(root, ns); + int ret = quota_root_init(quota, ns->list->event, root_name, + &root, &error); + if (ret == 0) + return; + if (ret < 0) { + e_error(ns->list->event, "Quota root %s: %s", root_name, error); + return; } + if (!quota_root_is_namespace_visible(ns)) + return; + /* first check if there already exists a namespace with the + exact same path. we don't want to count them twice. */ + if (quota_is_duplicate_namespace(root, ns)) + return; + + array_push_back(&root->namespaces, &ns); + + if (root->backend.v.namespace_added != NULL) + root->backend.v.namespace_added(root, ns); } void quota_remove_user_namespace(struct mail_namespace *ns) @@ -565,7 +492,7 @@ void quota_remove_user_namespace(struct mail_namespace *ns) return; } - array_foreach_elem("a->roots, root) { + array_foreach_elem("a->all_roots, root) { namespaces = array_get(&root->namespaces, &count); for (i = 0; i < count; i++) { if (namespaces[i] == ns) { @@ -599,36 +526,22 @@ quota_root_iter_init(struct mailbox *box) return iter; } -bool quota_root_is_namespace_visible(struct quota_root *root, - struct mail_namespace *ns) +static bool +quota_root_is_visible(struct quota_root *root, struct mailbox *box) { - struct mailbox_list *list = ns->list; - struct mail_storage *storage; - - /* this check works as long as there is only one storage per list */ - const char *vname = ""; - if (mailbox_list_get_storage(&list, &vname, 0, &storage) == 0 && - (storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) != 0) - return FALSE; - if (root->unwanted_ns == ns) - return FALSE; - - if (root->ns_prefix != NULL) { - if (root->ns != ns) + if (root->quota->user == box->storage->user) { + if (array_lsearch_ptr(&root->namespaces, box->list->ns) == NULL) return FALSE; } else { - if (ns->owner == NULL) - return FALSE; + /* This is a shared mailbox. The quota user is the actual owner + of the mailbox, but the mailbox is accessed via another + user. Currently each shared namepace gets its own owner + mail_user, even when the same user has multiple shared + namespaces. So we don't need to verify whether the namespace + matches - there is always only one. */ + i_assert(box->list->ns->type == MAIL_NAMESPACE_TYPE_SHARED); } - return TRUE; -} - -static bool -quota_root_is_visible(struct quota_root *root, struct mailbox *box) -{ - if (array_lsearch_ptr(&root->namespaces, box->list->ns) == NULL) - return FALSE; - if (array_count(&root->quota->roots) == 1) { + if (array_count(&root->quota->all_roots) == 1) { /* a single quota root: don't bother checking further */ return TRUE; } @@ -644,7 +557,7 @@ struct quota_root *quota_root_iter_next(struct quota_root_iter *iter) if (iter->quota == NULL) return NULL; - roots = array_get(&iter->quota->roots, &count); + roots = array_get(&iter->quota->all_roots, &count); if (iter->i >= count) return NULL; @@ -669,26 +582,33 @@ void quota_root_iter_deinit(struct quota_root_iter **_iter) i_free(iter); } -struct quota_root *quota_root_lookup(struct mail_user *user, const char *name) +static struct quota_root * +quota_root_find(struct quota *quota, const char *name) { - struct quota *quota; struct quota_root *const *roots; unsigned int i, count; - quota = quota_get_mail_user_quota(user); - if (quota == NULL) - return NULL; - roots = array_get("a->roots, &count); + roots = array_get("a->all_roots, &count); for (i = 0; i < count; i++) { - if (strcmp(roots[i]->set->name, name) == 0) + if (strcmp(roots[i]->set->quota_name, name) == 0) return roots[i]; } return NULL; } +struct quota_root *quota_root_lookup(struct mail_user *user, const char *name) +{ + struct quota *quota; + + quota = quota_get_mail_user_quota(user); + if (quota == NULL) + return NULL; + return quota_root_find(quota, name); +} + const char *quota_root_get_name(struct quota_root *root) { - return root->set->name; + return root->set->quota_name; } const char *const *quota_root_get_resources(struct quota_root *root) @@ -710,6 +630,7 @@ quota_get_resource(struct quota_root *root, struct mailbox *box, const char **error_r) { const char *error; + struct event *set_event; uint64_t bytes_limit, count_limit; bool ignored, kilobytes = FALSE; enum quota_get_result ret; @@ -735,8 +656,20 @@ quota_get_resource(struct quota_root *root, struct mailbox *box, return ret; } - quota_root_get_rule_limits(root, box, - &bytes_limit, &count_limit, &ignored); + if (box != NULL) + set_event = box->event; + else if (array_is_empty(&root->namespaces)) + set_event = NULL; + else { + struct mail_namespace *ns = + array_idx_elem(&root->namespaces, 0); + set_event = ns->list->event; + } + + if (quota_root_get_rule_limits(root, set_event, + &bytes_limit, &count_limit, + &ignored, error_r) < 0) + return -1; if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) *limit_r = bytes_limit; @@ -752,22 +685,10 @@ quota_get_resource(struct quota_root *root, struct mailbox *box, return *limit_r == 0 ? QUOTA_GET_RESULT_UNLIMITED : QUOTA_GET_RESULT_LIMITED; } -static bool quota_root_have_reverse_warnings(struct quota_root *root) -{ - const struct quota_warning_rule *warn; - array_foreach(&root->set->warning_rules, warn) { - if (warn->reverse) - return TRUE; - } - return FALSE; -} - struct quota_transaction_context *quota_transaction_begin(struct mailbox *box) { struct quota_transaction_context *ctx; struct quota_root *const *rootp; - const struct quota_rule *rule; - const char *mailbox_name; ctx = i_new(struct quota_transaction_context, 1); ctx->quota = box->list->ns->owner != NULL ? @@ -780,21 +701,25 @@ struct quota_transaction_context *quota_transaction_begin(struct mailbox *box) ctx->bytes_ceil2 = (uint64_t)-1; ctx->count_ceil = (uint64_t)-1; - mailbox_name = mailbox_get_vname(box); - (void)mail_namespace_find_unalias(box->storage->user->namespaces, - &mailbox_name); - ctx->auto_updating = TRUE; - array_foreach(&ctx->quota->roots, rootp) { + array_foreach(&ctx->quota->all_roots, rootp) { if (!quota_root_is_visible(*rootp, ctx->box)) continue; - rule = quota_root_rule_find((*rootp)->set, mailbox_name); - if (rule != NULL && rule->ignore) { + const struct quota_settings *set = NULL; + const char *error; + if (quota_root_settings_get(*rootp, box->event, + &set, &error) < 0) { + e_error(ctx->box->event, "%s", error); + ctx->failed = TRUE; + } else if (set->quota_ignore) { /* This mailbox isn't included in quota. This means it's also not included in quota_warnings, so make sure it's fully ignored. */ + settings_free(set); continue; + } else { + settings_free(set); } /* If there are reverse quota_warnings, we'll need to track @@ -803,7 +728,7 @@ struct quota_transaction_context *quota_transaction_begin(struct mailbox *box) before and after the expunges, but that's more complicated and probably isn't any better.) */ if (!(*rootp)->auto_updating || - quota_root_have_reverse_warnings(*rootp)) + quota_root_has_under_warnings(*rootp) != 0) ctx->auto_updating = FALSE; } @@ -842,7 +767,7 @@ int quota_transaction_set_limits(struct quota_transaction_context *ctx, } /* find the lowest quota limits from all roots and use them */ - roots = array_get(&ctx->quota->roots, &count); + roots = array_get(&ctx->quota->all_roots, &count); for (i = 0; i < count; i++) { /* make sure variables get initialized */ bytes_limit = count_limit = 0; @@ -850,10 +775,12 @@ int quota_transaction_set_limits(struct quota_transaction_context *ctx, continue; else if (roots[i]->no_enforcing) { ignored = FALSE; - } else { - quota_root_get_rule_limits(roots[i], ctx->box, - &bytes_limit, &count_limit, - &ignored); + } else if (quota_root_get_rule_limits(roots[i], ctx->box->event, + &bytes_limit, &count_limit, + &ignored, error_r) < 0) { + ctx->failed = TRUE; + *error_result_r = QUOTA_GET_RESULT_INTERNAL_ERROR; + return -1; } if (!ignored) ctx->no_quota_updates = FALSE; @@ -993,14 +920,12 @@ static void quota_warning_execute(struct quota_root *root, const char *cmd, static void quota_warnings_execute(struct quota_transaction_context *ctx, struct quota_root *root) { - struct quota_warning_rule *warnings; - unsigned int i, count; + const struct quota_settings *set; uint64_t bytes_current, bytes_before, bytes_limit; uint64_t count_current, count_before, count_limit; - const char *reason, *error; + const char *warn_name, *reason, *error; - warnings = array_get_modifiable(&root->set->warning_rules, &count); - if (count == 0) + if (array_is_empty(&root->set->quota_warnings)) return; if (quota_get_resource(root, NULL, QUOTA_NAME_STORAGE_BYTES, @@ -1027,25 +952,32 @@ static void quota_warnings_execute(struct quota_transaction_context *ctx, count_before = 0; else count_before = (int64_t)count_current - ctx->count_used; - for (i = 0; i < count; i++) { - if (quota_warning_match(root, &warnings[i], - bytes_before, bytes_current, + + array_foreach_elem(&root->set->quota_warnings, warn_name) { + if (settings_get_filter(root->backend.event, + "quota_warning", warn_name, + "a_setting_parser_info, 0, + &set, &error) < 0) { + e_error(root->backend.event, "%s", error); + return; + } + if (quota_warning_match(set, bytes_before, bytes_current, count_before, count_current, &reason)) { - quota_warning_execute(root, warnings[i].command, + quota_warning_execute(root, set->quota_warning_command, NULL, reason); + settings_free(set); break; } + settings_free(set); } } int quota_transaction_commit(struct quota_transaction_context **_ctx) { struct quota_transaction_context *ctx = *_ctx; - struct quota_rule *rule; struct quota_root *const *roots; unsigned int i, count; - const char *mailbox_name; int ret = 0; *_ctx = NULL; @@ -1056,28 +988,28 @@ int quota_transaction_commit(struct quota_transaction_context **_ctx) ctx->recalculate != QUOTA_RECALCULATE_DONT) T_BEGIN { ARRAY(struct quota_root *) warn_roots; - mailbox_name = mailbox_get_vname(ctx->box); - (void)mail_namespace_find_unalias( - ctx->box->storage->user->namespaces, &mailbox_name); - - roots = array_get(&ctx->quota->roots, &count); + roots = array_get(&ctx->quota->all_roots, &count); t_array_init(&warn_roots, count); for (i = 0; i < count; i++) { if (!quota_root_is_visible(roots[i], ctx->box)) continue; - rule = quota_root_rule_find(roots[i]->set, - mailbox_name); - if (rule != NULL && rule->ignore) { + const struct quota_settings *set = NULL; + const char *error; + if (quota_root_settings_get(roots[i], ctx->box->event, + &set, &error) < 0) { + e_error(ctx->box->event, "%s", error); + ret = -1; + } else if (set->quota_ignore) { /* mailbox not included in quota */ + settings_free(set); continue; } + settings_free(set); - const char *error; if (roots[i]->backend.v.update(roots[i], ctx, &error) < 0) { - e_error(ctx->quota->event, - "Failed to update quota for %s: %s", - mailbox_name, error); + e_error(ctx->box->event, + "Failed to update quota: %s", error); ret = -1; } else if (!ctx->sync_transaction) @@ -1099,47 +1031,33 @@ int quota_transaction_commit(struct quota_transaction_context **_ctx) } static bool -quota_over_status_init_root(struct quota_root *root, - const char **quota_over_script_r, - const char **quota_over_status_current_r, - bool *status_r) +quota_over_status_init_root(struct quota_root *root, bool *status_r) { - const char *name, *mask; - - *quota_over_status_current_r = NULL; *status_r = FALSE; - - name = t_strconcat(root->set->set_name, "_over_script", NULL); - *quota_over_script_r = mail_user_plugin_getenv(root->quota->user, name); - if (*quota_over_script_r == NULL) { + if (root->set->quota_over_status_script[0] == '\0') { e_debug(root->quota->event, "quota_over_status check: " - "%s unset - skipping", name); + "quota_over_script unset - skipping"); return FALSE; } /* e.g.: quota_over_status_mask=TRUE or quota_over_status_mask=* */ - name = t_strconcat(root->set->set_name, "_over_status_mask", NULL); - mask = mail_user_plugin_getenv(root->quota->user, name); - if (mask == NULL) { + if (root->set->quota_over_status_mask[0] == '\0') { e_debug(root->quota->event, "quota_over_status check: " - "%s unset - skipping", name); + "quota_over_mask unset - skipping"); return FALSE; } /* compare quota_over_status_current's value (that comes from userdb) to quota_over_status_mask and save the result. */ - name = t_strconcat(root->set->set_name, "_over_status_current", NULL); - *quota_over_status_current_r = - mail_user_plugin_getenv(root->quota->user, name); - *status_r = *quota_over_status_current_r != NULL && - wildcard_match_icase(*quota_over_status_current_r, mask); + *status_r = root->set->quota_over_status_current[0] != '\0' && + wildcard_match_icase(root->set->quota_over_status_current, + root->set->quota_over_status_mask); return TRUE; } static void quota_over_status_check_root(struct quota_root *root) { - const char *quota_over_script, *quota_over_status_current, *error; - const char *const *resources; + const char *error, *const *resources; unsigned int i; uint64_t value, limit; bool cur_overquota = FALSE; @@ -1164,9 +1082,7 @@ static void quota_over_status_check_root(struct quota_root *root) return; } root->quota_over_status_checked = TRUE; - if (!quota_over_status_init_root(root, "a_over_script, - "a_over_status_current, - "a_over_status)) + if (!quota_over_status_init_root(root, "a_over_status)) return; resources = quota_root_get_resources(root); @@ -1188,11 +1104,11 @@ static void quota_over_status_check_root(struct quota_root *root) } e_debug(root->quota->event, "quota_over_status=%d(%s) vs currently overquota=%d", quota_over_status ? 1 : 0, - quota_over_status_current == NULL ? "(null)" : quota_over_status_current, + root->set->quota_over_status_current, cur_overquota ? 1 : 0); if (cur_overquota != quota_over_status) { - quota_warning_execute(root, quota_over_script, - quota_over_status_current, + quota_warning_execute(root, root->set->quota_over_status_script, + root->set->quota_over_status_current, "quota_over_status mismatch"); } } @@ -1201,12 +1117,10 @@ void quota_over_status_check_startup(struct quota *quota) { struct quota_root *const *roots; unsigned int i, count; - const char *name; - roots = array_get("a->roots, &count); + roots = array_get("a->all_roots, &count); for (i = 0; i < count; i++) { - name = t_strconcat(roots[i]->set->set_name, "_over_status_lazy_check", NULL); - if (!mail_user_plugin_getenv_bool(roots[i]->quota->user, name)) + if (!roots[i]->set->quota_over_status_lazy_check) quota_over_status_check_root(roots[i]); } } @@ -1331,7 +1245,7 @@ static enum quota_alloc_result quota_default_test_alloc( } /* limit reached. */ - roots = array_get(&ctx->quota->roots, &count); + roots = array_get(&ctx->quota->all_roots, &count); for (i = 0; i < count; i++) { uint64_t bytes_limit, count_limit; @@ -1339,9 +1253,11 @@ static enum quota_alloc_result quota_default_test_alloc( roots[i]->no_enforcing) continue; - quota_root_get_rule_limits(roots[i], ctx->box, - &bytes_limit, &count_limit, - &ignore); + if (quota_root_get_rule_limits(roots[i], ctx->box->event, + &bytes_limit, &count_limit, + &ignore, error_r) < 0) { + return QUOTA_ALLOC_RESULT_TEMPFAIL; + } /* if size is bigger than any limit, then it is bigger than the lowest limit */ @@ -1397,11 +1313,6 @@ static void noenforcing_param_handler(struct quota_root *_root, const char *para _root->no_enforcing = TRUE; } -static void ns_param_handler(struct quota_root *_root, const char *param_value) -{ - _root->ns_prefix = p_strdup(_root->pool, param_value); -} - int quota_parse_parameters(struct quota_root *root, const char **args, const char **error_r, const struct quota_param_parser *valid_params, bool fail_on_unknown) { diff --git a/src/plugins/quota/quota.h b/src/plugins/quota/quota.h index e23cb713a4..347f7b5360 100644 --- a/src/plugins/quota/quota.h +++ b/src/plugins/quota/quota.h @@ -13,8 +13,6 @@ struct mail_user; #define QUOTA_NAME_MESSAGES "MESSAGE" struct quota; -struct quota_legacy_settings; -struct quota_root_legacy_settings; struct quota_root; struct quota_root_iter; struct quota_transaction_context; @@ -71,25 +69,10 @@ enum quota_get_result { const char *quota_alloc_result_errstr(enum quota_alloc_result res, struct quota_transaction_context *qt); -int quota_user_read_settings(struct mail_user *user, - struct quota_legacy_settings **set_r, - const char **error_r); -void quota_settings_deinit(struct quota_legacy_settings **quota_set); - -/* Add a new rule too the quota root. Returns 0 if ok, -1 if rule is invalid. */ -int quota_root_add_rule(struct event *event, pool_t pool, - struct quota_root_legacy_settings *root_set, - const char *rule_def, const char **error_r); -/* Add a new warning rule for the quota root. Returns 0 if ok, -1 if rule is - invalid. */ -int quota_root_add_warning_rule(struct event *event, pool_t pool, - struct quota_root_legacy_settings *root_set, - const char *rule_def, const char **error_r); - /* Initialize quota for the given user. Returns 0 and quota_r on success, -1 and error_r on failure. */ -int quota_init(struct quota_legacy_settings *quota_set, struct mail_user *user, - struct quota **quota_r, const char **error_r); +int quota_init(struct mail_user *user, struct quota **quota_r, + const char **error_r); void quota_deinit(struct quota **quota); /* List all visible quota roots. They don't need to be freed. */