quota-dict.c revision e5acc283bf030b0b5c79ca4e52d315c516a299fa
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "lib.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "str.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "dict.h"
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen#include "mail-user.h"
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen#include "mail-namespace.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "quota-private.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include <stdlib.h>
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
51dbc2d559815da774f8ee5faf0e28df3c8d40c0Timo Sirainen#define DICT_QUOTA_CURRENT_PATH DICT_PATH_PRIVATE"quota/"
51dbc2d559815da774f8ee5faf0e28df3c8d40c0Timo Sirainen#define DICT_QUOTA_CURRENT_BYTES_PATH DICT_QUOTA_CURRENT_PATH"storage"
51dbc2d559815da774f8ee5faf0e28df3c8d40c0Timo Sirainen#define DICT_QUOTA_CURRENT_COUNT_PATH DICT_QUOTA_CURRENT_PATH"messages"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstruct dict_quota_root {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct quota_root root;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct dict *dict;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen};
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenextern struct quota_backend quota_backend_dict;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic struct quota_root *dict_quota_alloc(void)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct dict_quota_root *root;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen root = i_new(struct dict_quota_root, 1);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen return &root->root;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int dict_quota_init(struct quota_root *_root, const char *args)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct dict_quota_root *root = (struct dict_quota_root *)_root;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const char *username, *p;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen p = args == NULL ? NULL : strchr(args, ':');
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (p == NULL) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("dict quota: URI missing from parameters");
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return -1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen username = t_strdup_until(args, p);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen args = p+1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen do {
65e14cef911d5d3fac8993c8a76911a587f05cd7Timo Sirainen /* FIXME: pretty ugly in here. the parameters should have
65e14cef911d5d3fac8993c8a76911a587f05cd7Timo Sirainen been designed to be extensible. do it in a future version */
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if (strncmp(args, "noenforcing:", 12) == 0) {
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen _root->no_enforcing = TRUE;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen args += 12;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen continue;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen }
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if (strncmp(args, "ns=", 3) == 0) {
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen p = strchr(args, ':');
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if (p == NULL)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen break;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen _root->ns_prefix = p_strdup_until(_root->pool,
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen args + 3, p);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen args = p + 1;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen continue;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen }
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen } while (0);
65e14cef911d5d3fac8993c8a76911a587f05cd7Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (*username == '\0')
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen username = _root->quota->user->username;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (_root->quota->set->debug) {
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk i_debug("dict quota: user=%s, uri=%s, noenforcing=%d",
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk username, args, _root->no_enforcing);
65e14cef911d5d3fac8993c8a76911a587f05cd7Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen /* FIXME: we should use 64bit integer as datatype instead but before
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen it can actually be used don't bother */
4073f0dbf3277f981a8fcee3b89ea15aaf380a7fTimo Sirainen root->dict = dict_init(args, DICT_DATA_TYPE_STRING, username,
4073f0dbf3277f981a8fcee3b89ea15aaf380a7fTimo Sirainen _root->quota->user->set->base_dir);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return root->dict != NULL ? 0 : -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic void dict_quota_deinit(struct quota_root *_root)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct dict_quota_root *root = (struct dict_quota_root *)_root;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen if (root->dict != NULL) {
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen (void)dict_wait(root->dict);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen dict_deinit(&root->dict);
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_free(root);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic const char *const *
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainendict_quota_root_get_resources(struct quota_root *root ATTR_UNUSED)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen static const char *resources[] = {
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen QUOTA_NAME_STORAGE_KILOBYTES, QUOTA_NAME_MESSAGES, NULL
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen };
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return resources;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainenstatic int
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainendict_quota_count(struct dict_quota_root *root,
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen bool want_bytes, uint64_t *value_r)
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen{
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen struct dict_transaction_context *dt;
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen uint64_t bytes, count;
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen if (quota_count(&root->root, &bytes, &count) < 0)
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen return -1;
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen T_BEGIN {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen dt = dict_transaction_begin(root->dict);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen dict_set(dt, DICT_QUOTA_CURRENT_BYTES_PATH, dec2str(bytes));
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen dict_set(dt, DICT_QUOTA_CURRENT_COUNT_PATH, dec2str(count));
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } T_END;
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen dict_transaction_commit_async(&dt, NULL, NULL);
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen *value_r = want_bytes ? bytes : count;
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen return 1;
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen}
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic int
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainendict_quota_get_resource(struct quota_root *_root,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen const char *name, uint64_t *value_r)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct dict_quota_root *root = (struct dict_quota_root *)_root;
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen bool want_bytes;
f8ead0942a9b7c8fcf91414ed1b534d5807ca555Timo Sirainen int ret;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0)
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen want_bytes = TRUE;
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0)
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen want_bytes = FALSE;
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen else
51dbc2d559815da774f8ee5faf0e28df3c8d40c0Timo Sirainen return 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen T_BEGIN {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen const char *value;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen ret = dict_lookup(root->dict, unsafe_data_stack_pool,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen want_bytes ? DICT_QUOTA_CURRENT_BYTES_PATH :
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen DICT_QUOTA_CURRENT_COUNT_PATH, &value);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (ret < 0)
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen *value_r = 0;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen else {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen long long tmp;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen /* recalculate quota if it's negative or if it
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen wasn't found */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen tmp = ret == 0 ? -1 : strtoll(value, NULL, 10);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (tmp >= 0)
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen *value_r = tmp;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen else {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen ret = dict_quota_count(root, want_bytes,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen value_r);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } T_END;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return ret;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainenstatic void dict_quota_update_callback(int ret, void *context)
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen{
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen struct dict_quota_root *root = context;
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen uint64_t value;
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen if (ret == 0) {
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen /* row doesn't exist, need to recalculate it */
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen (void)dict_quota_count(root, TRUE, &value);
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen }
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen}
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic int
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainendict_quota_update(struct quota_root *_root,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_transaction_context *ctx)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct dict_quota_root *root = (struct dict_quota_root *) _root;
51dbc2d559815da774f8ee5faf0e28df3c8d40c0Timo Sirainen struct dict_transaction_context *dt;
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen uint64_t value;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (ctx->recalculate) {
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen if (dict_quota_count(root, TRUE, &value) < 0)
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen return -1;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen } else {
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen dt = dict_transaction_begin(root->dict);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (ctx->bytes_used != 0) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen dict_atomic_inc(dt, DICT_QUOTA_CURRENT_BYTES_PATH,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ctx->bytes_used);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (ctx->count_used != 0) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen dict_atomic_inc(dt, DICT_QUOTA_CURRENT_COUNT_PATH,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ctx->count_used);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen dict_transaction_commit_async(&dt, dict_quota_update_callback,
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen root);
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstruct quota_backend quota_backend_dict = {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "dict",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen dict_quota_alloc,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen dict_quota_init,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen dict_quota_deinit,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen NULL,
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen NULL,
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen NULL,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen dict_quota_root_get_resources,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen dict_quota_get_resource,
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen dict_quota_update,
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen NULL
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen};