1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "str.h"
5 #include "mail-namespace.h"
6 #include "mailbox-list-private.h"
7 #include "dsync-ibc.h"
8 #include "dsync-mailbox-tree.h"
9 #include "dsync-brain-private.h"
10 
11 #include <ctype.h>
12 
dsync_brain_check_namespaces(struct dsync_brain * brain)13 static void dsync_brain_check_namespaces(struct dsync_brain *brain)
14 {
15 	struct mail_namespace *ns, *first_ns = NULL;
16 	char sep;
17 
18 	i_assert(brain->hierarchy_sep == '\0');
19 
20 	for (ns = brain->user->namespaces; ns != NULL; ns = ns->next) {
21 		if (!dsync_brain_want_namespace(brain, ns))
22 			continue;
23 
24 		sep = mail_namespace_get_sep(ns);
25 		if (first_ns == NULL) {
26 			brain->hierarchy_sep = sep;
27 			first_ns = ns;
28 		} else if (brain->hierarchy_sep != sep) {
29 			i_fatal("Synced namespaces have conflicting separators "
30 				"('%c' for prefix=\"%s\", '%c' for prefix=\"%s\")",
31 				brain->hierarchy_sep, first_ns->prefix,
32 				sep, ns->prefix);
33 		}
34 	}
35 	if (brain->hierarchy_sep != '\0')
36 		return;
37 
38 	i_fatal("All your namespaces have a location setting. "
39 		"Only namespaces with empty location settings are converted. "
40 		"(One namespace should default to mail_location setting)");
41 }
42 
dsync_brain_mailbox_trees_init(struct dsync_brain * brain)43 void dsync_brain_mailbox_trees_init(struct dsync_brain *brain)
44 {
45 	struct mail_namespace *ns;
46 
47 	dsync_brain_check_namespaces(brain);
48 
49 	brain->local_mailbox_tree =
50 		dsync_mailbox_tree_init(brain->hierarchy_sep, brain->alt_char);
51 	/* we'll convert remote mailbox names to use our own separator */
52 	brain->remote_mailbox_tree =
53 		dsync_mailbox_tree_init(brain->hierarchy_sep, brain->alt_char);
54 
55 	/* fill the local mailbox tree */
56 	for (ns = brain->user->namespaces; ns != NULL; ns = ns->next) {
57 		if (!dsync_brain_want_namespace(brain, ns))
58 			continue;
59 		if (brain->debug)
60 			i_debug("brain %c: Namespace %s has location %s",
61 				brain->master_brain ? 'M' : 'S',
62 				ns->prefix, ns->set->location);
63 		if (dsync_mailbox_tree_fill(brain->local_mailbox_tree, ns,
64 					    brain->sync_box,
65 					    brain->sync_box_guid,
66 					    brain->exclude_mailboxes,
67 					    &brain->mail_error) < 0) {
68 			brain->failed = TRUE;
69 			break;
70 		}
71 	}
72 
73 	brain->local_tree_iter =
74 		dsync_mailbox_tree_iter_init(brain->local_mailbox_tree);
75 }
76 
77 
dsync_brain_send_mailbox_tree(struct dsync_brain * brain)78 void dsync_brain_send_mailbox_tree(struct dsync_brain *brain)
79 {
80 	struct dsync_mailbox_node *node;
81 	enum dsync_ibc_send_ret ret;
82 	const char *full_name;
83 	char sep[2];
84 
85 	sep[0] = brain->hierarchy_sep; sep[1] = '\0';
86 	while (dsync_mailbox_tree_iter_next(brain->local_tree_iter,
87 					    &full_name, &node)) {
88 		if (node->ns == NULL) {
89 			/* This node was created when adding a namespace prefix
90 			   to the tree that has multiple hierarchical names,
91 			   but the parent names don't belong to any synced
92 			   namespace. For example when syncing "-n Shared/user/"
93 			   so "Shared/" is skipped. Or if there is e.g.
94 			   "Public/files/" namespace prefix, but no "Public/"
95 			   namespace at all. */
96 			continue;
97 		}
98 
99 		T_BEGIN {
100 			const char *const *parts;
101 
102 			if (brain->debug) {
103 				i_debug("brain %c: Local mailbox tree: %s %s",
104 					brain->master_brain ? 'M' : 'S', full_name,
105 					dsync_mailbox_node_to_string(node));
106 			}
107 
108 			/* Avoid sending out mailbox names with escape
109 			   characters. Especially when dsync is used for
110 			   migration, we don't want to end up having invalid
111 			   mUTF7 mailbox names locally. Also, remote might not
112 			   even be configured to use the same escape
113 			   character. */
114 			if (node->ns != NULL) {
115 				i_assert(brain->alt_char != '\0');
116 				full_name = t_str_replace(full_name,
117 					node->ns->list->set.vname_escape_char,
118 					brain->alt_char);
119 			}
120 			parts = t_strsplit(full_name, sep);
121 			ret = dsync_ibc_send_mailbox_tree_node(brain->ibc,
122 							       parts, node);
123 		} T_END;
124 		if (ret == DSYNC_IBC_SEND_RET_FULL)
125 			return;
126 	}
127 	dsync_mailbox_tree_iter_deinit(&brain->local_tree_iter);
128 	dsync_ibc_send_end_of_list(brain->ibc, DSYNC_IBC_EOL_MAILBOX_TREE);
129 
130 	brain->state = DSYNC_STATE_SEND_MAILBOX_TREE_DELETES;
131 }
132 
dsync_brain_send_mailbox_tree_deletes(struct dsync_brain * brain)133 void dsync_brain_send_mailbox_tree_deletes(struct dsync_brain *brain)
134 {
135 	const struct dsync_mailbox_delete *deletes;
136 	unsigned int count;
137 
138 	deletes = dsync_mailbox_tree_get_deletes(brain->local_mailbox_tree,
139 						 &count);
140 	dsync_ibc_send_mailbox_deletes(brain->ibc, deletes, count,
141 				       brain->hierarchy_sep);
142 
143 	brain->state = DSYNC_STATE_RECV_MAILBOX_TREE;
144 }
145 
146 static bool
dsync_namespace_match_parts(struct mail_namespace * ns,const char * const * name_parts)147 dsync_namespace_match_parts(struct mail_namespace *ns,
148 			    const char *const *name_parts)
149 {
150 	const char *part, *prefix = ns->prefix;
151 	size_t part_len;
152 	char ns_sep = mail_namespace_get_sep(ns);
153 
154 	if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
155 	    strcmp(name_parts[0], "INBOX") == 0 && name_parts[1] == NULL)
156 		return TRUE;
157 
158 	for (; *name_parts != NULL && *prefix != '\0'; name_parts++) {
159 		part = *name_parts;
160 		part_len = strlen(part);
161 
162 		if (!str_begins(prefix, part))
163 			return FALSE;
164 		if (prefix[part_len] != ns_sep)
165 			return FALSE;
166 		prefix += part_len + 1;
167 	}
168 	if (*name_parts != NULL) {
169 		/* namespace prefix found with a mailbox */
170 		return TRUE;
171 	}
172 	if (*prefix == '\0') {
173 		/* namespace prefix itself matched */
174 		return TRUE;
175 	}
176 	return FALSE;
177 }
178 
179 static struct mail_namespace *
dsync_find_namespace(struct dsync_brain * brain,const char * const * name_parts)180 dsync_find_namespace(struct dsync_brain *brain, const char *const *name_parts)
181 {
182 	struct mail_namespace *ns, *best_ns = NULL;
183 
184 	for (ns = brain->user->namespaces; ns != NULL; ns = ns->next) {
185 		if (!dsync_brain_want_namespace(brain, ns))
186 			continue;
187 
188 		if (ns->prefix_len == 0) {
189 			/* prefix="" is the fallback namespace */
190 			if (best_ns == NULL)
191 				best_ns = ns;
192 		} else if (dsync_namespace_match_parts(ns, name_parts)) {
193 			if (best_ns == NULL ||
194 			    best_ns->prefix_len < ns->prefix_len)
195 				best_ns = ns;
196 		}
197 	}
198 	return best_ns;
199 }
200 
201 static bool
dsync_is_valid_name(struct mail_namespace * ns,const char * vname)202 dsync_is_valid_name(struct mail_namespace *ns, const char *vname)
203 {
204 	struct mailbox *box;
205 	bool ret;
206 
207 	box = mailbox_alloc(ns->list, vname, MAILBOX_FLAG_READONLY);
208 	ret = mailbox_verify_create_name(box) == 0;
209 	mailbox_free(&box);
210 	return ret;
211 }
212 
213 static void
dsync_fix_mailbox_name(struct mail_namespace * ns,string_t * vname_str,char alt_char)214 dsync_fix_mailbox_name(struct mail_namespace *ns, string_t *vname_str,
215 		       char alt_char)
216 {
217 	const char *old_vname;
218 	char *vname, list_sep = mailbox_list_get_hierarchy_sep(ns->list);
219 	guid_128_t guid;
220 	unsigned int i, start_pos;
221 
222 	vname = str_c_modifiable(vname_str);
223 	if (strncmp(vname, ns->prefix, ns->prefix_len) == 0)
224 		start_pos = ns->prefix_len;
225 	else
226 		start_pos = 0;
227 
228 	/* replace control chars */
229 	for (i = start_pos; vname[i] != '\0'; i++) {
230 		if ((unsigned char)vname[i] < ' ')
231 			vname[i] = alt_char;
232 	}
233 	/* make it valid UTF8 */
234 	if (!uni_utf8_str_is_valid(vname)) {
235 		old_vname = t_strdup(vname + start_pos);
236 		str_truncate(vname_str, start_pos);
237 		if (uni_utf8_get_valid_data((const void *)old_vname,
238 					    strlen(old_vname), vname_str))
239 			i_unreached();
240 		vname = str_c_modifiable(vname_str);
241 	}
242 	if (dsync_is_valid_name(ns, vname))
243 		return;
244 
245 	/* 1) change any real separators to alt separators (this wouldn't
246 	   be necessary with listescape, but don't bother detecting it) */
247 	if (list_sep != mail_namespace_get_sep(ns)) {
248 		for (i = start_pos; vname[i] != '\0'; i++) {
249 			if (vname[i] == list_sep)
250 				vname[i] = alt_char;
251 		}
252 		if (dsync_is_valid_name(ns, vname))
253 			return;
254 	}
255 	/* 2) '/' characters aren't valid without listescape */
256 	if (mail_namespace_get_sep(ns) != '/' && list_sep != '/') {
257 		for (i = start_pos; vname[i] != '\0'; i++) {
258 			if (vname[i] == '/')
259 				vname[i] = alt_char;
260 		}
261 		if (dsync_is_valid_name(ns, vname))
262 			return;
263 	}
264 	/* 3) probably some reserved name (e.g. dbox-Mails) */
265 	str_insert(vname_str, ns->prefix_len, "_");
266 	if (dsync_is_valid_name(ns, str_c(vname_str)))
267 		return;
268 
269 	/* 4) name is too long? just give up and generate a unique name */
270 	guid_128_generate(guid);
271 	str_truncate(vname_str, 0);
272 	str_append(vname_str, ns->prefix);
273 	str_append(vname_str, guid_128_to_string(guid));
274 	i_assert(dsync_is_valid_name(ns, str_c(vname_str)));
275 }
276 
277 static int
dsync_get_mailbox_name(struct dsync_brain * brain,const char * const * name_parts,const char ** name_r,struct mail_namespace ** ns_r)278 dsync_get_mailbox_name(struct dsync_brain *brain, const char *const *name_parts,
279 		       const char **name_r, struct mail_namespace **ns_r)
280 {
281 	struct mail_namespace *ns;
282 	const char *p;
283 	string_t *vname;
284 	char ns_sep;
285 
286 	i_assert(*name_parts != NULL);
287 
288 	ns = dsync_find_namespace(brain, name_parts);
289 	if (ns == NULL)
290 		return -1;
291 	ns_sep = mail_namespace_get_sep(ns);
292 
293 	/* build the mailbox name */
294 	vname = t_str_new(128);
295 	for (; *name_parts != NULL; name_parts++) {
296 		for (p = *name_parts; *p != '\0'; p++) {
297 			if (*p != ns_sep)
298 				str_append_c(vname, *p);
299 			else
300 				str_append_c(vname, brain->alt_char);
301 		}
302 		str_append_c(vname, ns_sep);
303 	}
304 	str_truncate(vname, str_len(vname)-1);
305 
306 	dsync_fix_mailbox_name(ns, vname, brain->alt_char);
307 	*name_r = str_c(vname);
308 	*ns_r = ns;
309 	return 0;
310 }
311 
dsync_brain_mailbox_trees_sync(struct dsync_brain * brain)312 static void dsync_brain_mailbox_trees_sync(struct dsync_brain *brain)
313 {
314 	struct dsync_mailbox_tree_sync_ctx *ctx;
315 	const struct dsync_mailbox_tree_sync_change *change;
316 	enum dsync_mailbox_trees_sync_type sync_type;
317 	enum dsync_mailbox_trees_sync_flags sync_flags =
318 		(brain->debug ? DSYNC_MAILBOX_TREES_SYNC_FLAG_DEBUG : 0) |
319 		(brain->master_brain ? DSYNC_MAILBOX_TREES_SYNC_FLAG_MASTER_BRAIN : 0) |
320 		(brain->no_mailbox_renames ? DSYNC_MAILBOX_TREES_SYNC_FLAG_NO_RENAMES : 0);
321 	int ret;
322 
323 	if (brain->no_backup_overwrite)
324 		sync_type = DSYNC_MAILBOX_TREES_SYNC_TYPE_TWOWAY;
325 	else if (brain->backup_send)
326 		sync_type = DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL;
327 	else if (brain->backup_recv)
328 		sync_type = DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_REMOTE;
329 	else
330 		sync_type = DSYNC_MAILBOX_TREES_SYNC_TYPE_TWOWAY;
331 
332 	ctx = dsync_mailbox_trees_sync_init(brain->local_mailbox_tree,
333 					    brain->remote_mailbox_tree,
334 					    sync_type, sync_flags);
335 	while ((change = dsync_mailbox_trees_sync_next(ctx)) != NULL) {
336 		T_BEGIN {
337 			ret = dsync_brain_mailbox_tree_sync_change(
338 				brain, change, &brain->mail_error);
339 		} T_END;
340 		if (ret < 0) {
341 			brain->failed = TRUE;
342 			break;
343 		}
344 	}
345 	if (dsync_mailbox_trees_sync_deinit(&ctx) < 0)
346 		brain->failed = TRUE;
347 }
348 
349 static int
dsync_brain_recv_mailbox_tree_add(struct dsync_brain * brain,const char * const * parts,const struct dsync_mailbox_node * remote_node,const char * sep)350 dsync_brain_recv_mailbox_tree_add(struct dsync_brain *brain,
351 				  const char *const *parts,
352 				  const struct dsync_mailbox_node *remote_node,
353 				  const char *sep)
354 {
355 	struct dsync_mailbox_node *node;
356 	struct mail_namespace *ns;
357 	const char *name;
358 
359 	if (dsync_get_mailbox_name(brain, parts, &name, &ns) < 0)
360 		return -1;
361 	if (brain->debug) {
362 		i_debug("brain %c: Remote mailbox tree: %s %s",
363 			brain->master_brain ? 'M' : 'S',
364 			t_strarray_join(parts, sep),
365 			dsync_mailbox_node_to_string(remote_node));
366 	}
367 	node = dsync_mailbox_tree_get(brain->remote_mailbox_tree, name);
368 	node->ns = ns;
369 	dsync_mailbox_node_copy_data(node, remote_node);
370 	return 0;
371 }
372 
dsync_brain_recv_mailbox_tree(struct dsync_brain * brain)373 bool dsync_brain_recv_mailbox_tree(struct dsync_brain *brain)
374 {
375 	const struct dsync_mailbox_node *remote_node;
376 	struct dsync_mailbox_node *dup_node1, *dup_node2;
377 	const char *const *parts;
378 	enum dsync_ibc_recv_ret ret;
379 	int ret2;
380 	char sep[2];
381 	bool changed = FALSE;
382 
383 	sep[0] = brain->hierarchy_sep; sep[1] = '\0';
384 	while ((ret = dsync_ibc_recv_mailbox_tree_node(brain->ibc, &parts,
385 						       &remote_node)) > 0) {
386 		T_BEGIN {
387 			ret2 = dsync_brain_recv_mailbox_tree_add(
388 					brain, parts, remote_node, sep);
389 		} T_END;
390 		if (ret2 < 0) {
391 			i_error("Couldn't find namespace for mailbox %s",
392 				t_strarray_join(parts, sep));
393 			brain->failed = TRUE;
394 			return TRUE;
395 		}
396 	}
397 	if (ret != DSYNC_IBC_RECV_RET_FINISHED)
398 		return changed;
399 
400 	if (dsync_mailbox_tree_build_guid_hash(brain->remote_mailbox_tree,
401 					       &dup_node1, &dup_node2) < 0) {
402 		i_error("Remote sent duplicate mailbox GUID %s for mailboxes %s and %s",
403 			guid_128_to_string(dup_node1->mailbox_guid),
404 			dsync_mailbox_node_get_full_name(brain->remote_mailbox_tree,
405 							 dup_node1),
406 			dsync_mailbox_node_get_full_name(brain->remote_mailbox_tree,
407 							 dup_node2));
408 		brain->failed = TRUE;
409 	}
410 
411 	brain->state = DSYNC_STATE_RECV_MAILBOX_TREE_DELETES;
412 	return TRUE;
413 }
414 
415 static void
dsync_brain_mailbox_tree_add_delete(struct dsync_mailbox_tree * tree,struct dsync_mailbox_tree * other_tree,const struct dsync_mailbox_delete * other_del,const struct dsync_mailbox_node ** node_r,const char ** status_r)416 dsync_brain_mailbox_tree_add_delete(struct dsync_mailbox_tree *tree,
417 				    struct dsync_mailbox_tree *other_tree,
418 				    const struct dsync_mailbox_delete *other_del,
419 				    const struct dsync_mailbox_node **node_r,
420 				    const char **status_r)
421 {
422 	const struct dsync_mailbox_node *node;
423 	struct dsync_mailbox_node *other_node, *old_node;
424 	const char *name;
425 
426 	/* see if we can find the deletion based on mailbox tree that should
427 	   still have the mailbox */
428 	node = *node_r = dsync_mailbox_tree_find_delete(tree, other_del);
429 	if (node == NULL) {
430 		*status_r = "not found";
431 		return;
432 	}
433 
434 	switch (other_del->type) {
435 	case DSYNC_MAILBOX_DELETE_TYPE_MAILBOX:
436 		/* mailbox is always deleted */
437 		break;
438 	case DSYNC_MAILBOX_DELETE_TYPE_DIR:
439 		if (other_del->timestamp <= node->last_renamed_or_created) {
440 			/* we don't want to delete this directory, we already
441 			   have a newer timestamp for it */
442 			*status_r = "keep directory, we have a newer timestamp";
443 			return;
444 		}
445 		break;
446 	case DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE:
447 		if (other_del->timestamp <= node->last_subscription_change) {
448 			/* we don't want to unsubscribe, since we already have
449 			   a newer subscription timestamp */
450 			*status_r = "keep subscription, we have a newer timestamp";
451 			return;
452 		}
453 		break;
454 	}
455 
456 	/* make a node for it in the other mailbox tree */
457 	name = dsync_mailbox_node_get_full_name(tree, node);
458 	other_node = dsync_mailbox_tree_get(other_tree, name);
459 
460 	if (other_node->existence == DSYNC_MAILBOX_NODE_EXISTS &&
461 	    (!guid_128_is_empty(other_node->mailbox_guid) ||
462 	     other_del->type != DSYNC_MAILBOX_DELETE_TYPE_MAILBOX)) {
463 		/* other side has already created a new mailbox or
464 		   directory with this name, we can't delete it */
465 		*status_r = "name has already been recreated";
466 		return;
467 	}
468 
469 	/* ok, mark the other node deleted */
470 	if (other_del->type == DSYNC_MAILBOX_DELETE_TYPE_MAILBOX) {
471 		memcpy(other_node->mailbox_guid, node->mailbox_guid,
472 		       sizeof(other_node->mailbox_guid));
473 	}
474 	if (other_node->ns != node->ns && other_node->ns != NULL) {
475 		/* namespace mismatch for this node. this shouldn't happen
476 		   normally, but especially during some misconfigurations it's
477 		   possible that one side has created mailboxes that conflict
478 		   with another namespace's prefix. since we're here because
479 		   one of the mailboxes was deleted, we'll just ignore this. */
480 		*status_r = "namespace mismatch";
481 		return;
482 	}
483 	other_node->ns = node->ns;
484 	if (other_del->type != DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE) {
485 		other_node->existence = DSYNC_MAILBOX_NODE_DELETED;
486 		*status_r = "marked as deleted";
487 	} else {
488 		other_node->last_subscription_change = other_del->timestamp;
489 		other_node->subscribed = FALSE;
490 		*status_r = "marked as unsubscribed";
491 	}
492 
493 	if (dsync_mailbox_tree_guid_hash_add(other_tree, other_node,
494 					     &old_node) < 0)
495 		i_unreached();
496 }
497 
dsync_brain_recv_mailbox_tree_deletes(struct dsync_brain * brain)498 bool dsync_brain_recv_mailbox_tree_deletes(struct dsync_brain *brain)
499 {
500 	const struct dsync_mailbox_node *node;
501 	const char *status;
502 	const struct dsync_mailbox_delete *deletes;
503 	unsigned int i, count;
504 	char sep;
505 
506 	if (dsync_ibc_recv_mailbox_deletes(brain->ibc, &deletes, &count,
507 					   &sep) == 0)
508 		return FALSE;
509 
510 	/* apply remote's mailbox deletions based on our local tree */
511 	dsync_mailbox_tree_set_remote_sep(brain->local_mailbox_tree, sep);
512 	for (i = 0; i < count; i++) {
513 		dsync_brain_mailbox_tree_add_delete(brain->local_mailbox_tree,
514 						    brain->remote_mailbox_tree,
515 						    &deletes[i], &node, &status);
516 		if (brain->debug) {
517 			const char *node_name = node == NULL ? "" :
518 				dsync_mailbox_node_get_full_name(brain->local_mailbox_tree, node);
519 			i_debug("brain %c: Remote mailbox tree deletion: guid=%s type=%s timestamp=%ld name=%s local update=%s",
520 				brain->master_brain ? 'M' : 'S',
521 				guid_128_to_string(deletes[i].guid),
522 				dsync_mailbox_delete_type_to_string(deletes[i].type),
523 				deletes[i].timestamp, node_name, status);
524 		}
525 	}
526 
527 	/* apply local mailbox deletions based on remote tree */
528 	deletes = dsync_mailbox_tree_get_deletes(brain->local_mailbox_tree,
529 						 &count);
530 	dsync_mailbox_tree_set_remote_sep(brain->remote_mailbox_tree,
531 					  brain->hierarchy_sep);
532 	for (i = 0; i < count; i++) {
533 		dsync_brain_mailbox_tree_add_delete(brain->remote_mailbox_tree,
534 						    brain->local_mailbox_tree,
535 						    &deletes[i], &node, &status);
536 	}
537 
538 	dsync_brain_mailbox_trees_sync(brain);
539 	brain->state = brain->master_brain ?
540 		DSYNC_STATE_MASTER_SEND_MAILBOX :
541 		DSYNC_STATE_SLAVE_RECV_MAILBOX;
542 	i_assert(brain->local_tree_iter == NULL);
543 	brain->local_tree_iter =
544 		dsync_mailbox_tree_iter_init(brain->local_mailbox_tree);
545 	return TRUE;
546 }
547