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