quota.c revision 2454dfa32c93c20a8522c6ed42fe057baaac9f9a
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2005-2017 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;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenunsigned int quota_module_id = 0;
c4db1218645ed8ec8b5ae67c05bc5d7a80c1b8aeTimo Sirainenextern struct quota_backend quota_backend_count;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenextern struct quota_backend quota_backend_dict;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenextern struct quota_backend quota_backend_dirsize;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenextern struct quota_backend quota_backend_maildir;
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainenstatic const struct quota_backend *quota_backends[] = {
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainenstatic int quota_default_test_alloc(struct quota_transaction_context *ctx,
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainenstatic void quota_over_flag_check_root(struct quota_root *root);
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic const struct quota_backend *quota_backend_find(const char *name)
4316355ca8b7698516272520a972291378698140Timo Sirainen unsigned int i;
4316355ca8b7698516272520a972291378698140Timo Sirainen for (i = 0; i < N_ELEMENTS(quota_backends); i++) {
4316355ca8b7698516272520a972291378698140Timo Sirainen if (strcmp(quota_backends[i]->name, name) == 0)
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",
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;
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen quota_set->vsizes = mail_user_plugin_getenv(user, "quota_vsizes") != NULL;
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)
4316355ca8b7698516272520a972291378698140Timo Sirainen if (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)
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen const char *const *tmp;
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen else if (strcmp(*tmp, "ignoreunlimited") == 0)
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen "Unknown parameter for backend %s: %s",
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,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen unsigned int i, count;
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 unsigned int i, count;
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,
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen /* if default rule limits are 0, user has unlimited quota.
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen ignore any specific quota rules */
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen enabled = bytes_limit != 0 || count_limit != 0;
b089505afb8b5c5dfb43ef88af0ea7e0920db587Timo Sirainen (void)mail_namespace_find_unalias(root->quota->user->namespaces,
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen rule = enabled ? quota_root_rule_find(root->set, mailbox_name) : NULL;
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 unsigned int i, count;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen namespaces = array_get("a->namespaces, &count);
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen for (i = 0; i < count; i++) {
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen if (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)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int i, j, count;
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)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen unsigned int i, count;
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 unsigned int count;
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 unsigned int i, count;
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)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenint quota_get_resource(struct quota_root *root, const char *mailbox_name,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen 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. */
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen ret = root->backend.v.get_resource(root, name, value_r);
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen if (quota_root_get_rule_limits(root, mailbox_name,
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0)
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);
10ccd0e45768923d69be459e87ef6cd2574cec60Timo Sirainen /* ignore quota for dsync */
5b5da56e477dd55fbac23be9c96d8baa00482069Timo Sirainenint quota_transaction_set_limits(struct quota_transaction_context *ctx)
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen unsigned int i, count;
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,
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen ret = quota_get_resource(roots[i], mailbox_name,
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen /* over quota */
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen } else if (ret < 0) {
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen ret = quota_get_resource(roots[i], mailbox_name,
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen /* over quota */
7db5ff0dd17c40711bfaa0375aa5cc7f822a82f2Timo Sirainen } else if (ret < 0) {
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainenstatic void quota_warning_execute(struct quota_root *root, const char *cmd,
fa3fc0b4c821874ccc56a1512604f661b411d3a4Aki Tuomi const char *socket_path, *const *args, *error, *scheme, *ptr;
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,
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen unsigned int i, count;
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,
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen if (quota_get_resource(root, "", QUOTA_NAME_MESSAGES,
20dd6f6726db692904794d7daafe5b6e7238b720Timo Sirainen bytes_before = bytes_current - ctx->bytes_used;
20dd6f6726db692904794d7daafe5b6e7238b720Timo Sirainen count_before = count_current - ctx->count_used;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen for (i = 0; i < count; i++) {
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen quota_warning_execute(root, warnings[i].command, NULL);
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainenint quota_transaction_commit(struct quota_transaction_context **_ctx)
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen struct quota_transaction_context *ctx = *_ctx;
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen unsigned int i, count;
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 */
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen if (roots[i]->backend.v.update(roots[i], ctx) < 0)
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. */
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainenstatic void quota_over_flag_init_root(struct quota_root *root)
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);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen /* compare quota_over_flag's value to quota_over_flag_value and
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen save the result. */
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen name = t_strconcat(root->set->set_name, "_over_flag", NULL);
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen root->quota_over_flag = p_strdup_empty(root->pool,
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen mail_user_plugin_getenv(root->quota->user, name));
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen root->quota_over_flag_status = root->quota_over_flag != NULL &&
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen wildcard_match_icase(root->quota_over_flag, flag_mask);
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainenstatic void quota_over_flag_check_root(struct quota_root *root)
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen const char *const *resources;
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. */
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. */
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen ret = quota_get_resource(root, "", resources[i], &value, &limit);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen /* can't reliably verify this */
9871c4b8a06fc26de2101b825fedd7c80e8195b3Timo Sirainen i_debug("quota: Quota %s lookup failed - can't verify quota_over_flag",
9871c4b8a06fc26de2101b825fedd7c80e8195b3Timo Sirainen i_debug("quota: quota_over_flag check: %s ret=%d value=%llu limit=%llu",
9871c4b8a06fc26de2101b825fedd7c80e8195b3Timo Sirainen (unsigned long long)value,
9871c4b8a06fc26de2101b825fedd7c80e8195b3Timo Sirainen (unsigned long long)limit);
9871c4b8a06fc26de2101b825fedd7c80e8195b3Timo Sirainen i_debug("quota: quota_over_flag=%d(%s) vs currently overquota=%d",
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen root->quota_over_flag == NULL ? "(null)" : root->quota_over_flag,
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen if (cur_overquota != root->quota_over_flag_status) {
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen name = t_strconcat(root->set->set_name, "_over_script", NULL);
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen overquota_script = mail_user_plugin_getenv(root->quota->user, name);
cfeebb6dde3a3140972797c610ae78cc25c0b36cTimo Sirainen quota_warning_execute(root, overquota_script, root->quota_over_flag);
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainenvoid quota_over_flag_check_startup(struct quota *quota)
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen unsigned int i, count;
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen for (i = 0; i < count; i++) {
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen name = t_strconcat(roots[i]->set->set_name, "_over_flag_lazy_check", NULL);
c16f2d0725a16559cdeedec7628ce616725000cbTimo Sirainen if (mail_user_plugin_getenv(roots[i]->quota->user, name) == NULL)
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainenvoid quota_transaction_rollback(struct quota_transaction_context **_ctx)
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen struct quota_transaction_context *ctx = *_ctx;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenint quota_try_alloc(struct quota_transaction_context *ctx,
13a2b020c7ae44b1433a6328f7c79cc3ad8306feTimo Sirainen if (mail_get_physical_size(mail, &size) < 0) {
d6b606142e1349f4002a6bd83874399e0edd28bdTimo Sirainen const char *errstr = mailbox_get_last_error(mail->box, &error);
d6b606142e1349f4002a6bd83874399e0edd28bdTimo Sirainen /* mail being copied was already expunged. it'll fail,
d6b606142e1349f4002a6bd83874399e0edd28bdTimo Sirainen so just return success for the quota allocated. */
13a2b020c7ae44b1433a6328f7c79cc3ad8306feTimo Sirainen i_error("quota: Failed to get mail size (box=%s, uid=%u): %s",
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen ret = quota_test_alloc(ctx, size, too_large_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
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen quota_alloc() or quota_free*() was already used within the same
e2e64c109827f782e9e20b50a15c7489479bcadaTimo Sirainen transaction, but that doesn't normally happen. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenint quota_test_alloc(struct quota_transaction_context *ctx,
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen /* this is a virtual function mainly for trash plugin and similar,
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen which may automatically delete mails to stay under quota. */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen return ctx->quota->set->test_alloc(ctx, size, too_large_r);
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainenstatic int quota_default_test_alloc(struct quota_transaction_context *ctx,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int i, count;
30ad2b0309119501efad06c72ec9b1561b90d4afTimo Sirainen /* limit reached. only thing left to do now is to set too_large_r. */
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))
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* if size is bigger than any limit, then
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen it is bigger than the lowest limit */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenvoid quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenvoid quota_free(struct quota_transaction_context *ctx, struct mail *mail)
39dea5f2e78f6bfc3adc0655176f596ee211938fTimo Sirainen quota_recalculate(ctx, QUOTA_RECALCULATE_MISSING_FREES);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenvoid quota_free_bytes(struct quota_transaction_context *ctx,