bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainenquota_root_rule_find(struct quota_root_settings *root_set, const char *name)
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen array_foreach_modifiable(&root_set->rules, rule) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainenstatic struct quota_rule *
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainenquota_root_rule_find_exact(struct quota_root_settings *root_set,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen array_foreach_modifiable(&root_set->rules, rule) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainenquota_rule_parse_percentage(struct quota_root_settings *root_set,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (percentage <= -100 || percentage >= UINT_MAX) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen *error_r = "Default rule can't be a percentage";
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainenstatic int quota_limit_parse(struct quota_root_settings *root_set,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen const char **error_r)
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen /* default */
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (quota_rule_parse_percentage(root_set, rule, limit,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen *error_r = t_strdup_printf("Unknown unit: %s", unit);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainenquota_rule_recalculate_relative_rules(struct quota_rule *rule,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen rule->bytes_limit = bytes_limit * rule->bytes_percent / 100;
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen rule->count_limit = count_limit * rule->count_percent / 100;
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainenvoid quota_root_recalculate_relative_rules(struct quota_root_settings *root_set,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen array_foreach_modifiable(&root_set->rules, rule) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen quota_rule_recalculate_relative_rules(rule, bytes_limit,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen array_foreach_modifiable(&root_set->warning_rules, warning_rule) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen quota_rule_recalculate_relative_rules(&warning_rule->rule,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen quota_rule_recalculate_relative_rules(&root_set->grace_rule,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen root_set->last_mail_max_extra_bytes = root_set->grace_rule.bytes_limit;
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (root_set->set->debug && root_set->set->initialized) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen i_debug("Quota root %s: Recalculated relative rules with "
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi "bytes=%lld count=%lld. Now grace=%"PRIu64, root_set->name,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen (long long)bytes_limit, (long long)count_limit,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainenquota_rule_parse_limits(struct quota_root_settings *root_set,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen "obsolete configuration for rule '%s' "
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen "should be changed to '%s=+%s'",
e48f289d2e5b2546a2c5dcc90f7ab624cc58cca2Stephan Bosch *error_r = p_strdup_printf(root_set->set->pool,
e48f289d2e5b2546a2c5dcc90f7ab624cc58cca2Stephan Bosch *error_r = p_strdup_printf(root_set->set->pool,
e48f289d2e5b2546a2c5dcc90f7ab624cc58cca2Stephan Bosch *error_r = p_strdup_printf(root_set->set->pool,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen *error_r = p_strdup_printf(root_set->set->pool,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (quota_limit_parse(root_set, rule, p, multiply,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen *error_r = p_strdup_printf(root_set->set->pool,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen "Invalid rule limit value '%s': %s",
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainenint quota_root_add_rule(struct quota_root_settings *root_set,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen /* <mailbox mask>:<quota limits> */
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen rule = quota_root_rule_find_exact(root_set, mailbox_mask);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (strcmp(mailbox_mask, RULE_NAME_DEFAULT_NONFORCE) == 0)
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen else if (strcmp(mailbox_mask, RULE_NAME_DEFAULT_FORCE) == 0) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen rule->mailbox_mask = strcasecmp(mailbox_mask, "INBOX") == 0 ? "INBOX" :
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen i_debug("Quota rule: root=%s mailbox=%s ignored",
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (root_set->backend->v.parse_rule == NULL) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen } else if (!root_set->backend->v.parse_rule(root_set, rule,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen bool relative_rule = rule != &root_set->default_rule;
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (quota_rule_parse_limits(root_set, rule, p, rule_def,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen quota_root_recalculate_relative_rules(root_set,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen "bytes=%s%lld%s messages=%s%lld%s",
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen t_strdup_printf(" (%u%%)", rule->bytes_percent),
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen t_strdup_printf(" (%u%%)", rule->count_percent));
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainenint quota_root_add_warning_rule(struct quota_root_settings *root_set,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen const char *p, *q;
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen /* warn when exceeding quota */
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen /* warn when going below quota */
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen /* default: same as '+' */
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen ret = quota_rule_parse_limits(root_set, &rule, t_strdup_until(q, p),
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen warning = array_append_space(&root_set->warning_rules);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen warning->command = p_strdup(root_set->set->pool, p+1);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen quota_root_recalculate_relative_rules(root_set,
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi i_debug("Quota warning: bytes=%"PRId64"%s "
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi "messages=%"PRId64"%s reverse=%s command=%s",
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen t_strdup_printf(" (%u%%)", warning->rule.bytes_percent),
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen t_strdup_printf(" (%u%%)", warning->rule.count_percent),
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainenint quota_root_parse_grace(struct quota_root_settings *root_set,
e48f289d2e5b2546a2c5dcc90f7ab624cc58cca2Stephan Bosch const char *p;
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen /* default */
e48f289d2e5b2546a2c5dcc90f7ab624cc58cca2Stephan Bosch if (str_parse_int64(value, &root_set->grace_rule.bytes_limit, &p) < 0)
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (quota_limit_parse(root_set, &root_set->grace_rule, p, 1,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen &root_set->grace_rule.bytes_limit, error_r) < 0)
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen quota_rule_recalculate_relative_rules(&root_set->grace_rule,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen root_set->last_mail_max_extra_bytes = root_set->grace_rule.bytes_limit;
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen root_set->name, (long long)root_set->grace_rule.bytes_limit,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen root_set->grace_rule.bytes_percent == 0 ? "" :
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen t_strdup_printf(" (%u%%)", root_set->grace_rule.bytes_percent));
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainenbool quota_warning_match(const struct quota_warning_rule *w,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen uint64_t bytes_before, uint64_t bytes_current,
f97866381675fe12cdab0f2d56a059fe7b88457aTimo Sirainen uint64_t count_before, uint64_t count_current,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen#define QUOTA_EXCEEDED(before, current, limit) \
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen ((before) < (uint64_t)(limit) && (current) >= (uint64_t)(limit))
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen /* over quota (default) */
f97866381675fe12cdab0f2d56a059fe7b88457aTimo Sirainen if (QUOTA_EXCEEDED(bytes_before, bytes_current, w->rule.bytes_limit)) {
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi *reason_r = t_strdup_printf("bytes=%"PRIu64" -> %"PRIu64" over limit %"PRId64,
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi bytes_before, bytes_current, w->rule.bytes_limit);
f97866381675fe12cdab0f2d56a059fe7b88457aTimo Sirainen if (QUOTA_EXCEEDED(count_before, count_current, w->rule.count_limit)) {
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi *reason_r = t_strdup_printf("count=%"PRIu64" -> %"PRIu64" over limit %"PRId64,
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi count_before, count_current, w->rule.count_limit);
f97866381675fe12cdab0f2d56a059fe7b88457aTimo Sirainen if (QUOTA_EXCEEDED(bytes_current, bytes_before, w->rule.bytes_limit)) {
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi *reason_r = t_strdup_printf("bytes=%"PRIu64" -> %"PRIu64" below limit %"PRId64,
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi bytes_before, bytes_current, w->rule.bytes_limit);
f97866381675fe12cdab0f2d56a059fe7b88457aTimo Sirainen if (QUOTA_EXCEEDED(count_current, count_before, w->rule.count_limit)) {
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi *reason_r = t_strdup_printf("count=%"PRIu64" -> %"PRIu64" below limit %"PRId64,
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi count_before, count_current, w->rule.count_limit);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainenbool quota_transaction_is_over(struct quota_transaction_context *ctx,
de02255bbe97d5d5c54ca1054bb7c9304ee025a1Timo Sirainen /* we've deleted some messages. we should be ok, unless we
de02255bbe97d5d5c54ca1054bb7c9304ee025a1Timo Sirainen were already over quota and still are after these
de02255bbe97d5d5c54ca1054bb7c9304ee025a1Timo Sirainen deletions. */
1774ab4b71e7fcec670e7bae24a4a5b95738f560Timo Sirainen const uint64_t count_deleted = (uint64_t)-ctx->count_used;
de02255bbe97d5d5c54ca1054bb7c9304ee025a1Timo Sirainen ctx->count_ceil - 1 < (uint64_t)ctx->count_used) {
de02255bbe97d5d5c54ca1054bb7c9304ee025a1Timo Sirainen /* count limit reached */
de02255bbe97d5d5c54ca1054bb7c9304ee025a1Timo Sirainen const uint64_t bytes_deleted = (uint64_t)-ctx->bytes_used;
de02255bbe97d5d5c54ca1054bb7c9304ee025a1Timo Sirainen /* we've deleted some messages. same logic as above. */
de02255bbe97d5d5c54ca1054bb7c9304ee025a1Timo Sirainen /* even after deletions we're over quota */
de02255bbe97d5d5c54ca1054bb7c9304ee025a1Timo Sirainen } else if (size == 0) {
de02255bbe97d5d5c54ca1054bb7c9304ee025a1Timo Sirainen /* we need to explicitly test this case, since the generic
de02255bbe97d5d5c54ca1054bb7c9304ee025a1Timo Sirainen check would fail if user is already over quota */
de02255bbe97d5d5c54ca1054bb7c9304ee025a1Timo Sirainen ctx->bytes_ceil - size < (uint64_t)ctx->bytes_used) {
de02255bbe97d5d5c54ca1054bb7c9304ee025a1Timo Sirainen /* bytes limit reached */