quota.c revision 93f1642397e46497894e6695749e5c52fda61774
7cb128dc4cae2a03a742f63ba7afee23c78e3af0Phil Carmody/* Copyright (c) 2005-2015 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/"
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int i;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenunsigned int quota_module_id = 0;
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,
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)
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 const char *key;
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)
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen if (dict_init_full(root->set->limit_set, &set,
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen trans = dict_transaction_begin(root->limit_set_dict);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen key = t_strdup_printf(QUOTA_LIMIT_SET_PATH"%s", key);
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 */
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainenstatic int 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,
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen const char **new_args = t_new(const char *, count + 2);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen memcpy(new_args, args, sizeof(const char *) * count);
76a99afe0914951d20d96e0bf5e6d8d3ea3fd503Timo Sirainen socket_path = t_strconcat(root->quota->user->set->base_dir, "/",
76a99afe0914951d20d96e0bf5e6d8d3ea3fd503Timo Sirainen if ((fd = net_connect_unix_with_retries(socket_path, 1000)) < 0) {
596ec384269cad3b0f0661df89b9cf33cbd171b7Timo Sirainen i_error("quota: net_connect_unix(%s) failed: %m",
2d3731908f924bc8a2b7887a712cc033e618c998Timo Sirainen str_append(str, "VERSION\tscript\t3\t0\nnoreply\n");
76a99afe0914951d20d96e0bf5e6d8d3ea3fd503Timo Sirainen if (write_full(fd, str_data(str), str_len(str)) < 0)
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 ||
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. */
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainenquota_over_flag_check_root(struct mail_user *user, struct quota_root *root)
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen const char *name, *flag_mask, *overquota_value, *overquota_script;
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen const char *const *resources;
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen unsigned int i;
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen name = t_strconcat(root->set->set_name, "_over_script", NULL);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen overquota_script = mail_user_plugin_getenv(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);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen flag_mask = mail_user_plugin_getenv(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);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen overquota_value = mail_user_plugin_getenv(user, name);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen wildcard_match_icase(overquota_value, flag_mask);
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",
9871c4b8a06fc26de2101b825fedd7c80e8195b3Timo Sirainen overquota_flag, overquota_value != NULL ? "(null)" : overquota_value,
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen quota_warning_execute(root, overquota_script, overquota_value);
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainenvoid quota_over_flag_check(struct mail_user *user, struct quota *quota)
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen unsigned int i, count;
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen for (i = 0; i < count; i++)
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,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen ret = quota_test_alloc(ctx, size, too_large_r);
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)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenvoid quota_free_bytes(struct quota_transaction_context *ctx,