]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
quota: Initial conversion to use global settings
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 15 Aug 2024 21:18:19 +0000 (00:18 +0300)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Fri, 17 Jan 2025 08:40:00 +0000 (10:40 +0200)
13 files changed:
src/plugins/quota-clone/quota-clone-plugin.c
src/plugins/quota/doveadm-quota.c
src/plugins/quota/quota-count.c
src/plugins/quota/quota-fs.c
src/plugins/quota/quota-imapc.c
src/plugins/quota/quota-maildir.c
src/plugins/quota/quota-private.h
src/plugins/quota/quota-settings.c
src/plugins/quota/quota-settings.h
src/plugins/quota/quota-storage.c
src/plugins/quota/quota-util.c
src/plugins/quota/quota.c
src/plugins/quota/quota.h

index 81f1c2c9e6ee7804b8495602805b6faf93157eae..dfbbdcd88f2855db0edab6737455949cec79f38b 100644 (file)
@@ -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: "
index ff544fc9bb75a8f8bfbb47137ad70995ea436202..7eb0e67b8a8a0b151902f5d093e117b5c2bb2ead 100644 (file)
@@ -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,
index 8045b29126b0a0ca93801b3fa4e3e68a23e55d7d..80633815bd6e0408563edd123def7510fda36dad 100644 (file)
@@ -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, &quota_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;
 }
index b2005902a5ce48c7706c40024d448ca79f81bc4b..1d61f82a0cb48cdfd0c500d29c152fb4f1ba6505 100644 (file)
@@ -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}
        };
 
index 10950b044c923eb5d17d0f5d759a79e2248b4f61..3d9678d3ab7b751792298e442c5302621e388429 100644 (file)
@@ -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(&quota->roots, rootp) {
+       array_foreach(&quota->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(&quota->roots, rootp) {
+       array_foreach(&quota->all_roots, rootp) {
                if ((*rootp)->backend.name == quota_backend_imapc.name) {
                        struct imapc_quota_root *root =
                                (struct imapc_quota_root *)*rootp;
index 8f92c8884dc17d8190324ad8499e8c21d7282999..dbb5af7cee97d96082887bd4d687daa7089dc08c 100644 (file)
@@ -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, &quota_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;
                        }
index 738c3230fd9e661a78e9a75e23a1e9cad951cc1d..80cf943eefd3dc840e86eba61b04bb89141ec1ba 100644 (file)
@@ -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);
 
index 79c7aad3b755f6fad2d9a84b95a1183dc4952e0a..1f6990a292c5d3cd126e98bd5bc5338710a2638b 100644 (file)
@@ -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 = &quota_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;
index 72e66ec27382f455315341bb94bd990597929af6..3af4e9454d6dd6160b9478b371eeafc25e2665c2 100644 (file)
@@ -1,13 +1,75 @@
 #ifndef QUOTA_SETTINGS_H
 #define QUOTA_SETTINGS_H
 
+/* <settings checks> */
+#define QUOTA_WARNING_RESOURCE_STORAGE "storage"
+#define QUOTA_WARNING_RESOURCE_MESSAGE "message"
+
+#define QUOTA_WARNING_THRESHOLD_OVER "over"
+#define QUOTA_WARNING_THRESHOLD_UNDER "under"
+/* </settings checks> */
+
 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);
index 8fc9a224969cab88cf54324415de96971f3e8619..c6bd685caa3d36510f454474ea05c24607c7dfff 100644 (file)
@@ -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(&quota->roots, &count);
+       roots = array_get(&quota->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(&quota_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, &quota, &error) < 0) {
-                       quota_settings_deinit(&set);
-                       ret = -1;
-               }
-       }
-
-       if (ret < 0) {
+       if (quota_init(user, &quota, &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(&quota->roots, &count);
-       for (i = 0; i < count; i++) {
-               if (roots[i]->ns_prefix != NULL &&
-                   strcmp(roots[i]->ns_prefix, ns->prefix) == 0)
-                       return roots[i];
+       struct quota_root *root;
+       array_foreach_elem(&quota->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, &quota_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(&quota->roots, &count);
-       for (i = 0; i < count; i++)
-               quota_root_set_namespace(roots[i], namespaces);
 
        quota_over_status_check_startup(quota);
 }
index 63b77b4158eb6c00ea44018f3ff2e0a2d56ba5da..74b2e3ce9956c1514cf090c1264aa8c2802a13d0 100644 (file)
 /* 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 <ctype.h>
-
-#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>:<quota limits> */
-       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;
index 7c7818b650a95b63c4fde7d337b1852430979945..4a7129d055808f8d272b7cf3f4af5efcb3edd478 100644 (file)
@@ -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;
-
-       /* <backend>[:<quota root name>[:<backend args>]] */
-       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(&quota_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(&quota_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(&quota_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, &quota_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,
+                                       &quota_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,
+                               &quota_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(&quota->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, &quota_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(&quota->roots, 8);
-
-       root_sets = array_get(&quota_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(&quota);
-                       return -1;
-               }
-               if (ret > 0) {
-                       array_push_back(&quota->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(&quota->global_private_roots, 8);
+       i_array_init(&quota->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(&quota);
+                               return -1;
+                       }
+                       if (ret > 0)
+                               array_push_back(&quota->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(&quota->roots, &count);
+       roots = array_get(&quota->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(&quota->roots);
+       array_free(&quota->global_private_roots);
+       array_free(&quota->all_roots);
        event_unref(&quota->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(&quota->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(&quota->roots, root) {
+       array_foreach_elem(&quota->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(&quota->roots, &count);
+       roots = array_get(&quota->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,
+                                       &quota_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, &quota_over_script,
-                                      &quota_over_status_current,
-                                      &quota_over_status))
+       if (!quota_over_status_init_root(root, &quota_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(&quota->roots, &count);
+       roots = array_get(&quota->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)
 {
index e23cb713a4f3cc66f3f87e53ef6f773240152a26..347f7b5360bdbe4d77a1a1775e5caf47cb2a28fa 100644 (file)
@@ -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. */