/src/dovecot/src/lib-storage/index/index-attribute.c
Line | Count | Source |
1 | | /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "ioloop.h" |
5 | | #include "settings.h" |
6 | | #include "dict.h" |
7 | | #include "index-storage.h" |
8 | | |
9 | | struct index_storage_attribute_iter { |
10 | | struct mailbox_attribute_iter iter; |
11 | | struct dict_iterate_context *diter; |
12 | | char *prefix; |
13 | | size_t prefix_len; |
14 | | bool dict_disabled; |
15 | | }; |
16 | | |
17 | | static struct mail_namespace * |
18 | | mail_user_find_attribute_namespace(struct mail_user *user) |
19 | 0 | { |
20 | 0 | struct mail_namespace *ns; |
21 | |
|
22 | 0 | ns = mail_namespace_find_inbox(user->namespaces); |
23 | 0 | if (ns != NULL) |
24 | 0 | return ns; |
25 | | |
26 | 0 | for (ns = user->namespaces; ns != NULL; ns = ns->next) { |
27 | 0 | if (ns->type == MAIL_NAMESPACE_TYPE_PRIVATE) |
28 | 0 | return ns; |
29 | 0 | } |
30 | 0 | return NULL; |
31 | 0 | } |
32 | | |
33 | | static int |
34 | | index_storage_get_user_dict(struct mail_storage *err_storage, |
35 | | struct mail_user *user, struct dict **dict_r) |
36 | 0 | { |
37 | 0 | struct mail_namespace *ns; |
38 | 0 | const char *error; |
39 | 0 | int ret; |
40 | |
|
41 | 0 | if (user->_attr_dict != NULL) { |
42 | 0 | *dict_r = user->_attr_dict; |
43 | 0 | return 0; |
44 | 0 | } |
45 | 0 | if (user->attr_dict_failed) { |
46 | 0 | mail_storage_set_internal_error(err_storage); |
47 | 0 | return -1; |
48 | 0 | } |
49 | | |
50 | 0 | ns = mail_user_find_attribute_namespace(user); |
51 | 0 | if (ns == NULL) { |
52 | | /* probably never happens? */ |
53 | 0 | mail_storage_set_error(err_storage, MAIL_ERROR_NOTPOSSIBLE, |
54 | 0 | "Mailbox attributes not available for this mailbox"); |
55 | 0 | return -1; |
56 | 0 | } |
57 | | |
58 | 0 | struct event *event = event_create(user->event); |
59 | 0 | settings_event_add_filter_name(event, "mail_attribute"); |
60 | 0 | ret = dict_init_auto(event, &user->_attr_dict, &error); |
61 | 0 | event_unref(&event); |
62 | |
|
63 | 0 | if (ret < 0) { |
64 | 0 | mail_storage_set_critical(err_storage, |
65 | 0 | "mail_attribute: dict_init_auto() failed: %s", |
66 | 0 | error); |
67 | 0 | user->attr_dict_failed = TRUE; |
68 | 0 | return -1; |
69 | 0 | } |
70 | 0 | if (ret == 0) { |
71 | 0 | mail_storage_set_error(err_storage, MAIL_ERROR_NOTPOSSIBLE, |
72 | 0 | "Mailbox attributes not enabled"); |
73 | 0 | return -1; |
74 | 0 | } |
75 | | |
76 | 0 | *dict_r = user->_attr_dict; |
77 | 0 | return 0; |
78 | 0 | } |
79 | | |
80 | | static int |
81 | | index_storage_get_dict(struct mailbox *box, enum mail_attribute_type type_flags, |
82 | | struct dict **dict_r, const char **mailbox_prefix_r) |
83 | 0 | { |
84 | 0 | enum mail_attribute_type type = type_flags & MAIL_ATTRIBUTE_TYPE_MASK; |
85 | 0 | struct mail_storage *storage = box->storage; |
86 | 0 | struct mail_namespace *ns; |
87 | 0 | struct mailbox_metadata metadata; |
88 | 0 | const char *error; |
89 | 0 | int ret; |
90 | |
|
91 | 0 | if ((type_flags & MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED) != 0) { |
92 | | /* IMAP METADATA support isn't enabled, so don't allow using |
93 | | the mail_attribute's dict. */ |
94 | 0 | mail_storage_set_error(storage, MAIL_ERROR_NOTPOSSIBLE, |
95 | 0 | "Generic mailbox attributes not enabled"); |
96 | 0 | return -1; |
97 | 0 | } |
98 | | |
99 | 0 | if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) |
100 | 0 | return -1; |
101 | 0 | *mailbox_prefix_r = guid_128_to_string(metadata.guid); |
102 | |
|
103 | 0 | ns = mailbox_get_namespace(box); |
104 | 0 | if (type == MAIL_ATTRIBUTE_TYPE_PRIVATE) { |
105 | | /* private attributes are stored in user's own dict */ |
106 | 0 | return index_storage_get_user_dict(storage, storage->user, dict_r); |
107 | 0 | } else if (ns->user == ns->owner) { |
108 | | /* user owns the mailbox. shared attributes are stored in |
109 | | the same dict. */ |
110 | 0 | return index_storage_get_user_dict(storage, storage->user, dict_r); |
111 | 0 | } else if (ns->owner != NULL) { |
112 | | /* accessing shared attribute of a shared mailbox. |
113 | | use the owner's dict. */ |
114 | 0 | return index_storage_get_user_dict(storage, ns->owner, dict_r); |
115 | 0 | } |
116 | | |
117 | | /* accessing shared attributes of a public mailbox. no user owns it, |
118 | | so use the storage's dict. */ |
119 | 0 | if (storage->_shared_attr_dict != NULL) { |
120 | 0 | *dict_r = storage->_shared_attr_dict; |
121 | 0 | return 0; |
122 | 0 | } |
123 | 0 | if (storage->shared_attr_dict_failed) { |
124 | 0 | mail_storage_set_internal_error(storage); |
125 | 0 | return -1; |
126 | 0 | } |
127 | | |
128 | 0 | struct event *event = event_create(storage->event); |
129 | 0 | settings_event_add_filter_name(event, "mail_attribute"); |
130 | 0 | ret = dict_init_auto(event, &storage->_shared_attr_dict, &error); |
131 | 0 | event_unref(&event); |
132 | |
|
133 | 0 | if (ret < 0) { |
134 | 0 | mail_storage_set_critical(storage, |
135 | 0 | "mail_attribute: dict_init_auto() failed: %s", |
136 | 0 | error); |
137 | 0 | storage->shared_attr_dict_failed = TRUE; |
138 | 0 | return -1; |
139 | 0 | } |
140 | 0 | if (ret == 0) { |
141 | 0 | mail_storage_set_error(storage, MAIL_ERROR_NOTPOSSIBLE, |
142 | 0 | "Mailbox attributes not enabled"); |
143 | 0 | return -1; |
144 | 0 | } |
145 | | |
146 | 0 | *dict_r = storage->_shared_attr_dict; |
147 | 0 | return 0; |
148 | 0 | } |
149 | | |
150 | | static const char * |
151 | | key_get_prefixed(enum mail_attribute_type type_flags, const char *mailbox_prefix, |
152 | | const char *key) |
153 | 0 | { |
154 | 0 | enum mail_attribute_type type = type_flags & MAIL_ATTRIBUTE_TYPE_MASK; |
155 | |
|
156 | 0 | switch (type) { |
157 | 0 | case MAIL_ATTRIBUTE_TYPE_PRIVATE: |
158 | 0 | return t_strconcat(DICT_PATH_PRIVATE, mailbox_prefix, "/", |
159 | 0 | key, NULL); |
160 | 0 | case MAIL_ATTRIBUTE_TYPE_SHARED: |
161 | 0 | return t_strconcat(DICT_PATH_SHARED, mailbox_prefix, "/", |
162 | 0 | key, NULL); |
163 | 0 | } |
164 | 0 | i_unreached(); |
165 | 0 | } |
166 | | |
167 | | static int |
168 | | index_storage_attribute_get_dict_trans(struct mailbox_transaction_context *t, |
169 | | enum mail_attribute_type type_flags, |
170 | | struct dict_transaction_context **dtrans_r, |
171 | | const char **mailbox_prefix_r) |
172 | 0 | { |
173 | 0 | enum mail_attribute_type type = type_flags & MAIL_ATTRIBUTE_TYPE_MASK; |
174 | 0 | struct dict_transaction_context **dtransp = NULL; |
175 | 0 | struct dict *dict; |
176 | 0 | struct mailbox_metadata metadata; |
177 | |
|
178 | 0 | switch (type) { |
179 | 0 | case MAIL_ATTRIBUTE_TYPE_PRIVATE: |
180 | 0 | dtransp = &t->attr_pvt_trans; |
181 | 0 | break; |
182 | 0 | case MAIL_ATTRIBUTE_TYPE_SHARED: |
183 | 0 | dtransp = &t->attr_shared_trans; |
184 | 0 | break; |
185 | 0 | } |
186 | 0 | i_assert(dtransp != NULL); |
187 | | |
188 | 0 | if (*dtransp != NULL && |
189 | 0 | (type_flags & MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED) == 0) { |
190 | | /* Transaction already created. Even if it was, don't use it |
191 | | if _FLAG_VALIDATED is being used. It'll be handled below by |
192 | | returning failure. */ |
193 | 0 | if (mailbox_get_metadata(t->box, MAILBOX_METADATA_GUID, |
194 | 0 | &metadata) < 0) |
195 | 0 | return -1; |
196 | 0 | *mailbox_prefix_r = guid_128_to_string(metadata.guid); |
197 | 0 | *dtrans_r = *dtransp; |
198 | 0 | return 0; |
199 | 0 | } |
200 | | |
201 | 0 | if (index_storage_get_dict(t->box, type_flags, &dict, mailbox_prefix_r) < 0) |
202 | 0 | return -1; |
203 | 0 | i_assert(*dtransp == NULL); |
204 | | |
205 | 0 | struct mail_user *user = mailbox_list_get_user(t->box->list); |
206 | 0 | const struct dict_op_settings *set = mail_user_get_dict_op_settings(user); |
207 | 0 | *dtransp = *dtrans_r = dict_transaction_begin(dict, set); |
208 | 0 | return 0; |
209 | 0 | } |
210 | | |
211 | | int index_storage_attribute_set(struct mailbox_transaction_context *t, |
212 | | enum mail_attribute_type type_flags, |
213 | | const char *key, |
214 | | const struct mail_attribute_value *value) |
215 | 0 | { |
216 | 0 | enum mail_attribute_type type = type_flags & MAIL_ATTRIBUTE_TYPE_MASK; |
217 | 0 | struct dict_transaction_context *dtrans; |
218 | 0 | const char *mailbox_prefix; |
219 | 0 | bool pvt = type == MAIL_ATTRIBUTE_TYPE_PRIVATE; |
220 | 0 | time_t ts = value->last_change != 0 ? value->last_change : ioloop_time; |
221 | 0 | int ret = 0; |
222 | |
|
223 | 0 | if (index_storage_attribute_get_dict_trans(t, type_flags, &dtrans, |
224 | 0 | &mailbox_prefix) < 0) |
225 | 0 | return -1; |
226 | | |
227 | 0 | T_BEGIN { |
228 | 0 | const char *prefixed_key = |
229 | 0 | key_get_prefixed(type_flags, mailbox_prefix, key); |
230 | 0 | const char *value_str; |
231 | |
|
232 | 0 | if (mailbox_attribute_value_to_string(t->box->storage, value, |
233 | 0 | &value_str) < 0) { |
234 | 0 | ret = -1; |
235 | 0 | } else if (value_str != NULL) { |
236 | 0 | dict_set(dtrans, prefixed_key, value_str); |
237 | 0 | mail_index_attribute_set(t->itrans, pvt, key, |
238 | 0 | ts, strlen(value_str)); |
239 | 0 | } else { |
240 | 0 | dict_unset(dtrans, prefixed_key); |
241 | 0 | mail_index_attribute_unset(t->itrans, pvt, key, ts); |
242 | 0 | } |
243 | 0 | } T_END; |
244 | 0 | return ret; |
245 | 0 | } |
246 | | |
247 | | int index_storage_attribute_get(struct mailbox *box, |
248 | | enum mail_attribute_type type_flags, |
249 | | const char *key, |
250 | | struct mail_attribute_value *value_r) |
251 | 0 | { |
252 | 0 | struct dict *dict; |
253 | 0 | const char *mailbox_prefix, *error; |
254 | 0 | int ret; |
255 | |
|
256 | 0 | i_zero(value_r); |
257 | |
|
258 | 0 | if (index_storage_get_dict(box, type_flags, &dict, &mailbox_prefix) < 0) |
259 | 0 | return -1; |
260 | | |
261 | 0 | struct mail_user *user = mailbox_list_get_user(box->list); |
262 | 0 | const struct dict_op_settings *set = mail_user_get_dict_op_settings(user); |
263 | 0 | ret = dict_lookup(dict, set, pool_datastack_create(), |
264 | 0 | key_get_prefixed(type_flags, mailbox_prefix, key), |
265 | 0 | &value_r->value, &error); |
266 | 0 | if (ret < 0) { |
267 | 0 | mailbox_set_critical(box, |
268 | 0 | "Failed to get attribute %s: %s", key, error); |
269 | 0 | return -1; |
270 | 0 | } |
271 | 0 | return ret; |
272 | 0 | } |
273 | | |
274 | | struct mailbox_attribute_iter * |
275 | | index_storage_attribute_iter_init(struct mailbox *box, |
276 | | enum mail_attribute_type type_flags, |
277 | | const char *prefix) |
278 | 0 | { |
279 | 0 | struct index_storage_attribute_iter *iter; |
280 | 0 | struct dict *dict; |
281 | 0 | const char *mailbox_prefix; |
282 | |
|
283 | 0 | iter = i_new(struct index_storage_attribute_iter, 1); |
284 | 0 | iter->iter.box = box; |
285 | 0 | if (index_storage_get_dict(box, type_flags, &dict, &mailbox_prefix) < 0) { |
286 | 0 | if (mailbox_get_last_mail_error(box) == MAIL_ERROR_NOTPOSSIBLE) { |
287 | 0 | mail_storage_clear_error(box->storage); |
288 | 0 | iter->dict_disabled = TRUE; |
289 | 0 | } |
290 | 0 | } else { |
291 | 0 | iter->prefix = i_strdup(key_get_prefixed(type_flags, mailbox_prefix, |
292 | 0 | prefix)); |
293 | 0 | iter->prefix_len = strlen(iter->prefix); |
294 | 0 | struct mail_user *user = mailbox_list_get_user(box->list); |
295 | 0 | const struct dict_op_settings *set = mail_user_get_dict_op_settings(user); |
296 | 0 | iter->diter = dict_iterate_init(dict, set, iter->prefix, |
297 | 0 | DICT_ITERATE_FLAG_RECURSE | |
298 | 0 | DICT_ITERATE_FLAG_NO_VALUE); |
299 | 0 | } |
300 | 0 | return &iter->iter; |
301 | 0 | } |
302 | | |
303 | | const char * |
304 | | index_storage_attribute_iter_next(struct mailbox_attribute_iter *_iter) |
305 | 0 | { |
306 | 0 | struct index_storage_attribute_iter *iter = |
307 | 0 | (struct index_storage_attribute_iter *)_iter; |
308 | 0 | const char *key, *value; |
309 | |
|
310 | 0 | if (iter->diter == NULL || !dict_iterate(iter->diter, &key, &value)) |
311 | 0 | return NULL; |
312 | | |
313 | 0 | i_assert(strncmp(key, iter->prefix, iter->prefix_len) == 0); |
314 | 0 | key += iter->prefix_len; |
315 | 0 | return key; |
316 | 0 | } |
317 | | |
318 | | int index_storage_attribute_iter_deinit(struct mailbox_attribute_iter *_iter) |
319 | 0 | { |
320 | 0 | struct index_storage_attribute_iter *iter = |
321 | 0 | (struct index_storage_attribute_iter *)_iter; |
322 | 0 | const char *error; |
323 | 0 | int ret; |
324 | |
|
325 | 0 | if (iter->diter == NULL) { |
326 | 0 | ret = iter->dict_disabled ? 0 : -1; |
327 | 0 | } else { |
328 | 0 | if ((ret = dict_iterate_deinit(&iter->diter, &error)) < 0) { |
329 | 0 | mailbox_set_critical(_iter->box, |
330 | 0 | "dict_iterate(%s) failed: %s", |
331 | 0 | iter->prefix, error); |
332 | 0 | } |
333 | 0 | } |
334 | 0 | i_free(iter->prefix); |
335 | | i_free(iter); |
336 | 0 | return ret; |
337 | 0 | } |