summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/mailbox-attribute.h
blob: 9a70580f630ce6026f525b58c4bc4e8b41d24dd5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
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