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