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 = ®s[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