quota-imapc.c revision e83a11eb1dc0dea31754396e1d07c5205d810104
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen#include "lib.h"
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen#include "array.h"
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen#include "ioloop.h"
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen#include "imap-arg.h"
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen#include "imapc-storage.h"
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen#include "mailbox-list-private.h"
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen#include "quota-private.h"
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainenstruct imapc_quota_refresh_root {
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen const char *name;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen unsigned int order;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen uint64_t bytes_cur, count_cur;
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen uint64_t bytes_limit, count_limit;
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen};
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainenstruct imapc_quota_refresh {
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen pool_t pool;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen const char *box_name;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen ARRAY(struct imapc_quota_refresh_root) roots;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen};
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainenstruct imapc_quota_root {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen struct quota_root root;
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen const char *box_name, *root_name;
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen struct mail_namespace *imapc_ns;
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen struct imapc_storage_client *client;
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen bool initialized;
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen uint64_t bytes_last, count_last;
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen struct timeval last_refresh;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen struct imapc_quota_refresh refresh;
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen};
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainenextern struct quota_backend quota_backend_imapc;
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainenstatic struct quota_root *imapc_quota_alloc(void)
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen{
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen struct imapc_quota_root *root;
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen root = i_new(struct imapc_quota_root, 1);
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return &root->root;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen}
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainenstatic void handle_box_param(struct quota_root *_root, const char *param_value)
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen{
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen ((struct imapc_quota_root *)_root)->box_name = p_strdup(_root->pool, param_value);
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen}
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainenstatic void handle_root_param(struct quota_root *_root, const char *param_value)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen ((struct imapc_quota_root *)_root)->root_name = p_strdup(_root->pool, param_value);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen
31257b47d47510ceb093a6b218810a1a5b830c55Timo Sirainenstatic int imapc_quota_init(struct quota_root *_root, const char *args,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen const char **error_r)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen{
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct imapc_quota_root *root = (struct imapc_quota_root *)_root;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen const struct quota_param_parser imapc_params[] = {
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen {.param_name = "box=", .param_handler = handle_box_param},
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen {.param_name = "root=", .param_handler = handle_root_param},
31257b47d47510ceb093a6b218810a1a5b830c55Timo Sirainen quota_param_ns,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen {.param_name = NULL}
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen };
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen
4073f0dbf3277f981a8fcee3b89ea15aaf380a7fTimo Sirainen _root->auto_updating = TRUE;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen if (quota_parse_parameters(_root, &args, error_r, imapc_params, TRUE) < 0)
9844b5359f5cab77e4c31a7ac9e4a60a0073929eTimo Sirainen return -1;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
080a75584cfbe21ffd2d23c6bbb4cd8fdfd0990cTimo Sirainen if (root->box_name == NULL && root->root_name == NULL)
080a75584cfbe21ffd2d23c6bbb4cd8fdfd0990cTimo Sirainen root->box_name = "INBOX";
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* we'll never try to enforce the quota - it's just a lot of
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen unnecessary remote GETQUOTA calls. */
14cdb8cbe37616464c965aec90a8494b339db538Timo Sirainen _root->no_enforcing = TRUE;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return 0;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen}
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainenstatic void imapc_quota_deinit(struct quota_root *_root)
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen{
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen i_free(_root);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen}
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainenstatic void
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainenimapc_quota_root_namespace_added(struct quota_root *_root,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen struct mail_namespace *ns)
4073f0dbf3277f981a8fcee3b89ea15aaf380a7fTimo Sirainen{
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen struct imapc_quota_root *root = (struct imapc_quota_root *)_root;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (root->imapc_ns == NULL)
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen root->imapc_ns = ns;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen}
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenstatic struct imapc_quota_refresh *
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainenimapc_quota_root_refresh_find(struct imapc_storage_client *client)
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen{
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen struct imapc_storage *storage = client->_storage;
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen struct quota *quota;
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen struct quota_root *const *rootp;
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen i_assert(storage != NULL);
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen quota = quota_get_mail_user_quota(storage->storage.user);
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen i_assert(quota != NULL);
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen /* find the quota root that is being refreshed */
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen array_foreach(&quota->roots, rootp) {
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen if ((*rootp)->backend.name == quota_backend_imapc.name) {
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen struct imapc_quota_root *root =
f8ead0942a9b7c8fcf91414ed1b534d5807ca555Timo Sirainen (struct imapc_quota_root *)*rootp;
f8ead0942a9b7c8fcf91414ed1b534d5807ca555Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen if (root->refresh.pool != NULL)
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen return &root->refresh;
f8ead0942a9b7c8fcf91414ed1b534d5807ca555Timo Sirainen }
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen }
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return NULL;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen}
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainenstatic struct imapc_quota_refresh_root *
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainenimapc_quota_refresh_root_get(struct imapc_quota_refresh *refresh,
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen const char *root_name)
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen{
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen struct imapc_quota_refresh_root *refresh_root;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen array_foreach_modifiable(&refresh->roots, refresh_root) {
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen if (strcmp(refresh_root->name, root_name) == 0)
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen return refresh_root;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen }
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen refresh_root = array_append_space(&refresh->roots);
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen refresh_root->order = UINT_MAX;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen refresh_root->name = p_strdup(refresh->pool, root_name);
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen refresh_root->bytes_limit = (uint64_t)-1;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen refresh_root->count_limit = (uint64_t)-1;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen return refresh_root;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen}
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainenstatic void imapc_untagged_quotaroot(const struct imapc_untagged_reply *reply,
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen struct imapc_storage_client *client)
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen{
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen struct imapc_quota_refresh *refresh;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen struct imapc_quota_refresh_root *refresh_root;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen const char *mailbox_name, *root_name;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen unsigned int i;
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainen
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainen if (!imap_arg_get_astring(&reply->args[0], &mailbox_name))
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return;
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen if ((refresh = imapc_quota_root_refresh_find(client)) == NULL ||
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen refresh->box_name == NULL ||
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen strcmp(refresh->box_name, mailbox_name) != 0) {
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainen /* unsolicited QUOTAROOT reply - ignore */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return;
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen }
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen if (array_count(&refresh->roots) > 0) {
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen /* duplicate QUOTAROOT reply - ignore */
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen return;
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen }
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen i = 1;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen while (imap_arg_get_astring(&reply->args[i], &root_name)) {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen refresh_root = imapc_quota_refresh_root_get(refresh, root_name);
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen refresh_root->order = i;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen i++;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen }
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen}
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainenstatic void imapc_untagged_quota(const struct imapc_untagged_reply *reply,
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen struct imapc_storage_client *client)
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen{
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen const struct imap_arg *list;
d3fce898d31ad40b554c91f3035a7f4d7d52ed52Timo Sirainen struct imapc_quota_refresh *refresh;
d3fce898d31ad40b554c91f3035a7f4d7d52ed52Timo Sirainen struct imapc_quota_refresh_root *refresh_root;
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen const char *root_name, *resource, *value_str, *limit_str;
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen uint64_t value, limit;
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen unsigned int i;
d3fce898d31ad40b554c91f3035a7f4d7d52ed52Timo Sirainen
d3fce898d31ad40b554c91f3035a7f4d7d52ed52Timo Sirainen if (!imap_arg_get_astring(&reply->args[0], &root_name) ||
d3fce898d31ad40b554c91f3035a7f4d7d52ed52Timo Sirainen !imap_arg_get_list(&reply->args[1], &list))
d3fce898d31ad40b554c91f3035a7f4d7d52ed52Timo Sirainen return;
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen if ((refresh = imapc_quota_root_refresh_find(client)) == NULL) {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* unsolicited QUOTA reply - ignore */
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen return;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen }
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen refresh_root = imapc_quota_refresh_root_get(refresh, root_name);
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen for (i = 0; list[i].type != IMAP_ARG_EOL; i += 3) {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen if (!imap_arg_get_atom(&list[i], &resource) ||
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen !imap_arg_get_atom(&list[i+1], &value_str) ||
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen !imap_arg_get_atom(&list[i+2], &limit_str) ||
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* RFC2087 uses 32bit number, but be ready for future */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen str_to_uint64(value_str, &value) < 0 ||
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen str_to_uint64(limit_str, &limit) < 0)
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen return;
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen if (strcasecmp(resource, QUOTA_NAME_STORAGE_KILOBYTES) == 0) {
2be7df5df08ac4639ad83559ec5fcf552c84fb4aTimo Sirainen refresh_root->bytes_cur = value * 1024;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen refresh_root->bytes_limit = limit * 1024;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen } else if (strcasecmp(resource, QUOTA_NAME_MESSAGES) == 0) {
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen refresh_root->count_cur = value;
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen refresh_root->count_limit = limit;
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen }
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen }
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen}
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainenstatic bool imapc_quota_client_init(struct imapc_quota_root *root)
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen{
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen struct mailbox_list *list;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen struct mail_storage *storage;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen if (root->initialized)
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen return root->client != NULL;
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen root->initialized = TRUE;
2be7df5df08ac4639ad83559ec5fcf552c84fb4aTimo Sirainen
52de9d38fcad80df481667bac821cb8222785fe6Timo Sirainen list = root->imapc_ns->list;
2be7df5df08ac4639ad83559ec5fcf552c84fb4aTimo Sirainen if (mailbox_list_get_storage(&list, "", &storage) == 0 &&
2be7df5df08ac4639ad83559ec5fcf552c84fb4aTimo Sirainen strcmp(storage->name, IMAPC_STORAGE_NAME) != 0) {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* non-imapc namespace, skip */
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen if ((storage->class_flags &
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen MAIL_STORAGE_CLASS_FLAG_NOQUOTA) == 0) {
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen i_warning("quota: Namespace '%s' is not imapc, "
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen "skipping for imapc quota",
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen root->imapc_ns->prefix);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen }
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen return FALSE;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen }
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen root->client = ((struct imapc_storage *)storage)->client;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen imapc_storage_client_register_untagged(root->client, "QUOTAROOT",
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen imapc_untagged_quotaroot);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen imapc_storage_client_register_untagged(root->client, "QUOTA",
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen imapc_untagged_quota);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen return TRUE;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen}
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenstatic void imapc_quota_refresh_init(struct imapc_quota_refresh *refresh)
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen{
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen i_assert(refresh->pool == NULL);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen refresh->pool = pool_alloconly_create("imapc quota refresh", 256);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen p_array_init(&refresh->roots, refresh->pool, 4);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen}
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenstatic void
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenimapc_quota_refresh_update(struct quota *quota,
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen struct imapc_quota_refresh *refresh)
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen{
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen struct quota_root *const *rootp;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen const struct imapc_quota_refresh_root *refresh_root;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen if (array_count(&refresh->roots) == 0) {
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen i_error("quota: imapc didn't return any QUOTA results");
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen return;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen }
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen /* use the first quota root for everything */
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen refresh_root = array_idx(&refresh->roots, 0);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen array_foreach(&quota->roots, rootp) {
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen if ((*rootp)->backend.name == quota_backend_imapc.name) {
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen struct imapc_quota_root *root =
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen (struct imapc_quota_root *)*rootp;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen root->bytes_last = refresh_root->bytes_cur;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen root->count_last = refresh_root->count_cur;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen root->root.bytes_limit = refresh_root->bytes_limit;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen root->root.count_limit = refresh_root->count_limit;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen }
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen }
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen}
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenstatic void
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenimapc_quota_refresh_deinit(struct quota *quota,
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen struct imapc_quota_refresh *refresh, bool success)
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen{
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen if (success)
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen imapc_quota_refresh_update(quota, refresh);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen pool_unref(&refresh->pool);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen i_zero(refresh);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen}
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenstatic int
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenimapc_quota_refresh_root_order_cmp(const struct imapc_quota_refresh_root *root1,
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen const struct imapc_quota_refresh_root *root2)
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen{
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen if (root1->order < root2->order)
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen return -1;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen else if (root1->order > root2->order)
return 1;
else
return 0;
}
static int imapc_quota_refresh_mailbox(struct imapc_quota_root *root)
{
struct imapc_simple_context sctx;
struct imapc_command *cmd;
i_assert(root->box_name != NULL);
/* ask quotas for the configured mailbox */
imapc_quota_refresh_init(&root->refresh);
root->refresh.box_name = root->box_name;
imapc_simple_context_init(&sctx, root->client);
cmd = imapc_client_cmd(root->client->client,
imapc_simple_callback, &sctx);
imapc_command_sendf(cmd, "GETQUOTAROOT %s", root->box_name);
imapc_simple_run(&sctx);
/* if there are multiple quota roots, use the first one returned by
the QUOTAROOT */
array_sort(&root->refresh.roots, imapc_quota_refresh_root_order_cmp);
imapc_quota_refresh_deinit(root->root.quota, &root->refresh,
sctx.ret == 0);
return sctx.ret;
}
static int imapc_quota_refresh_root(struct imapc_quota_root *root)
{
struct imapc_simple_context sctx;
struct imapc_command *cmd;
i_assert(root->root_name != NULL);
/* ask quotas for the configured quota root */
imapc_quota_refresh_init(&root->refresh);
imapc_simple_context_init(&sctx, root->client);
cmd = imapc_client_cmd(root->client->client,
imapc_simple_callback, &sctx);
imapc_command_sendf(cmd, "GETQUOTA %s", root->root_name);
imapc_simple_run(&sctx);
/* there shouldn't be more than one QUOTA reply, but ignore anyway
anything we didn't expect. */
while (array_count(&root->refresh.roots) > 0) {
const struct imapc_quota_refresh_root *refresh_root =
array_idx(&root->refresh.roots, 0);
if (strcmp(refresh_root->name, root->root_name) == 0)
break;
array_delete(&root->refresh.roots, 0, 1);
}
imapc_quota_refresh_deinit(root->root.quota, &root->refresh,
sctx.ret == 0);
return sctx.ret;
}
static int imapc_quota_refresh(struct imapc_quota_root *root)
{
enum imapc_capability capa;
int ret;
if (root->imapc_ns == NULL) {
/* imapc namespace is missing - disable this quota backend */
return 0;
}
if (root->last_refresh.tv_sec == ioloop_timeval.tv_sec &&
root->last_refresh.tv_usec == ioloop_timeval.tv_usec)
return 0;
if (!imapc_quota_client_init(root))
return 0;
if (imapc_client_get_capabilities(root->client->client, &capa) < 0)
return -1;
if ((capa & IMAPC_CAPABILITY_QUOTA) == 0) {
/* no QUOTA capability - disable quota */
i_warning("quota: Remote IMAP server doesn't support QUOTA - disabling");
root->client = NULL;
return 0;
}
if (root->root_name == NULL)
ret = imapc_quota_refresh_mailbox(root);
else
ret = imapc_quota_refresh_root(root);
/* set the last_refresh only after the refresh, because it changes
ioloop_timeval. */
root->last_refresh = ioloop_timeval;
return ret;
}
static int imapc_quota_init_limits(struct quota_root *_root,
const char **error_r)
{
struct imapc_quota_root *root = (struct imapc_quota_root *)_root;
int ret = imapc_quota_refresh(root);
if (ret < 0)
*error_r = "Failed to get quota data from remote imap server";
return ret;
}
static void
imapc_quota_namespace_added(struct quota *quota, struct mail_namespace *ns)
{
struct quota_root **roots;
unsigned int i, count;
roots = array_get_modifiable(&quota->roots, &count);
for (i = 0; i < count; i++) {
if (roots[i]->backend.name == quota_backend_imapc.name &&
((roots[i]->ns_prefix == NULL &&
ns->type == MAIL_NAMESPACE_TYPE_PRIVATE) ||
roots[i]->ns == ns))
imapc_quota_root_namespace_added(roots[i], ns);
}
}
static const char *const *
imapc_quota_root_get_resources(struct quota_root *root ATTR_UNUSED)
{
static const char *resources_both[] = {
QUOTA_NAME_STORAGE_KILOBYTES,
QUOTA_NAME_MESSAGES,
NULL
};
return resources_both;
}
static int
imapc_quota_get_resource(struct quota_root *_root, const char *name,
uint64_t *value_r)
{
struct imapc_quota_root *root = (struct imapc_quota_root *)_root;
if (imapc_quota_refresh(root) < 0)
return -1;
if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0)
*value_r = root->bytes_last;
else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0)
*value_r = root->count_last;
else
return 0;
return 1;
}
static int
imapc_quota_update(struct quota_root *root ATTR_UNUSED,
struct quota_transaction_context *ctx ATTR_UNUSED)
{
return 0;
}
struct quota_backend quota_backend_imapc = {
.name = "imapc",
.v = {
.alloc = imapc_quota_alloc,
.init = imapc_quota_init,
.deinit = imapc_quota_deinit,
.init_limits = imapc_quota_init_limits,
.namespace_added = imapc_quota_namespace_added,
.get_resources = imapc_quota_root_get_resources,
.get_resource = imapc_quota_get_resource,
.update = imapc_quota_update,
}
};