bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenextern struct quota_backend quota_backend_imapc;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenstatic struct quota_root *imapc_quota_alloc(void)
bbd51f171bb2f4abd437910c720579a4eaf65c85Sergey Kitovstatic void handle_box_param(struct quota_root *_root, const char *param_value)
bbd51f171bb2f4abd437910c720579a4eaf65c85Sergey Kitov ((struct imapc_quota_root *)_root)->box_name = p_strdup(_root->pool, param_value);
bbd51f171bb2f4abd437910c720579a4eaf65c85Sergey Kitovstatic void handle_root_param(struct quota_root *_root, const char *param_value)
bbd51f171bb2f4abd437910c720579a4eaf65c85Sergey Kitov ((struct imapc_quota_root *)_root)->root_name = p_strdup(_root->pool, param_value);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenstatic int imapc_quota_init(struct quota_root *_root, const char *args,
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen const char **error_r)
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen struct imapc_quota_root *root = (struct imapc_quota_root *)_root;
bbd51f171bb2f4abd437910c720579a4eaf65c85Sergey Kitov const struct quota_param_parser imapc_params[] = {
bbd51f171bb2f4abd437910c720579a4eaf65c85Sergey Kitov {.param_name = "box=", .param_handler = handle_box_param},
bbd51f171bb2f4abd437910c720579a4eaf65c85Sergey Kitov {.param_name = "root=", .param_handler = handle_root_param},
fde14422caabc3c4ac4a6c5e3e5cf176cedd90a6Timo Sirainen if (quota_parse_parameters(_root, &args, error_r, imapc_params, TRUE) < 0)
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if (root->box_name == NULL && root->root_name == NULL)
3abc26e06e053228d283503a8583657dfca0f2e0Timo Sirainen /* we'll never try to enforce the quota - it's just a lot of
3abc26e06e053228d283503a8583657dfca0f2e0Timo Sirainen unnecessary remote GETQUOTA calls. */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenstatic void imapc_quota_deinit(struct quota_root *_root)
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenimapc_quota_root_namespace_added(struct quota_root *_root,
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen struct imapc_quota_root *root = (struct imapc_quota_root *)_root;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenimapc_quota_root_refresh_find(struct imapc_storage_client *client)
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen struct imapc_storage *storage = client->_storage;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen quota = quota_get_mail_user_quota(storage->storage.user);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen /* find the quota root that is being refreshed */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if ((*rootp)->backend.name == quota_backend_imapc.name) {
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenimapc_quota_refresh_root_get(struct imapc_quota_refresh *refresh,
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen struct imapc_quota_refresh_root *refresh_root;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen array_foreach_modifiable(&refresh->roots, refresh_root) {
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if (strcmp(refresh_root->name, root_name) == 0)
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen refresh_root = array_append_space(&refresh->roots);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen refresh_root->name = p_strdup(refresh->pool, root_name);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenstatic void imapc_untagged_quotaroot(const struct imapc_untagged_reply *reply,
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen struct imapc_quota_refresh_root *refresh_root;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen unsigned int i;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if (!imap_arg_get_astring(&reply->args[0], &mailbox_name))
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if ((refresh = imapc_quota_root_refresh_find(client)) == NULL ||
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen strcmp(refresh->box_name, mailbox_name) != 0) {
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen /* unsolicited QUOTAROOT reply - ignore */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen /* duplicate QUOTAROOT reply - ignore */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen while (imap_arg_get_astring(&reply->args[i], &root_name)) {
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen refresh_root = imapc_quota_refresh_root_get(refresh, root_name);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenstatic void imapc_untagged_quota(const struct imapc_untagged_reply *reply,
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen struct imapc_quota_refresh_root *refresh_root;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen const char *root_name, *resource, *value_str, *limit_str;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen unsigned int i;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if (!imap_arg_get_astring(&reply->args[0], &root_name) ||
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if ((refresh = imapc_quota_root_refresh_find(client)) == NULL) {
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen /* unsolicited QUOTA reply - ignore */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen refresh_root = imapc_quota_refresh_root_get(refresh, root_name);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen for (i = 0; list[i].type != IMAP_ARG_EOL; i += 3) {
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if (!imap_arg_get_atom(&list[i], &resource) ||
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen /* RFC2087 uses 32bit number, but be ready for future */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if (strcasecmp(resource, QUOTA_NAME_STORAGE_KILOBYTES) == 0) {
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen } else if (strcasecmp(resource, QUOTA_NAME_MESSAGES) == 0) {
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenstatic bool imapc_quota_client_init(struct imapc_quota_root *root)
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if (mailbox_list_get_storage(&list, "", &storage) == 0 &&
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen strcmp(storage->name, IMAPC_STORAGE_NAME) != 0) {
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen /* non-imapc namespace, skip */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen i_warning("quota: Namespace '%s' is not imapc, "
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen "skipping for imapc quota",
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen root->client = ((struct imapc_storage *)storage)->client;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen imapc_storage_client_register_untagged(root->client, "QUOTAROOT",
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen imapc_storage_client_register_untagged(root->client, "QUOTA",
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenstatic void imapc_quota_refresh_init(struct imapc_quota_refresh *refresh)
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen refresh->pool = pool_alloconly_create("imapc quota refresh", 256);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen p_array_init(&refresh->roots, refresh->pool, 4);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenimapc_quota_refresh_update(struct quota *quota,
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen const struct imapc_quota_refresh_root *refresh_root;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen i_error("quota: imapc didn't return any QUOTA results");
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen /* use the first quota root for everything */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if ((*rootp)->backend.name == quota_backend_imapc.name) {
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen root->root.bytes_limit = refresh_root->bytes_limit;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen root->root.count_limit = refresh_root->count_limit;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenimapc_quota_refresh_deinit(struct quota *quota,
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen struct imapc_quota_refresh *refresh, bool success)
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenimapc_quota_refresh_root_order_cmp(const struct imapc_quota_refresh_root *root1,
b82a5c78f10209abb99df1d203600d2d82492331Martti Rannanjärvistatic int imapc_quota_refresh_mailbox(struct imapc_quota_root *root,
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen /* ask quotas for the configured mailbox */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen imapc_simple_context_init(&sctx, root->client);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen imapc_command_sendf(cmd, "GETQUOTAROOT %s", root->box_name);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen /* if there are multiple quota roots, use the first one returned by
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen the QUOTAROOT */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen array_sort(&root->refresh.roots, imapc_quota_refresh_root_order_cmp);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen imapc_quota_refresh_deinit(root->root.quota, &root->refresh,
b82a5c78f10209abb99df1d203600d2d82492331Martti Rannanjärvi "GETQUOTAROOT %s failed: %s",
b82a5c78f10209abb99df1d203600d2d82492331Martti Rannanjärvi &root->client->_storage->storage, NULL));
b82a5c78f10209abb99df1d203600d2d82492331Martti Rannanjärvistatic int imapc_quota_refresh_root(struct imapc_quota_root *root,
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen /* ask quotas for the configured quota root */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen imapc_simple_context_init(&sctx, root->client);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen imapc_command_sendf(cmd, "GETQUOTA %s", root->root_name);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen /* there shouldn't be more than one QUOTA reply, but ignore anyway
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen anything we didn't expect. */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen while (array_count(&root->refresh.roots) > 0) {
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen const struct imapc_quota_refresh_root *refresh_root =
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if (strcmp(refresh_root->name, root->root_name) == 0)
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen imapc_quota_refresh_deinit(root->root.quota, &root->refresh,
b82a5c78f10209abb99df1d203600d2d82492331Martti Rannanjärvi "GETQUOTA %s failed: %s",
b82a5c78f10209abb99df1d203600d2d82492331Martti Rannanjärvi &root->client->_storage->storage, NULL));
b82a5c78f10209abb99df1d203600d2d82492331Martti Rannanjärvistatic int imapc_quota_refresh(struct imapc_quota_root *root,
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen /* imapc namespace is missing - disable this quota backend */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if (root->last_refresh.tv_sec == ioloop_timeval.tv_sec &&
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen root->last_refresh.tv_usec == ioloop_timeval.tv_usec)
b82a5c78f10209abb99df1d203600d2d82492331Martti Rannanjärvi if (imapc_client_get_capabilities(root->client->client, &capa) < 0) {
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi *error_r = "Failed to get server capabilities";
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen /* no QUOTA capability - disable quota */
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen i_warning("quota: Remote IMAP server doesn't support QUOTA - disabling");
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi ret = imapc_quota_refresh_mailbox(root, error_r);
12d7f667b636405ae54dcc4cb3031f4ba52aff04Martti Rannanjärvi ret = imapc_quota_refresh_root(root, error_r);
dee1520b5166708bd8dd4230c279c950bf01029aTimo Sirainen /* set the last_refresh only after the refresh, because it changes
dee1520b5166708bd8dd4230c279c950bf01029aTimo Sirainen ioloop_timeval. */
e83a11eb1dc0dea31754396e1d07c5205d810104Martti Rannanjärvistatic int imapc_quota_init_limits(struct quota_root *_root,
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen struct imapc_quota_root *root = (struct imapc_quota_root *)_root;
b82a5c78f10209abb99df1d203600d2d82492331Martti Rannanjärvi return imapc_quota_refresh(root, error_r);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenimapc_quota_namespace_added(struct quota *quota, struct mail_namespace *ns)
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen roots = array_get_modifiable("a->roots, &count);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen for (i = 0; i < count; i++) {
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if (roots[i]->backend.name == quota_backend_imapc.name &&
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen imapc_quota_root_namespace_added(roots[i], ns);
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenstatic const char *const *
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenimapc_quota_root_get_resources(struct quota_root *root ATTR_UNUSED)
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenimapc_quota_get_resource(struct quota_root *_root, const char *name,
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen struct imapc_quota_root *root = (struct imapc_quota_root *)_root;
b82a5c78f10209abb99df1d203600d2d82492331Martti Rannanjärvi if (imapc_quota_refresh(root, error_r) < 0)
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainen if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0)
654c60f1741fd195878d74a30df90bf130649d64Timo 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;
654c60f1741fd195878d74a30df90bf130649d64Timo Sirainenimapc_quota_update(struct quota_root *root ATTR_UNUSED,
7b3b617e946d5b32078baa821f5fc05f775e1dfeMartti Rannanjärvi struct quota_transaction_context *ctx ATTR_UNUSED,
5997118fa7aee2535edac28092261ca085a958aeMartti Rannanjärvi .namespace_added = imapc_quota_namespace_added,
5997118fa7aee2535edac28092261ca085a958aeMartti Rannanjärvi .get_resources = imapc_quota_root_get_resources,