summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/index/index-attribute.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
commitf7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch)
treea3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-storage/index/index-attribute.c
parentInitial commit. (diff)
downloaddovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz
dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-storage/index/index-attribute.c')
-rw-r--r--src/lib-storage/index/index-attribute.c333
1 files changed, 333 insertions, 0 deletions
diff --git a/src/lib-storage/index/index-attribute.c b/src/lib-storage/index/index-attribute.c
new file mode 100644
index 0000000..3d4c415
--- /dev/null
+++ b/src/lib-storage/index/index-attribute.c
@@ -0,0 +1,333 @@
+/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "dict.h"
+#include "index-storage.h"
+
+struct index_storage_attribute_iter {
+ struct mailbox_attribute_iter iter;
+ struct dict_iterate_context *diter;
+ char *prefix;
+ size_t prefix_len;
+ bool dict_disabled;
+};
+
+static struct mail_namespace *
+mail_user_find_attribute_namespace(struct mail_user *user)
+{
+ struct mail_namespace *ns;
+
+ ns = mail_namespace_find_inbox(user->namespaces);
+ if (ns != NULL)
+ return ns;
+
+ for (ns = user->namespaces; ns != NULL; ns = ns->next) {
+ if (ns->type == MAIL_NAMESPACE_TYPE_PRIVATE)
+ return ns;
+ }
+ return NULL;
+}
+
+static int
+index_storage_get_user_dict(struct mail_storage *err_storage,
+ struct mail_user *user, struct dict **dict_r)
+{
+ struct dict_settings dict_set;
+ struct mail_namespace *ns;
+ struct mail_storage *attr_storage;
+ const char *error;
+
+ if (user->_attr_dict != NULL) {
+ *dict_r = user->_attr_dict;
+ return 0;
+ }
+ if (user->attr_dict_failed) {
+ mail_storage_set_internal_error(err_storage);
+ return -1;
+ }
+
+ ns = mail_user_find_attribute_namespace(user);
+ if (ns == NULL) {
+ /* probably never happens? */
+ mail_storage_set_error(err_storage, MAIL_ERROR_NOTPOSSIBLE,
+ "Mailbox attributes not available for this mailbox");
+ return -1;
+ }
+ attr_storage = mail_namespace_get_default_storage(ns);
+
+ if (*attr_storage->set->mail_attribute_dict == '\0') {
+ mail_storage_set_error(err_storage, MAIL_ERROR_NOTPOSSIBLE,
+ "Mailbox attributes not enabled");
+ return -1;
+ }
+
+ i_zero(&dict_set);
+ dict_set.base_dir = user->set->base_dir;
+ dict_set.event_parent = user->event;
+ if (dict_init(attr_storage->set->mail_attribute_dict, &dict_set,
+ &user->_attr_dict, &error) < 0) {
+ mail_storage_set_critical(err_storage,
+ "mail_attribute_dict: dict_init(%s) failed: %s",
+ attr_storage->set->mail_attribute_dict, error);
+ user->attr_dict_failed = TRUE;
+ return -1;
+ }
+ *dict_r = user->_attr_dict;
+ return 0;
+}
+
+static int
+index_storage_get_dict(struct mailbox *box, enum mail_attribute_type type_flags,
+ struct dict **dict_r, const char **mailbox_prefix_r)
+{
+ enum mail_attribute_type type = type_flags & MAIL_ATTRIBUTE_TYPE_MASK;
+ struct mail_storage *storage = box->storage;
+ struct mail_namespace *ns;
+ struct mailbox_metadata metadata;
+ struct dict_settings set;
+ const char *error;
+
+ if ((type_flags & MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED) != 0) {
+ /* IMAP METADATA support isn't enabled, so don't allow using
+ mail_attribute_dict. */
+ mail_storage_set_error(storage, MAIL_ERROR_NOTPOSSIBLE,
+ "Generic mailbox attributes not enabled");
+ return -1;
+ }
+
+ if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0)
+ return -1;
+ *mailbox_prefix_r = guid_128_to_string(metadata.guid);
+
+ ns = mailbox_get_namespace(box);
+ if (type == MAIL_ATTRIBUTE_TYPE_PRIVATE) {
+ /* private attributes are stored in user's own dict */
+ return index_storage_get_user_dict(storage, storage->user, dict_r);
+ } else if (ns->user == ns->owner) {
+ /* user owns the mailbox. shared attributes are stored in
+ the same dict. */
+ return index_storage_get_user_dict(storage, storage->user, dict_r);
+ } else if (ns->owner != NULL) {
+ /* accessing shared attribute of a shared mailbox.
+ use the owner's dict. */
+ return index_storage_get_user_dict(storage, ns->owner, dict_r);
+ }
+
+ /* accessing shared attributes of a public mailbox. no user owns it,
+ so use the storage's dict. */
+ if (storage->_shared_attr_dict != NULL) {
+ *dict_r = storage->_shared_attr_dict;
+ return 0;
+ }
+ if (*storage->set->mail_attribute_dict == '\0') {
+ mail_storage_set_error(storage, MAIL_ERROR_NOTPOSSIBLE,
+ "Mailbox attributes not enabled");
+ return -1;
+ }
+ if (storage->shared_attr_dict_failed) {
+ mail_storage_set_internal_error(storage);
+ return -1;
+ }
+
+ i_zero(&set);
+ set.base_dir = storage->user->set->base_dir;
+ set.event_parent = storage->user->event;
+ if (dict_init(storage->set->mail_attribute_dict, &set,
+ &storage->_shared_attr_dict, &error) < 0) {
+ mail_storage_set_critical(storage,
+ "mail_attribute_dict: dict_init(%s) failed: %s",
+ storage->set->mail_attribute_dict, error);
+ storage->shared_attr_dict_failed = TRUE;
+ return -1;
+ }
+ *dict_r = storage->_shared_attr_dict;
+ return 0;
+}
+
+static const char *
+key_get_prefixed(enum mail_attribute_type type_flags, const char *mailbox_prefix,
+ const char *key)
+{
+ enum mail_attribute_type type = type_flags & MAIL_ATTRIBUTE_TYPE_MASK;
+
+ switch (type) {
+ case MAIL_ATTRIBUTE_TYPE_PRIVATE:
+ return t_strconcat(DICT_PATH_PRIVATE, mailbox_prefix, "/",
+ key, NULL);
+ case MAIL_ATTRIBUTE_TYPE_SHARED:
+ return t_strconcat(DICT_PATH_SHARED, mailbox_prefix, "/",
+ key, NULL);
+ }
+ i_unreached();
+}
+
+static int
+index_storage_attribute_get_dict_trans(struct mailbox_transaction_context *t,
+ enum mail_attribute_type type_flags,
+ struct dict_transaction_context **dtrans_r,
+ const char **mailbox_prefix_r)
+{
+ enum mail_attribute_type type = type_flags & MAIL_ATTRIBUTE_TYPE_MASK;
+ struct dict_transaction_context **dtransp = NULL;
+ struct dict *dict;
+ struct mailbox_metadata metadata;
+
+ switch (type) {
+ case MAIL_ATTRIBUTE_TYPE_PRIVATE:
+ dtransp = &t->attr_pvt_trans;
+ break;
+ case MAIL_ATTRIBUTE_TYPE_SHARED:
+ dtransp = &t->attr_shared_trans;
+ break;
+ }
+ i_assert(dtransp != NULL);
+
+ if (*dtransp != NULL &&
+ (type_flags & MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED) == 0) {
+ /* Transaction already created. Even if it was, don't use it
+ if _FLAG_VALIDATED is being used. It'll be handled below by
+ returning failure. */
+ if (mailbox_get_metadata(t->box, MAILBOX_METADATA_GUID,
+ &metadata) < 0)
+ return -1;
+ *mailbox_prefix_r = guid_128_to_string(metadata.guid);
+ *dtrans_r = *dtransp;
+ return 0;
+ }
+
+ if (index_storage_get_dict(t->box, type_flags, &dict, mailbox_prefix_r) < 0)
+ return -1;
+ i_assert(*dtransp == NULL);
+
+ struct mail_user *user = mailbox_list_get_user(t->box->list);
+ const struct dict_op_settings *set = mail_user_get_dict_op_settings(user);
+ *dtransp = *dtrans_r = dict_transaction_begin(dict, set);
+ return 0;
+}
+
+int index_storage_attribute_set(struct mailbox_transaction_context *t,
+ enum mail_attribute_type type_flags,
+ const char *key,
+ const struct mail_attribute_value *value)
+{
+ enum mail_attribute_type type = type_flags & MAIL_ATTRIBUTE_TYPE_MASK;
+ struct dict_transaction_context *dtrans;
+ const char *mailbox_prefix;
+ bool pvt = type == MAIL_ATTRIBUTE_TYPE_PRIVATE;
+ time_t ts = value->last_change != 0 ? value->last_change : ioloop_time;
+ int ret = 0;
+
+ if (index_storage_attribute_get_dict_trans(t, type_flags, &dtrans,
+ &mailbox_prefix) < 0)
+ return -1;
+
+ T_BEGIN {
+ const char *prefixed_key =
+ key_get_prefixed(type_flags, mailbox_prefix, key);
+ const char *value_str;
+
+ if (mailbox_attribute_value_to_string(t->box->storage, value,
+ &value_str) < 0) {
+ ret = -1;
+ } else if (value_str != NULL) {
+ dict_set(dtrans, prefixed_key, value_str);
+ mail_index_attribute_set(t->itrans, pvt, key,
+ ts, strlen(value_str));
+ } else {
+ dict_unset(dtrans, prefixed_key);
+ mail_index_attribute_unset(t->itrans, pvt, key, ts);
+ }
+ } T_END;
+ return ret;
+}
+
+int index_storage_attribute_get(struct mailbox *box,
+ enum mail_attribute_type type_flags,
+ const char *key,
+ struct mail_attribute_value *value_r)
+{
+ struct dict *dict;
+ const char *mailbox_prefix, *error;
+ int ret;
+
+ i_zero(value_r);
+
+ if (index_storage_get_dict(box, type_flags, &dict, &mailbox_prefix) < 0)
+ return -1;
+
+ struct mail_user *user = mailbox_list_get_user(box->list);
+ const struct dict_op_settings *set = mail_user_get_dict_op_settings(user);
+ ret = dict_lookup(dict, set, pool_datastack_create(),
+ key_get_prefixed(type_flags, mailbox_prefix, key),
+ &value_r->value, &error);
+ if (ret < 0) {
+ mailbox_set_critical(box,
+ "Failed to get attribute %s: %s", key, error);
+ return -1;
+ }
+ return ret;
+}
+
+struct mailbox_attribute_iter *
+index_storage_attribute_iter_init(struct mailbox *box,
+ enum mail_attribute_type type_flags,
+ const char *prefix)
+{
+ struct index_storage_attribute_iter *iter;
+ struct dict *dict;
+ const char *mailbox_prefix;
+
+ iter = i_new(struct index_storage_attribute_iter, 1);
+ iter->iter.box = box;
+ if (index_storage_get_dict(box, type_flags, &dict, &mailbox_prefix) < 0) {
+ if (mailbox_get_last_mail_error(box) == MAIL_ERROR_NOTPOSSIBLE)
+ iter->dict_disabled = TRUE;
+ } else {
+ iter->prefix = i_strdup(key_get_prefixed(type_flags, mailbox_prefix,
+ prefix));
+ iter->prefix_len = strlen(iter->prefix);
+ struct mail_user *user = mailbox_list_get_user(box->list);
+ const struct dict_op_settings *set = mail_user_get_dict_op_settings(user);
+ iter->diter = dict_iterate_init(dict, set, iter->prefix,
+ DICT_ITERATE_FLAG_RECURSE |
+ DICT_ITERATE_FLAG_NO_VALUE);
+ }
+ return &iter->iter;
+}
+
+const char *
+index_storage_attribute_iter_next(struct mailbox_attribute_iter *_iter)
+{
+ struct index_storage_attribute_iter *iter =
+ (struct index_storage_attribute_iter *)_iter;
+ const char *key, *value;
+
+ if (iter->diter == NULL || !dict_iterate(iter->diter, &key, &value))
+ return NULL;
+
+ i_assert(strncmp(key, iter->prefix, iter->prefix_len) == 0);
+ key += iter->prefix_len;
+ return key;
+}
+
+int index_storage_attribute_iter_deinit(struct mailbox_attribute_iter *_iter)
+{
+ struct index_storage_attribute_iter *iter =
+ (struct index_storage_attribute_iter *)_iter;
+ const char *error;
+ int ret;
+
+ if (iter->diter == NULL) {
+ ret = iter->dict_disabled ? 0 : -1;
+ } else {
+ if ((ret = dict_iterate_deinit(&iter->diter, &error)) < 0) {
+ mailbox_set_critical(_iter->box,
+ "dict_iterate(%s) failed: %s",
+ iter->prefix, error);
+ }
+ }
+ i_free(iter->prefix);
+ i_free(iter);
+ return ret;
+}