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