1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "mail-namespace.h"
5 #include "mail-storage.h"
6 #include "dsync-mailbox-tree.h"
7 #include "dsync-brain-private.h"
8 
9 static int
sync_create_box(struct dsync_brain * brain,struct mailbox * box,const guid_128_t mailbox_guid,uint32_t uid_validity,enum mail_error * error_r)10 sync_create_box(struct dsync_brain *brain, struct mailbox *box,
11 		const guid_128_t mailbox_guid, uint32_t uid_validity,
12 		enum mail_error *error_r)
13 {
14 	struct mailbox_metadata metadata;
15 	struct mailbox_update update;
16 	enum mail_error error;
17 	const char *errstr;
18 	int ret;
19 
20 	i_zero(&update);
21 	memcpy(update.mailbox_guid, mailbox_guid, sizeof(update.mailbox_guid));
22 	update.uid_validity = uid_validity;
23 
24 	if (mailbox_create(box, &update, FALSE) < 0) {
25 		errstr = mailbox_get_last_internal_error(box, &error);
26 		if (error != MAIL_ERROR_EXISTS) {
27 			i_error("Can't create mailbox %s: %s",
28 				mailbox_get_vname(box), errstr);
29 			*error_r = error;
30 			return -1;
31 		}
32 	}
33 	if (brain->no_mail_sync) {
34 		/* trust that create worked, we can't actually open it
35 		   and verify. */
36 		return 0;
37 	}
38 	/* sync the mailbox so we can look up its latest status */
39 	if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
40 		i_error("Can't sync mailbox %s: %s",
41 			mailbox_get_vname(box),
42 			mailbox_get_last_internal_error(box, error_r));
43 		return -1;
44 	}
45 
46 	/* verify that the GUID is what we wanted. if it's not, it probably
47 	   means that the mailbox had already been created. then we'll use the
48 	   GUID that is higher.
49 
50 	   mismatching UIDVALIDITY is handled later, because we choose it by
51 	   checking which mailbox has more messages */
52 	if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) {
53 		i_error("Can't get mailbox GUID %s: %s",
54 			mailbox_get_vname(box),
55 			mailbox_get_last_internal_error(box, error_r));
56 		return -1;
57 	}
58 
59 	ret = memcmp(mailbox_guid, metadata.guid, sizeof(metadata.guid));
60 
61 	/* if THEIR guid is bigger than OUR guid, and we are not doing
62 	   backup in either direction, OR GUID did not match and we are
63 	   receiving backup, try change the mailbox GUID.
64 	*/
65 
66 	if ((ret > 0 && !brain->backup_recv &&
67 	     !brain->backup_send) || (ret != 0 && brain->backup_recv)) {
68 		if (brain->debug) {
69 			i_debug("brain %c: Changing mailbox %s GUID %s -> %s",
70 				brain->master_brain ? 'M' : 'S',
71 				mailbox_get_vname(box),
72 				guid_128_to_string(metadata.guid),
73 				guid_128_to_string(mailbox_guid));
74 		}
75 		i_zero(&update);
76 		memcpy(update.mailbox_guid, mailbox_guid,
77 		       sizeof(update.mailbox_guid));
78 		if (mailbox_update(box, &update) < 0) {
79 			i_error("Can't update mailbox GUID %s: %s",
80 				mailbox_get_vname(box),
81 				mailbox_get_last_internal_error(box, error_r));
82 			return -1;
83 		}
84 		/* verify that the update worked */
85 		if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID,
86 					 &metadata) < 0) {
87 			i_error("Can't get mailbox GUID %s: %s",
88 				mailbox_get_vname(box),
89 				mailbox_get_last_internal_error(box, error_r));
90 			return -1;
91 		}
92 		if (memcmp(mailbox_guid, metadata.guid,
93 			   sizeof(metadata.guid)) != 0) {
94 			i_error("Backend didn't update mailbox %s GUID",
95 				mailbox_get_vname(box));
96 			*error_r = MAIL_ERROR_TEMP;
97 			return -1;
98 		}
99 	} else if (ret < 0) {
100 		if (brain->debug) {
101 			i_debug("brain %c: Other brain should change mailbox "
102 				"%s GUID %s -> %s",
103 				brain->master_brain ? 'M' : 'S',
104 				mailbox_get_vname(box),
105 				guid_128_to_string(mailbox_guid),
106 				guid_128_to_string(metadata.guid));
107 		}
108 	}
109 	return 0;
110 }
111 
dsync_brain_mailbox_tree_sync_change(struct dsync_brain * brain,const struct dsync_mailbox_tree_sync_change * change,enum mail_error * error_r)112 int dsync_brain_mailbox_tree_sync_change(struct dsync_brain *brain,
113 			const struct dsync_mailbox_tree_sync_change *change,
114 			enum mail_error *error_r)
115 {
116 	struct mailbox *box = NULL, *destbox;
117 	const char *errstr, *func_name = NULL, *storage_name;
118 	enum mail_error error;
119 	int ret = -1;
120 
121 	if (brain->backup_send) {
122 		i_assert(brain->no_backup_overwrite);
123 		return 0;
124 	}
125 
126 	switch (change->type) {
127 	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX:
128 		/* make sure we're deleting the correct mailbox */
129 		ret = dsync_brain_mailbox_alloc(brain, change->mailbox_guid,
130 						&box, &errstr, error_r);
131 		if (ret < 0) {
132 			i_error("Mailbox sync: Couldn't allocate mailbox %s GUID %s: %s",
133 				change->full_name,
134 				guid_128_to_string(change->mailbox_guid), errstr);
135 			return -1;
136 		}
137 		if (ret == 0) {
138 			dsync_brain_set_changes_during_sync(brain, t_strdup_printf(
139 				"Mailbox %s GUID %s deletion conflict: %s",
140 				change->full_name,
141 				guid_128_to_string(change->mailbox_guid), errstr));
142 			return 0;
143 		}
144 		break;
145 	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR:
146 		storage_name = mailbox_list_get_storage_name(change->ns->list,
147 							     change->full_name);
148 		if (mailbox_list_delete_dir(change->ns->list, storage_name) == 0)
149 			return 0;
150 
151 		errstr = mailbox_list_get_last_internal_error(change->ns->list, &error);
152 		if (error == MAIL_ERROR_NOTFOUND ||
153 		    error == MAIL_ERROR_EXISTS) {
154 			dsync_brain_set_changes_during_sync(brain, t_strdup_printf(
155 				"Mailbox %s mailbox_list_delete_dir conflict: %s",
156 				change->full_name, errstr));
157 			return 0;
158 		} else {
159 			i_error("Mailbox sync: mailbox_list_delete_dir failed: %s",
160 				errstr);
161 			*error_r = error;
162 			return -1;
163 		}
164 	default:
165 		box = mailbox_alloc(change->ns->list, change->full_name, 0);
166 		break;
167 	}
168 	mailbox_skip_create_name_restrictions(box, TRUE);
169 	switch (change->type) {
170 	case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_BOX:
171 		ret = sync_create_box(brain, box, change->mailbox_guid,
172 				      change->uid_validity, error_r);
173 		mailbox_free(&box);
174 		return ret;
175 	case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_DIR:
176 		ret = mailbox_create(box, NULL, TRUE);
177 		if (ret < 0 &&
178 		    mailbox_get_last_mail_error(box) == MAIL_ERROR_EXISTS) {
179 			/* it doesn't matter if somebody else created this
180 			   directory or we automatically did while creating its
181 			   child mailbox. it's there now anyway and we don't
182 			   gain anything by treating this failure any
183 			   differently from success. */
184 			ret = 0;
185 		}
186 		func_name = "mailbox_create";
187 		break;
188 	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX:
189 		ret = mailbox_delete(box);
190 		func_name = "mailbox_delete";
191 		break;
192 	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR:
193 		i_unreached();
194 	case DSYNC_MAILBOX_TREE_SYNC_TYPE_RENAME:
195 		destbox = mailbox_alloc(change->ns->list,
196 					change->rename_dest_name, 0);
197 		mailbox_skip_create_name_restrictions(destbox, TRUE);
198 		ret = mailbox_rename(box, destbox);
199 		func_name = "mailbox_rename";
200 		mailbox_free(&destbox);
201 		break;
202 	case DSYNC_MAILBOX_TREE_SYNC_TYPE_SUBSCRIBE:
203 		ret = mailbox_set_subscribed(box, TRUE);
204 		func_name = "mailbox_set_subscribed";
205 		break;
206 	case DSYNC_MAILBOX_TREE_SYNC_TYPE_UNSUBSCRIBE:
207 		ret = mailbox_set_subscribed(box, FALSE);
208 		func_name = "mailbox_set_subscribed";
209 		break;
210 	}
211 	if (ret < 0) {
212 		errstr = mailbox_get_last_internal_error(box, &error);
213 		if (error == MAIL_ERROR_EXISTS ||
214 		    error == MAIL_ERROR_NOTFOUND) {
215 			/* mailbox was already created or was already deleted.
216 			   let the next sync figure out what to do */
217 			dsync_brain_set_changes_during_sync(brain, t_strdup_printf(
218 				"Mailbox %s %s conflict: %s",
219 				mailbox_get_vname(box), func_name, errstr));
220 			ret = 0;
221 		} else {
222 			i_error("Mailbox %s sync: %s failed: %s",
223 				mailbox_get_vname(box), func_name, errstr);
224 			*error_r = error;
225 		}
226 	}
227 	mailbox_free(&box);
228 	return ret;
229 }
230