Mercurial > dovecot > core-2.2
view src/plugins/quota/quota-dict.c @ 18137:3009a1a6f6d5
global: freshen copyright
Robomatically:
git ls-files | xargs perl -p -i -e 's/(\d+)-201[0-4]/$1-2015/g;s/ (201[0-4]) Dovecot/ $1-2015 Dovecot/'
Happy 2015 everyone!
Signed-off-by: Phil Carmody <phil@dovecot.fi>
author | Phil Carmody <phil@dovecot.fi> |
---|---|
date | Mon, 05 Jan 2015 22:20:10 +0200 |
parents | 831f7e40546d |
children | 8b452eb97422 |
line wrap: on
line source
/* Copyright (c) 2005-2015 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "dict.h" #include "mail-user.h" #include "mail-namespace.h" #include "quota-private.h" #include <stdlib.h> #define DICT_QUOTA_CURRENT_PATH DICT_PATH_PRIVATE"quota/" #define DICT_QUOTA_CURRENT_BYTES_PATH DICT_QUOTA_CURRENT_PATH"storage" #define DICT_QUOTA_CURRENT_COUNT_PATH DICT_QUOTA_CURRENT_PATH"messages" struct dict_quota_root { struct quota_root root; struct dict *dict; }; extern struct quota_backend quota_backend_dict; static struct quota_root *dict_quota_alloc(void) { struct dict_quota_root *root; root = i_new(struct dict_quota_root, 1); return &root->root; } static int dict_quota_init(struct quota_root *_root, const char *args, const char **error_r) { struct dict_quota_root *root = (struct dict_quota_root *)_root; struct dict_settings set; const char *username, *p, *error; p = args == NULL ? NULL : strchr(args, ':'); if (p == NULL) { *error_r = "URI missing from parameters"; return -1; } username = t_strdup_until(args, p); args = p+1; do { /* FIXME: pretty ugly in here. the parameters should have been designed to be extensible. do it in a future version */ if (strncmp(args, "noenforcing:", 12) == 0) { _root->no_enforcing = TRUE; args += 12; continue; } if (strncmp(args, "hidden:", 7) == 0) { _root->hidden = TRUE; args += 7; continue; } if (strncmp(args, "ignoreunlimited:", 16) == 0) { _root->disable_unlimited_tracking = TRUE; args += 16; continue; } if (strncmp(args, "ns=", 3) == 0) { p = strchr(args, ':'); if (p == NULL) break; _root->ns_prefix = p_strdup_until(_root->pool, args + 3, p); args = p + 1; continue; } } while (0); if (*username == '\0') username = _root->quota->user->username; if (_root->quota->set->debug) { i_debug("dict quota: user=%s, uri=%s, noenforcing=%d", username, args, _root->no_enforcing); } /* FIXME: we should use 64bit integer as datatype instead but before it can actually be used don't bother */ memset(&set, 0, sizeof(set)); set.username = username; set.base_dir = _root->quota->user->set->base_dir; if (mail_user_get_home(_root->quota->user, &set.home_dir) <= 0) set.home_dir = NULL; if (dict_init_full(args, &set, &root->dict, &error) < 0) { *error_r = t_strdup_printf("dict_init(%s) failed: %s", args, error); return -1; } return 0; } static void dict_quota_deinit(struct quota_root *_root) { struct dict_quota_root *root = (struct dict_quota_root *)_root; if (root->dict != NULL) dict_deinit(&root->dict); i_free(root); } static const char *const * dict_quota_root_get_resources(struct quota_root *root ATTR_UNUSED) { static const char *resources[] = { QUOTA_NAME_STORAGE_KILOBYTES, QUOTA_NAME_MESSAGES, NULL }; return resources; } static int dict_quota_count(struct dict_quota_root *root, bool want_bytes, uint64_t *value_r) { struct dict_transaction_context *dt; uint64_t bytes, count; if (quota_count(&root->root, &bytes, &count) < 0) return -1; T_BEGIN { dt = dict_transaction_begin(root->dict); /* these unsets are mainly necessary for pgsql, because its trigger otherwise increases quota without deleting it */ dict_unset(dt, DICT_QUOTA_CURRENT_BYTES_PATH); dict_unset(dt, DICT_QUOTA_CURRENT_COUNT_PATH); dict_set(dt, DICT_QUOTA_CURRENT_BYTES_PATH, dec2str(bytes)); dict_set(dt, DICT_QUOTA_CURRENT_COUNT_PATH, dec2str(count)); } T_END; dict_transaction_commit_async(&dt, NULL, NULL); *value_r = want_bytes ? bytes : count; return 1; } static int dict_quota_get_resource(struct quota_root *_root, const char *name, uint64_t *value_r) { struct dict_quota_root *root = (struct dict_quota_root *)_root; bool want_bytes; int ret; if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) want_bytes = TRUE; else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) want_bytes = FALSE; else return 0; T_BEGIN { const char *value; ret = dict_lookup(root->dict, unsafe_data_stack_pool, want_bytes ? DICT_QUOTA_CURRENT_BYTES_PATH : DICT_QUOTA_CURRENT_COUNT_PATH, &value); if (ret < 0) *value_r = 0; else { long long tmp; /* recalculate quota if it's negative or if it wasn't found */ tmp = ret == 0 ? -1 : strtoll(value, NULL, 10); if (tmp >= 0) *value_r = tmp; else { ret = dict_quota_count(root, want_bytes, value_r); } } } T_END; return ret; } static void dict_quota_update_callback(int ret, void *context) { struct dict_quota_root *root = context; uint64_t value; if (ret == 0) { /* row doesn't exist, need to recalculate it */ (void)dict_quota_count(root, TRUE, &value); } else if (ret < 0) { i_error("dict quota: Quota update failed, it's now desynced"); } } static int dict_quota_update(struct quota_root *_root, struct quota_transaction_context *ctx) { struct dict_quota_root *root = (struct dict_quota_root *) _root; struct dict_transaction_context *dt; uint64_t value; if (ctx->recalculate) { if (dict_quota_count(root, TRUE, &value) < 0) return -1; } else { dt = dict_transaction_begin(root->dict); if (ctx->bytes_used != 0) { dict_atomic_inc(dt, DICT_QUOTA_CURRENT_BYTES_PATH, ctx->bytes_used); } if (ctx->count_used != 0) { dict_atomic_inc(dt, DICT_QUOTA_CURRENT_COUNT_PATH, ctx->count_used); } dict_transaction_commit_async(&dt, dict_quota_update_callback, root); } return 0; } static void dict_quota_flush(struct quota_root *_root) { struct dict_quota_root *root = (struct dict_quota_root *)_root; (void)dict_wait(root->dict); } struct quota_backend quota_backend_dict = { "dict", { dict_quota_alloc, dict_quota_init, dict_quota_deinit, NULL, NULL, NULL, dict_quota_root_get_resources, dict_quota_get_resource, dict_quota_update, NULL, dict_quota_flush } };