summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/mailbox-attribute.h
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/mailbox-attribute.h
parentInitial commit. (diff)
downloaddovecot-upstream.tar.xz
dovecot-upstream.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 '')
-rw-r--r--src/lib-storage/mailbox-attribute.h316
1 files changed, 316 insertions, 0 deletions
diff --git a/src/lib-storage/mailbox-attribute.h b/src/lib-storage/mailbox-attribute.h
new file mode 100644
index 0000000..9a70580
--- /dev/null
+++ b/src/lib-storage/mailbox-attribute.h
@@ -0,0 +1,316 @@
+#ifndef MAILBOX_ATTRIBUTE_H
+#define MAILBOX_ATTRIBUTE_H
+
+/*
+ * Attribute Handling in Dovecot
+ * =============================
+ *
+ * What IMAP & doveadm users see gets translated into one of several things
+ * depending on if we're operating on a mailbox or on server metadata (""
+ * mailbox in IMAP parlance). Consider these examples:
+ *
+ * /private/foo
+ * /shared/foo
+ *
+ * Here "foo" can be any RFC defined attribute name, or a vendor-prefixed
+ * non-standard name. (Our vendor prefix is "vendor/vendor.dovecot".)
+ *
+ * In all cases, the "/private" and "/shared" user visible prefixes get
+ * replaced by priv/<GUID> and shared/<GUID>, respectively. (Here, <GUID>
+ * is the GUID of the mailbox with which the attribute is associated.) This
+ * way, attributes for all mailboxes can be stored in a single dict. For
+ * example, the above examples would map to:
+ *
+ * priv/<GUID>/foo
+ * shared/<GUID>/foo
+ *
+ * More concrete examples:
+ *
+ * /private/comment
+ * /private/vendor/vendor.dovecot/abc
+ *
+ * turn into:
+ *
+ * priv/<GUID>/comment
+ * priv/<GUID>/vendor/vendor.dovecot/abc
+ *
+ * Server attributes, that is attributes not associated with a mailbox, are
+ * stored in the INBOX mailbox with a special prefix -
+ * vendor/vendor.dovecot/pvt/server. For example, the server attribute
+ * /private/comment gets mapped to:
+ *
+ * priv/<INBOX GUID>/vendor/vendor.dovecot/pvt/server/comment
+ *
+ * This means that if we set a /private/comment server attribute as well as
+ * /private/comment INBOX mailbox attribute, we'll see the following paths
+ * used in the dict:
+ *
+ * priv/<INBOX GUID>/comment <- mailbox attr
+ * priv/<INBOX GUID>/vendor/vendor.dovecot/pvt/server/comment <- server attr
+ *
+ * The case of vendor specific server attributes is a bit confusing, but
+ * consistent. For example, this server attribute:
+ *
+ * /private/vendor/vendor.dovecot/abc
+ *
+ * It will get mapped to:
+ *
+ * priv/<INBOX GUID>/vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/abc
+ * | | | |
+ * \----- server attr prefix -----/ \-- server attr name ---/
+ *
+ *
+ * Internal Attributes
+ * -------------------
+ *
+ * The final aspect of attribute handling in Dovecot are the so called
+ * "internal attributes".
+ *
+ * The easiest way to explain internal attributes is to summarize attributes
+ * in general. Attributes are just <key,value> pairs that are stored in a
+ * dict. The key is mangled according to the above rules before passed to
+ * the dict code. That is, the key already encodes whether the attribute is
+ * private or shared, the GUID of the mailbox (or of INBOX for server
+ * attributes), etc. There is no processing of the value. It is stored and
+ * returned to clients verbatim.
+ *
+ * Internal attributes, on the other hand, are special cased attributes.
+ * That is, the code contains a list of specific attribute names and how to
+ * handle them. Each internal attribute is defined by a struct
+ * mailbox_attribute_internal. It contains the pre-parsed name of the
+ * attribute (type, key, and flags), and how to handle getting and setting
+ * of the attribute (rank, get, and set).
+ *
+ * The values for these attributes may come from two places - from the
+ * attributes dict, or from the get function pointer. Which source to use
+ * is identified by the rank (MAIL_ATTRIBUTE_INTERNAL_RANK_*).
+ *
+ *
+ * Access
+ * ------
+ *
+ * In general, a user (IMAP or doveadm) can access all attributes for a
+ * mailbox. The one exception are attributes under:
+ *
+ * /private/vendor/vendor.dovecot/pvt
+ * /shared/vendor/vendor.dovecot/pvt
+ *
+ * Which as you may recall map to:
+ *
+ * priv/<GUID>/vendor/vendor.dovecot/pvt
+ * shared/<GUID>/vendor/vendor.dovecot/pvt
+ *
+ * These are deemed internal to Dovecot, and therefore of no concern to the
+ * user.
+ *
+ * Server attributes have a similar restriction. That is, attributes
+ * beginning with the following are not accessible:
+ *
+ * /private/vendor/vendor.dovecot/pvt
+ * /shared/vendor/vendor.dovecot/pvt
+ *
+ * However since server attributes are stored under the INBOX mailbox, these
+ * paths map to:
+ *
+ * priv/<INBOX GUID>/vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt
+ * shared/<INBOX GUID>/vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt
+ *
+ * As a result, the code performs access checks via the
+ * MAILBOX_ATTRIBUTE_KEY_IS_USER_ACCESSIBLE() macro to make sure that the
+ * user is allowed access to the attribute.
+ *
+ *
+ * Nicknames
+ * ---------
+ *
+ * Since every path stored in the dict begins with priv/<GUID> or
+ * shared/<GUID>, these prefixes are often omitted. This also matches the
+ * internal implementation where the priv/ or shared/ prefix is specified
+ * using an enum, and only the path after the GUID is handled as a string.
+ * For example:
+ *
+ * priv/<GUID>/vendor/vendor.dovecot/pvt/server/foo
+ *
+ * would be referred to as:
+ *
+ * vendor/vendor.dovecot/pvt/server/foo
+ *
+ * Since some of the generated paths are very long, developers often use a
+ * shorthand to refer to some of these paths. For example,
+ *
+ * pvt/server/pvt
+ *
+ * is really:
+ *
+ * vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt
+ *
+ * Which when fully specified with a type and INBOX's GUID would turn into
+ * one of the following:
+ *
+ * priv/<GUID>/vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt
+ * shared/<GUID>/vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt
+ */
+
+struct mailbox;
+struct mailbox_transaction_context;
+
+/* RFC 5464 specifies that this is vendor/<vendor-token>/. The registered
+ vendor-tokens always begin with "vendor." so there's some redundancy.. */
+#define MAILBOX_ATTRIBUTE_PREFIX_DOVECOT "vendor/vendor.dovecot/"
+/* Prefix used for attributes reserved for Dovecot's internal use. Normal
+ users cannot access these in any way. */
+#define MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT \
+ MAILBOX_ATTRIBUTE_PREFIX_DOVECOT"pvt/"
+/* Server attributes are currently stored in INBOX under this private prefix.
+ They're under the pvt/ prefix so they won't be listed as regular INBOX
+ attributes, but unlike other pvt/ attributes it's actually possible to
+ access these attributes as regular users.
+
+ If INBOX is deleted, attributes under this prefix are preserved. */
+#define MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER \
+ MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT"server/"
+
+/* User can get/set all non-pvt/ attributes and also pvt/server/
+ (but not pvt/server/pvt/) attributes. */
+#define MAILBOX_ATTRIBUTE_KEY_IS_USER_ACCESSIBLE(key) \
+ (!str_begins(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT) || \
+ (str_begins(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER) && \
+ strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT, \
+ strlen(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT)) != 0))
+
+enum mail_attribute_type {
+ MAIL_ATTRIBUTE_TYPE_PRIVATE,
+ MAIL_ATTRIBUTE_TYPE_SHARED
+};
+#define MAIL_ATTRIBUTE_TYPE_MASK 0x0f
+/* Allow accessing only attributes with
+ MAIL_ATTRIBUTE_INTERNAL_FLAG_VALIDATED. */
+#define MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED 0x80
+
+enum mail_attribute_value_flags {
+ MAIL_ATTRIBUTE_VALUE_FLAG_READONLY = 0x01,
+ MAIL_ATTRIBUTE_VALUE_FLAG_INT_STREAMS = 0x02
+};
+
+struct mail_attribute_value {
+ /* mailbox_attribute_set() can set either value or value_stream.
+ mailbox_attribute_get() returns only values, but
+ mailbox_attribute_get_stream() may return either value or
+ value_stream. The caller must unreference the returned streams. */
+ const char *value;
+ struct istream *value_stream;
+
+ /* Last time the attribute was changed (0 = unknown). This may be
+ returned even for values that don't exist anymore. */
+ time_t last_change;
+
+ enum mail_attribute_value_flags flags;
+};
+
+/*
+ * Internal attribute
+ */
+
+enum mail_attribute_internal_rank {
+ /* The internal attribute serves only as a source for a default value
+ when the normal mailbox attribute storage has no entry for this
+ attribute. Otherwise it is ignored. The `set' function is called
+ only as a notification, not with the intention to store the value.
+ The value is always assigned to the normal mailbox attribute storage.
+ */
+ MAIL_ATTRIBUTE_INTERNAL_RANK_DEFAULT = 0,
+ /* The internal attribute serves as the main source of the attribute
+ value. If the `get' function returns 0, the normal mailbox attribute
+ storage is attempted to obtain the value. The `set' function is
+ called only as a notification, not with the intention to store the
+ value. The value is assigned to the normal mailbox attribute storage.
+ */
+ MAIL_ATTRIBUTE_INTERNAL_RANK_OVERRIDE,
+ /* The value for the internal attribute is never read from the normal
+ mailbox attribute storage. If the `set' function is NULL, the
+ attribute is read-only. If it is not NULL it is used to assign the
+ attribute value; it is not assigned to the normal mailbox attribute
+ storage.
+ */
+ MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY
+};
+
+enum mail_attribute_internal_flags {
+ /* Apply this attribute to the given key and its children. */
+ MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN = 0x01,
+ /* This attribute can be set/get even without generic METADATA support.
+ These attributes don't count towards any quotas either, so the set()
+ callback should validate that the value isn't excessively large. */
+ MAIL_ATTRIBUTE_INTERNAL_FLAG_VALIDATED = 0x02,
+};
+
+struct mailbox_attribute_internal {
+ enum mail_attribute_type type;
+ const char *key; /* relative to the GUID, e.g., "comment" */
+ enum mail_attribute_internal_rank rank;
+ enum mail_attribute_internal_flags flags;
+
+ /* Get the value of this internal attribute */
+ int (*get)(struct mailbox *box, const char *key,
+ struct mail_attribute_value *value_r);
+ /* Set the value of this internal attribute */
+ int (*set)(struct mailbox_transaction_context *t, const char *key,
+ const struct mail_attribute_value *value);
+ /* If non-NULL, the function is responsible for iterating the
+ attribute. Typically this would be used for attributes with
+ MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN to get the children
+ iterated. If key_prefix is "", all keys should be returned.
+ Otherwise only the keys beginning with key_prefix should be
+ returned. The key_prefix is already relative to the
+ mailbox_attribute_internal.key. */
+ int (*iter)(struct mailbox *box, const char *key_prefix,
+ pool_t pool, ARRAY_TYPE(const_string) *keys);
+};
+
+void mailbox_attribute_register_internal(
+ const struct mailbox_attribute_internal *iattr);
+void mailbox_attribute_register_internals(
+ const struct mailbox_attribute_internal *iattrs, unsigned int count);
+
+void mailbox_attribute_unregister_internal(
+ const struct mailbox_attribute_internal *iattr);
+void mailbox_attribute_unregister_internals(
+ const struct mailbox_attribute_internal *iattrs, unsigned int count);
+
+/*
+ * Attribute API
+ */
+
+/* Set mailbox attribute key to value. The key should be compatible with
+ IMAP METADATA, so for Dovecot-specific keys use
+ MAILBOX_ATTRIBUTE_PREFIX_DOVECOT. */
+int mailbox_attribute_set(struct mailbox_transaction_context *t,
+ enum mail_attribute_type type_flags, const char *key,
+ const struct mail_attribute_value *value);
+/* Delete mailbox attribute key. This is just a wrapper to
+ mailbox_attribute_set() with value->value=NULL. */
+int mailbox_attribute_unset(struct mailbox_transaction_context *t,
+ enum mail_attribute_type type_flags, const char *key);
+/* Returns value for mailbox attribute key. Returns 1 if value was returned,
+ 0 if value wasn't found (set to NULL), -1 if error */
+int mailbox_attribute_get(struct mailbox *box,
+ enum mail_attribute_type type_flags, const char *key,
+ struct mail_attribute_value *value_r);
+/* Same as mailbox_attribute_get(), but the returned value may be either an
+ input stream or a string. */
+int mailbox_attribute_get_stream(struct mailbox *box,
+ enum mail_attribute_type type_flags,
+ const char *key,
+ struct mail_attribute_value *value_r);
+
+/* Iterate through mailbox attributes of the given type. The prefix can be used
+ to restrict what attributes are returned. */
+struct mailbox_attribute_iter *
+mailbox_attribute_iter_init(struct mailbox *box,
+ enum mail_attribute_type type_flags,
+ const char *prefix);
+/* Returns the attribute key or NULL if there are no more attributes. */
+const char *mailbox_attribute_iter_next(struct mailbox_attribute_iter *iter);
+int mailbox_attribute_iter_deinit(struct mailbox_attribute_iter **iter);
+
+#endif