1 #ifndef MAILBOX_ATTRIBUTE_H
2 #define MAILBOX_ATTRIBUTE_H
3 
4 /*
5  * Attribute Handling in Dovecot
6  * =============================
7  *
8  * What IMAP & doveadm users see gets translated into one of several things
9  * depending on if we're operating on a mailbox or on server metadata (""
10  * mailbox in IMAP parlance).  Consider these examples:
11  *
12  *	/private/foo
13  *	/shared/foo
14  *
15  * Here "foo" can be any RFC defined attribute name, or a vendor-prefixed
16  * non-standard name.  (Our vendor prefix is "vendor/vendor.dovecot".)
17  *
18  * In all cases, the "/private" and "/shared" user visible prefixes get
19  * replaced by priv/<GUID> and shared/<GUID>, respectively.  (Here, <GUID>
20  * is the GUID of the mailbox with which the attribute is associated.)  This
21  * way, attributes for all mailboxes can be stored in a single dict.  For
22  * example, the above examples would map to:
23  *
24  *	priv/<GUID>/foo
25  *	shared/<GUID>/foo
26  *
27  * More concrete examples:
28  *
29  *	/private/comment
30  *	/private/vendor/vendor.dovecot/abc
31  *
32  * turn into:
33  *
34  *	priv/<GUID>/comment
35  *	priv/<GUID>/vendor/vendor.dovecot/abc
36  *
37  * Server attributes, that is attributes not associated with a mailbox, are
38  * stored in the INBOX mailbox with a special prefix -
39  * vendor/vendor.dovecot/pvt/server.  For example, the server attribute
40  * /private/comment gets mapped to:
41  *
42  *	priv/<INBOX GUID>/vendor/vendor.dovecot/pvt/server/comment
43  *
44  * This means that if we set a /private/comment server attribute as well as
45  * /private/comment INBOX mailbox attribute, we'll see the following paths
46  * used in the dict:
47  *
48  *	priv/<INBOX GUID>/comment                                   <- mailbox attr
49  *	priv/<INBOX GUID>/vendor/vendor.dovecot/pvt/server/comment  <- server attr
50  *
51  * The case of vendor specific server attributes is a bit confusing, but
52  * consistent.  For example, this server attribute:
53  *
54  *	/private/vendor/vendor.dovecot/abc
55  *
56  * It will get mapped to:
57  *
58  *	priv/<INBOX GUID>/vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/abc
59  *	                  |                              | |                       |
60  *	                  \----- server attr prefix -----/ \-- server attr name ---/
61  *
62  *
63  * Internal Attributes
64  * -------------------
65  *
66  * The final aspect of attribute handling in Dovecot are the so called
67  * "internal attributes".
68  *
69  * The easiest way to explain internal attributes is to summarize attributes
70  * in general.  Attributes are just <key,value> pairs that are stored in a
71  * dict.  The key is mangled according to the above rules before passed to
72  * the dict code.  That is, the key already encodes whether the attribute is
73  * private or shared, the GUID of the mailbox (or of INBOX for server
74  * attributes), etc.  There is no processing of the value.  It is stored and
75  * returned to clients verbatim.
76  *
77  * Internal attributes, on the other hand, are special cased attributes.
78  * That is, the code contains a list of specific attribute names and how to
79  * handle them.  Each internal attribute is defined by a struct
80  * mailbox_attribute_internal.  It contains the pre-parsed name of the
81  * attribute (type, key, and flags), and how to handle getting and setting
82  * of the attribute (rank, get, and set).
83  *
84  * The values for these attributes may come from two places - from the
85  * attributes dict, or from the get function pointer.  Which source to use
86  * is identified by the rank (MAIL_ATTRIBUTE_INTERNAL_RANK_*).
87  *
88  *
89  * Access
90  * ------
91  *
92  * In general, a user (IMAP or doveadm) can access all attributes for a
93  * mailbox.  The one exception are attributes under:
94  *
95  *	/private/vendor/vendor.dovecot/pvt
96  *	/shared/vendor/vendor.dovecot/pvt
97  *
98  * Which as you may recall map to:
99  *
100  *	priv/<GUID>/vendor/vendor.dovecot/pvt
101  *	shared/<GUID>/vendor/vendor.dovecot/pvt
102  *
103  * These are deemed internal to Dovecot, and therefore of no concern to the
104  * user.
105  *
106  * Server attributes have a similar restriction.  That is, attributes
107  * beginning with the following are not accessible:
108  *
109  *	/private/vendor/vendor.dovecot/pvt
110  *	/shared/vendor/vendor.dovecot/pvt
111  *
112  * However since server attributes are stored under the INBOX mailbox, these
113  * paths map to:
114  *
115  *	priv/<INBOX GUID>/vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt
116  *	shared/<INBOX GUID>/vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt
117  *
118  * As a result, the code performs access checks via the
119  * MAILBOX_ATTRIBUTE_KEY_IS_USER_ACCESSIBLE() macro to make sure that the
120  * user is allowed access to the attribute.
121  *
122  *
123  * Nicknames
124  * ---------
125  *
126  * Since every path stored in the dict begins with priv/<GUID> or
127  * shared/<GUID>, these prefixes are often omitted.  This also matches the
128  * internal implementation where the priv/ or shared/ prefix is specified
129  * using an enum, and only the path after the GUID is handled as a string.
130  * For example:
131  *
132  *	priv/<GUID>/vendor/vendor.dovecot/pvt/server/foo
133  *
134  * would be referred to as:
135  *
136  *	vendor/vendor.dovecot/pvt/server/foo
137  *
138  * Since some of the generated paths are very long, developers often use a
139  * shorthand to refer to some of these paths.  For example,
140  *
141  *	pvt/server/pvt
142  *
143  * is really:
144  *
145  *	vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt
146  *
147  * Which when fully specified with a type and INBOX's GUID would turn into
148  * one of the following:
149  *
150  *	priv/<GUID>/vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt
151  *	shared/<GUID>/vendor/vendor.dovecot/pvt/server/vendor/vendor.dovecot/pvt
152  */
153 
154 struct mailbox;
155 struct mailbox_transaction_context;
156 
157 /* RFC 5464 specifies that this is vendor/<vendor-token>/. The registered
158    vendor-tokens always begin with "vendor." so there's some redundancy.. */
159 #define MAILBOX_ATTRIBUTE_PREFIX_DOVECOT "vendor/vendor.dovecot/"
160 /* Prefix used for attributes reserved for Dovecot's internal use. Normal
161    users cannot access these in any way. */
162 #define MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT \
163 	MAILBOX_ATTRIBUTE_PREFIX_DOVECOT"pvt/"
164 /* Server attributes are currently stored in INBOX under this private prefix.
165    They're under the pvt/ prefix so they won't be listed as regular INBOX
166    attributes, but unlike other pvt/ attributes it's actually possible to
167    access these attributes as regular users.
168 
169    If INBOX is deleted, attributes under this prefix are preserved. */
170 #define MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER \
171 	MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT"server/"
172 
173 /* User can get/set all non-pvt/ attributes and also pvt/server/
174    (but not pvt/server/pvt/) attributes. */
175 #define MAILBOX_ATTRIBUTE_KEY_IS_USER_ACCESSIBLE(key) \
176 	(!str_begins(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT) || \
177 	 (str_begins(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER) && \
178 	  strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT, \
179 		 strlen(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT)) != 0))
180 
181 enum mail_attribute_type {
182 	MAIL_ATTRIBUTE_TYPE_PRIVATE,
183 	MAIL_ATTRIBUTE_TYPE_SHARED
184 };
185 #define MAIL_ATTRIBUTE_TYPE_MASK		0x0f
186 /* Allow accessing only attributes with
187    MAIL_ATTRIBUTE_INTERNAL_FLAG_VALIDATED. */
188 #define MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED	0x80
189 
190 enum mail_attribute_value_flags {
191 	MAIL_ATTRIBUTE_VALUE_FLAG_READONLY	= 0x01,
192 	MAIL_ATTRIBUTE_VALUE_FLAG_INT_STREAMS	= 0x02
193 };
194 
195 struct mail_attribute_value {
196 	/* mailbox_attribute_set() can set either value or value_stream.
197 	   mailbox_attribute_get() returns only values, but
198 	   mailbox_attribute_get_stream() may return either value or
199 	   value_stream. The caller must unreference the returned streams. */
200 	const char *value;
201 	struct istream *value_stream;
202 
203 	/* Last time the attribute was changed (0 = unknown). This may be
204 	   returned even for values that don't exist anymore. */
205 	time_t last_change;
206 
207 	enum mail_attribute_value_flags flags;
208 };
209 
210 /*
211  * Internal attribute
212  */
213 
214 enum mail_attribute_internal_rank {
215 	/* The internal attribute serves only as a source for a default value
216 	   when the normal mailbox attribute storage has no entry for this
217 	   attribute. Otherwise it is ignored. The `set' function is called
218 	   only as a notification, not with the intention to store the value.
219 	   The value is always assigned to the normal mailbox attribute storage.
220 	 */
221 	MAIL_ATTRIBUTE_INTERNAL_RANK_DEFAULT = 0,
222 	/* The internal attribute serves as the main source of the attribute
223 	   value. If the `get' function returns 0, the normal mailbox attribute
224 	   storage is attempted to obtain the value. The `set' function is
225 	   called only as a notification, not with the intention to store the
226 	   value. The value is assigned to the normal mailbox attribute storage.
227 	 */
228 	MAIL_ATTRIBUTE_INTERNAL_RANK_OVERRIDE,
229 	/* The value for the internal attribute is never read from the normal
230 	   mailbox attribute storage. If the `set' function is NULL, the
231 	   attribute is read-only. If it is not NULL it is used to assign the
232 	   attribute value; it is not assigned to the normal mailbox attribute
233 	   storage.
234 	 */
235 	MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY
236 };
237 
238 enum mail_attribute_internal_flags {
239 	/* Apply this attribute to the given key and its children. */
240 	MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN	= 0x01,
241 	/* This attribute can be set/get even without generic METADATA support.
242 	   These attributes don't count towards any quotas either, so the set()
243 	   callback should validate that the value isn't excessively large. */
244 	MAIL_ATTRIBUTE_INTERNAL_FLAG_VALIDATED	= 0x02,
245 };
246 
247 struct mailbox_attribute_internal {
248 	enum mail_attribute_type type;
249 	const char *key; /* relative to the GUID, e.g., "comment" */
250 	enum mail_attribute_internal_rank rank;
251 	enum mail_attribute_internal_flags flags;
252 
253 	/* Get the value of this internal attribute */
254 	int (*get)(struct mailbox *box, const char *key,
255 		   struct mail_attribute_value *value_r);
256 	/* Set the value of this internal attribute */
257 	int (*set)(struct mailbox_transaction_context *t, const char *key,
258 		   const struct mail_attribute_value *value);
259 	/* If non-NULL, the function is responsible for iterating the
260 	   attribute. Typically this would be used for attributes with
261 	   MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN to get the children
262 	   iterated. If key_prefix is "", all keys should be returned.
263 	   Otherwise only the keys beginning with key_prefix should be
264 	   returned. The key_prefix is already relative to the
265 	   mailbox_attribute_internal.key. */
266 	int (*iter)(struct mailbox *box, const char *key_prefix,
267 		    pool_t pool, ARRAY_TYPE(const_string) *keys);
268 };
269 
270 void mailbox_attribute_register_internal(
271 	const struct mailbox_attribute_internal *iattr);
272 void mailbox_attribute_register_internals(
273 	const struct mailbox_attribute_internal *iattrs, unsigned int count);
274 
275 void mailbox_attribute_unregister_internal(
276 	const struct mailbox_attribute_internal *iattr);
277 void mailbox_attribute_unregister_internals(
278 	const struct mailbox_attribute_internal *iattrs, unsigned int count);
279 
280 /*
281  * Attribute API
282  */
283 
284 /* Set mailbox attribute key to value. The key should be compatible with
285    IMAP METADATA, so for Dovecot-specific keys use
286    MAILBOX_ATTRIBUTE_PREFIX_DOVECOT. */
287 int mailbox_attribute_set(struct mailbox_transaction_context *t,
288 			  enum mail_attribute_type type_flags, const char *key,
289 			  const struct mail_attribute_value *value);
290 /* Delete mailbox attribute key. This is just a wrapper to
291    mailbox_attribute_set() with value->value=NULL. */
292 int mailbox_attribute_unset(struct mailbox_transaction_context *t,
293 			    enum mail_attribute_type type_flags, const char *key);
294 /* Returns value for mailbox attribute key. Returns 1 if value was returned,
295    0 if value wasn't found (set to NULL), -1 if error */
296 int mailbox_attribute_get(struct mailbox *box,
297 			  enum mail_attribute_type type_flags, const char *key,
298 			  struct mail_attribute_value *value_r);
299 /* Same as mailbox_attribute_get(), but the returned value may be either an
300    input stream or a string. */
301 int mailbox_attribute_get_stream(struct mailbox *box,
302 				 enum mail_attribute_type type_flags,
303 				 const char *key,
304 				 struct mail_attribute_value *value_r);
305 
306 /* Iterate through mailbox attributes of the given type. The prefix can be used
307    to restrict what attributes are returned. */
308 struct mailbox_attribute_iter *
309 mailbox_attribute_iter_init(struct mailbox *box,
310 			    enum mail_attribute_type type_flags,
311 			    const char *prefix);
312 /* Returns the attribute key or NULL if there are no more attributes. */
313 const char *mailbox_attribute_iter_next(struct mailbox_attribute_iter *iter);
314 int mailbox_attribute_iter_deinit(struct mailbox_attribute_iter **iter);
315 
316 #endif
317