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