1 /* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "str.h"
6 #include "istream.h"
7 #include "mail-storage-private.h"
8 #include "bsearch-insert-pos.h"
9 #include "mailbox-attribute-internal.h"
10 
11 static ARRAY(struct mailbox_attribute_internal) mailbox_internal_attributes;
12 static pool_t mailbox_attribute_pool;
13 
mailbox_attributes_init(void)14 void mailbox_attributes_init(void)
15 {
16 	mailbox_attribute_pool =
17 		pool_alloconly_create("mailbox attributes", 2048);
18 	i_array_init(&mailbox_internal_attributes, 32);
19 
20 	/* internal mailbox attributes */
21 	mailbox_attributes_internal_init();
22 }
23 
mailbox_attributes_deinit(void)24 void mailbox_attributes_deinit(void)
25 {
26 	pool_unref(&mailbox_attribute_pool);
27 	array_free(&mailbox_internal_attributes);
28 }
29 
30 /*
31  * Internal attributes
32  */
33 
34 static int
mailbox_attribute_internal_cmp(const struct mailbox_attribute_internal * reg1,const struct mailbox_attribute_internal * reg2)35 mailbox_attribute_internal_cmp(
36 	const struct mailbox_attribute_internal *reg1,
37 	const struct mailbox_attribute_internal *reg2)
38 {
39 	if (reg1->type != reg2->type)
40 		return (int)reg1->type - (int)reg2->type;
41 	return strcmp(reg1->key, reg2->key);
42 }
43 
mailbox_attribute_register_internal(const struct mailbox_attribute_internal * iattr)44 void mailbox_attribute_register_internal(
45 	const struct mailbox_attribute_internal *iattr)
46 {
47 	struct mailbox_attribute_internal ireg;
48 	unsigned int insert_idx;
49 
50 	/* Validated attributes must have a set() callback that validates the
51 	   provided values. Also read-only _RANK_AUTHORITY attributes don't
52 	   need validation. */
53 	i_assert((iattr->flags & MAIL_ATTRIBUTE_INTERNAL_FLAG_VALIDATED) == 0 ||
54 		 iattr->set != NULL ||
55 		 iattr->rank == MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY);
56 
57 	(void)array_bsearch_insert_pos(&mailbox_internal_attributes,
58 		iattr, mailbox_attribute_internal_cmp, &insert_idx);
59 
60 	ireg = *iattr;
61 	ireg.key = p_strdup(mailbox_attribute_pool, iattr->key);
62 	array_insert(&mailbox_internal_attributes, insert_idx, &ireg, 1);
63 }
64 
mailbox_attribute_register_internals(const struct mailbox_attribute_internal * iattrs,unsigned int count)65 void mailbox_attribute_register_internals(
66 	const struct mailbox_attribute_internal *iattrs, unsigned int count)
67 {
68 	unsigned int i;
69 
70 	for (i = 0; i < count; i++)
71 		mailbox_attribute_register_internal(&iattrs[i]);
72 }
73 
mailbox_attribute_unregister_internal(const struct mailbox_attribute_internal * iattr)74 void mailbox_attribute_unregister_internal(
75 	const struct mailbox_attribute_internal *iattr)
76 {
77 	unsigned int idx;
78 
79 	if (!array_bsearch_insert_pos(&mailbox_internal_attributes,
80 				      iattr, mailbox_attribute_internal_cmp, &idx)) {
81 		i_panic("mailbox_attribute_unregister_internal(%s): "
82 			"key not found", iattr->key);
83 	}
84 
85 	array_delete(&mailbox_internal_attributes, idx, 1);
86 }
87 
mailbox_attribute_unregister_internals(const struct mailbox_attribute_internal * iattrs,unsigned int count)88 void mailbox_attribute_unregister_internals(
89 	const struct mailbox_attribute_internal *iattrs, unsigned int count)
90 {
91 	unsigned int i;
92 
93 	for (i = 0; i < count; i++)
94 		mailbox_attribute_unregister_internal(&iattrs[i]);
95 }
96 
97 static const struct mailbox_attribute_internal *
mailbox_internal_attribute_get_int(enum mail_attribute_type type_flags,const char * key)98 mailbox_internal_attribute_get_int(enum mail_attribute_type type_flags,
99 				   const char *key)
100 {
101 	const struct mailbox_attribute_internal *iattr;
102 	struct mailbox_attribute_internal dreg;
103 	unsigned int insert_idx;
104 
105 	i_zero(&dreg);
106 	dreg.type = type_flags & MAIL_ATTRIBUTE_TYPE_MASK;
107 	dreg.key = key;
108 
109 	if (array_bsearch_insert_pos(&mailbox_internal_attributes,
110 				     &dreg, mailbox_attribute_internal_cmp,
111 				     &insert_idx)) {
112 		/* exact match */
113 		return array_idx(&mailbox_internal_attributes, insert_idx);
114 	}
115 	if (insert_idx == 0) {
116 		/* not found at all */
117 		return NULL;
118 	}
119 	iattr = array_idx(&mailbox_internal_attributes, insert_idx-1);
120 	if (!str_begins(key, iattr->key)) {
121 		/* iattr isn't a prefix of key */
122 		return NULL;
123 	} else if ((iattr->flags & MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN) != 0) {
124 		/* iattr is a prefix of key and it wants to handle the key */
125 		return iattr;
126 	} else {
127 		return NULL;
128 	}
129 }
130 
131 static const struct mailbox_attribute_internal *
mailbox_internal_attribute_get(enum mail_attribute_type type_flags,const char * key)132 mailbox_internal_attribute_get(enum mail_attribute_type type_flags,
133 			       const char *key)
134 {
135 	const struct mailbox_attribute_internal *iattr;
136 
137 	iattr = mailbox_internal_attribute_get_int(type_flags, key);
138 	if ((type_flags & MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED) != 0 &&
139 	    iattr != NULL &&
140 	    (iattr->flags & MAIL_ATTRIBUTE_INTERNAL_FLAG_VALIDATED) == 0) {
141 		/* only validated attributes can be accessed */
142 		iattr = NULL;
143 	}
144 	return iattr;
145 }
146 
147 static void
mailbox_internal_attributes_add_prefixes(ARRAY_TYPE (const_string)* attrs,pool_t pool,unsigned int old_count,const char * key)148 mailbox_internal_attributes_add_prefixes(ARRAY_TYPE(const_string) *attrs,
149 					 pool_t pool, unsigned int old_count,
150 					 const char *key)
151 {
152 	unsigned int new_count;
153 
154 	if (key[0] == '\0')
155 		return;
156 	new_count = array_count(attrs);
157 	for (unsigned int i = old_count; i < new_count; i++) {
158 		const char *old_key = array_idx_elem(attrs, i);
159 		const char *prefixed_key;
160 
161 		if (old_key[0] == '\0')
162 			prefixed_key = p_strndup(pool, key, strlen(key)-1);
163 		else
164 			prefixed_key = p_strconcat(pool, key, old_key, NULL);
165 		array_idx_set(attrs, i, &prefixed_key);
166 	}
167 }
168 
169 static int
mailbox_internal_attributes_get(struct mailbox * box,enum mail_attribute_type type_flags,const char * prefix,pool_t attr_pool,bool have_dict,ARRAY_TYPE (const_string)* attrs)170 mailbox_internal_attributes_get(struct mailbox *box,
171 	enum mail_attribute_type type_flags, const char *prefix,
172 	pool_t attr_pool, bool have_dict, ARRAY_TYPE(const_string) *attrs)
173 {
174 	const struct mailbox_attribute_internal *regs;
175 	struct mailbox_attribute_internal dreg;
176 	char *bare_prefix;
177 	size_t plen;
178 	unsigned int count, i, j;
179 	int ret = 0;
180 
181 	bare_prefix = t_strdup_noconst(prefix);
182 	plen = strlen(bare_prefix);
183 	if (plen > 0 && bare_prefix[plen-1] == '/') {
184 		bare_prefix[plen-1] = '\0';
185 		plen--;
186 	}
187 
188 	i_zero(&dreg);
189 	dreg.type = type_flags & MAIL_ATTRIBUTE_TYPE_MASK;
190 	dreg.key = bare_prefix;
191 
192 	(void)array_bsearch_insert_pos(&mailbox_internal_attributes,
193 		&dreg, mailbox_attribute_internal_cmp, &i);
194 
195 	/* iterate attributes that might have children whose keys begins with
196 	   the prefix */
197 	regs = array_get(&mailbox_internal_attributes, &count);
198 	for (j = i; j > 0; j--) {
199 		const struct mailbox_attribute_internal *attr = &regs[j-1];
200 
201 		if ((attr->flags & MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN) == 0 ||
202 		    !str_begins(bare_prefix, attr->key))
203 			break;
204 
205 		/* For example: bare_prefix="foo/bar" and attr->key="foo/", so
206 		   iter() is called with key_prefix="bar". It could add to
207 		   attrs: { "", "baz" }, which means with the full prefix:
208 		   { "foo/bar", "foo/bar/baz" } */
209 		if (attr->iter != NULL &&
210 		    attr->iter(box, bare_prefix + strlen(attr->key),
211 			       attr_pool, attrs) < 0)
212 			ret = -1;
213 	}
214 
215 	/* iterate attributes whose key begins with the prefix */
216 	for (; i < count; i++) {
217 		const char *key = regs[i].key;
218 
219 		if (regs[i].type != dreg.type)
220 			return ret;
221 		if ((type_flags & MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED) != 0 &&
222 		    (regs[i].flags & MAIL_ATTRIBUTE_INTERNAL_FLAG_VALIDATED) == 0)
223 			continue;
224 
225 		if (plen > 0) {
226 			if (strncmp(key, bare_prefix, plen) != 0)
227 				return ret;
228 			if (key[plen] == '/') {
229 				/* remove prefix */
230 				key += plen + 1;
231 			} else if (key[plen] == '\0') {
232 				/* list the key itself, so this becomes an
233 				   empty key string. it's the same as how the
234 				   dict backend works too. */
235 				key += plen;
236 			} else {
237 				return ret;
238 			}
239 		}
240 		if (regs[i].iter != NULL) {
241 			/* For example: bare_prefix="foo" and
242 			   attr->key="foo/bar/", so key="bar/". iter() is
243 			   always called with key_prefix="", so we're also
244 			   responsible for adding the "bar/" prefix to the
245 			   attrs that iter() returns. */
246 			unsigned int old_count = array_count(attrs);
247 			if (regs[i].iter(box, "", attr_pool, attrs) < 0)
248 				ret = -1;
249 			mailbox_internal_attributes_add_prefixes(attrs, attr_pool,
250 								 old_count, key);
251 		} else if (have_dict || regs[i].rank == MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY)
252 			array_push_back(attrs, &key);
253 	}
254 	return ret;
255 }
256 
257 /*
258  * Attribute API
259  */
260 
261 static int
mailbox_attribute_set_common(struct mailbox_transaction_context * t,enum mail_attribute_type type_flags,const char * key,const struct mail_attribute_value * value)262 mailbox_attribute_set_common(struct mailbox_transaction_context *t,
263 			     enum mail_attribute_type type_flags,
264 			     const char *key,
265 			     const struct mail_attribute_value *value)
266 {
267 	enum mail_attribute_type type =
268 		type_flags & MAIL_ATTRIBUTE_TYPE_MASK;
269 	const struct mailbox_attribute_internal *iattr;
270 	int ret;
271 
272 	iattr = mailbox_internal_attribute_get(type_flags, key);
273 
274 	/* allow internal server attribute only for inbox */
275 	if (iattr != NULL && !t->box->inbox_any &&
276 	    str_begins(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER))
277 		iattr = NULL;
278 
279 	/* handle internal attribute */
280 	if (iattr != NULL) {
281 		switch (iattr->rank) {
282 		case MAIL_ATTRIBUTE_INTERNAL_RANK_DEFAULT:
283 		case MAIL_ATTRIBUTE_INTERNAL_RANK_OVERRIDE:
284 			/* notify about assignment */
285 			if (iattr->set != NULL && iattr->set(t, key, value) < 0)
286 				return -1;
287 			break;
288 		case MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY:
289 			if (iattr->set == NULL) {
290 				mail_storage_set_error(t->box->storage, MAIL_ERROR_NOTPOSSIBLE, t_strdup_printf(
291 					"The /%s/%s attribute cannot be changed",
292 					(type == MAIL_ATTRIBUTE_TYPE_SHARED ? "shared" : "private"), key));
293 				return -1;
294 			}
295 			/* assign internal attribute */
296 			return iattr->set(t, key, value);
297 		default:
298 			i_unreached();
299 		}
300 		/* the value was validated. */
301 		type_flags &= ENUM_NEGATE(MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED);
302 	}
303 
304 	ret = t->box->v.attribute_set(t, type_flags, key, value);
305 	return ret;
306 }
307 
mailbox_attribute_set(struct mailbox_transaction_context * t,enum mail_attribute_type type_flags,const char * key,const struct mail_attribute_value * value)308 int mailbox_attribute_set(struct mailbox_transaction_context *t,
309 			  enum mail_attribute_type type_flags, const char *key,
310 			  const struct mail_attribute_value *value)
311 {
312 	return mailbox_attribute_set_common(t, type_flags, key, value);
313 }
314 
mailbox_attribute_unset(struct mailbox_transaction_context * t,enum mail_attribute_type type_flags,const char * key)315 int mailbox_attribute_unset(struct mailbox_transaction_context *t,
316 			    enum mail_attribute_type type_flags, const char *key)
317 {
318 	struct mail_attribute_value value;
319 
320 	i_zero(&value);
321 	return mailbox_attribute_set_common(t, type_flags, key, &value);
322 }
323 
mailbox_attribute_value_to_string(struct mail_storage * storage,const struct mail_attribute_value * value,const char ** str_r)324 int mailbox_attribute_value_to_string(struct mail_storage *storage,
325 				      const struct mail_attribute_value *value,
326 				      const char **str_r)
327 {
328 	string_t *str;
329 	const unsigned char *data;
330 	size_t size;
331 
332 	if (value->value_stream == NULL) {
333 		*str_r = value->value;
334 		return 0;
335 	}
336 	str = t_str_new(128);
337 	i_stream_seek(value->value_stream, 0);
338 	while (i_stream_read_more(value->value_stream, &data, &size) > 0) {
339 		if (memchr(data, '\0', size) != NULL) {
340 			mail_storage_set_error(storage, MAIL_ERROR_PARAMS,
341 				"Attribute string value has NULs");
342 			return -1;
343 		}
344 		str_append_data(str, data, size);
345 		i_stream_skip(value->value_stream, size);
346 	}
347 	if (value->value_stream->stream_errno != 0) {
348 		mail_storage_set_critical(storage, "read(%s) failed: %s",
349 			i_stream_get_name(value->value_stream),
350 			i_stream_get_error(value->value_stream));
351 		return -1;
352 	}
353 	i_assert(value->value_stream->eof);
354 	*str_r = str_c(str);
355 	return 0;
356 }
357 
358 static int
mailbox_attribute_get_common(struct mailbox * box,enum mail_attribute_type type_flags,const char * key,struct mail_attribute_value * value_r)359 mailbox_attribute_get_common(struct mailbox *box,
360 			     enum mail_attribute_type type_flags,
361 			     const char *key,
362 			     struct mail_attribute_value *value_r)
363 {
364 	const struct mailbox_attribute_internal *iattr;
365 	int ret;
366 
367 	iattr = mailbox_internal_attribute_get(type_flags, key);
368 
369 	/* allow internal server attributes only for the inbox */
370 	if (iattr != NULL && !box->inbox_user &&
371 	    str_begins(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER))
372 		iattr = NULL;
373 
374 	/* internal attribute */
375 	if (iattr != NULL) {
376 		switch (iattr->rank) {
377 		case MAIL_ATTRIBUTE_INTERNAL_RANK_OVERRIDE:
378 			/* we already checked that this attribute has
379 			   validated-flag */
380 			type_flags &= ENUM_NEGATE(MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED);
381 
382 			if (iattr->get == NULL)
383 				break;
384 			if ((ret = iattr->get(box, key, value_r)) != 0) {
385 				if (ret < 0)
386 					return -1;
387 				value_r->flags |= MAIL_ATTRIBUTE_VALUE_FLAG_READONLY;
388 				return 1;
389 			}
390 			break;
391 		case MAIL_ATTRIBUTE_INTERNAL_RANK_DEFAULT:
392 			break;
393 		case MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY:
394 			if ((ret = iattr->get(box, key, value_r)) <= 0)
395 				return ret;
396 			value_r->flags |= MAIL_ATTRIBUTE_VALUE_FLAG_READONLY;
397 			return 1;
398 		default:
399 			i_unreached();
400 		}
401 	}
402 
403 	ret = box->v.attribute_get(box, type_flags, key, value_r);
404 	if (ret != 0)
405 		return ret;
406 
407 	/* default entries */
408 	if (iattr != NULL) {
409 		switch (iattr->rank) {
410 		case MAIL_ATTRIBUTE_INTERNAL_RANK_DEFAULT:
411 			if (iattr->get == NULL)
412 				ret = 0;
413 			else {
414 				if ((ret = iattr->get(box, key, value_r)) < 0)
415 					return ret;
416 			}
417 			if (ret > 0) {
418 				value_r->flags |= MAIL_ATTRIBUTE_VALUE_FLAG_READONLY;
419 				return 1;
420 			}
421 			break;
422 		case MAIL_ATTRIBUTE_INTERNAL_RANK_OVERRIDE:
423 			break;
424 		default:
425 			i_unreached();
426 		}
427 	}
428 	return 0;
429 }
430 
mailbox_attribute_get(struct mailbox * box,enum mail_attribute_type type_flags,const char * key,struct mail_attribute_value * value_r)431 int mailbox_attribute_get(struct mailbox *box,
432 			  enum mail_attribute_type type_flags, const char *key,
433 			  struct mail_attribute_value *value_r)
434 {
435 	int ret;
436 	i_zero(value_r);
437 	if ((ret = mailbox_attribute_get_common(box, type_flags, key,
438 				value_r)) <= 0)
439 		return ret;
440 	i_assert(value_r->value != NULL);
441 	return 1;
442 }
443 
mailbox_attribute_get_stream(struct mailbox * box,enum mail_attribute_type type_flags,const char * key,struct mail_attribute_value * value_r)444 int mailbox_attribute_get_stream(struct mailbox *box,
445 				 enum mail_attribute_type type_flags,
446 				 const char *key,
447 				 struct mail_attribute_value *value_r)
448 {
449 	int ret;
450 
451 	i_zero(value_r);
452 	value_r->flags |= MAIL_ATTRIBUTE_VALUE_FLAG_INT_STREAMS;
453 	if ((ret = mailbox_attribute_get_common(box, type_flags, key,
454 				value_r)) <= 0)
455 		return ret;
456 	i_assert(value_r->value != NULL || value_r->value_stream != NULL);
457 	return 1;
458 }
459 
460 struct mailbox_attribute_internal_iter {
461 	struct mailbox_attribute_iter iter;
462 	pool_t pool;
463 
464 	ARRAY_TYPE(const_string) extra_attrs;
465 	unsigned int extra_attr_idx;
466 
467 	struct mailbox_attribute_iter *real_iter;
468 	bool iter_failed;
469 };
470 
471 struct mailbox_attribute_iter *
mailbox_attribute_iter_init(struct mailbox * box,enum mail_attribute_type type_flags,const char * prefix)472 mailbox_attribute_iter_init(struct mailbox *box,
473 			    enum mail_attribute_type type_flags,
474 			    const char *prefix)
475 {
476 	struct mailbox_attribute_internal_iter *intiter;
477 	struct mailbox_attribute_iter *iter;
478 	ARRAY_TYPE(const_string) extra_attrs;
479 	const char *const *attr;
480 	pool_t pool;
481 	bool have_dict, failed = FALSE;
482 
483 	iter = box->v.attribute_iter_init(box, type_flags, prefix);
484 	i_assert(iter->box != NULL);
485 	box->attribute_iter_count++;
486 
487 	/* check which internal attributes may apply */
488 	t_array_init(&extra_attrs, 4);
489 	have_dict = box->storage->set->mail_attribute_dict[0] != '\0';
490 	pool = pool_alloconly_create("mailbox internal attribute iter", 128);
491 	if (mailbox_internal_attributes_get(box, type_flags, prefix, pool,
492 					    have_dict, &extra_attrs) < 0)
493 		failed = TRUE;
494 
495 	/* any extra internal attributes to add? */
496 	if (array_count(&extra_attrs) == 0 && !failed) {
497 		/* no */
498 		pool_unref(&pool);
499 		return iter;
500 	}
501 
502 	/* yes */
503 	intiter = p_new(pool, struct mailbox_attribute_internal_iter, 1);
504 	intiter->pool = pool;
505 	intiter->real_iter = iter;
506 	intiter->iter_failed = failed;
507 	p_array_init(&intiter->extra_attrs, pool, 4);
508 
509 	/* copy relevant attributes */
510 	array_foreach(&extra_attrs, attr) {
511 		/* skip internal server attributes unless we're iterating inbox */
512 		if (!box->inbox_user &&
513 		    strncmp(*attr, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER,
514 			    strlen(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER)) == 0)
515 			continue;
516 		array_push_back(&intiter->extra_attrs, attr);
517 	}
518 	return &intiter->iter;
519 }
520 
mailbox_attribute_iter_next(struct mailbox_attribute_iter * iter)521 const char *mailbox_attribute_iter_next(struct mailbox_attribute_iter *iter)
522 {
523 	struct mailbox_attribute_internal_iter *intiter;
524 	const char *const *attrs;
525 	unsigned int count, i;
526 	const char *result;
527 
528 	if (iter->box != NULL) {
529 		/* no internal attributes to add */
530 		return iter->box->v.attribute_iter_next(iter);
531 	}
532 
533 	/* filter out duplicate results */
534 	intiter = (struct mailbox_attribute_internal_iter *)iter;
535 	attrs = array_get(&intiter->extra_attrs, &count);
536 	while ((result = intiter->real_iter->box->
537 			v.attribute_iter_next(intiter->real_iter)) != NULL) {
538 		for (i = 0; i < count; i++) {
539 			if (strcasecmp(attrs[i], result) == 0)
540 				break;
541 		}
542 		if (i == count) {
543 			/* return normally */
544 			return result;
545 		}
546 		/* this attribute name is also to be returned as extra;
547 		   skip now */
548 	}
549 
550 	/* return extra attributes at the end */
551 	if (intiter->extra_attr_idx < count)
552 		return attrs[intiter->extra_attr_idx++];
553 	return NULL;
554 }
555 
mailbox_attribute_iter_deinit(struct mailbox_attribute_iter ** _iter)556 int mailbox_attribute_iter_deinit(struct mailbox_attribute_iter **_iter)
557 {
558 	struct mailbox_attribute_iter *iter = *_iter;
559 	struct mailbox_attribute_internal_iter *intiter;
560 	int ret;
561 
562 	*_iter = NULL;
563 
564 	if (iter->box != NULL) {
565 		/* not wrapped */
566 		i_assert(iter->box->attribute_iter_count > 0);
567 		iter->box->attribute_iter_count--;
568 		return iter->box->v.attribute_iter_deinit(iter);
569 	}
570 
571 	/* wrapped */
572 	intiter = (struct mailbox_attribute_internal_iter *)iter;
573 
574 	i_assert(intiter->real_iter->box->attribute_iter_count > 0);
575 	intiter->real_iter->box->attribute_iter_count--;
576 
577 	ret = intiter->real_iter->box->v.attribute_iter_deinit(intiter->real_iter);
578 	if (intiter->iter_failed)
579 		ret = -1;
580 	pool_unref(&intiter->pool);
581 	return ret;
582 }
583