}
/* 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: "
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: "
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);
}
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)
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,
#include "lib.h"
#include "ioloop.h"
+#include "settings.h"
#include "mailbox-list-iter.h"
#include "quota-private.h"
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;
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,
*bytes += metadata.virtual_size;
*count += status.messages;
}
+ settings_free(set);
+ event_unref(&event);
mailbox_free(&box);
return ret;
}
{.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}
};
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}
};
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;
/* 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;
#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"
static const char *
maildir_list_next(struct maildir_list_context *ctx, time_t *mtime_r)
{
- struct quota_rule *rule;
struct stat st;
for (;;) {
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;
}
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,
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,
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. */
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 {
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);
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);
#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),
};
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,
.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;
#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);
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) {
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]);
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;
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;
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);
}
/* 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;
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;
}
-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("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)
{
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);
}
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;
}
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
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)
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) {
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;
}
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;
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)
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;
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;
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 ?
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
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;
}
}
/* 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;
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;
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,
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;
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)
}
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;
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);
}
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");
}
}
{
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]);
}
}
}
/* 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;
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 */
_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)
{
#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;
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. */