bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
cb2b5a7d6e7e651191bf9ee1eda94a6e207288b0Timo Sirainen "Quota exceeded (mailbox for user is full)"
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen#define QUOTA_LIMIT_SET_PATH DICT_PATH_PRIVATE"quota/limit/"
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen/* How many seconds after the userdb lookup do we still want to execute the
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen quota_over_script. This applies to quota_over_flag_lazy_check=yes and also
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen after unhibernating IMAP connections. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int i;
c4db1218645ed8ec8b5ae67c05bc5d7a80c1b8aeTimo Sirainenextern struct quota_backend quota_backend_count;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenextern struct quota_backend quota_backend_dict;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenextern struct quota_backend quota_backend_dirsize;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenextern struct quota_backend quota_backend_imapc;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenextern struct quota_backend quota_backend_maildir;
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomistatic const struct quota_backend *quota_internal_backends[] = {
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomistatic ARRAY(const struct quota_backend*) quota_backends;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void hidden_param_handler(struct quota_root *_root, const char *param_value);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void ignoreunlim_param_handler(struct quota_root *_root, const char *param_value);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void noenforcing_param_handler(struct quota_root *_root, const char *param_value);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void ns_param_handler(struct quota_root *_root, const char *param_value);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstruct quota_param_parser quota_param_hidden = {.param_name = "hidden", .param_handler = hidden_param_handler};
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstruct quota_param_parser quota_param_ignoreunlimited = {.param_name = "ignoreunlimited", .param_handler = ignoreunlim_param_handler};
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstruct quota_param_parser quota_param_noenforcing = {.param_name = "noenforcing", .param_handler = noenforcing_param_handler};
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstruct quota_param_parser quota_param_ns = {.param_name = "ns=", .param_handler = ns_param_handler};
64f889c7c1707f9951755c997d750639be5dd41cMartti Rannanjärvistatic enum quota_alloc_result quota_default_test_alloc(
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi struct quota_transaction_context *ctx, uoff_t size,
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainenstatic void quota_over_flag_check_root(struct quota_root *root);
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic const struct quota_backend *quota_backend_find(const char *name)
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomivoid quota_backend_register(const struct quota_backend *backend)
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi i_assert(quota_backend_find(backend->name) == NULL);
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomivoid quota_backend_unregister(const struct quota_backend *backend)
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi for(unsigned int i = 0; i < array_count("a_backends); i++) {
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi array_append("a_backends, quota_internal_backends,
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi for(size_t i = 0; i < N_ELEMENTS(quota_internal_backends); i++) {
bd2a176b573f9679e7e45339c20ef71704f071c0Aki Tuomi quota_backend_unregister(quota_internal_backends[i]);
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic int quota_root_add_rules(struct mail_user *user, const char *root_name,
4316355ca8b7698516272520a972291378698140Timo Sirainen const char **error_r)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen unsigned int i;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen rule_name = t_strconcat(root_name, "_rule", NULL);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen for (i = 2;; i++) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen rule = mail_user_plugin_getenv(user, rule_name);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (quota_root_add_rule(root_set, rule, &error) < 0) {
4316355ca8b7698516272520a972291378698140Timo Sirainen *error_r = t_strdup_printf("Invalid rule %s: %s",
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen rule_name = t_strdup_printf("%s_rule%d", root_name, i);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenquota_root_add_warning_rules(struct mail_user *user, const char *root_name,
4316355ca8b7698516272520a972291378698140Timo Sirainen const char **error_r)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen unsigned int i;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen rule_name = t_strconcat(root_name, "_warning", NULL);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen for (i = 2;; i++) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen rule = mail_user_plugin_getenv(user, rule_name);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (quota_root_add_warning_rule(root_set, rule, &error) < 0) {
4316355ca8b7698516272520a972291378698140Timo Sirainen *error_r = t_strdup_printf("Invalid warning rule: %s",
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen rule_name = t_strdup_printf("%s_warning%d", root_name, i);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainenquota_root_parse_set(struct mail_user *user, const char *root_name,
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen const char **error_r)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen *error_r = t_strdup_printf("%s supports only dict backend", name);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen root_set->limit_set = p_strdup(root_set->set->pool, value+5);
4316355ca8b7698516272520a972291378698140Timo Sirainenquota_root_settings_init(struct quota_settings *quota_set, const char *root_def,
4316355ca8b7698516272520a972291378698140Timo Sirainen const char **error_r)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* <backend>[:<quota root name>[:<backend args>]] */
4316355ca8b7698516272520a972291378698140Timo Sirainen *error_r = t_strdup_printf("Unknown quota backend: %s",
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root_set = p_new(quota_set->pool, struct quota_root_settings, 1);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* save root's name */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root_set->name = p_strdup(quota_set->pool, args);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root_set->args = p_strdup(quota_set->pool, args);
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk i_debug("Quota root: name=%s backend=%s args=%s",
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk root_set->name, backend_name, args == NULL ? "" : args);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen p_array_init(&root_set->rules, quota_set->pool, 4);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen p_array_init(&root_set->warning_rules, quota_set->pool, 4);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen array_append("a_set->root_sets, &root_set, 1);
4316355ca8b7698516272520a972291378698140Timo Sirainenquota_root_add(struct quota_settings *quota_set, struct mail_user *user,
4316355ca8b7698516272520a972291378698140Timo Sirainen const char *env, const char *root_name, const char **error_r)
4316355ca8b7698516272520a972291378698140Timo Sirainen if (quota_root_settings_init(quota_set, env, &root_set, error_r) < 0)
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen root_set->set_name = p_strdup(quota_set->pool, root_name);
4316355ca8b7698516272520a972291378698140Timo Sirainen if (quota_root_add_rules(user, root_name, root_set, error_r) < 0)
4316355ca8b7698516272520a972291378698140Timo Sirainen if (quota_root_add_warning_rules(user, root_name, root_set, error_r) < 0)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (quota_root_parse_set(user, root_name, root_set, error_r) < 0)
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen set_name = t_strconcat(root_name, "_grace", NULL);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen value = mail_user_plugin_getenv(user, set_name);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (quota_root_parse_grace(root_set, value, error_r) < 0) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen *error_r = t_strdup_printf("Invalid %s value '%s': %s",
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärviconst char *quota_alloc_result_errstr(enum quota_alloc_result res,
b14f93ea4f7e9b78e5adbabcc78bd2f63d2bcd36Martti Rannanjärvi return "Blocked by an ongoing background quota calculation";
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return "Internal quota calculation error";
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi return "Mail size is larger than the maximum size allowed by "
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi "server configuration";
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi case QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT:
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvi return qt->quota->set->quota_exceeded_msg;
4316355ca8b7698516272520a972291378698140Timo Sirainenint quota_user_read_settings(struct mail_user *user,
4316355ca8b7698516272520a972291378698140Timo Sirainen const char **error_r)
4316355ca8b7698516272520a972291378698140Timo Sirainen unsigned int i;
4316355ca8b7698516272520a972291378698140Timo Sirainen pool = pool_alloconly_create("quota settings", 2048);
4316355ca8b7698516272520a972291378698140Timo Sirainen quota_set = p_new(pool, struct quota_settings, 1);
4316355ca8b7698516272520a972291378698140Timo Sirainen quota_set->test_alloc = quota_default_test_alloc;
4316355ca8b7698516272520a972291378698140Timo Sirainen mail_user_plugin_getenv(user, "quota_exceeded_message");
4316355ca8b7698516272520a972291378698140Timo Sirainen quota_set->quota_exceeded_msg = DEFAULT_QUOTA_EXCEEDED_MSG;
8fc6dd0572450cc2896f8089d23319a64a50267aMartti Rannanjärvi quota_set->vsizes = mail_user_plugin_getenv_bool(user, "quota_vsizes");
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi const char *max_size = mail_user_plugin_getenv(user,
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi "quota_max_mail_size");
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi if (settings_get_size(max_size, "a_set->max_mail_size,
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi *error_r = t_strdup_printf("quota_max_mail_size: %s",
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (i_strocpy(root_name, "quota", sizeof(root_name)) < 0)
4316355ca8b7698516272520a972291378698140Timo Sirainen for (i = 2;; i++) {
4316355ca8b7698516272520a972291378698140Timo Sirainen env = mail_user_plugin_getenv(user, root_name);
4316355ca8b7698516272520a972291378698140Timo Sirainen if (quota_root_add(quota_set, user, env, root_name,
4316355ca8b7698516272520a972291378698140Timo Sirainen *error_r = t_strdup_printf("Invalid quota root %s: %s",
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (i_snprintf(root_name, sizeof(root_name), "quota%d", i) < 0)
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi array_count("a_set->root_sets) == 0) {
4316355ca8b7698516272520a972291378698140Timo Sirainenvoid quota_settings_deinit(struct quota_settings **_quota_set)
4316355ca8b7698516272520a972291378698140Timo Sirainen struct quota_settings *quota_set = *_quota_set;
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void quota_root_deinit(struct quota_root *root)
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainenint quota_root_default_init(struct quota_root *root, const char *args,
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen const char **error_r)
aa089dcef71e735dcb902cb709f0b37643fd9842Sergey Kitov const struct quota_param_parser default_params[] = {
fde14422caabc3c4ac4a6c5e3e5cf176cedd90a6Timo Sirainen return quota_parse_parameters(root, &args, error_r, default_params, TRUE);
4316355ca8b7698516272520a972291378698140Timo Sirainenquota_root_init(struct quota_root_settings *root_set, struct quota *quota,
4316355ca8b7698516272520a972291378698140Timo Sirainen struct quota_root **root_r, const char **error_r)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root->pool = pool_alloconly_create("quota root", 512);
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen root->bytes_limit = root_set->default_rule.bytes_limit;
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen root->count_limit = root_set->default_rule.count_limit;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen array_create(&root->quota_module_contexts, root->pool,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen sizeof(void *), 10);
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen if (root->backend.v.init(root, root_set->args, error_r) < 0) {
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen *error_r = t_strdup_printf("%s quota init failed: %s",
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen if (quota_root_default_init(root, root_set->args, error_r) < 0)
d19d3aa4eafa34b48b7d7d311c9db31e1898576aTimo Sirainen if (root_set->default_rule.bytes_limit == 0 &&
4316355ca8b7698516272520a972291378698140Timo Sirainenint quota_init(struct quota_settings *quota_set, struct mail_user *user,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root_sets = array_get("a_set->root_sets, &count);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen for (i = 0; i < count; i++) {
4316355ca8b7698516272520a972291378698140Timo Sirainen ret = quota_root_init(root_sets[i], quota, &root, &error);
4316355ca8b7698516272520a972291378698140Timo Sirainen *error_r = t_strdup_printf("Quota root %s: %s",
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen for (i = 0; i < count; i++)
6b43203878bd3c87f5ae690617a1cbc694e24c01Timo Sirainen /* deinit quota roots before setting quser->quota=NULL */
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainenstatic int quota_root_get_rule_limits(struct quota_root *root,
e83a11eb1dc0dea31754396e1d07c5205d810104Martti Rannanjärvi if (root->backend.v.init_limits(root, &error) < 0) {
e83a11eb1dc0dea31754396e1d07c5205d810104Martti Rannanjärvi "Initializing limits failed for quota backend: %s",
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen /* if default rule limits are 0, user has unlimited quota.
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen ignore any specific quota rules */
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek if (bytes_limit != 0 || count_limit != 0) {
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek (void)mail_namespace_find_unalias(root->quota->user->namespaces,
086e8ecc50156d1017e5b807bdecc00b4fdc2a0eJosef 'Jeff' Sipek rule = quota_root_rule_find(root->set, mailbox_name);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen *bytes_limit_r = bytes_limit <= 0 ? 0 : bytes_limit;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen *count_limit_r = count_limit <= 0 ? 0 : count_limit;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainenquota_is_duplicate_namespace(struct quota *quota, struct mail_namespace *ns)
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen namespaces = array_get("a->namespaces, &count);
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen for (i = 0; i < count; i++) {
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen /* count namespace aliases only once. don't rely only on
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen alias_for != NULL, because the alias might have been
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen explicitly added as the wanted quota namespace. */
ecd54d6c65d15c1a518c6754383389acac655ec2Timo Sirainen mailbox_list_get_root_path(namespaces[i]->list,
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen /* duplicate path */
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0)
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen /* this is inbox=yes namespace, but the earlier one
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen that had the same location was inbox=no. we need to
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen include the INBOX also in quota calculations, so we
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen can't just ignore this namespace. but since we've
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen already called backend's namespace_added(), we can't
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen just remove it either. so just mark the old one as
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen unwanted namespace.
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen an alternative would be to do a bit larger change so
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen namespaces wouldn't be added until
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen mail_namespaces_created() hook is called */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenvoid quota_add_user_namespace(struct quota *quota, struct mail_namespace *ns)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* first check if there already exists a namespace with the exact same
de5c7c99783cd86f3bdbc057345cbee923b51a20Timo Sirainen path. we don't want to count them twice. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* @UNSAFE: get different backends into one array */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen backends = t_new(struct quota_backend *, count + 1);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < count; i++) {
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen if (backends[j]->name == roots[i]->backend.name)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenvoid quota_remove_user_namespace(struct mail_namespace *ns)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* no quota for this namespace */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen namespaces = array_get("a->namespaces, &count);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen for (i = 0; i < count; i++) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen quota_get_mail_user_quota(box->list->ns->owner) :
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen quota_get_mail_user_quota(box->list->ns->user);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenbool quota_root_is_namespace_visible(struct quota_root *root,
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen /* this check works as long as there is only one storage per list */
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen if (mailbox_list_get_storage(&list, "", &storage) == 0 &&
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen (storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NOQUOTA) != 0)
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainenquota_root_is_visible(struct quota_root *root, struct mailbox *box,
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen /* we don't want to include this root in quota enforcing */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (!quota_root_is_namespace_visible(root, box->list->ns))
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen /* a single quota root: don't bother checking further */
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen return root->backend.v.match_box == NULL ? TRUE :
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstruct quota_root *quota_root_iter_next(struct quota_root_iter *iter)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_root *const *roots, *root = NULL;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen roots = array_get(&iter->quota->roots, &count);
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen if (!quota_root_is_visible(roots[iter->i], iter->box, FALSE))
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenvoid quota_root_iter_deinit(struct quota_root_iter **_iter)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenstruct quota_root *quota_root_lookup(struct mail_user *user, const char *name)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen for (i = 0; i < count; i++) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenconst char *quota_root_get_name(struct quota_root *root)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenconst char *const *quota_root_get_resources(struct quota_root *root)
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen /* if we haven't checked the quota_over_flag yet, do it now */
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainenbool quota_root_is_hidden(struct quota_root *root)
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainenquota_get_resource(struct quota_root *root, const char *mailbox_name,
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi const char *name, uint64_t *value_r, uint64_t *limit_r,
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (strcmp(name, QUOTA_NAME_STORAGE_KILOBYTES) == 0) {
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen /* Get the value first. This call may also update quota limits if
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen they're defined externally. */
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi ret = root->backend.v.get_resource(root, name, value_r, &error);
1379bb74c2855aaf3415fdfe965164a44ac3c001Martti Rannanjärvi i_panic("Quota backend %s returned QUOTA_GET_RESULT_UNLIMITED "
1379bb74c2855aaf3415fdfe965164a44ac3c001Martti Rannanjärvi "while getting resource %s from box %s",
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi else if (ret != QUOTA_GET_RESULT_LIMITED) {
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi "quota-%s: %s", root->set->backend->name, error);
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen if (quota_root_get_rule_limits(root, mailbox_name,
d81702ddd148abde32cbe2a45f6239e1baee6907Timo Sirainen "Failed to get quota root rule limits for mailbox %s: %s",
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0)
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainen return *limit_r == 0 ? QUOTA_GET_RESULT_UNLIMITED : QUOTA_GET_RESULT_LIMITED;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainenint quota_set_resource(struct quota_root *root, const char *name,
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (strcasecmp(name, QUOTA_NAME_STORAGE_KILOBYTES) == 0)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen else if (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) == 0)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen else if (strcasecmp(name, QUOTA_NAME_MESSAGES) == 0)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen *error_r = t_strdup_printf("Unsupported resource name: %s", name);
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen set.base_dir = root->quota->user->set->base_dir;
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen if (mail_user_get_home(root->quota->user, &set.home_dir) <= 0)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen trans = dict_transaction_begin(root->limit_set_dict);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen key = t_strdup_printf(QUOTA_LIMIT_SET_PATH"%s", key);
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen if (dict_transaction_commit(&trans, &error) < 0) {
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen i_error("dict_transaction_commit() failed: %s", error);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen *error_r = "Internal quota limit update error";
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenstruct quota_transaction_context *quota_transaction_begin(struct mailbox *box)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ctx = i_new(struct quota_transaction_context, 1);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen quota_get_mail_user_quota(box->list->ns->owner) :
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen quota_get_mail_user_quota(box->list->ns->user);
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen (void)mail_namespace_find_unalias(box->storage->user->namespaces,
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen if (!quota_root_is_visible(*rootp, ctx->box, FALSE))
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen rule = quota_root_rule_find((*rootp)->set, mailbox_name);
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen /* This mailbox isn't included in quota. This means
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen it's also not included in quota_warnings, so make
3e170cc66eda9e1775825a5713c2d2b6a4891a04Timo Sirainen sure it's fully ignored. */
10207755f33a4cf1a1e901968137ae4f2bc93bbfTimo Sirainen /* If there are reverse quota_warnings, we'll need to track
10207755f33a4cf1a1e901968137ae4f2bc93bbfTimo Sirainen how many bytes were expunged even with auto_updating roots.
10207755f33a4cf1a1e901968137ae4f2bc93bbfTimo Sirainen (An alternative could be to get the current quota usage
10207755f33a4cf1a1e901968137ae4f2bc93bbfTimo Sirainen before and after the expunges, but that's more complicated
10207755f33a4cf1a1e901968137ae4f2bc93bbfTimo Sirainen and probably isn't any better.) */
10ccd0e45768923d69be459e87ef6cd2574cec60Timo Sirainen /* ignore quota for dsync */
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärviint quota_transaction_set_limits(struct quota_transaction_context *ctx,
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen uint64_t bytes_limit, count_limit, current, limit, diff;
e66cd209fcab4817d2234d0121b404925dc60034Timo Sirainen /* use quota_grace only for LDA/LMTP */
50ecf65993bff429af04deef6c832deb019c76a5Timo Sirainen use_grace = (ctx->box->flags & MAILBOX_FLAG_POST_SESSION) != 0;
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen /* find the lowest quota limits from all roots and use them */
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen roots = array_get(&ctx->quota->roots, &count);
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen for (i = 0; i < count; i++) {
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen if (!quota_root_is_visible(roots[i], ctx->box, TRUE))
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen if (quota_root_get_rule_limits(roots[i], mailbox_name,
c975f4c0c6a767050755e824d5a5ddeb1dbf7f6eMartti Rannanjärvi *error_result_r = QUOTA_GET_RESULT_INTERNAL_ERROR;
93b8808c974a4d6dfe97ba6006ca2f0f1df76cb5Martti Rannanjärvi "Failed to get quota root rule limits for %s: %s",
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen ret = quota_get_resource(roots[i], mailbox_name,
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen /* over quota */
c975f4c0c6a767050755e824d5a5ddeb1dbf7f6eMartti Rannanjärvi } else if (ret <= QUOTA_GET_RESULT_INTERNAL_ERROR) {
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi "Failed to get quota resource "
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen ret = quota_get_resource(roots[i], mailbox_name,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen /* over quota */
c975f4c0c6a767050755e824d5a5ddeb1dbf7f6eMartti Rannanjärvi } else if (ret <= QUOTA_GET_RESULT_INTERNAL_ERROR) {
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi "Failed to get quota resource "
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainenstatic void quota_warning_execute(struct quota_root *root, const char *cmd,
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi const char *socket_path, *const *args, *error, *scheme, *ptr;
f97866381675fe12cdab0f2d56a059fe7b88457aTimo Sirainen i_debug("quota: Executing warning: %s (because %s)", cmd, reason);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen const char **new_args = t_new(const char *, count + 2);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen memcpy(new_args, args, sizeof(const char *) * count);
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi socket_path = t_strdup_printf("%s:%s", scheme, socket_path);
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi if (program_client_create(socket_path, args, &set, TRUE,
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi i_error("program_client_create(%s) failed: %s", socket_path,
20dd6f6726db692904794d7daafe5b6e7238b720Timo Sirainenstatic void quota_warnings_execute(struct quota_transaction_context *ctx,
20dd6f6726db692904794d7daafe5b6e7238b720Timo Sirainen uint64_t bytes_current, bytes_before, bytes_limit;
20dd6f6726db692904794d7daafe5b6e7238b720Timo Sirainen uint64_t count_current, count_before, count_limit;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen warnings = array_get_modifiable(&root->set->warning_rules, &count);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen if (quota_get_resource(root, "", QUOTA_NAME_STORAGE_BYTES,
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi &bytes_current, &bytes_limit, &error) == QUOTA_GET_RESULT_INTERNAL_ERROR) {
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi i_error("Failed to get quota resource "QUOTA_NAME_STORAGE_BYTES
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen if (quota_get_resource(root, "", QUOTA_NAME_MESSAGES,
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi &count_current, &count_limit, &error) == QUOTA_GET_RESULT_INTERNAL_ERROR) {
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi i_error("Failed to get quota resource "QUOTA_NAME_MESSAGES
cd337e465bf8bf33d556fc397787d1519d1cef07Timo Sirainen if (ctx->bytes_used > 0 && bytes_current < (uint64_t)ctx->bytes_used)
cd337e465bf8bf33d556fc397787d1519d1cef07Timo Sirainen bytes_before = bytes_current - ctx->bytes_used;
cd337e465bf8bf33d556fc397787d1519d1cef07Timo Sirainen if (ctx->count_used > 0 && count_current < (uint64_t)ctx->count_used)
cd337e465bf8bf33d556fc397787d1519d1cef07Timo Sirainen count_before = count_current - ctx->count_used;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen for (i = 0; i < count; i++) {
f97866381675fe12cdab0f2d56a059fe7b88457aTimo Sirainen quota_warning_execute(root, warnings[i].command,
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainenint quota_transaction_commit(struct quota_transaction_context **_ctx)
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen struct quota_transaction_context *ctx = *_ctx;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen else if (ctx->bytes_used != 0 || ctx->count_used != 0 ||
39dea5f2e78f6bfc3adc0655176f596ee211938fTimo Sirainen ctx->recalculate != QUOTA_RECALCULATE_DONT) T_BEGIN {
b089505afb8b5c5dfb43ef88af0ea7e0920db587Timo Sirainen ctx->box->storage->user->namespaces, &mailbox_name);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen roots = array_get(&ctx->quota->roots, &count);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen for (i = 0; i < count; i++) {
89adb28d8f041e8c0b9c5156f089bb2f8f478ee3Timo Sirainen if (!quota_root_is_visible(roots[i], ctx->box, FALSE))
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen /* mailbox not included in quota */
7b3b617e946d5b32078baa821f5fc05f775e1dfeMartti Rannanjärvi if (roots[i]->backend.v.update(roots[i], ctx, &error) < 0) {
7b3b617e946d5b32078baa821f5fc05f775e1dfeMartti Rannanjärvi i_error("Failed to update quota for %s: %s",
d8548e14925a06e44c4f1983d7b118b0180939e0Timo Sirainen /* execute quota warnings after all updates. this makes it
d8548e14925a06e44c4f1983d7b118b0180939e0Timo Sirainen work correctly regardless of whether backend.get_resource()
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen returns updated values before backend.update() or not.
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen warnings aren't executed when dsync bring the user over,
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen because the user probably already got the warning on the
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen other replica. */
b711a7fc74bb2d21e1e951d7ae2e8517c53bb24bTimo Sirainenstatic bool quota_over_flag_init_root(struct quota_root *root,
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen name = t_strconcat(root->set->set_name, "_over_script", NULL);
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen *quota_over_script_r = mail_user_plugin_getenv(root->quota->user, name);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen /* e.g.: quota_over_flag_value=TRUE or quota_over_flag_value=* */
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen name = t_strconcat(root->set->set_name, "_over_flag_value", NULL);
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen flag_mask = mail_user_plugin_getenv(root->quota->user, name);
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen /* compare quota_over_flag's value (that comes from userdb) to
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen quota_over_flag_value and save the result. */
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen name = t_strconcat(root->set->set_name, "_over_flag", NULL);
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen *quota_over_flag_r = mail_user_plugin_getenv(root->quota->user, name);
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen wildcard_match_icase(*quota_over_flag_r, flag_mask);
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainenstatic void quota_over_flag_check_root(struct quota_root *root)
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi const char *quota_over_script, *quota_over_flag, *error;
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen unsigned int i;
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen QUOTA_OVER_FLAG_MAX_DELAY_SECS < ioloop_time) {
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen /* userdb's quota_over_flag lookup is too old. */
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen "Flag lookup time is too old - skipping");
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen /* we don't know whether the quota_over_script was executed
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen before hibernation. just assume that it was, so we don't
55639052253ff28c6a0f569ae76dfda33e02c5f2Timo Sirainen unnecessarily call it too often. */
bbe7af3946f071eaf3f81ac769dd1c735168b735Timo Sirainen "Session was already hibernated - skipping");
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen if (!quota_over_flag_init_root(root, "a_over_script,
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi ret = quota_get_resource(root, "", resources[i], &value,
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen /* can't reliably verify this */
cf41318871bd42358df3420e50614f5310b08c77Martti Rannanjärvi i_error("quota: Quota %s lookup failed - can't verify quota_over_flag: %s",
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi i_debug("quota: quota_over_flag check: %s ret=%d value=%"PRIu64" limit=%"PRIu64,
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainen if (ret == QUOTA_GET_RESULT_LIMITED && value >= limit)
9871c4b8a06fc26de2101b825fedd7c80e8195b3Timo Sirainen i_debug("quota: quota_over_flag=%d(%s) vs currently overquota=%d",
4009eb0bbe343958c22772bc0be88d17ee1c33feTimo Sirainen quota_over_flag == NULL ? "(null)" : quota_over_flag,
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen quota_warning_execute(root, quota_over_script, quota_over_flag,
40edfbbb81b7a76cddac2b327ed547d4feeb5e88Timo Sirainen "quota_over_flag mismatch");
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainenvoid quota_over_flag_check_startup(struct quota *quota)
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen for (i = 0; i < count; i++) {
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen name = t_strconcat(roots[i]->set->set_name, "_over_flag_lazy_check", NULL);
b1e097e04ceff5800101ec3615f815f2d0f2fd12Timo Sirainen if (!mail_user_plugin_getenv_bool(roots[i]->quota->user, name))
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainenvoid quota_transaction_rollback(struct quota_transaction_context **_ctx)
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen struct quota_transaction_context *ctx = *_ctx;
27569c07b5df5c4f079ca6cd329a5abb1b770d05Timo Sirainenstatic int quota_get_mail_size(struct quota_transaction_context *ctx,
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvienum quota_alloc_result quota_try_alloc(struct quota_transaction_context *ctx,
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi if (quota_transaction_set_limits(ctx, &error_res, error_r) < 0) {
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi if (error_res == QUOTA_GET_RESULT_BACKGROUND_CALC)
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi return QUOTA_ALLOC_RESULT_BACKGROUND_CALC;
27569c07b5df5c4f079ca6cd329a5abb1b770d05Timo Sirainen if (quota_get_mail_size(ctx, mail, &size) < 0) {
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi error = mailbox_get_last_internal_error(mail->box, &err);
d6b606142e1349f4002a6bd83874399e0edd28bdTimo Sirainen /* mail being copied was already expunged. it'll fail,
d6b606142e1349f4002a6bd83874399e0edd28bdTimo Sirainen so just return success for the quota allocated. */
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi "Failed to get mail size (box=%s, uid=%u): %s",
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi enum quota_alloc_result ret = quota_test_alloc(ctx, size, error_r);
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen /* with quota_try_alloc() we want to keep track of how many bytes
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen we've been adding/removing, so disable auto_updating=TRUE
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen optimization. this of course doesn't work perfectly if
7076aac11069d8400785373f6317c3c7f07d0883Timo Sirainen quota_alloc() or quota_free_bytes() was already used within the same
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen transaction, but that doesn't normally happen. */
38ce5769db11e7f52562610ee6e6fc4f0ea7888fMartti Rannanjärvienum quota_alloc_result quota_test_alloc(struct quota_transaction_context *ctx,
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi *error_r = "Quota transaction has failed earlier";
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi if (quota_transaction_set_limits(ctx, &error_res, error_r) < 0) {
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi if (error_res == QUOTA_GET_RESULT_BACKGROUND_CALC)
39914d983f6de82ba5613a9848e0935782013da1Martti Rannanjärvi return QUOTA_ALLOC_RESULT_BACKGROUND_CALC;
7f4fa37676bac8efcf4e2ac706172b1bad779a8aMartti Rannanjärvi uoff_t max_size = ctx->quota->set->max_mail_size;
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi "Requested allocation size %"PRIuUOFF_T" exceeds max "
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi "mail size %"PRIuUOFF_T, size, max_size);
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen /* this is a virtual function mainly for trash plugin and similar,
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen which may automatically delete mails to stay under quota. */
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi return ctx->quota->set->test_alloc(ctx, size, error_r);
64f889c7c1707f9951755c997d750639be5dd41cMartti Rannanjärvistatic enum quota_alloc_result quota_default_test_alloc(
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi struct quota_transaction_context *ctx, uoff_t size,
64f889c7c1707f9951755c997d750639be5dd41cMartti Rannanjärvi /* limit reached. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen roots = array_get(&ctx->quota->roots, &count);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < count; i++) {
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen if (!quota_root_is_visible(roots[i], ctx->box, TRUE))
efd291767a612b2cf3421f3d4ff1c3d8abd78484Martti Rannanjärvi "Failed to get quota root rule limits: %s",
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* if size is bigger than any limit, then
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen it is bigger than the lowest limit */
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi if (bytes_limit > 0 && size > bytes_limit) {
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi "Allocating %"PRIuUOFF_T" bytes would exceed quota limit",
64f889c7c1707f9951755c997d750639be5dd41cMartti Rannanjärvi return QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT;
b62139aae0a0099d0ede35b164b2870479f9b027Martti Rannanjärvi "Allocating %"PRIuUOFF_T" bytes would exceed quota", size);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenvoid quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
f9362223346d10a5866b376ed227b77b26ea4292Timo Sirainen if (quota_get_mail_size(ctx, mail, &size) == 0)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenvoid quota_free_bytes(struct quota_transaction_context *ctx,
39dea5f2e78f6bfc3adc0655176f596ee211938fTimo Sirainenvoid quota_recalculate(struct quota_transaction_context *ctx,
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void hidden_param_handler(struct quota_root *_root, const char *param_value ATTR_UNUSED)
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void ignoreunlim_param_handler(struct quota_root *_root, const char *param_value ATTR_UNUSED)
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void noenforcing_param_handler(struct quota_root *_root, const char *param_value ATTR_UNUSED)
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovstatic void ns_param_handler(struct quota_root *_root, const char *param_value)
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov _root->ns_prefix = p_strdup(_root->pool, param_value);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitovint quota_parse_parameters(struct quota_root *root, const char **args, const char **error_r,
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov const struct quota_param_parser *valid_params, bool fail_on_unknown)
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov for (; valid_params->param_name != NULL; ++valid_params) {
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov tmp_param_len = strlen(valid_params->param_name);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov if (strncmp(*args, tmp_param_name, tmp_param_len) == 0) {
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov if (tmp_param_name[tmp_param_len - 1] == '=') {
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov *args = (next_colon == NULL) ? NULL : next_colon + 1;
e8760ad579cae936b32308088e3ed78d9aeb273eSergey Kitov *args = ((*args)[0] == ':') ? *args + 1 : NULL;
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov /* in case parameter is a boolean second parameter
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov * string parameter value will be ignored by param_handler
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov * we just need some non-NULL value
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov * to indicate that argument is to be processed */
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov valid_params->param_handler(root, tmp_param_val);
afbdaecd328f6c912293d713975856cbb2d75452Sergey Kitov "Unknown parameter for backend %s: %s",