bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
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"
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenextern struct quota_backend quota_backend_dict;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic struct quota_root *dict_quota_alloc(void)
1f166c4a7498b4b6bdf6f072edeaebb388cc53ebSergey Kitovstatic void handle_nounset_param(struct quota_root *_root, const char *param_value ATTR_UNUSED)
1f166c4a7498b4b6bdf6f072edeaebb388cc53ebSergey Kitov ((struct dict_quota_root *)_root)->disable_unset = TRUE;
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainenstatic int dict_quota_init(struct quota_root *_root, const char *args,
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen const char **error_r)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct dict_quota_root *root = (struct dict_quota_root *)_root;
1f166c4a7498b4b6bdf6f072edeaebb388cc53ebSergey Kitov const struct quota_param_parser dict_params[] = {
1f166c4a7498b4b6bdf6f072edeaebb388cc53ebSergey Kitov {.param_name = "no-unset", .param_handler = handle_nounset_param},
1f166c4a7498b4b6bdf6f072edeaebb388cc53ebSergey Kitov quota_param_hidden, quota_param_ignoreunlimited, quota_param_noenforcing, quota_param_ns,
fde14422caabc3c4ac4a6c5e3e5cf176cedd90a6Timo Sirainen if (quota_parse_parameters(_root, &args, error_r, dict_params, FALSE) < 0)
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk i_debug("dict quota: user=%s, uri=%s, noenforcing=%d",
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen /* FIXME: we should use 64bit integer as datatype instead but before
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen it can actually be used don't bother */
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)
20e04227229970d148801c507946666e2a9bd838Timo Sirainen if (dict_init(args, &set, &root->dict, &error) < 0) {
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen *error_r = t_strdup_printf("dict_init(%s) failed: %s", args, error);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic void dict_quota_deinit(struct quota_root *_root)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct dict_quota_root *root = (struct dict_quota_root *)_root;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic const char *const *
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainendict_quota_root_get_resources(struct quota_root *root ATTR_UNUSED)
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen QUOTA_NAME_STORAGE_KILOBYTES, QUOTA_NAME_MESSAGES, NULL
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi if (quota_count(&root->root, &bytes, &count, &error_res, error_r) < 0)
5729882717902b5f3f5d62f71ddf2894b67fc7a6Martti Rannanjärvi /* these unsets are mainly necessary for pgsql, because its
5729882717902b5f3f5d62f71ddf2894b67fc7a6Martti Rannanjärvi trigger otherwise increases quota without deleting it.
5729882717902b5f3f5d62f71ddf2894b67fc7a6Martti Rannanjärvi but some people with other databases want to store the
5729882717902b5f3f5d62f71ddf2894b67fc7a6Martti Rannanjärvi quota usage among other data in the same row, which
5729882717902b5f3f5d62f71ddf2894b67fc7a6Martti Rannanjärvi shouldn't be deleted. */
5729882717902b5f3f5d62f71ddf2894b67fc7a6Martti Rannanjärvi dict_unset(dt, DICT_QUOTA_CURRENT_BYTES_PATH);
5729882717902b5f3f5d62f71ddf2894b67fc7a6Martti Rannanjärvi dict_unset(dt, DICT_QUOTA_CURRENT_COUNT_PATH);
5729882717902b5f3f5d62f71ddf2894b67fc7a6Martti Rannanjärvi dict_set(dt, DICT_QUOTA_CURRENT_BYTES_PATH, dec2str(bytes));
5729882717902b5f3f5d62f71ddf2894b67fc7a6Martti Rannanjärvi dict_set(dt, DICT_QUOTA_CURRENT_COUNT_PATH, dec2str(count));
1bb7fb04a0583f0d5160706f24b2df08d31ada46Timo Sirainen "count=%"PRIu64" bytes=%"PRIu64, count, bytes);
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen dict_transaction_commit_async(&dt, NULL, NULL);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainendict_quota_get_resource(struct quota_root *_root,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct dict_quota_root *root = (struct dict_quota_root *)_root;
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0)
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0)
1379bb74c2855aaf3415fdfe965164a44ac3c001Martti Rannanjärvi *error_r = QUOTA_UNKNOWN_RESOURCE_ERROR_STRING;
1379bb74c2855aaf3415fdfe965164a44ac3c001Martti Rannanjärvi return QUOTA_GET_RESULT_UNKNOWN_RESOURCE;
5729882717902b5f3f5d62f71ddf2894b67fc7a6Martti Rannanjärvi key = want_bytes ? DICT_QUOTA_CURRENT_BYTES_PATH :
5729882717902b5f3f5d62f71ddf2894b67fc7a6Martti Rannanjärvi ret = dict_lookup(root->dict, unsafe_data_stack_pool,
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi "dict_lookup(%s) failed: %s", key, error);
995c0a88e9a32e0ec1460567ce5f2ce6e7ba1f13Martti Rannanjärvi /* recalculate quota if it's negative or if it wasn't found */
995c0a88e9a32e0ec1460567ce5f2ce6e7ba1f13Martti Rannanjärvi if (ret == 0 || str_to_intmax(value, &tmp) < 0)
4f52de745cf26ee9dcbde7ca4500d442116226cfMartti Rannanjärvi return dict_quota_count(root, want_bytes, value_r, error_r);
54a8bb6e9b852d9a96a8cdda1bb55a85ce0e10daTimo Sirainenstatic void dict_quota_recalc_timeout(struct dict_quota_root *root)
4f52de745cf26ee9dcbde7ca4500d442116226cfMartti Rannanjärvi if (dict_quota_count(root, TRUE, &value, &error)
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi i_error("quota-dict: Recalculation failed: %s", error);
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainenstatic void dict_quota_update_callback(const struct dict_commit_result *result,
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen /* row doesn't exist, need to recalculate it */
54a8bb6e9b852d9a96a8cdda1bb55a85ce0e10daTimo Sirainen root->to_update = timeout_add_short(0, dict_quota_recalc_timeout, root);
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen i_error("dict quota: Quota update failed: %s "
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct dict_quota_root *root = (struct dict_quota_root *) _root;
39dea5f2e78f6bfc3adc0655176f596ee211938fTimo Sirainen if (ctx->recalculate != QUOTA_RECALCULATE_DONT) {
4f52de745cf26ee9dcbde7ca4500d442116226cfMartti Rannanjärvi if (dict_quota_count(root, TRUE, &value, error_r)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen dict_atomic_inc(dt, DICT_QUOTA_CURRENT_BYTES_PATH,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen dict_atomic_inc(dt, DICT_QUOTA_CURRENT_COUNT_PATH,
02d91785bcf42ced46080db91c29bb534fbe2d1cTimo Sirainen dict_transaction_commit_async(&dt, dict_quota_update_callback,
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainenstatic void dict_quota_flush(struct quota_root *_root)
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen struct dict_quota_root *root = (struct dict_quota_root *)_root;