/src/dovecot/src/lib-storage/mail-storage.c
Line | Count | Source |
1 | | /* Copyright (c) Dovecot authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "ioloop.h" |
5 | | #include "array.h" |
6 | | #include "llist.h" |
7 | | #include "mail-storage.h" |
8 | | #include "str.h" |
9 | | #include "str-parse.h" |
10 | | #include "sha1.h" |
11 | | #include "unicode-data.h" |
12 | | #include "unichar.h" |
13 | | #include "hex-binary.h" |
14 | | #include "fs-api.h" |
15 | | #include "file-dotlock.h" |
16 | | #include "file-create-locked.h" |
17 | | #include "istream.h" |
18 | | #include "eacces-error.h" |
19 | | #include "mkdir-parents.h" |
20 | | #include "time-util.h" |
21 | | #include "wildcard-match.h" |
22 | | #include "settings.h" |
23 | | #include "dsasl-client.h" |
24 | | #include "imap-date.h" |
25 | | #include "mail-index-private.h" |
26 | | #include "mail-index-alloc-cache.h" |
27 | | #include "mailbox-tree.h" |
28 | | #include "mailbox-list-private.h" |
29 | | #include "mail-storage-private.h" |
30 | | #include "mail-storage-service.h" |
31 | | #include "mail-storage-settings.h" |
32 | | #include "mail-namespace.h" |
33 | | #include "mail-search.h" |
34 | | #include "mail-search-register.h" |
35 | | #include "mail-search-mime-register.h" |
36 | | #include "mailbox-search-result-private.h" |
37 | | #include "mailbox-guid-cache.h" |
38 | | #include "mail-cache.h" |
39 | | #include "utc-mktime.h" |
40 | | |
41 | | #include <ctype.h> |
42 | | |
43 | 0 | #define MAILBOX_DELETE_RETRY_SECS 30 |
44 | 0 | #define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 255 |
45 | | |
46 | | extern struct mail_search_register *mail_search_register_imap4rev2; |
47 | | extern struct mail_search_register *mail_search_register_imap4rev1; |
48 | | extern struct mail_search_register *mail_search_register_human; |
49 | | |
50 | | struct event_category event_category_storage = { |
51 | | .name = "storage", |
52 | | }; |
53 | | struct event_category event_category_mailbox = { |
54 | | .parent = &event_category_storage, |
55 | | .name = "mailbox", |
56 | | }; |
57 | | struct event_category event_category_mail = { |
58 | | .parent = &event_category_mailbox, |
59 | | .name = "mail", |
60 | | }; |
61 | | |
62 | | struct mail_storage_module_register mail_storage_module_register = { 0 }; |
63 | | struct mail_module_register mail_module_register = { 0 }; |
64 | | |
65 | | struct mail_storage_mail_index_module mail_storage_mail_index_module = |
66 | | MODULE_CONTEXT_INIT(&mail_index_module_register); |
67 | | ARRAY_TYPE(mail_storage) mail_storage_classes; |
68 | | |
69 | | static int mail_storage_init_refcount = 0; |
70 | | |
71 | | static const char * |
72 | | mailbox_get_name_without_prefix(struct mail_namespace *ns, |
73 | | const char *vname) |
74 | 0 | { |
75 | 0 | if (ns->prefix_len > 0 && |
76 | 0 | strncmp(ns->prefix, vname, ns->prefix_len-1) == 0) { |
77 | 0 | if (vname[ns->prefix_len-1] == mail_namespace_get_sep(ns)) |
78 | 0 | vname += ns->prefix_len; |
79 | 0 | else if (vname[ns->prefix_len-1] == '\0') { |
80 | | /* namespace prefix itself */ |
81 | 0 | vname = ""; |
82 | 0 | } |
83 | 0 | } |
84 | 0 | return vname; |
85 | 0 | } |
86 | | |
87 | | void mail_storage_init(void) |
88 | 0 | { |
89 | 0 | if (mail_storage_init_refcount++ > 0) |
90 | 0 | return; |
91 | 0 | dsasl_clients_init(); |
92 | 0 | mailbox_attributes_init(); |
93 | 0 | mailbox_lists_init(); |
94 | 0 | mail_storage_hooks_init(); |
95 | 0 | i_array_init(&mail_storage_classes, 8); |
96 | 0 | mail_storage_register_all(); |
97 | 0 | mailbox_list_register_all(); |
98 | 0 | settings_info_register(&mail_storage_setting_parser_info); |
99 | 0 | } |
100 | | |
101 | | void mail_storage_deinit(void) |
102 | 0 | { |
103 | 0 | i_assert(mail_storage_init_refcount > 0); |
104 | 0 | if (--mail_storage_init_refcount > 0) |
105 | 0 | return; |
106 | 0 | if (mail_search_register_human != NULL) |
107 | 0 | mail_search_register_deinit(&mail_search_register_human); |
108 | 0 | if (mail_search_register_imap4rev1 != NULL) |
109 | 0 | mail_search_register_deinit(&mail_search_register_imap4rev1); |
110 | 0 | if (mail_search_register_imap4rev2 != NULL) |
111 | 0 | mail_search_register_deinit(&mail_search_register_imap4rev2); |
112 | 0 | mail_search_mime_register_deinit(); |
113 | 0 | if (array_is_created(&mail_storage_classes)) |
114 | 0 | array_free(&mail_storage_classes); |
115 | 0 | mail_storage_hooks_deinit(); |
116 | 0 | mailbox_lists_deinit(); |
117 | 0 | mailbox_attributes_deinit(); |
118 | 0 | dsasl_clients_deinit(); |
119 | 0 | } |
120 | | |
121 | | void mail_storage_class_register(struct mail_storage *storage_class) |
122 | 0 | { |
123 | 0 | i_assert(mail_storage_find_class(storage_class->name) == NULL); |
124 | | |
125 | 0 | if (storage_class->set_info != NULL) |
126 | 0 | settings_info_register(storage_class->set_info); |
127 | | |
128 | | /* append it after the list, so the autodetection order is correct */ |
129 | 0 | array_push_back(&mail_storage_classes, &storage_class); |
130 | 0 | } |
131 | | |
132 | | void mail_storage_class_unregister(struct mail_storage *storage_class) |
133 | 0 | { |
134 | 0 | unsigned int i; |
135 | |
|
136 | 0 | if (!array_lsearch_ptr_idx(&mail_storage_classes, storage_class, &i)) |
137 | 0 | i_unreached(); |
138 | 0 | array_delete(&mail_storage_classes, i, 1); |
139 | 0 | } |
140 | | |
141 | | struct mail_storage *mail_storage_find_class(const char *name) |
142 | 0 | { |
143 | 0 | struct mail_storage *const *classes; |
144 | 0 | unsigned int i, count; |
145 | |
|
146 | 0 | i_assert(name != NULL); |
147 | | |
148 | 0 | classes = array_get(&mail_storage_classes, &count); |
149 | 0 | for (i = 0; i < count; i++) { |
150 | 0 | if (strcasecmp(classes[i]->name, name) == 0) |
151 | 0 | return classes[i]; |
152 | 0 | } |
153 | 0 | return NULL; |
154 | 0 | } |
155 | | |
156 | | static struct mail_storage * |
157 | | mail_storage_autodetect(const struct mail_namespace *ns, |
158 | | const struct mail_storage_settings *mail_set, |
159 | | const char **root_path_override, |
160 | | const char **inbox_path_override) |
161 | 0 | { |
162 | 0 | struct mail_storage *const *classes; |
163 | 0 | const char *root_path, *inbox_path = NULL; |
164 | 0 | unsigned int i, count; |
165 | |
|
166 | 0 | classes = array_get(&mail_storage_classes, &count); |
167 | 0 | for (i = 0; i < count; i++) { |
168 | 0 | if (classes[i]->v.autodetect != NULL) { |
169 | 0 | if (classes[i]->v.autodetect(ns, mail_set, |
170 | 0 | &root_path, &inbox_path)) { |
171 | 0 | *root_path_override = root_path; |
172 | 0 | *inbox_path_override = inbox_path; |
173 | 0 | return classes[i]; |
174 | 0 | } |
175 | 0 | } |
176 | 0 | } |
177 | 0 | return NULL; |
178 | 0 | } |
179 | | |
180 | | static struct mail_storage * |
181 | | mail_storage_get_class(struct mail_namespace *ns, const char *driver, |
182 | | struct event *set_event, |
183 | | const char **root_path_override, |
184 | | const char **inbox_path_override, const char **error_r) |
185 | 0 | { |
186 | 0 | struct mail_storage *storage_class = NULL; |
187 | 0 | const char *home; |
188 | |
|
189 | 0 | if (driver[0] == '\0') { |
190 | | /* empty mail_driver setting, autodetect */ |
191 | 0 | } else if (strcmp(driver, "auto") == 0) { |
192 | | /* explicit autodetection with "auto" driver. */ |
193 | 0 | } else { |
194 | 0 | storage_class = mail_user_get_storage_class(ns->user, driver); |
195 | 0 | if (storage_class == NULL) { |
196 | 0 | *error_r = t_strdup_printf( |
197 | 0 | "Unknown mail storage driver %s", driver); |
198 | 0 | return NULL; |
199 | 0 | } |
200 | 0 | } |
201 | | |
202 | 0 | if (storage_class != NULL) |
203 | 0 | return storage_class; |
204 | | |
205 | 0 | const struct mail_storage_settings *mail_set; |
206 | 0 | if (settings_get(set_event, &mail_storage_setting_parser_info, 0, |
207 | 0 | &mail_set, error_r) < 0) |
208 | 0 | return NULL; |
209 | | |
210 | 0 | storage_class = mail_storage_autodetect(ns, mail_set, root_path_override, |
211 | 0 | inbox_path_override); |
212 | 0 | if (storage_class != NULL) { |
213 | 0 | settings_free(mail_set); |
214 | 0 | return storage_class; |
215 | 0 | } |
216 | | |
217 | 0 | (void)mail_user_get_home(ns->user, &home); |
218 | 0 | if (home == NULL || *home == '\0') home = "(not set)"; |
219 | |
|
220 | 0 | *error_r = t_strdup_printf( |
221 | 0 | "Mail storage autodetection failed (home=%s, mail_path=%s) - " |
222 | 0 | "Set mail_driver explicitly", |
223 | 0 | home, mail_set->mail_path); |
224 | 0 | settings_free(mail_set); |
225 | 0 | return NULL; |
226 | 0 | } |
227 | | |
228 | | static int |
229 | | mail_storage_verify_root(const char *root_dir, const char *dir_type, |
230 | | const char **error_r) |
231 | 0 | { |
232 | 0 | struct stat st; |
233 | |
|
234 | 0 | if (stat(root_dir, &st) == 0) { |
235 | | /* exists */ |
236 | 0 | if (S_ISDIR(st.st_mode)) |
237 | 0 | return 0; |
238 | 0 | *error_r = t_strdup_printf( |
239 | 0 | "Root mail directory is a file: %s", root_dir); |
240 | 0 | return -1; |
241 | 0 | } else if (ENOACCESS(errno)) { |
242 | 0 | *error_r = mail_error_eacces_msg("stat", root_dir); |
243 | 0 | return -1; |
244 | 0 | } else if (errno != ENOENT) { |
245 | 0 | *error_r = t_strdup_printf("stat(%s) failed: %m", root_dir); |
246 | 0 | return -1; |
247 | 0 | } else { |
248 | 0 | *error_r = t_strdup_printf( |
249 | 0 | "Root %s directory doesn't exist: %s", dir_type, root_dir); |
250 | 0 | return -1; |
251 | 0 | } |
252 | 0 | } |
253 | | |
254 | | static int |
255 | | mail_storage_create_root(struct mailbox_list *list, |
256 | | enum mail_storage_flags flags, const char **error_r) |
257 | 0 | { |
258 | 0 | const char *root_dir, *type_name, *error; |
259 | 0 | enum mailbox_list_path_type type; |
260 | |
|
261 | 0 | if (list->mail_set->mailbox_list_iter_from_index_dir) { |
262 | 0 | type = MAILBOX_LIST_PATH_TYPE_INDEX; |
263 | 0 | type_name = "index"; |
264 | 0 | } else { |
265 | 0 | type = MAILBOX_LIST_PATH_TYPE_MAILBOX; |
266 | 0 | type_name = "mail"; |
267 | 0 | } |
268 | 0 | if (!mailbox_list_get_root_path(list, type, &root_dir)) { |
269 | | /* storage doesn't use directories (e.g. shared root) */ |
270 | 0 | return 0; |
271 | 0 | } |
272 | | |
273 | 0 | if ((flags & MAIL_STORAGE_FLAG_NO_AUTOVERIFY) != 0) { |
274 | 0 | if (!event_want_debug_log(list->event)) |
275 | 0 | return 0; |
276 | | |
277 | | /* we don't need to verify, but since debugging is |
278 | | enabled, check and log if the root doesn't exist */ |
279 | 0 | if (mail_storage_verify_root(root_dir, type_name, &error) < 0) { |
280 | 0 | e_debug(list->event, |
281 | 0 | "Namespace %s: Creating storage despite: %s", |
282 | 0 | list->ns->set->name, error); |
283 | 0 | } |
284 | 0 | return 0; |
285 | 0 | } |
286 | | |
287 | 0 | if ((flags & MAIL_STORAGE_FLAG_NO_AUTOCREATE) == 0) { |
288 | | /* If the directories don't exist, we'll just autocreate them |
289 | | later. */ |
290 | 0 | return 0; |
291 | 0 | } |
292 | 0 | return mail_storage_verify_root(root_dir, type_name, error_r); |
293 | 0 | } |
294 | | |
295 | | static bool |
296 | | mail_storage_match_class(struct mail_storage *storage, |
297 | | const struct mail_storage *storage_class, |
298 | | const struct mail_storage_settings *mail_set) |
299 | 0 | { |
300 | 0 | if (strcmp(storage->name, storage_class->name) != 0) |
301 | 0 | return FALSE; |
302 | | |
303 | 0 | if ((storage->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) != 0 && |
304 | 0 | strcmp(storage->unique_root_dir, mail_set->mail_path) != 0) |
305 | 0 | return FALSE; |
306 | | |
307 | 0 | if (strcmp(storage->name, "shared") == 0) { |
308 | | /* allow multiple independent shared namespaces */ |
309 | 0 | return FALSE; |
310 | 0 | } |
311 | 0 | return TRUE; |
312 | 0 | } |
313 | | |
314 | | static struct mail_storage * |
315 | | mail_storage_find(struct mail_user *user, |
316 | | const struct mail_storage *storage_class, |
317 | | const struct mail_storage_settings *mail_set) |
318 | 0 | { |
319 | 0 | struct mail_storage *storage = user->storages; |
320 | |
|
321 | 0 | for (; storage != NULL; storage = storage->next) { |
322 | 0 | if (mail_storage_match_class(storage, storage_class, mail_set)) |
323 | 0 | return storage; |
324 | 0 | } |
325 | 0 | return NULL; |
326 | 0 | } |
327 | | |
328 | | static void |
329 | | mail_storage_create_ns_instance(struct mail_namespace *ns, |
330 | | struct event *set_event) |
331 | 0 | { |
332 | 0 | if (ns->_set_instance != NULL) |
333 | 0 | return; |
334 | | |
335 | 0 | struct settings_instance *set_instance = |
336 | 0 | mail_storage_service_user_get_settings_instance( |
337 | 0 | ns->user->service_user); |
338 | 0 | ns->_set_instance = settings_instance_dup(set_instance); |
339 | 0 | event_set_ptr(set_event, SETTINGS_EVENT_INSTANCE, ns->_set_instance); |
340 | 0 | } |
341 | | |
342 | | static int |
343 | | mail_storage_create_list(struct mail_namespace *ns, |
344 | | struct mail_storage *storage_class, |
345 | | struct event *parent_set_event, |
346 | | enum mail_storage_flags flags, |
347 | | const char *root_path_override, |
348 | | const char *inbox_path_override, |
349 | | const char **error_r) |
350 | 0 | { |
351 | 0 | enum mailbox_list_flags list_flags = 0; |
352 | 0 | if (mail_storage_is_mailbox_file(storage_class)) |
353 | 0 | list_flags |= MAILBOX_LIST_FLAG_MAILBOX_FILES; |
354 | 0 | if ((storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) != 0) |
355 | 0 | list_flags |= MAILBOX_LIST_FLAG_NO_MAIL_FILES; |
356 | 0 | if ((storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_LIST_DELETES) != 0) |
357 | 0 | list_flags |= MAILBOX_LIST_FLAG_NO_DELETES; |
358 | |
|
359 | 0 | struct mailbox_list *list; |
360 | 0 | struct event *set_event = event_create(parent_set_event); |
361 | | /* Lookup storage-specific settings, especially to get |
362 | | storage-specific defaults for mailbox list settings. */ |
363 | 0 | settings_event_add_filter_name(set_event, storage_class->name); |
364 | | /* Set namespace, but don't overwrite if it already is set. |
365 | | Shared storage uses the same shared namespace here also for the |
366 | | user's root prefix="" namespace. */ |
367 | 0 | if (event_find_field_recursive(set_event, SETTINGS_EVENT_NAMESPACE_NAME) == NULL) { |
368 | 0 | event_add_str(set_event, SETTINGS_EVENT_NAMESPACE_NAME, ns->set->name); |
369 | 0 | settings_event_add_list_filter_name(set_event, |
370 | 0 | SETTINGS_EVENT_NAMESPACE_NAME, ns->set->name); |
371 | 0 | } |
372 | |
|
373 | 0 | if ((flags & MAIL_STORAGE_FLAG_SHARED_DYNAMIC) != 0) { |
374 | 0 | mail_storage_create_ns_instance(ns, set_event); |
375 | 0 | settings_override(ns->_set_instance, |
376 | 0 | "*/mailbox_list_layout", "shared", |
377 | 0 | SETTINGS_OVERRIDE_TYPE_CODE); |
378 | 0 | } |
379 | |
|
380 | 0 | const struct mailbox_list_layout_settings *layout_set; |
381 | 0 | if (settings_get(set_event, &mailbox_list_layout_setting_parser_info, 0, |
382 | 0 | &layout_set, error_r) < 0) { |
383 | 0 | event_unref(&set_event); |
384 | 0 | return -1; |
385 | 0 | } |
386 | | |
387 | | /* Lookup also layout-specific settings, especially defaults */ |
388 | 0 | struct event *set_event2 = event_create(set_event); |
389 | 0 | event_unref(&set_event); |
390 | 0 | set_event = set_event2; |
391 | 0 | settings_event_add_filter_name(set_event, t_strdup_printf("layout_%s", |
392 | 0 | t_str_lcase(layout_set->mailbox_list_layout))); |
393 | 0 | settings_free(layout_set); |
394 | |
|
395 | 0 | if (root_path_override != NULL) { |
396 | 0 | mail_storage_create_ns_instance(ns, set_event); |
397 | 0 | settings_override(ns->_set_instance, "*/mail_path", |
398 | 0 | root_path_override, |
399 | 0 | SETTINGS_OVERRIDE_TYPE_CODE); |
400 | 0 | } |
401 | 0 | if (inbox_path_override != NULL) { |
402 | 0 | mail_storage_create_ns_instance(ns, set_event); |
403 | 0 | settings_override(ns->_set_instance, "*/mail_inbox_path", |
404 | 0 | inbox_path_override, |
405 | 0 | SETTINGS_OVERRIDE_TYPE_CODE); |
406 | 0 | } |
407 | |
|
408 | 0 | const struct mail_storage_settings *mail_set; |
409 | 0 | if (settings_get(set_event, &mail_storage_setting_parser_info, 0, |
410 | 0 | &mail_set, error_r) < 0) { |
411 | 0 | event_unref(&set_event); |
412 | 0 | return -1; |
413 | 0 | } |
414 | | |
415 | 0 | if (mail_set->mail_path[0] == '\0') { |
416 | | /* no root directory given. is this allowed? */ |
417 | 0 | if ((flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) == 0) { |
418 | | /* autodetection should take care of this */ |
419 | 0 | } else if ((storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) != 0) { |
420 | | /* root not required for this storage */ |
421 | 0 | } else { |
422 | 0 | *error_r = "Root mail directory not given"; |
423 | 0 | settings_free(mail_set); |
424 | 0 | event_unref(&set_event); |
425 | 0 | return -1; |
426 | 0 | } |
427 | 0 | } |
428 | | |
429 | | /* Use parent_set_event instead of set_event mainly to avoid |
430 | | permanently having SETTINGS_EVENT_FILTER_NAME=storage_name in |
431 | | mailbox_list->event. This would be wrong, since mailbox_list can |
432 | | support multiple storages. */ |
433 | 0 | struct event *event = event_create(parent_set_event); |
434 | 0 | event_add_str(event, SETTINGS_EVENT_NAMESPACE_NAME, ns->set->name); |
435 | 0 | settings_event_add_list_filter_name(event, |
436 | 0 | SETTINGS_EVENT_NAMESPACE_NAME, ns->set->name); |
437 | 0 | int ret = mailbox_list_create(event, ns, mail_set, list_flags, |
438 | 0 | &list, error_r); |
439 | 0 | if (ret < 0) { |
440 | 0 | *error_r = t_strdup_printf("mailbox_list_layout %s: %s", |
441 | 0 | mail_set->mailbox_list_layout, *error_r); |
442 | 0 | } |
443 | 0 | settings_free(mail_set); |
444 | 0 | event_unref(&event); |
445 | 0 | event_unref(&set_event); |
446 | 0 | return ret; |
447 | 0 | } |
448 | | |
449 | | static bool ATTR_PURE pop3_uidl_format_has_md5(const char *fmt) |
450 | 0 | { |
451 | 0 | struct var_expand_program *prog; |
452 | 0 | const char *error; |
453 | 0 | if (var_expand_program_create(fmt, &prog, &error) < 0) |
454 | 0 | i_fatal("Invalid pop3_uidl_format: %s", error); |
455 | 0 | const char *const *vars = var_expand_program_variables(prog); |
456 | 0 | bool has_md5 = str_array_find(vars, "md5"); |
457 | 0 | var_expand_program_free(&prog); |
458 | 0 | return has_md5; |
459 | 0 | } |
460 | | |
461 | | static int |
462 | | mail_storage_create_real(struct mail_namespace *ns, struct event *set_event, |
463 | | enum mail_storage_flags flags, |
464 | | struct mail_storage **storage_r, const char **error_r) |
465 | 0 | { |
466 | 0 | struct mail_storage *storage_class, *storage = NULL; |
467 | 0 | const struct mail_driver_settings *driver_set; |
468 | 0 | const char *driver = NULL; |
469 | 0 | const char *inbox_path_override = NULL; |
470 | 0 | const char *root_path_override = NULL; |
471 | | |
472 | | /* Lookup initial mailbox list settings. Once they're found, another |
473 | | settings lookup is done with mailbox format as an additional |
474 | | filter. */ |
475 | 0 | if (settings_get(set_event, &mail_driver_setting_parser_info, 0, |
476 | 0 | &driver_set, error_r) < 0) |
477 | 0 | return -1; |
478 | 0 | driver = driver_set->mail_driver; |
479 | |
|
480 | 0 | if ((flags & MAIL_STORAGE_FLAG_SHARED_DYNAMIC) != 0) { |
481 | | /* internal shared namespace */ |
482 | 0 | driver = MAIL_SHARED_STORAGE_NAME; |
483 | 0 | root_path_override = ns->user->set->base_dir; |
484 | 0 | } |
485 | |
|
486 | 0 | storage_class = mail_storage_get_class(ns, driver, set_event, |
487 | 0 | &root_path_override, |
488 | 0 | &inbox_path_override, error_r); |
489 | 0 | settings_free(driver_set); |
490 | 0 | if (storage_class == NULL) |
491 | 0 | return -1; |
492 | | |
493 | 0 | if (ns->list == NULL) { |
494 | | /* first storage for namespace */ |
495 | 0 | if (mail_storage_create_list(ns, storage_class, set_event, |
496 | 0 | flags, root_path_override, |
497 | 0 | inbox_path_override, error_r) < 0) |
498 | 0 | return -1; |
499 | 0 | if ((storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) == 0) { |
500 | 0 | if (mail_storage_create_root(ns->list, flags, error_r) < 0) |
501 | 0 | return -1; |
502 | 0 | } |
503 | 0 | } |
504 | | |
505 | 0 | storage = mail_storage_find(ns->user, storage_class, |
506 | 0 | ns->list->mail_set); |
507 | 0 | if (storage != NULL) { |
508 | | /* using an existing storage */ |
509 | 0 | storage->refcount++; |
510 | 0 | mail_namespace_add_storage(ns, storage); |
511 | 0 | *storage_r = storage; |
512 | 0 | return 0; |
513 | 0 | } |
514 | | |
515 | 0 | if ((flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) == 0 && |
516 | 0 | ns->list->mail_set->pop3_uidl_format != NULL) { |
517 | | /* if pop3_uidl_format contains %m, we want to keep the |
518 | | header MD5 sums stored even if we're not running POP3 |
519 | | right now. */ |
520 | 0 | if (pop3_uidl_format_has_md5(ns->list->mail_set->pop3_uidl_format)) |
521 | 0 | flags |= MAIL_STORAGE_FLAG_KEEP_HEADER_MD5; |
522 | 0 | } |
523 | |
|
524 | 0 | storage = storage_class->v.alloc(); |
525 | 0 | storage->refcount = 1; |
526 | 0 | storage->storage_class = storage_class; |
527 | 0 | storage->user = ns->user; |
528 | 0 | storage->set = ns->list->mail_set; |
529 | 0 | pool_ref(storage->set->pool); |
530 | 0 | storage->flags = flags; |
531 | | /* Set to UINT32_MAX manually to denote 'unset', as the default 0 is |
532 | | used for mails currently being saved. */ |
533 | 0 | storage->last_internal_error_mail_uid = UINT32_MAX; |
534 | |
|
535 | 0 | storage->event = event_create(ns->user->event); |
536 | 0 | if (storage_class->event_category != NULL) |
537 | 0 | event_add_category(storage->event, storage_class->event_category); |
538 | 0 | event_set_append_log_prefix( |
539 | 0 | storage->event, t_strdup_printf("%s: ", storage_class->name)); |
540 | 0 | p_array_init(&storage->module_contexts, storage->pool, 5); |
541 | |
|
542 | 0 | if (storage->v.create != NULL && |
543 | 0 | storage->v.create(storage, ns, error_r) < 0) { |
544 | 0 | *error_r = t_strdup_printf("%s: %s", storage->name, *error_r); |
545 | 0 | storage->v.destroy(storage); |
546 | 0 | settings_free(storage->set); |
547 | 0 | event_unref(&storage->event); |
548 | 0 | pool_unref(&storage->pool); |
549 | 0 | return -1; |
550 | 0 | } |
551 | | |
552 | | /* If storage supports list index rebuild, |
553 | | provide default mailboxes_fs unless storage |
554 | | wants to use its own. */ |
555 | 0 | if (storage->v.list_index_rebuild != NULL && |
556 | 0 | storage->mailboxes_fs == NULL) { |
557 | 0 | struct fs_parameters fs_params; |
558 | 0 | const char *error; |
559 | 0 | i_zero(&fs_params); |
560 | 0 | mail_user_init_fs_parameters(storage->user, &fs_params); |
561 | |
|
562 | 0 | struct settings_instance *set_instance = |
563 | 0 | mail_storage_service_user_get_settings_instance( |
564 | 0 | storage->user->service_user); |
565 | 0 | storage->mailboxes_fs_set_instance = |
566 | 0 | settings_instance_dup(set_instance); |
567 | 0 | settings_override(storage->mailboxes_fs_set_instance, |
568 | 0 | "*/fs", "__posix", |
569 | 0 | SETTINGS_OVERRIDE_TYPE_CODE); |
570 | 0 | settings_override(storage->mailboxes_fs_set_instance, |
571 | 0 | "fs/__posix/fs_driver", "posix", |
572 | 0 | SETTINGS_OVERRIDE_TYPE_CODE); |
573 | |
|
574 | 0 | struct event *event = event_create(storage->event); |
575 | 0 | event_set_ptr(event, SETTINGS_EVENT_INSTANCE, |
576 | 0 | storage->mailboxes_fs_set_instance); |
577 | 0 | if (fs_init_auto(event, &fs_params, &storage->mailboxes_fs, |
578 | 0 | &error) <= 0) { |
579 | 0 | *error_r = t_strdup_printf("fs_init(posix) failed: %s", error); |
580 | 0 | event_unref(&event); |
581 | 0 | storage->v.destroy(storage); |
582 | 0 | settings_free(storage->set); |
583 | 0 | event_unref(&storage->event); |
584 | 0 | pool_unref(&storage->pool); |
585 | 0 | return -1; |
586 | 0 | } |
587 | 0 | event_unref(&event); |
588 | 0 | } |
589 | | |
590 | 0 | T_BEGIN { |
591 | 0 | hook_mail_storage_created(storage); |
592 | 0 | } T_END; |
593 | | |
594 | 0 | i_assert(storage->unique_root_dir != NULL || |
595 | 0 | (storage->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) == 0); |
596 | 0 | DLLIST_PREPEND(&ns->user->storages, storage); |
597 | 0 | mail_namespace_add_storage(ns, storage); |
598 | 0 | *storage_r = storage; |
599 | 0 | return 0; |
600 | 0 | } |
601 | | |
602 | | int mail_storage_create(struct mail_namespace *ns, struct event *set_event, |
603 | | enum mail_storage_flags flags, |
604 | | struct mail_storage **storage_r, const char **error_r) |
605 | 0 | { |
606 | 0 | int ret; |
607 | 0 | T_BEGIN { |
608 | 0 | ret = mail_storage_create_real(ns, set_event, flags, |
609 | 0 | storage_r, error_r); |
610 | 0 | } T_END_PASS_STR_IF(ret < 0, error_r); |
611 | 0 | return ret; |
612 | 0 | } |
613 | | |
614 | | void mail_storage_unref(struct mail_storage **_storage) |
615 | 0 | { |
616 | 0 | struct mail_storage *storage = *_storage; |
617 | |
|
618 | 0 | i_assert(storage->refcount > 0); |
619 | | |
620 | | /* set *_storage=NULL only after calling destroy() callback. |
621 | | for example mdbox wants to access ns->storage */ |
622 | 0 | if (--storage->refcount > 0) { |
623 | 0 | *_storage = NULL; |
624 | 0 | return; |
625 | 0 | } |
626 | | |
627 | 0 | if (storage->mailboxes != NULL) { |
628 | 0 | i_panic("Trying to deinit storage without freeing mailbox %s", |
629 | 0 | storage->mailboxes->vname); |
630 | 0 | } |
631 | 0 | if (storage->obj_refcount != 0) |
632 | 0 | i_panic("Trying to deinit storage before freeing its objects"); |
633 | | |
634 | 0 | DLLIST_REMOVE(&storage->user->storages, storage); |
635 | |
|
636 | 0 | storage->v.destroy(storage); |
637 | 0 | mail_storage_clear_error(storage); |
638 | 0 | if (array_is_created(&storage->error_stack)) { |
639 | 0 | i_assert(array_count(&storage->error_stack) == 0); |
640 | 0 | array_free(&storage->error_stack); |
641 | 0 | } |
642 | 0 | fs_unref(&storage->mailboxes_fs); |
643 | 0 | settings_instance_free(&storage->mailboxes_fs_set_instance); |
644 | 0 | settings_free(storage->set); |
645 | 0 | event_unref(&storage->event); |
646 | |
|
647 | 0 | *_storage = NULL; |
648 | 0 | pool_unref(&storage->pool); |
649 | |
|
650 | 0 | mail_index_alloc_cache_destroy_unrefed(); |
651 | 0 | } |
652 | | |
653 | | void mail_storage_obj_ref(struct mail_storage *storage) |
654 | 0 | { |
655 | 0 | i_assert(storage->refcount > 0); |
656 | | |
657 | 0 | if (storage->obj_refcount++ == 0) |
658 | 0 | mail_user_ref(storage->user); |
659 | 0 | } |
660 | | |
661 | | void mail_storage_obj_unref(struct mail_storage *storage) |
662 | 0 | { |
663 | 0 | i_assert(storage->refcount > 0); |
664 | 0 | i_assert(storage->obj_refcount > 0); |
665 | | |
666 | 0 | if (--storage->obj_refcount == 0) { |
667 | 0 | struct mail_user *user = storage->user; |
668 | 0 | mail_user_unref(&user); |
669 | 0 | } |
670 | 0 | } |
671 | | |
672 | | void mail_storage_clear_error(struct mail_storage *storage) |
673 | 0 | { |
674 | 0 | i_free_and_null(storage->error_string); |
675 | |
|
676 | 0 | i_free(storage->last_internal_error); |
677 | 0 | i_free(storage->last_internal_error_mailbox); |
678 | 0 | storage->last_error_is_internal = FALSE; |
679 | 0 | storage->error = MAIL_ERROR_NONE; |
680 | 0 | storage->last_internal_error_mail_uid = UINT32_MAX; |
681 | 0 | } |
682 | | |
683 | | void mail_storage_set_error(struct mail_storage *storage, |
684 | | enum mail_error error, const char *string) |
685 | 0 | { |
686 | 0 | if (storage->error_string != string) { |
687 | 0 | i_free(storage->error_string); |
688 | 0 | storage->error_string = i_strdup(string); |
689 | 0 | } |
690 | 0 | storage->last_error_is_internal = FALSE; |
691 | 0 | storage->error = error; |
692 | 0 | storage->last_internal_error_mail_uid = UINT32_MAX; |
693 | 0 | } |
694 | | |
695 | | void mail_storage_set_internal_error(struct mail_storage *storage) |
696 | 0 | { |
697 | 0 | const char *str; |
698 | |
|
699 | 0 | str = t_strflocaltime(MAIL_ERRSTR_CRITICAL_MSG_STAMP, ioloop_time); |
700 | |
|
701 | 0 | i_free(storage->error_string); |
702 | 0 | storage->error_string = i_strdup(str); |
703 | 0 | storage->error = MAIL_ERROR_TEMP; |
704 | | |
705 | | /* this function doesn't set last_internal_error, so |
706 | | last_error_is_internal can't be TRUE. */ |
707 | 0 | storage->last_error_is_internal = FALSE; |
708 | 0 | i_free(storage->last_internal_error); |
709 | 0 | i_free(storage->last_internal_error_mailbox); |
710 | 0 | storage->last_internal_error_mail_uid = UINT32_MAX; |
711 | 0 | } |
712 | | |
713 | | static void |
714 | | mail_storage_set_critical_error(struct mail_storage *storage, const char *str, |
715 | | const char *mailbox_vname, uint32_t mail_uid) |
716 | 0 | { |
717 | 0 | char *old_error = storage->error_string; |
718 | 0 | char *old_internal_error = storage->last_internal_error; |
719 | 0 | char *old_internal_error_mailbox = storage->last_internal_error_mailbox; |
720 | |
|
721 | 0 | storage->error_string = NULL; |
722 | 0 | storage->last_internal_error = NULL; |
723 | 0 | storage->last_internal_error_mailbox = NULL; |
724 | | /* critical errors may contain sensitive data, so let user |
725 | | see only "Internal error" with a timestamp to make it |
726 | | easier to look from log files the actual error message. */ |
727 | 0 | mail_storage_set_internal_error(storage); |
728 | |
|
729 | 0 | storage->last_internal_error = i_strdup(str); |
730 | 0 | storage->last_internal_error_mailbox = i_strdup(mailbox_vname); |
731 | 0 | storage->last_internal_error_mail_uid = mail_uid; |
732 | 0 | storage->last_error_is_internal = TRUE; |
733 | | |
734 | | /* free the old_error and old_internal_error only after the new error |
735 | | is generated, because they may be one of the parameters. */ |
736 | 0 | i_free(old_error); |
737 | 0 | i_free(old_internal_error); |
738 | 0 | i_free(old_internal_error_mailbox); |
739 | 0 | } |
740 | | |
741 | | void mail_storage_set_critical(struct mail_storage *storage, |
742 | | const char *fmt, ...) |
743 | 0 | { |
744 | 0 | va_list va; |
745 | |
|
746 | 0 | va_start(va, fmt); |
747 | 0 | T_BEGIN { |
748 | 0 | const char *str = t_strdup_vprintf(fmt, va); |
749 | 0 | mail_storage_set_critical_error(storage, str, NULL, UINT32_MAX); |
750 | 0 | e_error(storage->event, "%s", str); |
751 | 0 | } T_END; |
752 | 0 | va_end(va); |
753 | 0 | } |
754 | | |
755 | | void mailbox_set_critical(struct mailbox *box, const char *fmt, ...) |
756 | 0 | { |
757 | 0 | va_list va; |
758 | |
|
759 | 0 | va_start(va, fmt); |
760 | 0 | T_BEGIN { |
761 | 0 | const char *str = t_strdup_vprintf(fmt, va); |
762 | 0 | mail_storage_set_critical_error(box->storage, str, box->vname, |
763 | 0 | UINT32_MAX); |
764 | 0 | e_error(box->event, "%s", str); |
765 | 0 | } T_END; |
766 | 0 | va_end(va); |
767 | 0 | } |
768 | | |
769 | | void mail_set_critical(struct mail *mail, const char *fmt, ...) |
770 | 0 | { |
771 | 0 | va_list va; |
772 | |
|
773 | 0 | va_start(va, fmt); |
774 | 0 | T_BEGIN { |
775 | 0 | const char *formatted_msg = t_strdup_vprintf(fmt, va); |
776 | 0 | mail_storage_set_critical_error(mail->box->storage, formatted_msg, |
777 | 0 | mail->box->vname, mail->uid); |
778 | 0 | e_error(mail_event(mail), "%s", formatted_msg); |
779 | 0 | } T_END; |
780 | 0 | va_end(va); |
781 | 0 | } |
782 | | |
783 | | /* Note: mail_storage_get_last_internal_error() will always include |
784 | | the mailbox prefix, while mailbox_get_last_internal_error() and |
785 | | mail_get_last_internal_error() usually will not. */ |
786 | | const char *mail_storage_get_last_internal_error(struct mail_storage *storage, |
787 | | enum mail_error *error_r) |
788 | 0 | { |
789 | 0 | if (error_r != NULL) |
790 | 0 | *error_r = storage->error; |
791 | 0 | if (storage->last_error_is_internal) { |
792 | 0 | i_assert(storage->last_internal_error != NULL); |
793 | | |
794 | 0 | bool is_mailbox_error_set = storage->last_internal_error_mailbox != NULL; |
795 | 0 | bool is_mail_error_set = |
796 | 0 | storage->last_internal_error_mail_uid != UINT32_MAX; |
797 | |
|
798 | 0 | if (is_mail_error_set) { |
799 | 0 | i_assert(is_mailbox_error_set); |
800 | 0 | return t_strdup_printf( |
801 | 0 | "Mailbox %s: UID %u: %s", |
802 | 0 | mailbox_name_sanitize(storage->last_internal_error_mailbox), |
803 | 0 | storage->last_internal_error_mail_uid, |
804 | 0 | storage->last_internal_error); |
805 | 0 | } |
806 | 0 | if (is_mailbox_error_set) |
807 | 0 | return t_strdup_printf( |
808 | 0 | "Mailbox %s: %s", |
809 | 0 | mailbox_name_sanitize(storage->last_internal_error_mailbox), |
810 | 0 | storage->last_internal_error); |
811 | | |
812 | 0 | return storage->last_internal_error; |
813 | 0 | } |
814 | 0 | return mail_storage_get_last_error(storage, error_r); |
815 | 0 | } |
816 | | |
817 | | /* Note: mailbox_get_last_internal_error() will include the mailbox prefix only |
818 | | when mailbox->vname does not match last_internal_error_mailbox, which |
819 | | might happen with e.g. virtual mailboxes logging about physical |
820 | | mailboxes, while mail_storage_get_last_internal_error() always does. */ |
821 | | const char *mailbox_get_last_internal_error(struct mailbox *box, |
822 | | enum mail_error *error_r) |
823 | 0 | { |
824 | 0 | struct mail_storage *storage = mailbox_get_storage(box); |
825 | 0 | const char *last_mailbox = storage->last_internal_error_mailbox; |
826 | 0 | if (last_mailbox != NULL && |
827 | 0 | strcmp(last_mailbox, box->vname) != 0) |
828 | 0 | return mail_storage_get_last_internal_error(storage, error_r); |
829 | | |
830 | 0 | if (error_r != NULL) |
831 | 0 | *error_r = storage->error; |
832 | 0 | if (storage->last_error_is_internal) { |
833 | 0 | i_assert(storage->last_internal_error != NULL); |
834 | 0 | if (storage->last_internal_error_mail_uid != UINT32_MAX) |
835 | 0 | return t_strdup_printf("UID %u: %s", |
836 | 0 | storage->last_internal_error_mail_uid, |
837 | 0 | storage->last_internal_error); |
838 | 0 | return storage->last_internal_error; |
839 | 0 | } |
840 | 0 | return mail_storage_get_last_error(storage, error_r); |
841 | 0 | } |
842 | | |
843 | | /* Note: mail_get_last_internal_error() will include the mail prefix only when |
844 | | mail->uid does not match last_internal_error_mail_uid, while |
845 | | mail_storage_get_last_internal_error() always does. */ |
846 | | const char * |
847 | | mail_get_last_internal_error(struct mail *mail, enum mail_error *error_r) |
848 | 0 | { |
849 | 0 | struct mail_storage *storage = mailbox_get_storage(mail->box); |
850 | 0 | const char *last_mailbox = storage->last_internal_error_mailbox; |
851 | 0 | if (last_mailbox != NULL && |
852 | 0 | strcmp(last_mailbox, mail->box->vname) != 0) |
853 | 0 | return mail_storage_get_last_internal_error(storage, error_r); |
854 | | |
855 | 0 | uint32_t last_mail_uid = storage->last_internal_error_mail_uid; |
856 | 0 | if (last_mail_uid == UINT32_MAX || last_mail_uid != mail->uid) |
857 | 0 | return mailbox_get_last_internal_error(mail->box, error_r); |
858 | | |
859 | 0 | if (error_r != NULL) |
860 | 0 | *error_r = storage->error; |
861 | 0 | if (storage->last_error_is_internal) { |
862 | 0 | i_assert(storage->last_internal_error != NULL); |
863 | 0 | return storage->last_internal_error; |
864 | 0 | } |
865 | 0 | return mail_storage_get_last_error(storage, error_r); |
866 | 0 | } |
867 | | |
868 | | void mail_storage_copy_error(struct mail_storage *dest, |
869 | | struct mail_storage *src) |
870 | 0 | { |
871 | 0 | const char *str; |
872 | 0 | enum mail_error error; |
873 | |
|
874 | 0 | if (src == dest) |
875 | 0 | return; |
876 | | |
877 | 0 | str = mail_storage_get_last_error(src, &error); |
878 | 0 | mail_storage_set_error(dest, error, str); |
879 | 0 | } |
880 | | |
881 | | void mail_storage_copy_list_error(struct mail_storage *storage, |
882 | | struct mailbox_list *list) |
883 | 0 | { |
884 | 0 | const char *str; |
885 | 0 | enum mail_error error; |
886 | |
|
887 | 0 | str = mailbox_list_get_last_error(list, &error); |
888 | 0 | mail_storage_set_error(storage, error, str); |
889 | 0 | } |
890 | | |
891 | | void mailbox_set_index_error(struct mailbox *box) |
892 | 0 | { |
893 | 0 | if (mail_index_is_deleted(box->index)) { |
894 | 0 | mailbox_set_deleted(box); |
895 | 0 | mail_index_reset_error(box->index); |
896 | 0 | } else { |
897 | 0 | i_free(box->storage->last_internal_error_mailbox); |
898 | 0 | box->storage->last_internal_error_mailbox = i_strdup(box->vname); |
899 | 0 | mail_storage_set_index_error(box->storage, box->index); |
900 | 0 | } |
901 | 0 | } |
902 | | |
903 | | void mail_storage_set_index_error(struct mail_storage *storage, |
904 | | struct mail_index *index) |
905 | 0 | { |
906 | 0 | const char *index_error; |
907 | |
|
908 | 0 | mail_storage_set_internal_error(storage); |
909 | | /* use the lib-index's error as our internal error string */ |
910 | 0 | index_error = mail_index_get_last_error(index, NULL); |
911 | 0 | if (index_error == NULL) |
912 | 0 | index_error = "BUG: Unknown internal index error"; |
913 | 0 | storage->last_internal_error = i_strdup(index_error); |
914 | 0 | storage->last_error_is_internal = TRUE; |
915 | 0 | mail_index_reset_error(index); |
916 | 0 | } |
917 | | |
918 | | const struct mail_storage_settings * |
919 | | mail_storage_get_settings(struct mail_storage *storage) |
920 | 0 | { |
921 | 0 | return storage->set; |
922 | 0 | } |
923 | | |
924 | | struct mail_user *mail_storage_get_user(struct mail_storage *storage) |
925 | 0 | { |
926 | 0 | return storage->user; |
927 | 0 | } |
928 | | |
929 | | void mail_storage_set_callbacks(struct mail_storage *storage, |
930 | | struct mail_storage_callbacks *callbacks, |
931 | | void *context) |
932 | 0 | { |
933 | 0 | storage->callbacks = *callbacks; |
934 | 0 | storage->callback_context = context; |
935 | 0 | } |
936 | | |
937 | | int mail_storage_purge(struct mail_storage *storage) |
938 | 0 | { |
939 | 0 | if (storage->v.purge == NULL) |
940 | 0 | return 0; |
941 | | |
942 | 0 | int ret; |
943 | 0 | T_BEGIN { |
944 | 0 | ret = storage->v.purge(storage); |
945 | 0 | } T_END; |
946 | 0 | return ret; |
947 | 0 | } |
948 | | |
949 | | const char *mail_storage_get_last_error(struct mail_storage *storage, |
950 | | enum mail_error *error_r) |
951 | 0 | { |
952 | | /* We get here only in error situations, so we have to return some |
953 | | error. If storage->error is NONE, it means we forgot to set it at |
954 | | some point.. */ |
955 | 0 | if (storage->error == MAIL_ERROR_NONE) { |
956 | 0 | if (error_r != NULL) |
957 | 0 | *error_r = MAIL_ERROR_TEMP; |
958 | 0 | return storage->error_string != NULL ? storage->error_string : |
959 | 0 | "BUG: Unknown internal error"; |
960 | 0 | } |
961 | | |
962 | 0 | if (storage->error_string == NULL) { |
963 | | /* This shouldn't happen.. */ |
964 | 0 | storage->error_string = |
965 | 0 | i_strdup_printf("BUG: Unknown 0x%x error", |
966 | 0 | storage->error); |
967 | 0 | } |
968 | |
|
969 | 0 | if (error_r != NULL) |
970 | 0 | *error_r = storage->error; |
971 | 0 | return storage->error_string; |
972 | 0 | } |
973 | | |
974 | | const char *mailbox_get_last_error(struct mailbox *box, |
975 | | enum mail_error *error_r) |
976 | 0 | { |
977 | 0 | return mail_storage_get_last_error(box->storage, error_r); |
978 | 0 | } |
979 | | |
980 | | enum mail_error mailbox_get_last_mail_error(struct mailbox *box) |
981 | 0 | { |
982 | 0 | enum mail_error error; |
983 | |
|
984 | 0 | mail_storage_get_last_error(box->storage, &error); |
985 | 0 | return error; |
986 | 0 | } |
987 | | |
988 | | void mail_storage_last_error_push(struct mail_storage *storage) |
989 | 0 | { |
990 | 0 | struct mail_storage_error *err; |
991 | |
|
992 | 0 | if (!array_is_created(&storage->error_stack)) |
993 | 0 | i_array_init(&storage->error_stack, 2); |
994 | 0 | err = array_append_space(&storage->error_stack); |
995 | 0 | err->error_string = i_strdup(storage->error_string); |
996 | 0 | err->error = storage->error; |
997 | 0 | err->last_error_is_internal = storage->last_error_is_internal; |
998 | | /* Initially set to UINT32_MAX manually to denote 'unset', as the |
999 | | default 0 is used for mails currently being saved. If there is no |
1000 | | internal error, the attribute would not be set otherwise. */ |
1001 | 0 | err->last_internal_error_mail_uid = UINT32_MAX; |
1002 | 0 | if (err->last_error_is_internal) { |
1003 | 0 | err->last_internal_error = i_strdup(storage->last_internal_error); |
1004 | 0 | err->last_internal_error_mailbox = |
1005 | 0 | i_strdup(storage->last_internal_error_mailbox); |
1006 | 0 | err->last_internal_error_mail_uid = |
1007 | 0 | storage->last_internal_error_mail_uid; |
1008 | 0 | } |
1009 | 0 | } |
1010 | | |
1011 | | void mail_storage_last_error_pop(struct mail_storage *storage) |
1012 | 0 | { |
1013 | 0 | unsigned int count = array_count(&storage->error_stack); |
1014 | 0 | const struct mail_storage_error *err = |
1015 | 0 | array_idx(&storage->error_stack, count-1); |
1016 | |
|
1017 | 0 | i_free(storage->error_string); |
1018 | 0 | i_free(storage->last_internal_error); |
1019 | 0 | i_free(storage->last_internal_error_mailbox); |
1020 | 0 | storage->error_string = err->error_string; |
1021 | 0 | storage->error = err->error; |
1022 | 0 | storage->last_error_is_internal = err->last_error_is_internal; |
1023 | 0 | storage->last_internal_error = err->last_internal_error; |
1024 | 0 | storage->last_internal_error_mailbox = err->last_internal_error_mailbox; |
1025 | 0 | storage->last_internal_error_mail_uid = err->last_internal_error_mail_uid; |
1026 | 0 | array_delete(&storage->error_stack, count-1, 1); |
1027 | 0 | } |
1028 | | |
1029 | | bool mail_storage_is_mailbox_file(struct mail_storage *storage) |
1030 | 0 | { |
1031 | 0 | return (storage->class_flags & |
1032 | 0 | MAIL_STORAGE_CLASS_FLAG_MAILBOX_IS_FILE) != 0; |
1033 | 0 | } |
1034 | | |
1035 | | bool mail_storage_set_error_from_errno(struct mail_storage *storage) |
1036 | 0 | { |
1037 | 0 | const char *error_string; |
1038 | 0 | enum mail_error error; |
1039 | |
|
1040 | 0 | if (!mail_error_from_errno(&error, &error_string)) |
1041 | 0 | return FALSE; |
1042 | 0 | if (event_want_debug_log(storage->event) && error != MAIL_ERROR_NOTFOUND) { |
1043 | | /* debugging is enabled - admin may be debugging a |
1044 | | (permission) problem, so return FALSE to get the caller to |
1045 | | log the full error message. */ |
1046 | 0 | return FALSE; |
1047 | 0 | } |
1048 | | |
1049 | 0 | mail_storage_set_error(storage, error, error_string); |
1050 | 0 | return TRUE; |
1051 | 0 | } |
1052 | | |
1053 | | static int |
1054 | | mailbox_list_get_default_box_settings(struct mailbox_list *list, |
1055 | | const struct mailbox_settings **set_r, |
1056 | | const char **error_r) |
1057 | 0 | { |
1058 | 0 | if (list->default_box_set == NULL) { |
1059 | 0 | if (settings_get(list->event, |
1060 | 0 | &mailbox_setting_parser_info, 0, |
1061 | 0 | &list->default_box_set, error_r) < 0) |
1062 | 0 | return -1; |
1063 | 0 | } |
1064 | 0 | pool_ref(list->default_box_set->pool); |
1065 | 0 | *set_r = list->default_box_set; |
1066 | 0 | return 1; |
1067 | 0 | } |
1068 | | |
1069 | | int mailbox_name_try_get_settings(struct mailbox_list *list, const char *vname, |
1070 | | const struct mailbox_settings **set_r, |
1071 | | const char **error_r) |
1072 | 0 | { |
1073 | 0 | if (array_is_empty(&list->ns->set->mailboxes)) |
1074 | 0 | return mailbox_list_get_default_box_settings(list, set_r, error_r); |
1075 | | |
1076 | 0 | const char *vname_without_prefix = |
1077 | 0 | mailbox_get_name_without_prefix(list->ns, vname); |
1078 | 0 | unsigned int i, count; |
1079 | 0 | const struct mailbox_settings *set = NULL, *const *mailboxes = |
1080 | 0 | array_get(&list->ns->set->parsed_mailboxes, &count); |
1081 | |
|
1082 | 0 | for (i = 0; i < count; i++) { |
1083 | 0 | if (!wildcard_match(vname_without_prefix, mailboxes[i]->name)) |
1084 | 0 | continue; |
1085 | | |
1086 | 0 | if (set == NULL) |
1087 | 0 | set = mailboxes[i]; |
1088 | 0 | else { |
1089 | | /* multiple mailbox named list filters match - need to |
1090 | | lookup settings to get them merged. */ |
1091 | 0 | return 0; |
1092 | 0 | } |
1093 | 0 | } |
1094 | 0 | if (set == NULL) |
1095 | 0 | return mailbox_list_get_default_box_settings(list, set_r, error_r); |
1096 | | |
1097 | 0 | pool_ref(set->pool); |
1098 | 0 | *set_r = set; |
1099 | 0 | return 1; |
1100 | 0 | } |
1101 | | |
1102 | | struct mailbox *mailbox_alloc(struct mailbox_list *list, const char *vname, |
1103 | | enum mailbox_flags flags) |
1104 | 0 | { |
1105 | 0 | const struct mail_storage_settings *mail_set = list->mail_set; |
1106 | 0 | bool nfc = mail_set->mailbox_list_normalize_names_to_nfc; |
1107 | 0 | struct mailbox_list *new_list = list; |
1108 | 0 | struct mail_storage *storage; |
1109 | 0 | struct mailbox *box; |
1110 | 0 | enum mail_error open_error = 0; |
1111 | 0 | const char *suffix, *errstr = NULL; |
1112 | 0 | int ret; |
1113 | |
|
1114 | 0 | if (str_begins_icase(vname, "INBOX", &suffix) && |
1115 | 0 | !str_begins_with(vname, "INBOX")) { |
1116 | | /* make sure INBOX shows up in uppercase everywhere. do this |
1117 | | regardless of whether we're in inbox=yes namespace, because |
1118 | | clients expect INBOX to be case-insensitive regardless of |
1119 | | server's internal configuration. */ |
1120 | 0 | if (suffix[0] == '\0') |
1121 | 0 | vname = "INBOX"; |
1122 | 0 | else if (suffix[0] != mail_namespace_get_sep(list->ns)) |
1123 | 0 | /* not INBOX prefix */ ; |
1124 | 0 | else if (strncasecmp(list->ns->prefix, vname, 6) == 0 && |
1125 | 0 | !str_begins_with(list->ns->prefix, "INBOX")) { |
1126 | 0 | mailbox_list_set_critical(list, |
1127 | 0 | "Invalid server configuration: " |
1128 | 0 | "Namespace %s: prefix=%s must be uppercase INBOX", |
1129 | 0 | list->ns->set->name, list->ns->prefix); |
1130 | 0 | open_error = MAIL_ERROR_TEMP; |
1131 | 0 | } else { |
1132 | 0 | vname = t_strconcat("INBOX", suffix, NULL); |
1133 | 0 | } |
1134 | 0 | } |
1135 | |
|
1136 | 0 | T_BEGIN { |
1137 | 0 | const char *vname_raw = vname; |
1138 | 0 | bool mailbox_name_changed_by_nfc = FALSE; |
1139 | |
|
1140 | 0 | if (nfc && HAS_NO_BITS(flags, MAILBOX_FLAG_RAW_NAME)) { |
1141 | 0 | ret = uni_utf8_to_nfc(vname, strlen(vname), &vname); |
1142 | 0 | i_assert(ret >= 0); |
1143 | 0 | mailbox_name_changed_by_nfc = |
1144 | 0 | (strcmp(vname_raw, vname) != 0); |
1145 | 0 | } |
1146 | | |
1147 | 0 | const char *error, *orig_vname = vname; |
1148 | 0 | enum mailbox_list_get_storage_flags storage_flags = 0; |
1149 | 0 | bool mailbox_not_original = FALSE; |
1150 | 0 | int ret; |
1151 | |
|
1152 | 0 | if ((flags & MAILBOX_FLAG_SAVEONLY) != 0) |
1153 | 0 | storage_flags |= MAILBOX_LIST_GET_STORAGE_FLAG_SAVEONLY; |
1154 | 0 | if (mailbox_list_get_storage(&new_list, &vname, |
1155 | 0 | storage_flags, &storage) < 0) { |
1156 | | /* do a delayed failure at mailbox_open() */ |
1157 | 0 | storage = mail_namespace_get_default_storage(list->ns); |
1158 | 0 | errstr = mailbox_list_get_last_error(new_list, &open_error); |
1159 | 0 | errstr = t_strdup(errstr); |
1160 | 0 | } else if (strcmp(orig_vname, vname) != 0) { |
1161 | 0 | mailbox_not_original = TRUE; |
1162 | |
|
1163 | 0 | vname_raw = orig_vname; |
1164 | 0 | if (nfc && HAS_NO_BITS(flags, MAILBOX_FLAG_RAW_NAME)) { |
1165 | 0 | ret = uni_utf8_to_nfc(vname, strlen(vname), &vname); |
1166 | 0 | i_assert(ret >= 0); |
1167 | 0 | mailbox_name_changed_by_nfc = |
1168 | 0 | (strcmp(vname_raw, vname) != 0); |
1169 | 0 | } |
1170 | 0 | } |
1171 | | |
1172 | 0 | if ((new_list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 && |
1173 | 0 | new_list->ns->type == MAIL_NAMESPACE_TYPE_SHARED && |
1174 | 0 | str_begins(vname, new_list->ns->prefix, &suffix) && |
1175 | 0 | strcasecmp(suffix, "INBOX") == 0 && |
1176 | 0 | strcmp(suffix, "INBOX") != 0) { |
1177 | | /* Accessing INBOX explicitly in a shared namespace - |
1178 | | convert to uppercase. This is done regardless of the |
1179 | | mail_shared_explicit_inbox setting, since even with |
1180 | | "no" adding INBOX to the namespace prefix accesses |
1181 | | the same INBOX. When accessing the shared INBOX |
1182 | | as the namespace prefix, the uppercase INBOX is |
1183 | | appended when getting the storage_name in mailbox |
1184 | | list code. */ |
1185 | 0 | vname = t_strconcat(new_list->ns->prefix, "INBOX", NULL); |
1186 | 0 | } |
1187 | |
|
1188 | 0 | box = storage->v.mailbox_alloc(storage, new_list, vname, flags); |
1189 | 0 | if (open_error != 0) { |
1190 | 0 | box->open_error = open_error; |
1191 | 0 | mail_storage_set_error(storage, open_error, errstr); |
1192 | 0 | } else if ((ret = mailbox_name_try_get_settings(box->list, |
1193 | 0 | vname, &box->set, &error)) < 0) { |
1194 | 0 | mailbox_set_critical(box, "%s", error); |
1195 | 0 | box->open_error = box->storage->error; |
1196 | 0 | } else if (ret == 0) { |
1197 | 0 | if (settings_get(box->event, |
1198 | 0 | &mailbox_setting_parser_info, 0, |
1199 | 0 | &box->set, &error) < 0) { |
1200 | 0 | mailbox_set_critical(box, "%s", error); |
1201 | 0 | box->open_error = box->storage->error; |
1202 | 0 | } |
1203 | 0 | } |
1204 | 0 | if (vname_raw == vname || strcmp(vname_raw, vname) == 0) |
1205 | 0 | box->vname_raw = box->vname; |
1206 | 0 | else |
1207 | 0 | box->vname_raw = p_strdup(box->pool, vname_raw); |
1208 | 0 | box->mailbox_not_original = mailbox_not_original; |
1209 | 0 | box->mailbox_name_changed_by_nfc = mailbox_name_changed_by_nfc; |
1210 | 0 | hook_mailbox_allocated(box); |
1211 | 0 | } T_END; |
1212 | | |
1213 | 0 | DLLIST_PREPEND(&box->storage->mailboxes, box); |
1214 | 0 | mail_storage_obj_ref(box->storage); |
1215 | 0 | return box; |
1216 | 0 | } |
1217 | | |
1218 | | struct mailbox *mailbox_alloc_guid(struct mailbox_list *list, |
1219 | | const guid_128_t guid, |
1220 | | enum mailbox_flags flags) |
1221 | 0 | { |
1222 | 0 | struct mailbox *box = NULL; |
1223 | 0 | struct mailbox_metadata metadata; |
1224 | 0 | enum mail_error open_error = MAIL_ERROR_TEMP; |
1225 | 0 | const char *vname; |
1226 | |
|
1227 | 0 | if (mailbox_guid_cache_find(list, guid, &vname) < 0) { |
1228 | 0 | vname = NULL; |
1229 | 0 | } else if (vname != NULL) { |
1230 | 0 | box = mailbox_alloc(list, vname, flags); |
1231 | 0 | if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, |
1232 | 0 | &metadata) < 0) { |
1233 | 0 | } else if (memcmp(metadata.guid, guid, |
1234 | 0 | sizeof(metadata.guid)) != 0) { |
1235 | | /* GUID mismatch, refresh cache and try again */ |
1236 | 0 | mailbox_free(&box); |
1237 | 0 | mailbox_guid_cache_refresh(list); |
1238 | 0 | return mailbox_alloc_guid(list, guid, flags); |
1239 | 0 | } else { |
1240 | | /* successfully opened the correct mailbox */ |
1241 | 0 | return box; |
1242 | 0 | } |
1243 | 0 | e_error(list->event, "mailbox_alloc_guid(%s): " |
1244 | 0 | "Couldn't verify mailbox GUID: %s", |
1245 | 0 | guid_128_to_string(guid), |
1246 | 0 | mailbox_get_last_internal_error(box, NULL)); |
1247 | 0 | vname = NULL; |
1248 | 0 | mailbox_free(&box); |
1249 | 0 | } else { |
1250 | 0 | vname = t_strdup_printf("(nonexistent mailbox with GUID=%s)", |
1251 | 0 | guid_128_to_string(guid)); |
1252 | 0 | open_error = MAIL_ERROR_NOTFOUND; |
1253 | 0 | } |
1254 | | |
1255 | 0 | if (vname == NULL) { |
1256 | 0 | vname = t_strdup_printf("(error in mailbox with GUID=%s)", |
1257 | 0 | guid_128_to_string(guid)); |
1258 | 0 | } |
1259 | 0 | box = mailbox_alloc(list, vname, flags); |
1260 | 0 | box->open_error = open_error; |
1261 | 0 | return box; |
1262 | 0 | } |
1263 | | |
1264 | | static bool |
1265 | | str_contains_special_use(const char *str, const char *special_use) |
1266 | 0 | { |
1267 | 0 | const char *const *uses; |
1268 | |
|
1269 | 0 | i_assert(special_use != NULL); |
1270 | 0 | if (*special_use != '\\') |
1271 | 0 | return FALSE; |
1272 | | |
1273 | 0 | bool ret; |
1274 | 0 | T_BEGIN { |
1275 | 0 | uses = t_strsplit_spaces(str, " "); |
1276 | 0 | ret = str_array_icase_find(uses, special_use); |
1277 | 0 | } T_END; |
1278 | 0 | return ret; |
1279 | 0 | } |
1280 | | |
1281 | | static int |
1282 | | namespace_find_special_use(struct mail_namespace *ns, const char *special_use, |
1283 | | const char **vname_r, enum mail_error *error_code_r) |
1284 | 0 | { |
1285 | 0 | struct mailbox_list *list = ns->list; |
1286 | 0 | struct mailbox_list_iterate_context *ctx; |
1287 | 0 | const struct mailbox_info *info; |
1288 | 0 | int ret = 0; |
1289 | |
|
1290 | 0 | *vname_r = NULL; |
1291 | 0 | *error_code_r = MAIL_ERROR_NONE; |
1292 | |
|
1293 | 0 | if (!ns->set->parsed_have_special_use_mailboxes) |
1294 | 0 | return 0; |
1295 | 0 | if (!HAS_ALL_BITS(ns->type, MAIL_NAMESPACE_TYPE_PRIVATE)) |
1296 | 0 | return 0; |
1297 | | |
1298 | 0 | ctx = mailbox_list_iter_init(list, "*", |
1299 | 0 | MAILBOX_LIST_ITER_SELECT_SPECIALUSE | |
1300 | 0 | MAILBOX_LIST_ITER_RETURN_SPECIALUSE); |
1301 | 0 | while ((info = mailbox_list_iter_next(ctx)) != NULL) { |
1302 | 0 | if ((info->flags & |
1303 | 0 | (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) != 0) |
1304 | 0 | continue; |
1305 | | /* iter can only return mailboxes that have non-empty |
1306 | | special-use */ |
1307 | 0 | i_assert(info->special_use != NULL && |
1308 | 0 | *info->special_use != '\0'); |
1309 | | |
1310 | 0 | if (str_contains_special_use(info->special_use, special_use)) { |
1311 | 0 | *vname_r = t_strdup(info->vname); |
1312 | 0 | ret = 1; |
1313 | 0 | break; |
1314 | 0 | } |
1315 | 0 | } |
1316 | 0 | if (mailbox_list_iter_deinit(&ctx) < 0) { |
1317 | 0 | const char *error; |
1318 | |
|
1319 | 0 | error = mailbox_list_get_last_error(ns->list, error_code_r); |
1320 | 0 | e_error(ns->list->event, |
1321 | 0 | "Namespace %s: Failed to find mailbox with SPECIAL-USE flag '%s': %s", |
1322 | 0 | ns->set->name, special_use, error); |
1323 | 0 | return -1; |
1324 | 0 | } |
1325 | 0 | return ret; |
1326 | 0 | } |
1327 | | |
1328 | | static int |
1329 | | namespaces_find_special_use(struct mail_namespace *namespaces, |
1330 | | const char *special_use, |
1331 | | struct mail_namespace **ns_r, |
1332 | | const char **vname_r, enum mail_error *error_code_r) |
1333 | 0 | { |
1334 | 0 | struct mail_namespace *ns_inbox; |
1335 | 0 | int ret; |
1336 | |
|
1337 | 0 | *error_code_r = MAIL_ERROR_NONE; |
1338 | 0 | *vname_r = NULL; |
1339 | | |
1340 | | /* check user's INBOX namespace first */ |
1341 | 0 | *ns_r = ns_inbox = mail_namespace_find_inbox(namespaces); |
1342 | 0 | ret = namespace_find_special_use(*ns_r, special_use, |
1343 | 0 | vname_r, error_code_r); |
1344 | 0 | if (ret != 0) |
1345 | 0 | return ret; |
1346 | | |
1347 | | /* check other namespaces */ |
1348 | 0 | for (*ns_r = namespaces; *ns_r != NULL; *ns_r = (*ns_r)->next) { |
1349 | 0 | if (*ns_r == ns_inbox) { |
1350 | | /* already checked */ |
1351 | 0 | continue; |
1352 | 0 | } |
1353 | 0 | ret = namespace_find_special_use(*ns_r, special_use, |
1354 | 0 | vname_r, error_code_r); |
1355 | 0 | if (ret != 0) |
1356 | 0 | return ret; |
1357 | 0 | } |
1358 | | |
1359 | 0 | *ns_r = ns_inbox; |
1360 | 0 | return 0; |
1361 | 0 | } |
1362 | | |
1363 | | struct mailbox * |
1364 | | mailbox_alloc_for_user(struct mail_user *user, const char *mname, |
1365 | | enum mailbox_flags flags) |
1366 | 0 | { |
1367 | 0 | struct mail_namespace *ns; |
1368 | 0 | struct mailbox *box; |
1369 | 0 | const char *vname; |
1370 | 0 | enum mail_error open_error = MAIL_ERROR_NONE; |
1371 | 0 | int ret; |
1372 | |
|
1373 | 0 | if (HAS_ALL_BITS(flags, MAILBOX_FLAG_SPECIAL_USE)) { |
1374 | 0 | ret = namespaces_find_special_use(user->namespaces, mname, |
1375 | 0 | &ns, &vname, &open_error); |
1376 | 0 | if (ret < 0) { |
1377 | 0 | i_assert(open_error != MAIL_ERROR_NONE); |
1378 | 0 | vname = t_strdup_printf( |
1379 | 0 | "(error finding mailbox with SPECIAL-USE=%s)", |
1380 | 0 | mname); |
1381 | 0 | } else if (ret == 0) { |
1382 | 0 | i_assert(open_error == MAIL_ERROR_NONE); |
1383 | 0 | vname = t_strdup_printf( |
1384 | 0 | "(nonexistent mailbox with SPECIAL-USE=%s)", |
1385 | 0 | mname); |
1386 | 0 | open_error = MAIL_ERROR_NOTFOUND; |
1387 | 0 | } |
1388 | 0 | } else { |
1389 | 0 | vname = mname; |
1390 | 0 | ns = mail_namespace_find(user->namespaces, mname); |
1391 | 0 | } |
1392 | | |
1393 | 0 | if (HAS_ALL_BITS(flags, MAILBOX_FLAG_POST_SESSION)) { |
1394 | 0 | flags |= MAILBOX_FLAG_SAVEONLY; |
1395 | |
|
1396 | 0 | if (strcmp(vname, ns->prefix) == 0 && |
1397 | 0 | (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { |
1398 | | /* delivering to a namespace prefix means we actually |
1399 | | want to deliver to the INBOX instead */ |
1400 | 0 | vname = "INBOX"; |
1401 | 0 | ns = mail_namespace_find_inbox(user->namespaces); |
1402 | 0 | } |
1403 | |
|
1404 | 0 | if (strcasecmp(vname, "INBOX") == 0) { |
1405 | | /* deliveries to INBOX must always succeed, |
1406 | | regardless of ACLs */ |
1407 | 0 | flags |= MAILBOX_FLAG_IGNORE_ACLS; |
1408 | 0 | } |
1409 | 0 | } |
1410 | |
|
1411 | 0 | i_assert(ns != NULL); |
1412 | 0 | box = mailbox_alloc(ns->list, vname, flags); |
1413 | 0 | if (open_error != MAIL_ERROR_NONE) |
1414 | 0 | box->open_error = open_error; |
1415 | 0 | return box; |
1416 | 0 | } |
1417 | | |
1418 | | bool mailbox_is_autocreated(struct mailbox *box) |
1419 | 0 | { |
1420 | 0 | if (box->inbox_user) |
1421 | 0 | return TRUE; |
1422 | 0 | if ((box->flags & MAILBOX_FLAG_AUTO_CREATE) != 0) |
1423 | 0 | return TRUE; |
1424 | 0 | return box->set != NULL && |
1425 | 0 | strcmp(box->set->autocreate, MAILBOX_SET_AUTO_NO) != 0; |
1426 | 0 | } |
1427 | | |
1428 | | bool mailbox_is_autosubscribed(struct mailbox *box) |
1429 | 0 | { |
1430 | 0 | if ((box->flags & MAILBOX_FLAG_AUTO_SUBSCRIBE) != 0) |
1431 | 0 | return TRUE; |
1432 | 0 | return box->set != NULL && |
1433 | 0 | strcmp(box->set->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) == 0; |
1434 | 0 | } |
1435 | | |
1436 | | static int mailbox_autocreate(struct mailbox *box) |
1437 | 0 | { |
1438 | 0 | const char *errstr; |
1439 | 0 | enum mail_error error; |
1440 | |
|
1441 | 0 | if (mailbox_create(box, NULL, FALSE) < 0) { |
1442 | 0 | errstr = mailbox_get_last_internal_error(box, &error); |
1443 | 0 | if (error == MAIL_ERROR_NOTFOUND && box->acl_no_lookup_right) { |
1444 | | /* ACL prevents creating this mailbox */ |
1445 | 0 | return -1; |
1446 | 0 | } |
1447 | 0 | if (error != MAIL_ERROR_EXISTS) { |
1448 | 0 | mailbox_set_critical(box, |
1449 | 0 | "Failed to autocreate mailbox: %s", |
1450 | 0 | errstr); |
1451 | 0 | return -1; |
1452 | 0 | } |
1453 | 0 | } else if (mailbox_is_autosubscribed(box)) { |
1454 | 0 | if (mailbox_set_subscribed(box, TRUE) < 0) { |
1455 | 0 | mailbox_set_critical(box, |
1456 | 0 | "Failed to autosubscribe to mailbox: %s", |
1457 | 0 | mailbox_get_last_internal_error(box, NULL)); |
1458 | 0 | return -1; |
1459 | 0 | } |
1460 | 0 | } |
1461 | 0 | return 0; |
1462 | 0 | } |
1463 | | |
1464 | | static int mailbox_autocreate_and_reopen(struct mailbox *box) |
1465 | 0 | { |
1466 | 0 | int ret; |
1467 | |
|
1468 | 0 | if (mailbox_autocreate(box) < 0) |
1469 | 0 | return -1; |
1470 | 0 | mailbox_close(box); |
1471 | |
|
1472 | 0 | ret = box->v.open(box); |
1473 | 0 | if (ret < 0 && box->inbox_user && !box->acl_no_lookup_right && |
1474 | 0 | !box->storage->user->inbox_open_error_logged) { |
1475 | 0 | box->storage->user->inbox_open_error_logged = TRUE; |
1476 | 0 | mailbox_set_critical(box, |
1477 | 0 | "Opening INBOX failed: %s", |
1478 | 0 | mailbox_get_last_internal_error(box, NULL)); |
1479 | 0 | } |
1480 | 0 | return ret; |
1481 | 0 | } |
1482 | | |
1483 | | static void |
1484 | | mailbox_vname_normalize_ns_prefix_nfc(struct mailbox_list *list, |
1485 | | const char **vname) |
1486 | 0 | { |
1487 | 0 | struct mail_namespace *ns = list->ns; |
1488 | |
|
1489 | 0 | if (*ns->prefix == '\0') |
1490 | 0 | return; |
1491 | | |
1492 | | /* Find the prefix in the raw name by splitting it at the expected |
1493 | | separator. */ |
1494 | | |
1495 | 0 | char sep = mail_namespace_get_sep(ns); |
1496 | 0 | int seps_in_ns_prefix = 0; |
1497 | 0 | const char *p = ns->prefix, *p_sep; |
1498 | |
|
1499 | 0 | while ((p = strchr(p, sep)) != NULL) { |
1500 | 0 | seps_in_ns_prefix++; |
1501 | 0 | p++; |
1502 | 0 | } |
1503 | |
|
1504 | 0 | p = *vname; |
1505 | 0 | while ((p_sep = strchr(p, sep)) != NULL && seps_in_ns_prefix > 0) { |
1506 | 0 | seps_in_ns_prefix--; |
1507 | 0 | p = p_sep + 1; |
1508 | 0 | } |
1509 | 0 | i_assert(seps_in_ns_prefix == 0); |
1510 | | |
1511 | | /* Normalize the prefix */ |
1512 | | |
1513 | 0 | string_t *str = t_str_new(128); |
1514 | 0 | int ret; |
1515 | |
|
1516 | 0 | ret = uni_utf8_write_nfc(*vname, p - *vname, str); |
1517 | 0 | i_assert(ret >= 0); |
1518 | 0 | str_append(str, p); |
1519 | |
|
1520 | 0 | *vname = str_c(str); |
1521 | 0 | } |
1522 | | |
1523 | | bool mailbox_was_vname_changed_by_nfc(struct mailbox *box, |
1524 | | const char **orig_vname_r) |
1525 | 0 | { |
1526 | 0 | if (!box->mailbox_name_changed_by_nfc) |
1527 | 0 | return FALSE; |
1528 | 0 | if (orig_vname_r != NULL) |
1529 | 0 | *orig_vname_r = box->vname_raw; |
1530 | 0 | return TRUE; |
1531 | 0 | } |
1532 | | |
1533 | | void mailbox_suppress_nfc_name_change_notification(struct mailbox *box) |
1534 | 0 | { |
1535 | 0 | box->notifying_nfc_name_change = TRUE; |
1536 | 0 | } |
1537 | | |
1538 | | int mailbox_rename_nfc_forced(struct mailbox_list *list, const char *vname_raw, |
1539 | | const char *vname_nfc, const char **vname_new_r, |
1540 | | const char **error_r) |
1541 | 0 | { |
1542 | 0 | struct mailbox *box_old, *box_new; |
1543 | 0 | int ret; |
1544 | |
|
1545 | 0 | mailbox_vname_normalize_ns_prefix_nfc(list, &vname_raw); |
1546 | 0 | *vname_new_r = vname_nfc; |
1547 | 0 | if (strcmp(vname_raw, vname_nfc) == 0) { |
1548 | | /* Only namespace prefix was not NFC */ |
1549 | 0 | return 0; |
1550 | 0 | } |
1551 | 0 | box_old = mailbox_alloc(list, vname_raw, (MAILBOX_FLAG_RAW_NAME | |
1552 | 0 | MAILBOX_FLAG_IGNORE_ACLS)); |
1553 | 0 | box_old->notifying_nfc_name_change = TRUE; |
1554 | 0 | box_new = mailbox_alloc(list, vname_nfc, MAILBOX_FLAG_RAW_NAME); |
1555 | 0 | box_new->skip_create_name_restrictions = TRUE; |
1556 | 0 | box_new->notifying_nfc_name_change = TRUE; |
1557 | |
|
1558 | 0 | ret = mailbox_rename(box_old, box_new); |
1559 | 0 | if (ret < 0 && |
1560 | 0 | mailbox_get_last_mail_error(box_old) == MAIL_ERROR_NOTFOUND) { |
1561 | | /* ignore */ |
1562 | 0 | ret = 0; |
1563 | 0 | } else if (ret < 0 && |
1564 | 0 | mailbox_get_last_mail_error(box_old) == MAIL_ERROR_EXISTS) { |
1565 | | /* generate a new unique name */ |
1566 | 0 | guid_128_t guid; |
1567 | 0 | guid_128_generate(guid); |
1568 | |
|
1569 | 0 | *vname_new_r = t_strdup_printf("%s-%s", vname_nfc, |
1570 | 0 | guid_128_to_string(guid)); |
1571 | 0 | mailbox_free(&box_new); |
1572 | 0 | box_new = mailbox_alloc(list, *vname_new_r, 0); |
1573 | 0 | ret = mailbox_rename(box_old, box_new); |
1574 | 0 | } |
1575 | 0 | if (ret < 0) |
1576 | 0 | *error_r = mailbox_get_last_error(box_old, NULL); |
1577 | 0 | mailbox_free(&box_old); |
1578 | 0 | mailbox_free(&box_new); |
1579 | |
|
1580 | 0 | return (ret == 0 ? 1 : ret); |
1581 | 0 | } |
1582 | | |
1583 | | static int mailbox_nfc_normalize(struct mailbox *box) |
1584 | 0 | { |
1585 | 0 | const char *vname, *error; |
1586 | 0 | int ret; |
1587 | |
|
1588 | 0 | ret = mailbox_rename_nfc_forced(box->list, box->vname_raw, box->vname, |
1589 | 0 | &vname, &error); |
1590 | 0 | if (ret < 0) { |
1591 | 0 | mailbox_set_critical(box, |
1592 | 0 | "Failed to rename mailbox for NFC normalization: %s", |
1593 | 0 | error); |
1594 | 0 | return -1; |
1595 | 0 | } |
1596 | 0 | if (ret == 0) |
1597 | 0 | return 0; |
1598 | 0 | if (strcmp(box->vname, vname) == 0) { |
1599 | 0 | e_debug(box->event, "Mailbox renamed for NFC normalization"); |
1600 | 0 | } else { |
1601 | 0 | box->vname = p_strdup(box->pool, vname); |
1602 | 0 | e_debug(box->event, |
1603 | 0 | "Mailbox renamed to %s for NFC normalization " |
1604 | 0 | "(mailbox with NFC normalized name existed already)", |
1605 | 0 | box->vname); |
1606 | 0 | } |
1607 | 0 | return 1; |
1608 | 0 | } |
1609 | | |
1610 | | static bool |
1611 | | mailbox_name_verify_extra_separators(const char *vname, char sep, |
1612 | | const char **error_r) |
1613 | 0 | { |
1614 | 0 | unsigned int i; |
1615 | 0 | bool prev_sep = FALSE; |
1616 | | |
1617 | | /* Make sure the vname doesn't have extra separators: |
1618 | | |
1619 | | 1) Must not have adjacent separators. If we allow these, these could |
1620 | | end up pointing to existing mailboxes due to kernel ignoring |
1621 | | duplicate '/' in paths. However, this might cause us to handle some |
1622 | | of our own checks wrong, such as skipping ACLs. |
1623 | | |
1624 | | 2) Must not end with separator. Similar reasoning as above. |
1625 | | */ |
1626 | 0 | for (i = 0; vname[i] != '\0'; i++) { |
1627 | 0 | if (vname[i] == sep) { |
1628 | 0 | if (prev_sep) { |
1629 | 0 | *error_r = "Has adjacent hierarchy separators"; |
1630 | 0 | return FALSE; |
1631 | 0 | } |
1632 | 0 | prev_sep = TRUE; |
1633 | 0 | } else { |
1634 | 0 | prev_sep = FALSE; |
1635 | 0 | } |
1636 | 0 | } |
1637 | 0 | if (prev_sep && i > 0) { |
1638 | 0 | *error_r = "Ends with hierarchy separator"; |
1639 | 0 | return FALSE; |
1640 | 0 | } |
1641 | 0 | return TRUE; |
1642 | 0 | } |
1643 | | |
1644 | | static bool |
1645 | | mailbox_verify_name_prefix(struct mail_namespace *ns, const char **vnamep, |
1646 | | const char **error_r) |
1647 | 0 | { |
1648 | 0 | const char *vname = *vnamep; |
1649 | |
|
1650 | 0 | if (ns->prefix_len == 0) |
1651 | 0 | return TRUE; |
1652 | | |
1653 | | /* vname is either "namespace/box" or "namespace" */ |
1654 | 0 | if (strncmp(vname, ns->prefix, ns->prefix_len-1) != 0 || |
1655 | 0 | (vname[ns->prefix_len-1] != '\0' && |
1656 | 0 | vname[ns->prefix_len-1] != ns->prefix[ns->prefix_len-1])) { |
1657 | | /* User input shouldn't normally be able to get us in |
1658 | | here. The main reason this isn't an assert is to |
1659 | | allow any input at all to mailbox_verify_*_name() |
1660 | | without crashing. */ |
1661 | 0 | *error_r = t_strdup_printf("Missing namespace prefix '%s'", |
1662 | 0 | ns->prefix); |
1663 | 0 | return FALSE; |
1664 | 0 | } |
1665 | 0 | vname += ns->prefix_len - 1; |
1666 | 0 | if (vname[0] != '\0') { |
1667 | 0 | i_assert(vname[0] == ns->prefix[ns->prefix_len-1]); |
1668 | 0 | vname++; |
1669 | |
|
1670 | 0 | if (vname[0] == '\0') { |
1671 | | /* "namespace/" isn't a valid mailbox name. */ |
1672 | 0 | *error_r = "Ends with hierarchy separator"; |
1673 | 0 | return FALSE; |
1674 | 0 | } |
1675 | 0 | } |
1676 | 0 | *vnamep = vname; |
1677 | 0 | return TRUE; |
1678 | 0 | } |
1679 | | |
1680 | | static int mailbox_verify_name_int(struct mailbox *box) |
1681 | 0 | { |
1682 | 0 | struct mail_namespace *ns = box->list->ns; |
1683 | 0 | const char *error, *vname = box->vname; |
1684 | 0 | char list_sep, ns_sep; |
1685 | |
|
1686 | 0 | if (box->inbox_user) { |
1687 | | /* this is INBOX - don't bother with further checks */ |
1688 | 0 | return 0; |
1689 | 0 | } |
1690 | | |
1691 | | /* Verify the namespace prefix here. Change vname to skip the prefix |
1692 | | for the following checks. */ |
1693 | 0 | if (!mailbox_verify_name_prefix(box->list->ns, &vname, &error)) { |
1694 | 0 | mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, |
1695 | 0 | t_strdup_printf("Invalid mailbox name '%s': %s", |
1696 | 0 | mailbox_name_sanitize(vname), error)); |
1697 | 0 | return -1; |
1698 | 0 | } |
1699 | | |
1700 | 0 | list_sep = mailbox_list_get_hierarchy_sep(box->list); |
1701 | 0 | ns_sep = mail_namespace_get_sep(ns); |
1702 | | |
1703 | | /* If namespace { separator } differs from the mailbox_list separator, |
1704 | | the list separator can't actually be used in the mailbox name |
1705 | | unless it's escaped with storage_name_escape_char. For example if |
1706 | | namespace separator is '/' and mailbox_list_layout=Maildir++ has '.' |
1707 | | as the separator, there's no way to use '.' in the mailbox name |
1708 | | (without escaping) because it would end up becoming a hierarchy |
1709 | | separator. */ |
1710 | 0 | if (ns_sep != list_sep && |
1711 | 0 | box->list->mail_set->mailbox_list_storage_escape_char[0] == '\0' && |
1712 | 0 | strchr(vname, list_sep) != NULL) { |
1713 | 0 | mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, t_strdup_printf( |
1714 | 0 | "Character not allowed in mailbox name: '%c'", list_sep)); |
1715 | 0 | return -1; |
1716 | 0 | } |
1717 | | /* vname must not begin with the hierarchy separator normally. |
1718 | | For example we don't want to allow accessing /etc/passwd. However, |
1719 | | if mail_full_filesystem_access=yes, we do actually want to allow |
1720 | | that. */ |
1721 | 0 | if (vname[0] == ns_sep && |
1722 | 0 | !box->storage->set->mail_full_filesystem_access) { |
1723 | 0 | mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, |
1724 | 0 | "Invalid mailbox name: Begins with hierarchy separator"); |
1725 | 0 | return -1; |
1726 | 0 | } |
1727 | | |
1728 | 0 | if (!mailbox_name_verify_extra_separators(vname, ns_sep, &error) || |
1729 | 0 | !mailbox_list_is_valid_name(box->list, box->name, &error)) { |
1730 | 0 | mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, |
1731 | 0 | t_strdup_printf("Invalid mailbox name: %s", error)); |
1732 | 0 | return -1; |
1733 | 0 | } |
1734 | 0 | return 0; |
1735 | 0 | } |
1736 | | |
1737 | | int mailbox_verify_name(struct mailbox *box) |
1738 | 0 | { |
1739 | 0 | int ret; |
1740 | 0 | T_BEGIN { |
1741 | 0 | ret = mailbox_verify_name_int(box); |
1742 | 0 | } T_END; |
1743 | 0 | return ret; |
1744 | 0 | } |
1745 | | |
1746 | | static int mailbox_verify_existing_name_int(struct mailbox *box) |
1747 | 0 | { |
1748 | 0 | const char *path; |
1749 | |
|
1750 | 0 | if (box->opened) |
1751 | 0 | return 0; |
1752 | | |
1753 | 0 | if (mailbox_verify_name(box) < 0) |
1754 | 0 | return -1; |
1755 | | |
1756 | | /* Make sure box->_path is set, so mailbox_get_path() works from |
1757 | | now on. Note that this may also fail with some backends if the |
1758 | | mailbox doesn't exist. */ |
1759 | 0 | int ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path); |
1760 | 0 | if (ret < 0 && box->storage->error == MAIL_ERROR_NOTFOUND && |
1761 | 0 | box->mailbox_name_changed_by_nfc) { |
1762 | 0 | ret = mailbox_nfc_normalize(box); |
1763 | 0 | if (ret == 0) { |
1764 | 0 | i_assert(box->storage->error == MAIL_ERROR_NOTFOUND); |
1765 | 0 | ret = -1; |
1766 | 0 | } else { |
1767 | 0 | ret = mailbox_get_path_to(box, |
1768 | 0 | MAILBOX_LIST_PATH_TYPE_MAILBOX, &path); |
1769 | 0 | } |
1770 | 0 | } |
1771 | | |
1772 | 0 | if (ret < 0) { |
1773 | 0 | if (box->storage->error != MAIL_ERROR_NOTFOUND || |
1774 | 0 | !mailbox_is_autocreated(box)) |
1775 | 0 | return -1; |
1776 | | /* if this is an autocreated mailbox, create it now */ |
1777 | 0 | if (mailbox_autocreate(box) < 0) |
1778 | 0 | return -1; |
1779 | 0 | mailbox_close(box); |
1780 | 0 | if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, |
1781 | 0 | &path) < 0) |
1782 | 0 | return -1; |
1783 | 0 | } |
1784 | 0 | return 0; |
1785 | 0 | } |
1786 | | |
1787 | | static int mailbox_verify_existing_name(struct mailbox *box) |
1788 | 0 | { |
1789 | 0 | int ret; |
1790 | 0 | T_BEGIN { |
1791 | 0 | ret = mailbox_verify_existing_name_int(box); |
1792 | 0 | } T_END; |
1793 | 0 | return ret; |
1794 | 0 | } |
1795 | | |
1796 | | static int |
1797 | | mailbox_name_check_forbidden_chars(const char *name, const char **error_r) |
1798 | 0 | { |
1799 | 0 | const unsigned char *input = (const unsigned char *)name; |
1800 | 0 | size_t size = strlen(name); |
1801 | | |
1802 | | /* RFC 9755, Section 3: |
1803 | | |
1804 | | Mailbox names MUST comply with the Net-Unicode Definition ([RFC5198], |
1805 | | Section 2) with the specific exception that they MUST NOT contain |
1806 | | control characters (U+0000 - U+001F and U+0080 - U+009F), a delete |
1807 | | character (U+007F), a line separator (U+2028), or a paragraph |
1808 | | separator (U+2029). |
1809 | | |
1810 | | RFC 5198, Section 2: |
1811 | | |
1812 | | 5. As suggested in Section 6 of RFC 3629, the Byte Order Mark |
1813 | | ("BOM") signature MUST NOT appear at the beginning of these text |
1814 | | strings. |
1815 | | |
1816 | | 6. Systems conforming to this specification MUST NOT transmit any |
1817 | | string containing any code point that is unassigned in the |
1818 | | version of Unicode on which they are dependent. The version of |
1819 | | NFC and the version of Unicode used by that system MUST be |
1820 | | consistent. |
1821 | | */ |
1822 | |
|
1823 | 0 | while (size > 0) { |
1824 | 0 | unichar_t chr; |
1825 | 0 | int bytes; |
1826 | |
|
1827 | 0 | bytes = uni_utf8_get_char_n(input, size, &chr); |
1828 | 0 | i_assert(bytes > 0); /* UTF8 already checked */ |
1829 | 0 | input += bytes; |
1830 | 0 | size -= bytes; |
1831 | |
|
1832 | 0 | if (chr <= 0x1f || chr == 0x7f || |
1833 | 0 | (chr >= 0x0080 && chr <= 0x009f) || |
1834 | 0 | chr == 0x2028 || chr == 0x2029 || chr == 0xFEFF || |
1835 | 0 | !unicode_code_point_is_assigned(chr)) { |
1836 | 0 | *error_r = t_strdup_printf( |
1837 | 0 | "New mailbox name contains forbidden character " |
1838 | 0 | "(U+%04X)", chr); |
1839 | 0 | return -1; |
1840 | 0 | } |
1841 | 0 | } |
1842 | 0 | return 0; |
1843 | 0 | } |
1844 | | |
1845 | | void mailbox_skip_create_name_restrictions(struct mailbox *box, bool set) |
1846 | 0 | { |
1847 | 0 | box->skip_create_name_restrictions = set; |
1848 | 0 | } |
1849 | | |
1850 | | int mailbox_verify_create_name(struct mailbox *box) |
1851 | 0 | { |
1852 | 0 | const char *error; |
1853 | | |
1854 | | /* mailbox_alloc() already checks that vname is valid UTF8, |
1855 | | so we don't need to verify that. |
1856 | | |
1857 | | check vname instead of storage name, because vname is what is |
1858 | | visible to users, while storage name may be a fixed length GUID. */ |
1859 | 0 | if (mailbox_verify_name(box) < 0) |
1860 | 0 | return -1; |
1861 | 0 | if (box->skip_create_name_restrictions) |
1862 | 0 | return 0; |
1863 | 0 | if (mailbox_name_check_forbidden_chars(box->vname, &error) < 0) { |
1864 | 0 | mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, error); |
1865 | 0 | return -1; |
1866 | 0 | } |
1867 | 0 | if (strlen(box->vname) > MAILBOX_LIST_NAME_MAX_LENGTH) { |
1868 | 0 | mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, |
1869 | 0 | "Mailbox name too long"); |
1870 | 0 | return -1; |
1871 | 0 | } |
1872 | | /* check individual component names, too */ |
1873 | 0 | const char *old_name = box->name; |
1874 | 0 | const char *name; |
1875 | 0 | const char sep = mailbox_list_get_hierarchy_sep(box->list); |
1876 | 0 | while((name = strchr(old_name, sep)) != NULL) { |
1877 | 0 | if (name - old_name > MAILBOX_MAX_HIERARCHY_NAME_LENGTH) { |
1878 | 0 | mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, |
1879 | 0 | "Mailbox name too long"); |
1880 | 0 | return -1; |
1881 | 0 | } |
1882 | 0 | name++; |
1883 | 0 | old_name = name; |
1884 | 0 | } |
1885 | 0 | if (strlen(old_name) > MAILBOX_MAX_HIERARCHY_NAME_LENGTH) { |
1886 | 0 | mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, |
1887 | 0 | "Mailbox name too long"); |
1888 | 0 | return -1; |
1889 | 0 | } |
1890 | 0 | return 0; |
1891 | 0 | } |
1892 | | |
1893 | | static bool have_listable_namespace_prefix(struct mail_namespace *ns, |
1894 | | const char *name) |
1895 | 0 | { |
1896 | 0 | size_t name_len = strlen(name); |
1897 | |
|
1898 | 0 | for (; ns != NULL; ns = ns->next) { |
1899 | 0 | if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX | |
1900 | 0 | NAMESPACE_FLAG_LIST_CHILDREN)) == 0) |
1901 | 0 | continue; |
1902 | | |
1903 | 0 | if (ns->prefix_len <= name_len) |
1904 | 0 | continue; |
1905 | | |
1906 | | /* if prefix has multiple hierarchies, match |
1907 | | any of the hierarchies */ |
1908 | 0 | if (strncmp(ns->prefix, name, name_len) == 0 && |
1909 | 0 | ns->prefix[name_len] == mail_namespace_get_sep(ns)) |
1910 | 0 | return TRUE; |
1911 | 0 | } |
1912 | 0 | return FALSE; |
1913 | 0 | } |
1914 | | |
1915 | | int mailbox_exists(struct mailbox *box, bool auto_boxes, |
1916 | | enum mailbox_existence *existence_r) |
1917 | 0 | { |
1918 | 0 | switch (box->open_error) { |
1919 | 0 | case 0: |
1920 | 0 | break; |
1921 | 0 | case MAIL_ERROR_NOTFOUND: |
1922 | 0 | *existence_r = MAILBOX_EXISTENCE_NONE; |
1923 | 0 | return 0; |
1924 | 0 | default: |
1925 | | /* unsure if this exists or not */ |
1926 | 0 | return -1; |
1927 | 0 | } |
1928 | 0 | if (mailbox_verify_name(box) < 0) { |
1929 | | /* the mailbox name is invalid. we don't know if it currently |
1930 | | exists or not, but since it can never be accessed in any way |
1931 | | report it as if it didn't exist. */ |
1932 | 0 | *existence_r = MAILBOX_EXISTENCE_NONE; |
1933 | 0 | return 0; |
1934 | 0 | } |
1935 | | |
1936 | 0 | int ret; |
1937 | 0 | T_BEGIN { |
1938 | 0 | ret = box->v.exists(box, auto_boxes, existence_r); |
1939 | 0 | } T_END; |
1940 | 0 | if (ret == 0 && *existence_r == MAILBOX_EXISTENCE_NONE && |
1941 | 0 | box->mailbox_name_changed_by_nfc) { |
1942 | 0 | const char *nfc_vname = box->vname; |
1943 | 0 | box->vname = box->vname_raw; |
1944 | 0 | T_BEGIN { |
1945 | 0 | ret = box->v.exists(box, auto_boxes, existence_r); |
1946 | 0 | } T_END; |
1947 | 0 | box->vname = nfc_vname; |
1948 | 0 | } |
1949 | 0 | if (ret < 0) |
1950 | 0 | return -1; |
1951 | | |
1952 | 0 | if (!box->inbox_user && *existence_r == MAILBOX_EXISTENCE_NOSELECT && |
1953 | 0 | have_listable_namespace_prefix(box->storage->user->namespaces, |
1954 | 0 | box->vname)) { |
1955 | | /* listable namespace prefix always exists. */ |
1956 | 0 | *existence_r = MAILBOX_EXISTENCE_NOSELECT; |
1957 | 0 | return 0; |
1958 | 0 | } |
1959 | | |
1960 | | /* if this is a shared namespace with only INBOX and |
1961 | | mail_shared_explicit_inbox=no, we'll need to mark the namespace as |
1962 | | usable here since nothing else will. */ |
1963 | 0 | box->list->ns->flags |= NAMESPACE_FLAG_USABLE; |
1964 | 0 | return 0; |
1965 | 0 | } |
1966 | | |
1967 | | static int ATTR_NULL(2) |
1968 | | mailbox_open_full(struct mailbox *box, struct istream *input) |
1969 | 0 | { |
1970 | 0 | struct mail_storage *storage = box->storage; |
1971 | 0 | bool notifying_nfc_name_change = box->notifying_nfc_name_change; |
1972 | 0 | int ret; |
1973 | |
|
1974 | 0 | if (box->opened) |
1975 | 0 | return 0; |
1976 | | |
1977 | 0 | switch (box->open_error) { |
1978 | 0 | case 0: |
1979 | 0 | e_debug(box->event, "Mailbox opened"); |
1980 | 0 | break; |
1981 | 0 | case MAIL_ERROR_NOTFOUND: |
1982 | 0 | mail_storage_set_error(storage, MAIL_ERROR_NOTFOUND, |
1983 | 0 | T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); |
1984 | 0 | return -1; |
1985 | 0 | default: |
1986 | 0 | mail_storage_set_internal_error(storage); |
1987 | 0 | storage->error = box->open_error; |
1988 | 0 | return -1; |
1989 | 0 | } |
1990 | | |
1991 | 0 | ret = mailbox_verify_existing_name(box); |
1992 | 0 | if (ret < 0 && storage->error == MAIL_ERROR_NOTFOUND && |
1993 | 0 | box->mailbox_name_changed_by_nfc) { |
1994 | 0 | ret = mailbox_nfc_normalize(box); |
1995 | 0 | if (ret == 0) { |
1996 | 0 | i_assert(storage->error == MAIL_ERROR_NOTFOUND); |
1997 | 0 | ret = -1; |
1998 | 0 | } |
1999 | 0 | } |
2000 | 0 | if (ret < 0) |
2001 | 0 | return -1; |
2002 | | |
2003 | 0 | if (input != NULL) { |
2004 | 0 | if ((storage->class_flags & |
2005 | 0 | MAIL_STORAGE_CLASS_FLAG_OPEN_STREAMS) == 0) { |
2006 | 0 | mailbox_set_critical(box, |
2007 | 0 | "Storage doesn't support streamed mailboxes"); |
2008 | 0 | return -1; |
2009 | 0 | } |
2010 | 0 | box->input = input; |
2011 | 0 | box->flags |= MAILBOX_FLAG_READONLY; |
2012 | 0 | i_stream_ref(box->input); |
2013 | 0 | } |
2014 | | |
2015 | 0 | box->notifying_nfc_name_change = TRUE; |
2016 | 0 | ret = box->v.open(box); |
2017 | 0 | if (ret < 0 && storage->error == MAIL_ERROR_NOTFOUND && |
2018 | 0 | box->mailbox_name_changed_by_nfc) { |
2019 | 0 | ret = mailbox_nfc_normalize(box); |
2020 | 0 | if (ret == 0) { |
2021 | 0 | i_assert(storage->error == MAIL_ERROR_NOTFOUND); |
2022 | 0 | ret = -1; |
2023 | 0 | } |
2024 | 0 | if (ret > 0) { |
2025 | 0 | mailbox_close(box); |
2026 | 0 | ret = box->v.open(box); |
2027 | 0 | } |
2028 | 0 | } |
2029 | 0 | if (ret < 0 && storage->error == MAIL_ERROR_NOTFOUND && |
2030 | 0 | !box->deleting && !box->creating && |
2031 | 0 | box->input == NULL && mailbox_is_autocreated(box)) T_BEGIN { |
2032 | 0 | ret = mailbox_autocreate_and_reopen(box); |
2033 | 0 | } T_END; |
2034 | | |
2035 | 0 | if (ret < 0) { |
2036 | 0 | if (box->input != NULL) |
2037 | 0 | i_stream_unref(&box->input); |
2038 | 0 | box->notifying_nfc_name_change = notifying_nfc_name_change; |
2039 | 0 | return -1; |
2040 | 0 | } |
2041 | | |
2042 | | /* Always notify an NFC normalization rename, even when the physical |
2043 | | rename happened in the past. */ |
2044 | 0 | if (!notifying_nfc_name_change && box->mailbox_name_changed_by_nfc && |
2045 | 0 | storage->callbacks.notify_mailbox_implicit_rename != NULL) { |
2046 | 0 | storage->callbacks.notify_mailbox_implicit_rename( |
2047 | 0 | box, box->vname_raw, storage->callback_context); |
2048 | 0 | } |
2049 | |
|
2050 | 0 | box->notifying_nfc_name_change = notifying_nfc_name_change; |
2051 | 0 | box->list->ns->flags |= NAMESPACE_FLAG_USABLE; |
2052 | 0 | return 0; |
2053 | 0 | } |
2054 | | |
2055 | | static bool mailbox_try_undelete(struct mailbox *box) |
2056 | 0 | { |
2057 | 0 | time_t mtime; |
2058 | |
|
2059 | 0 | i_assert(!box->mailbox_undeleting); |
2060 | | |
2061 | 0 | if ((box->flags & MAILBOX_FLAG_READONLY) != 0) { |
2062 | | /* most importantly we don't do this because we want to avoid |
2063 | | a loop: mdbox storage rebuild -> mailbox_open() -> |
2064 | | mailbox_mark_index_deleted() -> mailbox_sync() -> |
2065 | | mdbox storage rebuild. */ |
2066 | 0 | return FALSE; |
2067 | 0 | } |
2068 | 0 | if (box->index == NULL) { |
2069 | | /* mailbox_open() failed before the index was opened (e.g. the |
2070 | | backend's delete failed and we're reverting the deletion). |
2071 | | There's nothing to undelete. */ |
2072 | 0 | return FALSE; |
2073 | 0 | } |
2074 | 0 | if (mail_index_get_modification_time(box->index, &mtime) < 0) |
2075 | 0 | return FALSE; |
2076 | 0 | if (mtime + MAILBOX_DELETE_RETRY_SECS > time(NULL)) |
2077 | 0 | return FALSE; |
2078 | | |
2079 | 0 | box->mailbox_undeleting = TRUE; |
2080 | 0 | int ret = mailbox_mark_index_deleted(box, FALSE); |
2081 | 0 | box->mailbox_undeleting = FALSE; |
2082 | 0 | if (ret < 0) |
2083 | 0 | return FALSE; |
2084 | 0 | box->mailbox_deleted = FALSE; |
2085 | 0 | return TRUE; |
2086 | 0 | } |
2087 | | |
2088 | | int mailbox_open(struct mailbox *box) |
2089 | 0 | { |
2090 | 0 | int ret; |
2091 | |
|
2092 | 0 | T_BEGIN { |
2093 | 0 | ret = mailbox_open_full(box, NULL); |
2094 | 0 | } T_END; |
2095 | 0 | if (ret < 0) { |
2096 | 0 | if (!box->mailbox_deleted || box->mailbox_undeleting) |
2097 | 0 | return -1; |
2098 | | |
2099 | | /* mailbox has been marked as deleted. if this deletion |
2100 | | started (and crashed) a long time ago, it can be confusing |
2101 | | to user that the mailbox can't be opened. so we'll just |
2102 | | undelete it and reopen. */ |
2103 | 0 | if(!mailbox_try_undelete(box)) |
2104 | 0 | return -1; |
2105 | | |
2106 | | /* make sure we close the mailbox in the middle. some backends |
2107 | | may not have fully opened the mailbox while it was being |
2108 | | undeleted. */ |
2109 | 0 | mailbox_close(box); |
2110 | 0 | T_BEGIN { |
2111 | 0 | ret = mailbox_open_full(box, NULL); |
2112 | 0 | } T_END; |
2113 | 0 | if (ret < 0) |
2114 | 0 | return -1; |
2115 | 0 | } |
2116 | 0 | return 0; |
2117 | 0 | } |
2118 | | |
2119 | | static int mailbox_alloc_index_pvt(struct mailbox *box) |
2120 | 0 | { |
2121 | 0 | const char *index_dir; |
2122 | 0 | int ret; |
2123 | |
|
2124 | 0 | if (box->index_pvt != NULL) |
2125 | 0 | return 1; |
2126 | | |
2127 | 0 | ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE, |
2128 | 0 | &index_dir); |
2129 | 0 | if (ret <= 0) |
2130 | 0 | return ret; /* error / no private indexes */ |
2131 | | |
2132 | 0 | if (mailbox_create_missing_dir(box, MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE) < 0) |
2133 | 0 | return -1; |
2134 | | |
2135 | | /* Note that this may cause box->event to live longer than box */ |
2136 | 0 | box->index_pvt = mail_index_alloc_cache_get(box->event, |
2137 | 0 | NULL, index_dir, t_strconcat(box->index_prefix, ".pvt", NULL)); |
2138 | 0 | mail_index_set_fsync_mode(box->index_pvt, |
2139 | 0 | box->storage->set->parsed_fsync_mode, 0); |
2140 | 0 | mail_index_set_lock_method(box->index_pvt, |
2141 | 0 | box->storage->set->parsed_lock_method, |
2142 | 0 | mail_storage_get_lock_timeout(box->storage, UINT_MAX)); |
2143 | 0 | return 1; |
2144 | 0 | } |
2145 | | |
2146 | | int mailbox_open_index_pvt(struct mailbox *box) |
2147 | 0 | { |
2148 | 0 | enum mail_index_open_flags index_flags; |
2149 | 0 | int ret; |
2150 | |
|
2151 | 0 | if (box->view_pvt != NULL) |
2152 | 0 | return 1; |
2153 | 0 | if (mailbox_get_private_flags_mask(box) == 0) |
2154 | 0 | return 0; |
2155 | | |
2156 | 0 | if ((ret = mailbox_alloc_index_pvt(box)) <= 0) |
2157 | 0 | return ret; |
2158 | 0 | index_flags = MAIL_INDEX_OPEN_FLAG_CREATE | |
2159 | 0 | mail_storage_settings_to_index_flags(box->storage->set); |
2160 | 0 | if ((box->flags & MAILBOX_FLAG_SAVEONLY) != 0) |
2161 | 0 | index_flags |= MAIL_INDEX_OPEN_FLAG_SAVEONLY; |
2162 | 0 | if (mail_index_open(box->index_pvt, index_flags) < 0) |
2163 | 0 | return -1; |
2164 | 0 | box->view_pvt = mail_index_view_open(box->index_pvt); |
2165 | 0 | return 1; |
2166 | 0 | } |
2167 | | |
2168 | | int mailbox_open_stream(struct mailbox *box, struct istream *input) |
2169 | 0 | { |
2170 | 0 | return mailbox_open_full(box, input); |
2171 | 0 | } |
2172 | | |
2173 | | int mailbox_enable(struct mailbox *box, enum mailbox_feature features) |
2174 | 0 | { |
2175 | 0 | if (mailbox_verify_name(box) < 0) |
2176 | 0 | return -1; |
2177 | | |
2178 | 0 | int ret; |
2179 | 0 | T_BEGIN { |
2180 | 0 | ret = box->v.enable(box, features); |
2181 | 0 | } T_END; |
2182 | 0 | return ret; |
2183 | 0 | } |
2184 | | |
2185 | | enum mailbox_feature mailbox_get_enabled_features(struct mailbox *box) |
2186 | 0 | { |
2187 | 0 | return box->enabled_features; |
2188 | 0 | } |
2189 | | |
2190 | | void mail_storage_free_binary_cache(struct mail_storage *storage) |
2191 | 0 | { |
2192 | 0 | if (storage->binary_cache.box == NULL) |
2193 | 0 | return; |
2194 | | |
2195 | 0 | timeout_remove(&storage->binary_cache.to); |
2196 | 0 | i_stream_destroy(&storage->binary_cache.input); |
2197 | 0 | i_zero(&storage->binary_cache); |
2198 | 0 | } |
2199 | | |
2200 | | void mailbox_close(struct mailbox *box) |
2201 | 0 | { |
2202 | 0 | if (!box->opened) |
2203 | 0 | return; |
2204 | | |
2205 | 0 | if (box->transaction_count != 0) { |
2206 | 0 | i_panic("Trying to close mailbox %s with open transactions", |
2207 | 0 | box->name); |
2208 | 0 | } |
2209 | 0 | T_BEGIN { |
2210 | 0 | box->v.close(box); |
2211 | 0 | } T_END; |
2212 | | |
2213 | 0 | if (box->storage->binary_cache.box == box) |
2214 | 0 | mail_storage_free_binary_cache(box->storage); |
2215 | 0 | box->opened = FALSE; |
2216 | 0 | box->mailbox_deleted = FALSE; |
2217 | 0 | array_clear(&box->search_results); |
2218 | |
|
2219 | 0 | if (array_is_created(&box->recent_flags)) |
2220 | 0 | array_free(&box->recent_flags); |
2221 | 0 | box->recent_flags_prev_uid = 0; |
2222 | 0 | box->recent_flags_count = 0; |
2223 | 0 | } |
2224 | | |
2225 | | void mailbox_free(struct mailbox **_box) |
2226 | 0 | { |
2227 | 0 | struct mailbox *box = *_box; |
2228 | |
|
2229 | 0 | *_box = NULL; |
2230 | |
|
2231 | 0 | mailbox_close(box); |
2232 | 0 | box->v.free(box); |
2233 | |
|
2234 | 0 | if (box->attribute_iter_count != 0) { |
2235 | 0 | i_panic("Trying to free mailbox %s with %u open attribute iterators", |
2236 | 0 | box->name, box->attribute_iter_count); |
2237 | 0 | } |
2238 | | |
2239 | 0 | DLLIST_REMOVE(&box->storage->mailboxes, box); |
2240 | 0 | mail_storage_obj_unref(box->storage); |
2241 | 0 | settings_free(box->set); |
2242 | 0 | pool_unref(&box->pool); |
2243 | 0 | } |
2244 | | |
2245 | | bool mailbox_equals(const struct mailbox *box1, |
2246 | | const struct mail_namespace *ns2, const char *vname2) |
2247 | 0 | { |
2248 | 0 | struct mail_namespace *ns1 = mailbox_get_namespace(box1); |
2249 | 0 | const char *name1; |
2250 | |
|
2251 | 0 | if (ns1 != ns2) |
2252 | 0 | return FALSE; |
2253 | | |
2254 | 0 | name1 = mailbox_get_vname(box1); |
2255 | 0 | if (strcmp(name1, vname2) == 0) |
2256 | 0 | return TRUE; |
2257 | | |
2258 | 0 | return strcasecmp(name1, "INBOX") == 0 && |
2259 | 0 | strcasecmp(vname2, "INBOX") == 0; |
2260 | 0 | } |
2261 | | |
2262 | | bool mailbox_is_any_inbox(struct mailbox *box) |
2263 | 0 | { |
2264 | 0 | return box->inbox_any; |
2265 | 0 | } |
2266 | | |
2267 | | bool mailbox_has_special_use(struct mailbox *box, const char *special_use) |
2268 | 0 | { |
2269 | 0 | if (box->set == NULL) |
2270 | 0 | return FALSE; |
2271 | 0 | return str_contains_special_use(t_array_const_string_join(&box->set->special_use, " "), |
2272 | 0 | special_use); |
2273 | 0 | } |
2274 | | |
2275 | | static void mailbox_copy_cache_decisions_from_inbox(struct mailbox *box) |
2276 | 0 | { |
2277 | 0 | struct mail_namespace *ns = |
2278 | 0 | mail_namespace_find_inbox(box->storage->user->namespaces); |
2279 | 0 | struct mailbox *inbox = |
2280 | 0 | mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY); |
2281 | 0 | enum mailbox_existence existence; |
2282 | | |
2283 | | /* this should be NoSelect but since inbox can never be |
2284 | | NoSelect we use EXISTENCE_NONE to avoid creating inbox by accident */ |
2285 | 0 | if (mailbox_exists(inbox, FALSE, &existence) == 0 && |
2286 | 0 | existence != MAILBOX_EXISTENCE_NONE && |
2287 | 0 | mailbox_open(inbox) == 0 && |
2288 | 0 | mailbox_open(box) == 0) { |
2289 | | /* we can't do much about errors here */ |
2290 | 0 | (void)mail_cache_decisions_copy(inbox->cache, box->cache); |
2291 | 0 | } |
2292 | |
|
2293 | 0 | mailbox_free(&inbox); |
2294 | 0 | } |
2295 | | |
2296 | | int mailbox_create(struct mailbox *box, const struct mailbox_update *update, |
2297 | | bool directory) |
2298 | 0 | { |
2299 | 0 | struct mail_storage *storage = box->storage; |
2300 | 0 | bool notifying_nfc_name_change = box->notifying_nfc_name_change; |
2301 | 0 | int ret; |
2302 | |
|
2303 | 0 | if (mailbox_verify_create_name(box) < 0) |
2304 | 0 | return -1; |
2305 | | |
2306 | 0 | struct event_reason *reason = event_reason_begin("mailbox:create"); |
2307 | | |
2308 | | /* Avoid race conditions by keeping mailbox list locked during changes. |
2309 | | This especially fixes a race during INBOX creation with |
2310 | | mailbox_list_layout=index |
2311 | | because it scans for missing mailboxes if INBOX doesn't exist. The |
2312 | | second process's scan can find a half-created INBOX and add it, |
2313 | | causing the first process to become confused. */ |
2314 | 0 | if (mailbox_list_lock(box->list) < 0) { |
2315 | 0 | mail_storage_copy_list_error(box->storage, box->list); |
2316 | 0 | event_reason_end(&reason); |
2317 | 0 | return -1; |
2318 | 0 | } |
2319 | 0 | box->creating = TRUE; |
2320 | 0 | box->notifying_nfc_name_change = TRUE; |
2321 | 0 | if ((box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) != 0) { |
2322 | | /* Layout doesn't support creating \NoSelect mailboxes. |
2323 | | Switch to creating a selectable mailbox */ |
2324 | 0 | directory = FALSE; |
2325 | 0 | } |
2326 | 0 | T_BEGIN { |
2327 | 0 | ret = box->v.create_box(box, update, directory); |
2328 | 0 | } T_END; |
2329 | 0 | box->creating = FALSE; |
2330 | 0 | mailbox_list_unlock(box->list); |
2331 | |
|
2332 | 0 | if (ret == 0) { |
2333 | 0 | box->list->guid_cache_updated = TRUE; |
2334 | 0 | if (!box->inbox_any) T_BEGIN { |
2335 | 0 | mailbox_copy_cache_decisions_from_inbox(box); |
2336 | 0 | } T_END; |
2337 | | /* Notify NFC normalization */ |
2338 | 0 | if (!notifying_nfc_name_change && box->mailbox_name_changed_by_nfc && |
2339 | 0 | storage->callbacks.notify_mailbox_implicit_rename != NULL) { |
2340 | 0 | storage->callbacks.notify_mailbox_implicit_rename( |
2341 | 0 | box, box->vname_raw, storage->callback_context); |
2342 | 0 | } |
2343 | 0 | } else if (box->opened) { |
2344 | | /* Creation failed after (partially) opening the mailbox. |
2345 | | It may not be in a valid state, so close it. */ |
2346 | 0 | mail_storage_last_error_push(box->storage); |
2347 | 0 | mailbox_close(box); |
2348 | 0 | mail_storage_last_error_pop(box->storage); |
2349 | 0 | } |
2350 | 0 | box->notifying_nfc_name_change = notifying_nfc_name_change; |
2351 | 0 | event_reason_end(&reason); |
2352 | 0 | return ret; |
2353 | 0 | } |
2354 | | |
2355 | | int mailbox_update(struct mailbox *box, const struct mailbox_update *update) |
2356 | 0 | { |
2357 | 0 | int ret; |
2358 | |
|
2359 | 0 | i_assert(update->min_next_uid == 0 || |
2360 | 0 | update->min_first_recent_uid == 0 || |
2361 | 0 | update->min_first_recent_uid <= update->min_next_uid); |
2362 | | |
2363 | 0 | if (mailbox_verify_existing_name(box) < 0) |
2364 | 0 | return -1; |
2365 | | |
2366 | 0 | struct event_reason *reason = event_reason_begin("mailbox:update"); |
2367 | 0 | T_BEGIN { |
2368 | 0 | ret = box->v.update_box(box, update); |
2369 | 0 | } T_END; |
2370 | 0 | if (!guid_128_is_empty(update->mailbox_guid)) |
2371 | 0 | box->list->guid_cache_invalidated = TRUE; |
2372 | 0 | event_reason_end(&reason); |
2373 | 0 | return ret; |
2374 | 0 | } |
2375 | | |
2376 | | int mailbox_mark_index_deleted(struct mailbox *box, bool del) |
2377 | 0 | { |
2378 | 0 | struct mail_index_transaction *trans; |
2379 | 0 | enum mail_index_transaction_flags trans_flags = 0; |
2380 | 0 | enum mailbox_flags old_flag; |
2381 | 0 | int ret; |
2382 | |
|
2383 | 0 | e_debug(box->event, "Attempting to %s mailbox", del ? |
2384 | 0 | "delete" : "undelete"); |
2385 | |
|
2386 | 0 | if (box->marked_deleted && del) { |
2387 | | /* we already marked it deleted. this allows plugins to |
2388 | | "lock" the deletion earlier. */ |
2389 | 0 | return 0; |
2390 | 0 | } |
2391 | | |
2392 | 0 | old_flag = box->flags & MAILBOX_FLAG_OPEN_DELETED; |
2393 | 0 | box->flags |= MAILBOX_FLAG_OPEN_DELETED; |
2394 | 0 | ret = mailbox_open(box); |
2395 | 0 | box->flags = (box->flags & ENUM_NEGATE(MAILBOX_FLAG_OPEN_DELETED)) | old_flag; |
2396 | 0 | if (ret < 0) |
2397 | 0 | return -1; |
2398 | | |
2399 | 0 | trans_flags = del ? 0 : MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL; |
2400 | 0 | trans = mail_index_transaction_begin(box->view, trans_flags); |
2401 | 0 | if (del) |
2402 | 0 | mail_index_set_deleted(trans); |
2403 | 0 | else |
2404 | 0 | mail_index_set_undeleted(trans); |
2405 | 0 | if (mail_index_transaction_commit(&trans) < 0) { |
2406 | 0 | mailbox_set_index_error(box); |
2407 | 0 | return -1; |
2408 | 0 | } |
2409 | | |
2410 | 0 | if (del) { |
2411 | | /* sync the mailbox. this finishes the index deletion and it |
2412 | | can succeed only for a single session. we do it here, so the |
2413 | | rest of the deletion code doesn't have to worry about race |
2414 | | conditions. */ |
2415 | 0 | box->delete_sync_check = TRUE; |
2416 | 0 | ret = mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ); |
2417 | 0 | box->delete_sync_check = FALSE; |
2418 | 0 | if (ret < 0) |
2419 | 0 | return -1; |
2420 | 0 | } |
2421 | | |
2422 | 0 | box->marked_deleted = del; |
2423 | 0 | return 0; |
2424 | 0 | } |
2425 | | |
2426 | | static void mailbox_close_reset_path(struct mailbox *box) |
2427 | 0 | { |
2428 | 0 | i_zero(&box->_perm); |
2429 | 0 | box->_path = NULL; |
2430 | 0 | box->_index_path = NULL; |
2431 | 0 | } |
2432 | | |
2433 | | static int mailbox_delete_real(struct mailbox *box) |
2434 | 0 | { |
2435 | 0 | bool list_locked; |
2436 | 0 | int ret; |
2437 | |
|
2438 | 0 | if (*box->name == '\0') { |
2439 | 0 | mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, |
2440 | 0 | "Storage root can't be deleted"); |
2441 | 0 | return -1; |
2442 | 0 | } |
2443 | | |
2444 | 0 | struct event_reason *reason = event_reason_begin("mailbox:delete"); |
2445 | |
|
2446 | 0 | box->deleting = TRUE; |
2447 | 0 | if (mailbox_open(box) < 0) { |
2448 | 0 | if (mailbox_get_last_mail_error(box) != MAIL_ERROR_NOTFOUND && |
2449 | 0 | !box->mailbox_deleted) { |
2450 | 0 | event_reason_end(&reason); |
2451 | 0 | return -1; |
2452 | 0 | } |
2453 | | /* might be a \noselect mailbox, so continue deletion */ |
2454 | 0 | } |
2455 | | |
2456 | 0 | if (mailbox_list_lock(box->list) < 0) { |
2457 | 0 | mail_storage_copy_list_error(box->storage, box->list); |
2458 | 0 | list_locked = FALSE; |
2459 | 0 | ret = -1; |
2460 | 0 | } else { |
2461 | 0 | list_locked = TRUE; |
2462 | 0 | ret = box->v.delete_box(box); |
2463 | 0 | } |
2464 | 0 | if (ret < 0 && box->marked_deleted) { |
2465 | | /* deletion failed. revert the mark so it can maybe be |
2466 | | tried again later. */ |
2467 | 0 | if (mailbox_mark_index_deleted(box, FALSE) < 0) |
2468 | 0 | ret = -1; |
2469 | 0 | } |
2470 | 0 | if (list_locked) |
2471 | 0 | mailbox_list_unlock(box->list); |
2472 | |
|
2473 | 0 | box->deleting = FALSE; |
2474 | 0 | mailbox_close(box); |
2475 | | |
2476 | | /* if mailbox is reopened, its path may be different with |
2477 | | mailbox_list_layout=index */ |
2478 | 0 | mailbox_close_reset_path(box); |
2479 | 0 | event_reason_end(&reason); |
2480 | 0 | return ret; |
2481 | 0 | } |
2482 | | |
2483 | | int mailbox_delete(struct mailbox *box) |
2484 | 0 | { |
2485 | 0 | int ret; |
2486 | 0 | T_BEGIN { |
2487 | 0 | ret = mailbox_delete_real(box); |
2488 | 0 | } T_END; |
2489 | 0 | return ret; |
2490 | 0 | } |
2491 | | |
2492 | | int mailbox_delete_empty(struct mailbox *box) |
2493 | 0 | { |
2494 | 0 | int ret; |
2495 | | |
2496 | | /* FIXME: should be a parameter to delete(), but since it changes API |
2497 | | don't do it for now */ |
2498 | 0 | box->deleting_must_be_empty = TRUE; |
2499 | 0 | ret = mailbox_delete(box); |
2500 | 0 | box->deleting_must_be_empty = FALSE; |
2501 | 0 | return ret; |
2502 | 0 | } |
2503 | | |
2504 | | static bool |
2505 | | mail_storages_rename_compatible(struct mail_storage *storage1, |
2506 | | struct mail_storage *storage2, |
2507 | | const char **error_r) |
2508 | 0 | { |
2509 | 0 | if (storage1 == storage2) |
2510 | 0 | return TRUE; |
2511 | | |
2512 | 0 | if (strcmp(storage1->name, storage2->name) != 0) { |
2513 | 0 | *error_r = t_strdup_printf("storage %s != %s", |
2514 | 0 | storage1->name, storage2->name); |
2515 | 0 | return FALSE; |
2516 | 0 | } |
2517 | 0 | if ((storage1->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) != 0) { |
2518 | | /* e.g. mdbox where all mails are in storage/ directory and |
2519 | | they can't be easily moved from there. */ |
2520 | 0 | *error_r = t_strdup_printf("storage %s uses unique root", |
2521 | 0 | storage1->name); |
2522 | 0 | return FALSE; |
2523 | 0 | } |
2524 | 0 | return TRUE; |
2525 | 0 | } |
2526 | | |
2527 | | static bool nullequals(const void *p1, const void *p2) |
2528 | 0 | { |
2529 | 0 | return (p1 == NULL && p2 == NULL) || (p1 != NULL && p2 != NULL); |
2530 | 0 | } |
2531 | | |
2532 | | static bool |
2533 | | mailbox_lists_rename_compatible(struct mailbox_list *list1, |
2534 | | struct mailbox_list *list2, |
2535 | | const char **error_r) |
2536 | 0 | { |
2537 | 0 | if (!nullequals(list1->mail_set->mail_alt_path, |
2538 | 0 | list2->mail_set->mail_alt_path)) { |
2539 | 0 | *error_r = t_strdup_printf( |
2540 | 0 | "Namespace %s has mail_alt_path, %s doesn't", |
2541 | 0 | list1->ns->set->name, list2->ns->set->name); |
2542 | 0 | return FALSE; |
2543 | 0 | } |
2544 | 0 | if (!nullequals(list1->mail_set->mail_index_path, |
2545 | 0 | list2->mail_set->mail_index_path)) { |
2546 | 0 | *error_r = t_strdup_printf( |
2547 | 0 | "Namespace %s has mail_index_path, %s doesn't", |
2548 | 0 | list1->ns->set->name, list2->ns->set->name); |
2549 | 0 | return FALSE; |
2550 | 0 | } |
2551 | 0 | if (!nullequals(list1->mail_set->mail_cache_path, |
2552 | 0 | list2->mail_set->mail_cache_path)) { |
2553 | 0 | *error_r = t_strdup_printf( |
2554 | 0 | "Namespace %s has mail_cache_path, %s doesn't", |
2555 | 0 | list1->ns->set->name, list2->ns->set->name); |
2556 | 0 | return FALSE; |
2557 | 0 | } |
2558 | 0 | if (!nullequals(list1->mail_set->mail_control_path, |
2559 | 0 | list2->mail_set->mail_control_path)) { |
2560 | 0 | *error_r = t_strdup_printf( |
2561 | 0 | "Namespace %s has mail_control_path, %s doesn't", |
2562 | 0 | list1->ns->set->name, list2->ns->set->name); |
2563 | 0 | return FALSE; |
2564 | 0 | } |
2565 | 0 | return TRUE; |
2566 | 0 | } |
2567 | | |
2568 | | static |
2569 | | int mailbox_rename_check_children(struct mailbox *src, struct mailbox *dest) |
2570 | 0 | { |
2571 | 0 | int ret = 0; |
2572 | 0 | size_t src_prefix_len = strlen(src->vname)+1; /* include separator */ |
2573 | 0 | size_t dest_prefix_len = strlen(dest->vname)+1; |
2574 | | /* this can return folders with * in their name, that are not |
2575 | | actually our children */ |
2576 | 0 | char ns_sep = mail_namespace_get_sep(src->list->ns); |
2577 | 0 | const char *pattern = t_strdup_printf("%s%c*", src->vname, ns_sep); |
2578 | |
|
2579 | 0 | struct mailbox_list_iterate_context *iter = mailbox_list_iter_init(src->list, pattern, |
2580 | 0 | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); |
2581 | |
|
2582 | 0 | const struct mailbox_info *child; |
2583 | 0 | while((child = mailbox_list_iter_next(iter)) != NULL) { |
2584 | 0 | if (strncmp(child->vname, src->vname, src_prefix_len-1) != 0 || |
2585 | 0 | child->vname[src_prefix_len-1] != ns_sep) |
2586 | 0 | continue; /* not our child */ |
2587 | | /* if total length of new name exceeds the limit, fail */ |
2588 | 0 | if (strlen(child->vname + src_prefix_len)+dest_prefix_len > MAILBOX_LIST_NAME_MAX_LENGTH) { |
2589 | 0 | mail_storage_set_error(src->storage, MAIL_ERROR_PARAMS, |
2590 | 0 | "Mailbox or child name too long"); |
2591 | 0 | ret = -1; |
2592 | 0 | break; |
2593 | 0 | } |
2594 | 0 | } |
2595 | | |
2596 | | /* something went bad */ |
2597 | 0 | if (mailbox_list_iter_deinit(&iter) < 0) { |
2598 | 0 | mail_storage_copy_list_error(src->storage, src->list); |
2599 | 0 | ret = -1; |
2600 | 0 | } |
2601 | 0 | return ret; |
2602 | 0 | } |
2603 | | |
2604 | | static int mailbox_rename_real(struct mailbox *src, struct mailbox *dest) |
2605 | 0 | { |
2606 | 0 | const char *error = NULL; |
2607 | 0 | bool src_notifying_nfc_name_change = src->notifying_nfc_name_change; |
2608 | 0 | bool dest_notifying_nfc_name_change = dest->notifying_nfc_name_change; |
2609 | | |
2610 | | /* Check only name validity, \Noselect don't necessarily exist. */ |
2611 | 0 | if (mailbox_verify_name(src) < 0) |
2612 | 0 | return -1; |
2613 | 0 | if (*src->name == '\0') { |
2614 | 0 | mail_storage_set_error(src->storage, MAIL_ERROR_PARAMS, |
2615 | 0 | "Can't rename mailbox root"); |
2616 | 0 | return -1; |
2617 | 0 | } |
2618 | 0 | if (mailbox_verify_create_name(dest) < 0) { |
2619 | 0 | mail_storage_copy_error(src->storage, dest->storage); |
2620 | 0 | return -1; |
2621 | 0 | } |
2622 | 0 | if (mailbox_rename_check_children(src, dest) != 0) { |
2623 | 0 | return -1; |
2624 | 0 | } |
2625 | | |
2626 | 0 | if (!mail_storages_rename_compatible(src->storage, |
2627 | 0 | dest->storage, &error) || |
2628 | 0 | !mailbox_lists_rename_compatible(src->list, |
2629 | 0 | dest->list, &error)) { |
2630 | 0 | e_debug(src->event, |
2631 | 0 | "Can't rename '%s' to '%s': %s", |
2632 | 0 | src->vname, dest->vname, error); |
2633 | 0 | mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, |
2634 | 0 | "Can't rename mailboxes across specified storages."); |
2635 | 0 | return -1; |
2636 | 0 | } |
2637 | 0 | if (src->list != dest->list && |
2638 | 0 | (src->list->ns->type != MAIL_NAMESPACE_TYPE_PRIVATE || |
2639 | 0 | dest->list->ns->type != MAIL_NAMESPACE_TYPE_PRIVATE)) { |
2640 | 0 | mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, |
2641 | 0 | "Renaming not supported across non-private namespaces."); |
2642 | 0 | return -1; |
2643 | 0 | } |
2644 | 0 | if (src->list == dest->list && strcmp(src->name, dest->name) == 0) { |
2645 | 0 | mail_storage_set_error(src->storage, MAIL_ERROR_EXISTS, |
2646 | 0 | "Can't rename mailbox to itself."); |
2647 | 0 | return -1; |
2648 | 0 | } |
2649 | | |
2650 | | /* It would be safer to lock both source and destination, but that |
2651 | | could lead to deadlocks. So at least for now lets just lock only the |
2652 | | destination list. */ |
2653 | 0 | if (mailbox_list_lock(dest->list) < 0) { |
2654 | 0 | mail_storage_copy_list_error(src->storage, dest->list); |
2655 | 0 | return -1; |
2656 | 0 | } |
2657 | | |
2658 | | /* Make sure nested calls don't repeat notification of NFC |
2659 | | normalization. */ |
2660 | 0 | src->notifying_nfc_name_change = TRUE; |
2661 | 0 | dest->notifying_nfc_name_change = TRUE; |
2662 | |
|
2663 | 0 | int ret = src->v.rename_box(src, dest); |
2664 | 0 | if (ret < 0 && src->storage->error == MAIL_ERROR_NOTFOUND && |
2665 | 0 | src->mailbox_name_changed_by_nfc) { |
2666 | 0 | ret = mailbox_nfc_normalize(src); |
2667 | 0 | if (ret >= 0) |
2668 | 0 | ret = src->v.rename_box(src, dest); |
2669 | 0 | } |
2670 | 0 | mailbox_list_unlock(dest->list); |
2671 | 0 | if (ret < 0) { |
2672 | 0 | src->notifying_nfc_name_change = src_notifying_nfc_name_change; |
2673 | 0 | dest->notifying_nfc_name_change = dest_notifying_nfc_name_change; |
2674 | 0 | return -1; |
2675 | 0 | } |
2676 | | |
2677 | | /* Always notify an NFC normalization rename, even when the physical |
2678 | | rename happened in the past. However, don't send the notification |
2679 | | here when this is a nested call. */ |
2680 | 0 | if (!src_notifying_nfc_name_change && |
2681 | 0 | src->mailbox_name_changed_by_nfc && |
2682 | 0 | src->storage->callbacks.notify_mailbox_implicit_rename != NULL) { |
2683 | 0 | src->storage->callbacks.notify_mailbox_implicit_rename( |
2684 | 0 | src, src->vname_raw, src->storage->callback_context); |
2685 | 0 | } |
2686 | 0 | if (!dest_notifying_nfc_name_change && |
2687 | 0 | dest->mailbox_name_changed_by_nfc && |
2688 | 0 | dest->storage->callbacks.notify_mailbox_implicit_rename != NULL) { |
2689 | 0 | dest->storage->callbacks.notify_mailbox_implicit_rename( |
2690 | 0 | dest, dest->vname_raw, dest->storage->callback_context); |
2691 | 0 | } |
2692 | |
|
2693 | 0 | src->notifying_nfc_name_change = src_notifying_nfc_name_change; |
2694 | 0 | dest->notifying_nfc_name_change = dest_notifying_nfc_name_change; |
2695 | |
|
2696 | 0 | src->list->guid_cache_invalidated = TRUE; |
2697 | 0 | dest->list->guid_cache_invalidated = TRUE; |
2698 | 0 | return 0; |
2699 | 0 | } |
2700 | | |
2701 | | int mailbox_rename(struct mailbox *src, struct mailbox *dest) |
2702 | 0 | { |
2703 | 0 | int ret; |
2704 | 0 | T_BEGIN { |
2705 | 0 | struct event_reason *reason = |
2706 | 0 | event_reason_begin("mailbox:rename"); |
2707 | 0 | ret = mailbox_rename_real(src, dest); |
2708 | 0 | event_reason_end(&reason); |
2709 | 0 | } T_END; |
2710 | 0 | return ret; |
2711 | 0 | } |
2712 | | |
2713 | | int mailbox_set_subscribed(struct mailbox *box, bool set) |
2714 | 0 | { |
2715 | 0 | int ret; |
2716 | |
|
2717 | 0 | if (mailbox_verify_name(box) < 0) |
2718 | 0 | return -1; |
2719 | | |
2720 | 0 | struct event_reason *reason = |
2721 | 0 | event_reason_begin(set ? "mailbox:subscribe" : |
2722 | 0 | "mailbox:unsubscribe"); |
2723 | 0 | if (mailbox_list_iter_subscriptions_refresh(box->list) < 0) { |
2724 | 0 | mail_storage_copy_list_error(box->storage, box->list); |
2725 | 0 | ret = -1; |
2726 | 0 | } else if (mailbox_is_subscribed(box) == set) |
2727 | 0 | ret = 0; |
2728 | 0 | else T_BEGIN { |
2729 | 0 | ret = box->v.set_subscribed(box, set); |
2730 | 0 | } T_END; |
2731 | 0 | event_reason_end(&reason); |
2732 | 0 | return ret; |
2733 | 0 | } |
2734 | | |
2735 | | bool mailbox_is_subscribed(struct mailbox *box) |
2736 | 0 | { |
2737 | 0 | struct mailbox_node *node; |
2738 | |
|
2739 | 0 | i_assert(box->list->subscriptions != NULL); |
2740 | | |
2741 | 0 | node = mailbox_tree_lookup(box->list->subscriptions, box->vname); |
2742 | 0 | return node != NULL && (node->flags & MAILBOX_SUBSCRIBED) != 0; |
2743 | 0 | } |
2744 | | |
2745 | | struct mail_storage *mailbox_get_storage(const struct mailbox *box) |
2746 | 0 | { |
2747 | 0 | return box->storage; |
2748 | 0 | } |
2749 | | |
2750 | | struct mail_namespace * |
2751 | | mailbox_get_namespace(const struct mailbox *box) |
2752 | 0 | { |
2753 | 0 | return box->list->ns; |
2754 | 0 | } |
2755 | | |
2756 | | const struct mailbox_settings *mailbox_get_settings(struct mailbox *box) |
2757 | 0 | { |
2758 | 0 | return box->set; |
2759 | 0 | } |
2760 | | |
2761 | | const char *mailbox_get_name(const struct mailbox *box) |
2762 | 0 | { |
2763 | 0 | return box->name; |
2764 | 0 | } |
2765 | | |
2766 | | const char *mailbox_get_vname(const struct mailbox *box) |
2767 | 0 | { |
2768 | 0 | return box->vname; |
2769 | 0 | } |
2770 | | |
2771 | | bool mailbox_is_readonly(struct mailbox *box) |
2772 | 0 | { |
2773 | 0 | i_assert(box->opened); |
2774 | | |
2775 | 0 | return box->v.is_readonly(box); |
2776 | 0 | } |
2777 | | |
2778 | | bool mailbox_backends_equal(const struct mailbox *box1, |
2779 | | const struct mailbox *box2) |
2780 | 0 | { |
2781 | 0 | struct mail_namespace *ns1 = box1->list->ns, *ns2 = box2->list->ns; |
2782 | |
|
2783 | 0 | if (strcmp(box1->name, box2->name) != 0) |
2784 | 0 | return FALSE; |
2785 | | |
2786 | 0 | while (ns1->alias_for != NULL) |
2787 | 0 | ns1 = ns1->alias_for; |
2788 | 0 | while (ns2->alias_for != NULL) |
2789 | 0 | ns2 = ns2->alias_for; |
2790 | 0 | return ns1 == ns2; |
2791 | 0 | } |
2792 | | |
2793 | | static void |
2794 | | mailbox_get_status_set_defaults(struct mailbox *box, |
2795 | | struct mailbox_status *status_r) |
2796 | 0 | { |
2797 | 0 | i_zero(status_r); |
2798 | 0 | if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUIDS) != 0) |
2799 | 0 | status_r->have_guids = TRUE; |
2800 | 0 | if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_SAVE_GUIDS) != 0) |
2801 | 0 | status_r->have_save_guids = TRUE; |
2802 | 0 | if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUID128) != 0) |
2803 | 0 | status_r->have_only_guid128 = TRUE; |
2804 | 0 | } |
2805 | | |
2806 | | int mailbox_get_status(struct mailbox *box, |
2807 | | enum mailbox_status_items items, |
2808 | | struct mailbox_status *status_r) |
2809 | 0 | { |
2810 | 0 | mailbox_get_status_set_defaults(box, status_r); |
2811 | 0 | if (mailbox_verify_existing_name(box) < 0) |
2812 | 0 | return -1; |
2813 | | |
2814 | 0 | int ret; |
2815 | 0 | T_BEGIN { |
2816 | 0 | ret = box->v.get_status(box, items, status_r); |
2817 | 0 | } T_END; |
2818 | 0 | if (ret < 0) |
2819 | 0 | return -1; |
2820 | 0 | i_assert(status_r->have_guids || !status_r->have_save_guids); |
2821 | 0 | return 0; |
2822 | 0 | } |
2823 | | |
2824 | | void mailbox_get_open_status(struct mailbox *box, |
2825 | | enum mailbox_status_items items, |
2826 | | struct mailbox_status *status_r) |
2827 | 0 | { |
2828 | 0 | i_assert(box->opened); |
2829 | 0 | i_assert((items & MAILBOX_STATUS_FAILING_ITEMS) == 0); |
2830 | | |
2831 | 0 | mailbox_get_status_set_defaults(box, status_r); |
2832 | 0 | T_BEGIN { |
2833 | 0 | if (box->v.get_status(box, items, status_r) < 0) |
2834 | 0 | i_unreached(); |
2835 | 0 | } T_END; |
2836 | 0 | } |
2837 | | |
2838 | | int mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, |
2839 | | struct mailbox_metadata *metadata_r) |
2840 | 0 | { |
2841 | 0 | i_zero(metadata_r); |
2842 | 0 | if (mailbox_verify_existing_name(box) < 0) |
2843 | 0 | return -1; |
2844 | | |
2845 | | /* NOTE: metadata_r->cache_fields is currently returned from |
2846 | | data stack, so can't use a data stack frame here. */ |
2847 | 0 | if (box->v.get_metadata(box, items, metadata_r) < 0) |
2848 | 0 | return -1; |
2849 | | |
2850 | 0 | i_assert((items & MAILBOX_METADATA_GUID) == 0 || |
2851 | 0 | !guid_128_is_empty(metadata_r->guid)); |
2852 | 0 | return 0; |
2853 | 0 | } |
2854 | | |
2855 | | enum mail_flags mailbox_get_private_flags_mask(struct mailbox *box) |
2856 | 0 | { |
2857 | 0 | if (box->v.get_private_flags_mask != NULL) |
2858 | 0 | return box->v.get_private_flags_mask(box); |
2859 | 0 | else if (box->list->mail_set->mail_index_private_path[0] != '\0') |
2860 | 0 | return MAIL_SEEN; /* FIXME */ |
2861 | 0 | else |
2862 | 0 | return 0; |
2863 | 0 | } |
2864 | | |
2865 | | struct mailbox_sync_context * |
2866 | | mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) |
2867 | 0 | { |
2868 | 0 | struct mailbox_sync_context *ctx; |
2869 | |
|
2870 | 0 | if (box->transaction_count != 0) { |
2871 | 0 | i_panic("Trying to sync mailbox %s with open transactions", |
2872 | 0 | box->name); |
2873 | 0 | } |
2874 | 0 | if (!box->opened) { |
2875 | 0 | if (mailbox_open(box) < 0) { |
2876 | 0 | ctx = i_new(struct mailbox_sync_context, 1); |
2877 | 0 | ctx->box = box; |
2878 | 0 | ctx->flags = flags; |
2879 | 0 | ctx->open_failed = TRUE; |
2880 | 0 | return ctx; |
2881 | 0 | } |
2882 | 0 | } |
2883 | 0 | T_BEGIN { |
2884 | 0 | ctx = box->v.sync_init(box, flags); |
2885 | 0 | } T_END; |
2886 | 0 | return ctx; |
2887 | 0 | } |
2888 | | |
2889 | | bool mailbox_sync_next(struct mailbox_sync_context *ctx, |
2890 | | struct mailbox_sync_rec *sync_rec_r) |
2891 | 0 | { |
2892 | 0 | if (ctx->open_failed) |
2893 | 0 | return FALSE; |
2894 | | |
2895 | 0 | bool ret; |
2896 | 0 | T_BEGIN { |
2897 | 0 | ret = ctx->box->v.sync_next(ctx, sync_rec_r); |
2898 | 0 | } T_END; |
2899 | 0 | return ret; |
2900 | 0 | } |
2901 | | |
2902 | | int mailbox_sync_deinit(struct mailbox_sync_context **_ctx, |
2903 | | struct mailbox_sync_status *status_r) |
2904 | 0 | { |
2905 | 0 | struct mailbox_sync_context *ctx = *_ctx; |
2906 | 0 | struct mailbox *box = ctx->box; |
2907 | 0 | const char *errormsg; |
2908 | 0 | enum mail_error error; |
2909 | 0 | int ret; |
2910 | |
|
2911 | 0 | *_ctx = NULL; |
2912 | |
|
2913 | 0 | i_zero(status_r); |
2914 | |
|
2915 | 0 | if (!ctx->open_failed) { |
2916 | 0 | T_BEGIN { |
2917 | 0 | ret = box->v.sync_deinit(ctx, status_r); |
2918 | 0 | } T_END; |
2919 | 0 | } else { |
2920 | 0 | i_free(ctx); |
2921 | 0 | ret = -1; |
2922 | 0 | } |
2923 | 0 | if (ret < 0 && box->inbox_user && |
2924 | 0 | !box->storage->user->inbox_open_error_logged) { |
2925 | 0 | errormsg = mailbox_get_last_internal_error(box, &error); |
2926 | 0 | if (error == MAIL_ERROR_NOTPOSSIBLE) { |
2927 | 0 | box->storage->user->inbox_open_error_logged = TRUE; |
2928 | 0 | e_error(box->event, "Syncing INBOX failed: %s", errormsg); |
2929 | 0 | } |
2930 | 0 | } |
2931 | 0 | if (ret == 0) |
2932 | 0 | box->synced = TRUE; |
2933 | 0 | return ret; |
2934 | 0 | } |
2935 | | |
2936 | | int mailbox_sync(struct mailbox *box, enum mailbox_sync_flags flags) |
2937 | 0 | { |
2938 | 0 | struct mailbox_sync_context *ctx; |
2939 | 0 | struct mailbox_sync_status status; |
2940 | |
|
2941 | 0 | if (array_count(&box->search_results) == 0) { |
2942 | | /* we don't care about mailbox's current state, so we might |
2943 | | as well fix inconsistency state */ |
2944 | 0 | flags |= MAILBOX_SYNC_FLAG_FIX_INCONSISTENT; |
2945 | 0 | } |
2946 | |
|
2947 | 0 | ctx = mailbox_sync_init(box, flags); |
2948 | 0 | return mailbox_sync_deinit(&ctx, &status); |
2949 | 0 | } |
2950 | | |
2951 | | #undef mailbox_notify_changes |
2952 | | void mailbox_notify_changes(struct mailbox *box, |
2953 | | mailbox_notify_callback_t *callback, void *context) |
2954 | 0 | { |
2955 | 0 | i_assert(box->opened); |
2956 | | |
2957 | 0 | box->notify_callback = callback; |
2958 | 0 | box->notify_context = context; |
2959 | |
|
2960 | 0 | T_BEGIN { |
2961 | 0 | box->v.notify_changes(box); |
2962 | 0 | } T_END; |
2963 | 0 | } |
2964 | | |
2965 | | void mailbox_notify_changes_stop(struct mailbox *box) |
2966 | 0 | { |
2967 | 0 | i_assert(box->opened); |
2968 | | |
2969 | 0 | box->notify_callback = NULL; |
2970 | 0 | box->notify_context = NULL; |
2971 | |
|
2972 | 0 | T_BEGIN { |
2973 | 0 | box->v.notify_changes(box); |
2974 | 0 | } T_END; |
2975 | 0 | } |
2976 | | |
2977 | | struct mail_search_context * |
2978 | | mailbox_search_init(struct mailbox_transaction_context *t, |
2979 | | struct mail_search_args *args, |
2980 | | const enum mail_sort_type *sort_program, |
2981 | | enum mail_fetch_field wanted_fields, |
2982 | | struct mailbox_header_lookup_ctx *wanted_headers) |
2983 | 0 | { |
2984 | 0 | i_assert(wanted_headers == NULL || wanted_headers->box == t->box); |
2985 | | |
2986 | 0 | mail_search_args_ref(args); |
2987 | 0 | if (!args->simplified) |
2988 | 0 | mail_search_args_simplify(args); |
2989 | |
|
2990 | 0 | struct mail_search_context *ctx; |
2991 | 0 | T_BEGIN { |
2992 | 0 | ctx = t->box->v.search_init(t, args, sort_program, |
2993 | 0 | wanted_fields, wanted_headers); |
2994 | 0 | } T_END; |
2995 | 0 | return ctx; |
2996 | 0 | } |
2997 | | |
2998 | | int mailbox_search_deinit(struct mail_search_context **_ctx) |
2999 | 0 | { |
3000 | 0 | struct mail_search_context *ctx = *_ctx; |
3001 | 0 | struct mail_search_args *args = ctx->args; |
3002 | 0 | int ret; |
3003 | |
|
3004 | 0 | *_ctx = NULL; |
3005 | 0 | mailbox_search_results_initial_done(ctx); |
3006 | 0 | T_BEGIN { |
3007 | 0 | ret = ctx->transaction->box->v.search_deinit(ctx); |
3008 | 0 | } T_END; |
3009 | 0 | mail_search_args_unref(&args); |
3010 | 0 | return ret; |
3011 | 0 | } |
3012 | | |
3013 | | void mailbox_search_reset_progress_start(struct mail_search_context *ctx) |
3014 | 0 | { |
3015 | 0 | i_zero(&ctx->search_start_time); |
3016 | 0 | i_zero(&ctx->last_notify); |
3017 | 0 | } |
3018 | | |
3019 | | void |
3020 | | mailbox_search_set_progress_hidden(struct mail_search_context *ctx, bool hidden) |
3021 | 0 | { |
3022 | 0 | ctx->progress_hidden = hidden; |
3023 | 0 | } |
3024 | | |
3025 | | void mailbox_search_notify(struct mailbox *box, struct mail_search_context *ctx) |
3026 | 0 | { |
3027 | 0 | if (ctx->search_start_time.tv_sec == 0) { |
3028 | 0 | ctx->search_start_time = ioloop_timeval; |
3029 | 0 | return; |
3030 | 0 | } |
3031 | | |
3032 | 0 | if (ctx->last_notify.tv_sec == 0) |
3033 | 0 | ctx->last_notify = ctx->search_start_time; |
3034 | |
|
3035 | 0 | if (box->storage->callbacks.notify_progress == NULL || |
3036 | 0 | ctx->progress_hidden) |
3037 | 0 | return; |
3038 | | |
3039 | 0 | if (++ctx->search_notify_passes % 1024 == 0) |
3040 | 0 | io_loop_time_refresh(); |
3041 | |
|
3042 | 0 | if (ioloop_time - ctx->last_notify.tv_sec < MAIL_STORAGE_NOTIFY_INTERVAL_SECS) |
3043 | 0 | return; |
3044 | | |
3045 | 0 | struct mail_storage_progress_details dtl = { |
3046 | 0 | .total = ctx->progress_max, |
3047 | 0 | .processed = ctx->progress_cur, |
3048 | 0 | .start_time = ctx->search_start_time, |
3049 | 0 | .now = ioloop_timeval, |
3050 | 0 | }; |
3051 | |
|
3052 | 0 | box->storage->callbacks.notify_progress(box, &dtl, |
3053 | 0 | box->storage->callback_context); |
3054 | |
|
3055 | 0 | ctx->last_notify = ioloop_timeval; |
3056 | 0 | } |
3057 | | |
3058 | | bool mailbox_search_next(struct mail_search_context *ctx, struct mail **mail_r) |
3059 | 0 | { |
3060 | 0 | bool tryagain; |
3061 | |
|
3062 | 0 | while (!mailbox_search_next_nonblock(ctx, mail_r, &tryagain)) { |
3063 | 0 | if (!tryagain) |
3064 | 0 | return FALSE; |
3065 | 0 | } |
3066 | 0 | return TRUE; |
3067 | 0 | } |
3068 | | |
3069 | | bool mailbox_search_next_nonblock(struct mail_search_context *ctx, |
3070 | | struct mail **mail_r, bool *tryagain_r) |
3071 | 0 | { |
3072 | 0 | struct mailbox *box = ctx->transaction->box; |
3073 | 0 | bool ret; |
3074 | |
|
3075 | 0 | *mail_r = NULL; |
3076 | 0 | *tryagain_r = FALSE; |
3077 | |
|
3078 | 0 | T_BEGIN { |
3079 | 0 | mailbox_search_notify(box, ctx); |
3080 | 0 | ret = box->v.search_next_nonblock(ctx, mail_r, tryagain_r); |
3081 | 0 | } T_END; |
3082 | 0 | if (!ret) |
3083 | 0 | return FALSE; |
3084 | 0 | else { |
3085 | 0 | mailbox_search_results_add(ctx, (*mail_r)->uid); |
3086 | 0 | return TRUE; |
3087 | 0 | } |
3088 | 0 | } |
3089 | | |
3090 | | bool mailbox_search_seen_lost_data(struct mail_search_context *ctx) |
3091 | 0 | { |
3092 | 0 | return ctx->seen_lost_data; |
3093 | 0 | } |
3094 | | |
3095 | | void mailbox_search_mail_detach(struct mail_search_context *ctx, |
3096 | | struct mail *mail) |
3097 | 0 | { |
3098 | 0 | struct mail_private *pmail = |
3099 | 0 | container_of(mail, struct mail_private, mail); |
3100 | 0 | unsigned int idx; |
3101 | |
|
3102 | 0 | if (!array_lsearch_ptr_idx(&ctx->mails, mail, &idx)) |
3103 | 0 | i_unreached(); |
3104 | 0 | pmail->search_mail = FALSE; |
3105 | 0 | array_delete(&ctx->mails, idx, 1); |
3106 | 0 | } |
3107 | | |
3108 | | int mailbox_search_result_build(struct mailbox_transaction_context *t, |
3109 | | struct mail_search_args *args, |
3110 | | enum mailbox_search_result_flags flags, |
3111 | | struct mail_search_result **result_r) |
3112 | 0 | { |
3113 | 0 | struct mail_search_context *ctx; |
3114 | 0 | struct mail *mail; |
3115 | 0 | int ret; |
3116 | |
|
3117 | 0 | ctx = mailbox_search_init(t, args, NULL, 0, NULL); |
3118 | 0 | *result_r = mailbox_search_result_save(ctx, flags); |
3119 | 0 | while (mailbox_search_next(ctx, &mail)) ; |
3120 | |
|
3121 | 0 | ret = mailbox_search_deinit(&ctx); |
3122 | 0 | if (ret < 0) |
3123 | 0 | mailbox_search_result_free(result_r); |
3124 | 0 | return ret; |
3125 | 0 | } |
3126 | | |
3127 | | struct mailbox_transaction_context * |
3128 | | mailbox_transaction_begin(struct mailbox *box, |
3129 | | enum mailbox_transaction_flags flags, |
3130 | | const char *reason) |
3131 | 0 | { |
3132 | 0 | struct mailbox_transaction_context *trans; |
3133 | |
|
3134 | 0 | i_assert(box->opened); |
3135 | | |
3136 | 0 | box->transaction_count++; |
3137 | 0 | T_BEGIN { |
3138 | 0 | trans = box->v.transaction_begin(box, flags, reason); |
3139 | 0 | } T_END; |
3140 | 0 | i_assert(trans->reason != NULL); |
3141 | 0 | return trans; |
3142 | 0 | } |
3143 | | |
3144 | | int mailbox_transaction_commit(struct mailbox_transaction_context **t) |
3145 | 0 | { |
3146 | 0 | struct mail_transaction_commit_changes changes; |
3147 | 0 | int ret; |
3148 | | |
3149 | | /* Store changes temporarily so that plugins overriding |
3150 | | transaction_commit() can look at them. */ |
3151 | 0 | ret = mailbox_transaction_commit_get_changes(t, &changes); |
3152 | 0 | pool_unref(&changes.pool); |
3153 | 0 | return ret; |
3154 | 0 | } |
3155 | | |
3156 | | int mailbox_transaction_commit_get_changes( |
3157 | | struct mailbox_transaction_context **_t, |
3158 | | struct mail_transaction_commit_changes *changes_r) |
3159 | 0 | { |
3160 | 0 | struct mailbox_transaction_context *t = *_t; |
3161 | 0 | struct mailbox *box = t->box; |
3162 | 0 | unsigned int save_count = t->save_count; |
3163 | 0 | struct event_reason *reason = NULL; |
3164 | 0 | int ret; |
3165 | |
|
3166 | 0 | changes_r->pool = NULL; |
3167 | |
|
3168 | 0 | *_t = NULL; |
3169 | |
|
3170 | 0 | if (t->itrans->attribute_updates != NULL && |
3171 | 0 | t->itrans->attribute_updates->used > 0) { |
3172 | | /* attribute changes are also done directly via lib-index |
3173 | | by ACL and Sieve */ |
3174 | 0 | reason = event_reason_begin("mailbox:attributes_changed"); |
3175 | 0 | } |
3176 | 0 | T_BEGIN { |
3177 | 0 | ret = box->v.transaction_commit(t, changes_r); |
3178 | 0 | } T_END; |
3179 | | /* either all the saved messages get UIDs or none, because a) we |
3180 | | failed, b) MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS not set, |
3181 | | c) backend doesn't support it (e.g. virtual plugin) */ |
3182 | 0 | i_assert(ret < 0 || |
3183 | 0 | seq_range_count(&changes_r->saved_uids) == save_count || |
3184 | 0 | array_count(&changes_r->saved_uids) == 0); |
3185 | | /* decrease the transaction count only after transaction_commit(). |
3186 | | that way if it creates and destroys transactions internally, we |
3187 | | don't see transaction_count=0 until the parent transaction is fully |
3188 | | finished */ |
3189 | 0 | box->transaction_count--; |
3190 | 0 | event_reason_end(&reason); |
3191 | 0 | if (ret == 0 && box->mailbox_not_original) { |
3192 | | /* The mailbox name changed while opening it. This is |
3193 | | intentional when virtual mailbox is opened for saving mails, |
3194 | | which causes the backend mailbox to be opened instead. In |
3195 | | this situation the UIDVALIDITY / UIDs are for the physical |
3196 | | mailbox, not the virtual mailbox. Use this flag to prevent |
3197 | | IMAP APPEND from returning any UIDs in the tagged reply, |
3198 | | since they would be wrong. */ |
3199 | 0 | changes_r->no_read_perm = TRUE; |
3200 | 0 | } |
3201 | 0 | if (ret < 0 && changes_r->pool != NULL) |
3202 | 0 | pool_unref(&changes_r->pool); |
3203 | 0 | return ret; |
3204 | 0 | } |
3205 | | |
3206 | | void mailbox_transaction_rollback(struct mailbox_transaction_context **_t) |
3207 | 0 | { |
3208 | 0 | struct mailbox_transaction_context *t = *_t; |
3209 | 0 | struct mailbox *box = t->box; |
3210 | |
|
3211 | 0 | *_t = NULL; |
3212 | 0 | T_BEGIN { |
3213 | 0 | box->v.transaction_rollback(t); |
3214 | 0 | } T_END; |
3215 | 0 | box->transaction_count--; |
3216 | 0 | } |
3217 | | |
3218 | | unsigned int mailbox_transaction_get_count(const struct mailbox *box) |
3219 | 0 | { |
3220 | 0 | return box->transaction_count; |
3221 | 0 | } |
3222 | | |
3223 | | void mailbox_transaction_set_max_modseq(struct mailbox_transaction_context *t, |
3224 | | uint64_t max_modseq, |
3225 | | ARRAY_TYPE(seq_range) *seqs) |
3226 | 0 | { |
3227 | 0 | mail_index_transaction_set_max_modseq(t->itrans, max_modseq, seqs); |
3228 | 0 | } |
3229 | | |
3230 | | struct mailbox * |
3231 | | mailbox_transaction_get_mailbox(const struct mailbox_transaction_context *t) |
3232 | 0 | { |
3233 | 0 | return t->box; |
3234 | 0 | } |
3235 | | |
3236 | | static void mailbox_save_dest_mail_close(struct mail_save_context *ctx) |
3237 | 0 | { |
3238 | 0 | struct mail_private *mail = (struct mail_private *)ctx->dest_mail; |
3239 | |
|
3240 | 0 | T_BEGIN { |
3241 | 0 | mail->v.close(&mail->mail); |
3242 | 0 | } T_END; |
3243 | 0 | } |
3244 | | |
3245 | | struct mail_save_context * |
3246 | | mailbox_save_alloc(struct mailbox_transaction_context *t) |
3247 | 0 | { |
3248 | 0 | struct mail_save_context *ctx; |
3249 | 0 | T_BEGIN { |
3250 | 0 | ctx = t->box->v.save_alloc(t); |
3251 | 0 | } T_END; |
3252 | 0 | i_assert(!ctx->unfinished); |
3253 | 0 | ctx->unfinished = TRUE; |
3254 | 0 | ctx->data.received_date = (time_t)-1; |
3255 | 0 | ctx->data.save_date = (time_t)-1; |
3256 | | |
3257 | | /* Always have a dest_mail available. A lot of plugins make use |
3258 | | of this. */ |
3259 | 0 | if (ctx->dest_mail == NULL) |
3260 | 0 | ctx->dest_mail = mail_alloc(t, 0, NULL); |
3261 | 0 | else { |
3262 | | /* make sure the mail isn't used before mail_set_seq_saving() */ |
3263 | 0 | mailbox_save_dest_mail_close(ctx); |
3264 | 0 | } |
3265 | |
|
3266 | 0 | return ctx; |
3267 | 0 | } |
3268 | | |
3269 | | void mailbox_save_context_deinit(struct mail_save_context *ctx) |
3270 | 0 | { |
3271 | 0 | i_assert(ctx->dest_mail != NULL); |
3272 | | |
3273 | 0 | mail_free(&ctx->dest_mail); |
3274 | 0 | } |
3275 | | |
3276 | | void mailbox_save_set_flags(struct mail_save_context *ctx, |
3277 | | enum mail_flags flags, |
3278 | | struct mail_keywords *keywords) |
3279 | 0 | { |
3280 | 0 | struct mailbox *box = ctx->transaction->box; |
3281 | |
|
3282 | 0 | if (ctx->data.keywords != NULL) |
3283 | 0 | mailbox_keywords_unref(&ctx->data.keywords); |
3284 | |
|
3285 | 0 | ctx->data.flags = flags & ENUM_NEGATE(mailbox_get_private_flags_mask(box)); |
3286 | 0 | ctx->data.pvt_flags = flags & mailbox_get_private_flags_mask(box); |
3287 | 0 | ctx->data.keywords = keywords; |
3288 | 0 | if (keywords != NULL) |
3289 | 0 | mailbox_keywords_ref(keywords); |
3290 | 0 | } |
3291 | | |
3292 | | void mailbox_save_copy_flags(struct mail_save_context *ctx, struct mail *mail) |
3293 | 0 | { |
3294 | 0 | const char *const *keywords_list; |
3295 | 0 | struct mail_keywords *keywords; |
3296 | |
|
3297 | 0 | keywords_list = mail_get_keywords(mail); |
3298 | 0 | keywords = str_array_length(keywords_list) == 0 ? NULL : |
3299 | 0 | mailbox_keywords_create_valid(ctx->transaction->box, |
3300 | 0 | keywords_list); |
3301 | 0 | mailbox_save_set_flags(ctx, mail_get_flags(mail), keywords); |
3302 | 0 | if (keywords != NULL) |
3303 | 0 | mailbox_keywords_unref(&keywords); |
3304 | 0 | } |
3305 | | |
3306 | | void mailbox_save_set_min_modseq(struct mail_save_context *ctx, |
3307 | | uint64_t min_modseq) |
3308 | 0 | { |
3309 | 0 | ctx->data.min_modseq = min_modseq; |
3310 | 0 | } |
3311 | | |
3312 | | void mailbox_save_set_received_date(struct mail_save_context *ctx, |
3313 | | time_t received_date, int timezone_offset) |
3314 | 0 | { |
3315 | 0 | ctx->data.received_date = received_date; |
3316 | 0 | ctx->data.received_tz_offset = timezone_offset; |
3317 | 0 | } |
3318 | | |
3319 | | void mailbox_save_set_save_date(struct mail_save_context *ctx, |
3320 | | time_t save_date) |
3321 | 0 | { |
3322 | 0 | ctx->data.save_date = save_date; |
3323 | 0 | } |
3324 | | |
3325 | | void mailbox_save_set_from_envelope(struct mail_save_context *ctx, |
3326 | | const char *envelope) |
3327 | 0 | { |
3328 | 0 | i_free(ctx->data.from_envelope); |
3329 | 0 | ctx->data.from_envelope = i_strdup(envelope); |
3330 | 0 | } |
3331 | | |
3332 | | void mailbox_save_set_uid(struct mail_save_context *ctx, uint32_t uid) |
3333 | 0 | { |
3334 | 0 | ctx->data.uid = uid; |
3335 | 0 | } |
3336 | | |
3337 | | void mailbox_save_set_guid(struct mail_save_context *ctx, const char *guid) |
3338 | 0 | { |
3339 | 0 | i_assert(guid == NULL || *guid != '\0'); |
3340 | | |
3341 | 0 | i_free(ctx->data.guid); |
3342 | 0 | ctx->data.guid = i_strdup(guid); |
3343 | 0 | } |
3344 | | |
3345 | | void mailbox_save_set_pop3_uidl(struct mail_save_context *ctx, const char *uidl) |
3346 | 0 | { |
3347 | 0 | i_assert(*uidl != '\0'); |
3348 | 0 | i_assert(strchr(uidl, '\n') == NULL); |
3349 | | |
3350 | 0 | i_free(ctx->data.pop3_uidl); |
3351 | 0 | ctx->data.pop3_uidl = i_strdup(uidl); |
3352 | 0 | } |
3353 | | |
3354 | | void mailbox_save_set_pop3_order(struct mail_save_context *ctx, |
3355 | | unsigned int order) |
3356 | 0 | { |
3357 | 0 | i_assert(order > 0); |
3358 | | |
3359 | 0 | ctx->data.pop3_order = order; |
3360 | 0 | } |
3361 | | |
3362 | | struct mail *mailbox_save_get_dest_mail(struct mail_save_context *ctx) |
3363 | 0 | { |
3364 | 0 | return ctx->dest_mail; |
3365 | 0 | } |
3366 | | |
3367 | | int mailbox_save_begin(struct mail_save_context **ctx, struct istream *input) |
3368 | 0 | { |
3369 | 0 | struct mailbox *box = (*ctx)->transaction->box; |
3370 | 0 | int ret; |
3371 | |
|
3372 | 0 | if (mail_index_is_deleted(box->index)) { |
3373 | 0 | mailbox_set_deleted(box); |
3374 | 0 | mailbox_save_cancel(ctx); |
3375 | 0 | return -1; |
3376 | 0 | } |
3377 | | |
3378 | | /* make sure parts get parsed early on */ |
3379 | 0 | if (box->storage->set->parsed_mail_attachment_detection_add_flags) |
3380 | 0 | mail_add_temp_wanted_fields((*ctx)->dest_mail, |
3381 | 0 | MAIL_FETCH_MESSAGE_PARTS, NULL); |
3382 | |
|
3383 | 0 | if (!(*ctx)->copying_or_moving) { |
3384 | | /* We're actually saving the mail. We're not being called by |
3385 | | mail_storage_copy() because backend didn't support fast |
3386 | | copying. */ |
3387 | 0 | i_assert(!(*ctx)->copying_via_save); |
3388 | 0 | (*ctx)->saving = TRUE; |
3389 | 0 | } else { |
3390 | 0 | i_assert((*ctx)->copying_via_save); |
3391 | 0 | } |
3392 | 0 | if (box->v.save_begin == NULL) { |
3393 | 0 | mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, |
3394 | 0 | "Saving messages not supported"); |
3395 | 0 | ret = -1; |
3396 | 0 | } else T_BEGIN { |
3397 | 0 | ret = box->v.save_begin(*ctx, input); |
3398 | 0 | } T_END; |
3399 | | |
3400 | 0 | if (ret < 0) { |
3401 | 0 | mailbox_save_cancel(ctx); |
3402 | 0 | return -1; |
3403 | 0 | } |
3404 | 0 | return 0; |
3405 | 0 | } |
3406 | | |
3407 | | int mailbox_save_begin_replace(struct mail_save_context **ctx, |
3408 | | struct istream *input, |
3409 | | struct mail *replaced) |
3410 | 0 | { |
3411 | 0 | (*ctx)->expunged_mail = replaced; |
3412 | 0 | return mailbox_save_begin(ctx, input); |
3413 | 0 | } |
3414 | | |
3415 | | int mailbox_save_continue(struct mail_save_context *ctx) |
3416 | 0 | { |
3417 | 0 | int ret; |
3418 | |
|
3419 | 0 | T_BEGIN { |
3420 | 0 | ret = ctx->transaction->box->v.save_continue(ctx); |
3421 | 0 | } T_END; |
3422 | 0 | return ret; |
3423 | 0 | } |
3424 | | |
3425 | | static void |
3426 | | mailbox_save_add_pvt_flags(struct mailbox_transaction_context *t, |
3427 | | enum mail_flags pvt_flags) |
3428 | 0 | { |
3429 | 0 | struct mail_save_private_changes *save; |
3430 | |
|
3431 | 0 | if (!array_is_created(&t->pvt_saves)) |
3432 | 0 | i_array_init(&t->pvt_saves, 8); |
3433 | 0 | save = array_append_space(&t->pvt_saves); |
3434 | 0 | save->mailnum = t->save_count; |
3435 | 0 | save->flags = pvt_flags; |
3436 | 0 | } |
3437 | | |
3438 | | static void |
3439 | | mailbox_save_context_reset(struct mail_save_context *ctx, bool success) |
3440 | 0 | { |
3441 | 0 | i_assert(!ctx->unfinished); |
3442 | 0 | if (!ctx->copying_or_moving) { |
3443 | | /* we're finishing a save (not copy/move). Note that we could |
3444 | | have come here also from mailbox_save_cancel(), in which |
3445 | | case ctx->saving may be FALSE. */ |
3446 | 0 | i_assert(!ctx->copying_via_save); |
3447 | 0 | i_assert(ctx->saving || !success); |
3448 | 0 | ctx->saving = FALSE; |
3449 | 0 | } else { |
3450 | 0 | i_assert(ctx->copying_via_save || !success); |
3451 | | /* We came from mailbox_copy(). saving==TRUE is possible here |
3452 | | if we also came from mailbox_save_using_mail(). Don't set |
3453 | | saving=FALSE yet in that case, because copy() is still |
3454 | | running. */ |
3455 | 0 | } |
3456 | 0 | } |
3457 | | |
3458 | | int mailbox_save_finish(struct mail_save_context **_ctx) |
3459 | 0 | { |
3460 | 0 | struct mail_save_context *ctx = *_ctx; |
3461 | 0 | struct mailbox_transaction_context *t = ctx->transaction; |
3462 | | /* we need to keep a copy of this because save_finish implementations |
3463 | | will likely zero the data structure during cleanup */ |
3464 | 0 | enum mail_flags pvt_flags = ctx->data.pvt_flags; |
3465 | 0 | bool copying_via_save = ctx->copying_via_save; |
3466 | 0 | int ret; |
3467 | | |
3468 | | /* Do one final continue. The caller may not have done it if the |
3469 | | input stream's offset already matched the number of bytes that |
3470 | | were wanted to be saved. But due to nested istreams some of the |
3471 | | underlying ones may not have seen the EOF yet, and haven't flushed |
3472 | | out the pending data. */ |
3473 | 0 | if (mailbox_save_continue(ctx) < 0) { |
3474 | 0 | mailbox_save_cancel(_ctx); |
3475 | 0 | return -1; |
3476 | 0 | } |
3477 | 0 | *_ctx = NULL; |
3478 | |
|
3479 | 0 | ctx->finishing = TRUE; |
3480 | 0 | T_BEGIN { |
3481 | 0 | ret = t->box->v.save_finish(ctx); |
3482 | 0 | } T_END; |
3483 | 0 | ctx->finishing = FALSE; |
3484 | |
|
3485 | 0 | if (ret == 0 && !copying_via_save) { |
3486 | 0 | if (pvt_flags != 0) |
3487 | 0 | mailbox_save_add_pvt_flags(t, pvt_flags); |
3488 | 0 | t->save_count++; |
3489 | 0 | if (ctx->expunged_mail != NULL) |
3490 | 0 | mail_expunge(ctx->expunged_mail); |
3491 | 0 | } |
3492 | |
|
3493 | 0 | mailbox_save_context_reset(ctx, TRUE); |
3494 | 0 | return ret; |
3495 | 0 | } |
3496 | | |
3497 | | void mailbox_save_cancel(struct mail_save_context **_ctx) |
3498 | 0 | { |
3499 | 0 | struct mail_save_context *ctx = *_ctx; |
3500 | |
|
3501 | 0 | *_ctx = NULL; |
3502 | 0 | T_BEGIN { |
3503 | 0 | ctx->transaction->box->v.save_cancel(ctx); |
3504 | 0 | } T_END; |
3505 | | |
3506 | | /* the dest_mail is no longer valid. if we're still saving |
3507 | | more mails, the mail sequence may get reused. make sure |
3508 | | the mail gets reset in between */ |
3509 | 0 | mailbox_save_dest_mail_close(ctx); |
3510 | |
|
3511 | 0 | mailbox_save_context_reset(ctx, FALSE); |
3512 | 0 | } |
3513 | | |
3514 | | struct mailbox_transaction_context * |
3515 | | mailbox_save_get_transaction(struct mail_save_context *ctx) |
3516 | 0 | { |
3517 | 0 | return ctx->transaction; |
3518 | 0 | } |
3519 | | |
3520 | | static int mailbox_copy_int(struct mail_save_context **_ctx, struct mail *mail) |
3521 | 0 | { |
3522 | 0 | struct mail_save_context *ctx = *_ctx; |
3523 | 0 | struct mailbox_transaction_context *t = ctx->transaction; |
3524 | 0 | enum mail_flags pvt_flags = ctx->data.pvt_flags; |
3525 | 0 | struct mail *backend_mail; |
3526 | 0 | int ret; |
3527 | |
|
3528 | 0 | *_ctx = NULL; |
3529 | |
|
3530 | 0 | if (mail_index_is_deleted(t->box->index)) { |
3531 | 0 | mailbox_set_deleted(t->box); |
3532 | 0 | mailbox_save_cancel(&ctx); |
3533 | 0 | return -1; |
3534 | 0 | } |
3535 | | |
3536 | | /* bypass virtual storage, so hard linking can be used whenever |
3537 | | possible */ |
3538 | 0 | if (mail_get_backend_mail(mail, &backend_mail) < 0) { |
3539 | 0 | mailbox_save_cancel(&ctx); |
3540 | 0 | return -1; |
3541 | 0 | } |
3542 | | |
3543 | 0 | i_assert(!ctx->copying_or_moving); |
3544 | 0 | i_assert(ctx->copy_src_mail == NULL); |
3545 | 0 | ctx->copying_or_moving = TRUE; |
3546 | 0 | ctx->copy_src_mail = mail; |
3547 | 0 | ctx->finishing = TRUE; |
3548 | 0 | T_BEGIN { |
3549 | 0 | ret = t->box->v.copy(ctx, backend_mail); |
3550 | 0 | } T_END; |
3551 | 0 | ctx->finishing = FALSE; |
3552 | 0 | if (ret == 0) { |
3553 | 0 | if (pvt_flags != 0) |
3554 | 0 | mailbox_save_add_pvt_flags(t, pvt_flags); |
3555 | 0 | t->save_count++; |
3556 | 0 | } |
3557 | 0 | i_assert(!ctx->unfinished); |
3558 | | |
3559 | 0 | ctx->copy_src_mail = NULL; |
3560 | 0 | ctx->copying_via_save = FALSE; |
3561 | 0 | ctx->copying_or_moving = FALSE; |
3562 | 0 | ctx->saving = FALSE; /* if we came from mailbox_save_using_mail() */ |
3563 | 0 | return ret; |
3564 | 0 | } |
3565 | | |
3566 | | int mailbox_copy(struct mail_save_context **_ctx, struct mail *mail) |
3567 | 0 | { |
3568 | 0 | struct mail_save_context *ctx = *_ctx; |
3569 | |
|
3570 | 0 | i_assert(!ctx->saving); |
3571 | 0 | i_assert(!ctx->moving); |
3572 | | |
3573 | 0 | int ret; |
3574 | 0 | T_BEGIN { |
3575 | 0 | ret = mailbox_copy_int(_ctx, mail); |
3576 | 0 | } T_END; |
3577 | | |
3578 | 0 | return ret; |
3579 | 0 | } |
3580 | | |
3581 | | int mailbox_move(struct mail_save_context **_ctx, struct mail *mail) |
3582 | 0 | { |
3583 | 0 | struct mail_save_context *ctx = *_ctx; |
3584 | 0 | int ret; |
3585 | |
|
3586 | 0 | i_assert(!ctx->saving); |
3587 | 0 | i_assert(!ctx->moving); |
3588 | | |
3589 | 0 | ctx->moving = TRUE; |
3590 | 0 | ctx->expunged_mail = mail; |
3591 | 0 | T_BEGIN { |
3592 | 0 | if ((ret = mailbox_copy_int(_ctx, mail)) == 0) |
3593 | 0 | mail_expunge(mail); |
3594 | 0 | } T_END; |
3595 | 0 | ctx->moving = FALSE; |
3596 | 0 | return ret; |
3597 | 0 | } |
3598 | | |
3599 | | int mailbox_save_using_mail(struct mail_save_context **_ctx, struct mail *mail) |
3600 | 0 | { |
3601 | 0 | struct mail_save_context *ctx = *_ctx; |
3602 | |
|
3603 | 0 | i_assert(!ctx->saving); |
3604 | 0 | i_assert(!ctx->moving); |
3605 | | |
3606 | 0 | ctx->saving = TRUE; |
3607 | 0 | return mailbox_copy_int(_ctx, mail); |
3608 | 0 | } |
3609 | | |
3610 | | bool mailbox_is_inconsistent(struct mailbox *box) |
3611 | 0 | { |
3612 | 0 | return box->mailbox_deleted || box->v.is_inconsistent(box); |
3613 | 0 | } |
3614 | | |
3615 | | void mailbox_set_deleted(struct mailbox *box) |
3616 | 0 | { |
3617 | 0 | mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, |
3618 | 0 | "Mailbox was deleted under us"); |
3619 | 0 | box->mailbox_deleted = TRUE; |
3620 | 0 | } |
3621 | | |
3622 | | static int get_path_to(struct mailbox *box, enum mailbox_list_path_type type, |
3623 | | const char **internal_path, const char **path_r) |
3624 | 0 | { |
3625 | 0 | int ret; |
3626 | |
|
3627 | 0 | if (internal_path != NULL && *internal_path != NULL) { |
3628 | 0 | if ((*internal_path)[0] == '\0') { |
3629 | 0 | *path_r = NULL; |
3630 | 0 | return 0; |
3631 | 0 | } |
3632 | 0 | *path_r = *internal_path; |
3633 | 0 | return 1; |
3634 | 0 | } |
3635 | 0 | ret = mailbox_list_get_path(box->list, box->name, type, path_r); |
3636 | 0 | if (ret < 0) { |
3637 | 0 | mail_storage_copy_list_error(box->storage, box->list); |
3638 | 0 | return -1; |
3639 | 0 | } |
3640 | 0 | if (internal_path != NULL && *internal_path == NULL) |
3641 | 0 | *internal_path = ret == 0 ? "" : p_strdup(box->pool, *path_r); |
3642 | 0 | return ret; |
3643 | 0 | } |
3644 | | |
3645 | | int mailbox_get_path_to(struct mailbox *box, enum mailbox_list_path_type type, |
3646 | | const char **path_r) |
3647 | 0 | { |
3648 | 0 | if (type == MAILBOX_LIST_PATH_TYPE_MAILBOX) |
3649 | 0 | return get_path_to(box, type, &box->_path, path_r); |
3650 | 0 | if (type == MAILBOX_LIST_PATH_TYPE_INDEX) |
3651 | 0 | return get_path_to(box, type, &box->_index_path, path_r); |
3652 | 0 | return get_path_to(box, type, NULL, path_r); |
3653 | 0 | } |
3654 | | |
3655 | | const char *mailbox_get_path(struct mailbox *box) |
3656 | 0 | { |
3657 | 0 | i_assert(box->_path != NULL); |
3658 | 0 | i_assert(box->_path[0] != '\0'); |
3659 | 0 | return box->_path; |
3660 | 0 | } |
3661 | | |
3662 | | const char *mailbox_get_index_path(struct mailbox *box) |
3663 | 0 | { |
3664 | 0 | i_assert(box->_index_path != NULL); |
3665 | 0 | i_assert(box->_index_path[0] != '\0'); |
3666 | 0 | return box->_index_path; |
3667 | 0 | } |
3668 | | |
3669 | | static void mailbox_get_permissions_if_not_set(struct mailbox *box) |
3670 | 0 | { |
3671 | 0 | if (box->_perm.file_create_mode != 0) |
3672 | 0 | return; |
3673 | | |
3674 | 0 | if (box->input != NULL) { |
3675 | 0 | box->_perm.file_uid = geteuid(); |
3676 | 0 | box->_perm.file_create_mode = 0600; |
3677 | 0 | box->_perm.dir_create_mode = 0700; |
3678 | 0 | box->_perm.file_create_gid = (gid_t)-1; |
3679 | 0 | box->_perm.file_create_gid_origin = "defaults"; |
3680 | 0 | return; |
3681 | 0 | } |
3682 | | |
3683 | 0 | struct mailbox_permissions perm; |
3684 | 0 | mailbox_list_get_permissions(box->list, box->name, &perm); |
3685 | 0 | mailbox_permissions_copy(&box->_perm, &perm, box->pool); |
3686 | 0 | } |
3687 | | |
3688 | | const struct mailbox_permissions *mailbox_get_permissions(struct mailbox *box) |
3689 | 0 | { |
3690 | 0 | mailbox_get_permissions_if_not_set(box); |
3691 | |
|
3692 | 0 | if (!box->_perm.mail_index_permissions_set && box->index != NULL) { |
3693 | 0 | box->_perm.mail_index_permissions_set = TRUE; |
3694 | 0 | mail_index_set_permissions(box->index, |
3695 | 0 | box->_perm.file_create_mode, |
3696 | 0 | box->_perm.file_create_gid, |
3697 | 0 | box->_perm.file_create_gid_origin); |
3698 | 0 | } |
3699 | 0 | return &box->_perm; |
3700 | 0 | } |
3701 | | |
3702 | | void mailbox_refresh_permissions(struct mailbox *box) |
3703 | 0 | { |
3704 | 0 | i_zero(&box->_perm); |
3705 | 0 | (void)mailbox_get_permissions(box); |
3706 | 0 | } |
3707 | | |
3708 | | int mailbox_create_fd(struct mailbox *box, const char *path, int flags, |
3709 | | int *fd_r) |
3710 | 0 | { |
3711 | 0 | const struct mailbox_permissions *perm = mailbox_get_permissions(box); |
3712 | 0 | mode_t old_mask; |
3713 | 0 | int fd; |
3714 | |
|
3715 | 0 | i_assert((flags & O_CREAT) != 0); |
3716 | | |
3717 | 0 | *fd_r = -1; |
3718 | |
|
3719 | 0 | old_mask = umask(0); |
3720 | 0 | fd = open(path, flags, perm->file_create_mode); |
3721 | 0 | umask(old_mask); |
3722 | |
|
3723 | 0 | if (fd != -1) { |
3724 | | /* ok */ |
3725 | 0 | } else if (errno == EEXIST) { |
3726 | | /* O_EXCL used, caller will handle this error */ |
3727 | 0 | return 0; |
3728 | 0 | } else if (errno == ENOENT) { |
3729 | 0 | mailbox_set_deleted(box); |
3730 | 0 | return -1; |
3731 | 0 | } else if (errno == ENOTDIR) { |
3732 | 0 | mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, |
3733 | 0 | "Mailbox doesn't allow inferior mailboxes"); |
3734 | 0 | return -1; |
3735 | 0 | } else if (mail_storage_set_error_from_errno(box->storage)) { |
3736 | 0 | return -1; |
3737 | 0 | } else { |
3738 | 0 | mailbox_set_critical(box, "open(%s, O_CREAT) failed: %m", path); |
3739 | 0 | return -1; |
3740 | 0 | } |
3741 | | |
3742 | 0 | if (perm->file_create_gid != (gid_t)-1) { |
3743 | 0 | if (fchown(fd, (uid_t)-1, perm->file_create_gid) == 0) { |
3744 | | /* ok */ |
3745 | 0 | } else if (errno == EPERM) { |
3746 | 0 | mailbox_set_critical(box, "%s", |
3747 | 0 | eperm_error_get_chgrp("fchown", path, |
3748 | 0 | perm->file_create_gid, |
3749 | 0 | perm->file_create_gid_origin)); |
3750 | 0 | } else { |
3751 | 0 | mailbox_set_critical(box, |
3752 | 0 | "fchown(%s) failed: %m", path); |
3753 | 0 | } |
3754 | 0 | } |
3755 | 0 | *fd_r = fd; |
3756 | 0 | return 1; |
3757 | 0 | } |
3758 | | |
3759 | | int mailbox_mkdir(struct mailbox *box, const char *path, |
3760 | | enum mailbox_list_path_type type) |
3761 | 0 | { |
3762 | 0 | const struct mailbox_permissions *perm = mailbox_get_permissions(box); |
3763 | 0 | const char *root_dir; |
3764 | |
|
3765 | 0 | if (!perm->gid_origin_is_mailbox_path) { |
3766 | | /* mailbox root directory doesn't exist, create it */ |
3767 | 0 | root_dir = mailbox_list_get_root_forced(box->list, type); |
3768 | 0 | if (mailbox_list_mkdir_root(box->list, root_dir, type) < 0) { |
3769 | 0 | mail_storage_copy_list_error(box->storage, box->list); |
3770 | 0 | return -1; |
3771 | 0 | } |
3772 | 0 | } |
3773 | | |
3774 | 0 | if (mkdir_parents_chgrp(path, perm->dir_create_mode, |
3775 | 0 | perm->file_create_gid, |
3776 | 0 | perm->file_create_gid_origin) == 0) |
3777 | 0 | return 1; |
3778 | 0 | else if (errno == EEXIST) |
3779 | 0 | return 0; |
3780 | 0 | else if (errno == ENOTDIR) { |
3781 | 0 | mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, |
3782 | 0 | "Mailbox doesn't allow inferior mailboxes"); |
3783 | 0 | return -1; |
3784 | 0 | } else if (mail_storage_set_error_from_errno(box->storage)) { |
3785 | 0 | return -1; |
3786 | 0 | } else { |
3787 | 0 | mailbox_set_critical(box, "mkdir_parents(%s) failed: %m", path); |
3788 | 0 | return -1; |
3789 | 0 | } |
3790 | 0 | } |
3791 | | |
3792 | | int mailbox_create_missing_dir(struct mailbox *box, |
3793 | | enum mailbox_list_path_type type) |
3794 | 0 | { |
3795 | 0 | const char *mail_dir, *dir; |
3796 | 0 | struct stat st; |
3797 | 0 | int ret; |
3798 | |
|
3799 | 0 | if ((ret = mailbox_get_path_to(box, type, &dir)) <= 0) |
3800 | 0 | return ret; |
3801 | 0 | if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, |
3802 | 0 | &mail_dir) < 0) |
3803 | 0 | return -1; |
3804 | 0 | if (null_strcmp(dir, mail_dir) != 0) { |
3805 | | /* Mailbox directory is different - create a missing dir */ |
3806 | 0 | } else if ((box->list->props & MAILBOX_LIST_PROP_AUTOCREATE_DIRS) != 0) { |
3807 | | /* This layout (e.g. imapc) wants to autocreate missing mailbox |
3808 | | directories as well. */ |
3809 | 0 | } else { |
3810 | | /* If the mailbox directory doesn't exist, the mailbox |
3811 | | shouldn't exist at all. So just assume that it's already |
3812 | | created and if there's a race condition just fail later. */ |
3813 | 0 | return 0; |
3814 | 0 | } |
3815 | | |
3816 | | /* we call this function even when the directory exists, so first do a |
3817 | | quick check to see if we need to mkdir anything */ |
3818 | 0 | if (stat(dir, &st) == 0) |
3819 | 0 | return 0; |
3820 | | |
3821 | 0 | if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) == 0 && |
3822 | 0 | null_strcmp(dir, mail_dir) != 0 && mail_dir != NULL && |
3823 | 0 | stat(mail_dir, &st) < 0 && (errno == ENOENT || errno == ENOTDIR)) { |
3824 | | /* Race condition - mail root directory doesn't exist |
3825 | | anymore either. We shouldn't create this directory |
3826 | | anymore. */ |
3827 | 0 | mailbox_set_deleted(box); |
3828 | 0 | return -1; |
3829 | 0 | } |
3830 | | |
3831 | 0 | return mailbox_mkdir(box, dir, type); |
3832 | 0 | } |
3833 | | |
3834 | | unsigned int mail_storage_get_lock_timeout(struct mail_storage *storage, |
3835 | | unsigned int secs) |
3836 | 0 | { |
3837 | 0 | return storage->set->mail_max_lock_timeout == 0 ? secs : |
3838 | 0 | I_MIN(secs, storage->set->mail_max_lock_timeout); |
3839 | 0 | } |
3840 | | |
3841 | | enum mail_index_open_flags |
3842 | | mail_storage_settings_to_index_flags(const struct mail_storage_settings *set) |
3843 | 0 | { |
3844 | 0 | enum mail_index_open_flags index_flags = 0; |
3845 | |
|
3846 | 0 | #ifndef MMAP_CONFLICTS_WRITE |
3847 | 0 | if (set->mmap_disable) |
3848 | 0 | #endif |
3849 | 0 | index_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE; |
3850 | 0 | if (set->dotlock_use_excl) |
3851 | 0 | index_flags |= MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL; |
3852 | 0 | if (set->mail_nfs_index) |
3853 | 0 | index_flags |= MAIL_INDEX_OPEN_FLAG_NFS_FLUSH; |
3854 | 0 | return index_flags; |
3855 | 0 | } |
3856 | | |
3857 | | static void mailbox_settings_filters_add(struct event *event, |
3858 | | struct mailbox_list *list, |
3859 | | const char *vname) |
3860 | 0 | { |
3861 | 0 | if (array_is_empty(&list->ns->set->mailboxes)) |
3862 | 0 | return; |
3863 | | |
3864 | 0 | const char *vname_without_prefix = |
3865 | 0 | mailbox_get_name_without_prefix(list->ns, vname); |
3866 | 0 | unsigned int i, count; |
3867 | 0 | const struct mailbox_settings *const *mailboxes = |
3868 | 0 | array_get(&list->ns->set->parsed_mailboxes, &count); |
3869 | |
|
3870 | 0 | for (i = 0; i < count; i++) { |
3871 | 0 | if (!wildcard_match(vname_without_prefix, mailboxes[i]->name)) |
3872 | 0 | continue; |
3873 | | |
3874 | 0 | const char *filter_name = |
3875 | 0 | array_idx_elem(&list->ns->set->mailboxes, i); |
3876 | 0 | settings_event_add_list_filter_name(event, |
3877 | 0 | "mailbox", filter_name); |
3878 | 0 | } |
3879 | 0 | } |
3880 | | |
3881 | | struct event * |
3882 | | mail_storage_mailbox_create_event(struct event *parent, |
3883 | | struct mailbox_list *list, const char *vname) |
3884 | 0 | { |
3885 | 0 | struct event *event = event_create(parent); |
3886 | 0 | event_add_category(event, &event_category_mailbox); |
3887 | |
|
3888 | 0 | mailbox_settings_filters_add(event, list, vname); |
3889 | 0 | event_add_str(event, "mailbox", vname); |
3890 | 0 | event_add_str(event, SETTINGS_EVENT_NAMESPACE_NAME, list->ns->set->name); |
3891 | 0 | settings_event_add_list_filter_name(event, |
3892 | 0 | SETTINGS_EVENT_NAMESPACE_NAME, list->ns->set->name); |
3893 | |
|
3894 | 0 | event_drop_parent_log_prefixes(event, 1); |
3895 | 0 | event_set_append_log_prefix(event, t_strdup_printf( |
3896 | 0 | "Mailbox %s: ", mailbox_name_sanitize(vname))); |
3897 | 0 | return event; |
3898 | 0 | } |
3899 | | |
3900 | | int mail_parse_human_timestamp(const char *str, time_t *timestamp_r, |
3901 | | bool *utc_r) |
3902 | 0 | { |
3903 | 0 | struct tm tm; |
3904 | 0 | unsigned int secs; |
3905 | 0 | const char *error; |
3906 | 0 | int tz; |
3907 | |
|
3908 | 0 | if (i_isdigit(str[0]) && i_isdigit(str[1]) && |
3909 | 0 | i_isdigit(str[2]) && i_isdigit(str[3]) && str[4] == '-' && |
3910 | 0 | i_isdigit(str[5]) && i_isdigit(str[6]) && str[7] == '-' && |
3911 | 0 | i_isdigit(str[8]) && i_isdigit(str[9]) && str[10] == '\0') { |
3912 | | /* yyyy-mm-dd */ |
3913 | 0 | i_zero(&tm); |
3914 | 0 | tm.tm_year = (str[0]-'0') * 1000 + (str[1]-'0') * 100 + |
3915 | 0 | (str[2]-'0') * 10 + (str[3]-'0') - 1900; |
3916 | 0 | tm.tm_mon = (str[5]-'0') * 10 + (str[6]-'0') - 1; |
3917 | 0 | tm.tm_mday = (str[8]-'0') * 10 + (str[9]-'0'); |
3918 | 0 | *timestamp_r = utc_mktime(&tm); |
3919 | 0 | *utc_r = TRUE; |
3920 | 0 | return 0; |
3921 | 0 | } else if (imap_parse_date(str, timestamp_r)) { |
3922 | | /* imap datetime */ |
3923 | 0 | *utc_r = FALSE; |
3924 | 0 | return 0; |
3925 | 0 | } else if (imap_parse_datetime(str, timestamp_r, &tz)) { |
3926 | | /* imap datetime */ |
3927 | 0 | *utc_r = TRUE; |
3928 | 0 | return 0; |
3929 | 0 | } else if (str_to_time(str, timestamp_r) == 0) { |
3930 | | /* unix timestamp */ |
3931 | 0 | *utc_r = TRUE; |
3932 | 0 | return 0; |
3933 | 0 | } else if (str_parse_get_interval(str, &secs, &error) == 0) { |
3934 | 0 | *timestamp_r = ioloop_time - secs; |
3935 | 0 | *utc_r = TRUE; |
3936 | 0 | return 0; |
3937 | 0 | } else { |
3938 | 0 | return -1; |
3939 | 0 | } |
3940 | 0 | } |
3941 | | |
3942 | | void mail_set_mail_cache_corrupted(struct mail *mail, const char *fmt, ...) |
3943 | 0 | { |
3944 | 0 | struct mail_cache_view *cache_view = |
3945 | 0 | mail->transaction->cache_view; |
3946 | |
|
3947 | 0 | i_assert(cache_view != NULL); |
3948 | | |
3949 | 0 | va_list va; |
3950 | 0 | va_start(va, fmt); |
3951 | |
|
3952 | 0 | T_BEGIN { |
3953 | 0 | mail_cache_set_seq_corrupted_reason(cache_view, mail->seq, |
3954 | 0 | t_strdup_vprintf(fmt, va)); |
3955 | 0 | } T_END; |
3956 | | |
3957 | | /* update also the storage's internal error */ |
3958 | 0 | mailbox_set_index_error(mail->box); |
3959 | |
|
3960 | 0 | va_end(va); |
3961 | 0 | } |
3962 | | |
3963 | | static int |
3964 | | mail_storage_dotlock_create(const char *lock_path, |
3965 | | const struct file_create_settings *lock_set, |
3966 | | const struct mail_storage_settings *mail_set, |
3967 | | struct file_lock **lock_r, const char **error_r) |
3968 | 0 | { |
3969 | 0 | const struct dotlock_settings dotlock_set = { |
3970 | 0 | .timeout = lock_set->lock_timeout_secs, |
3971 | 0 | .stale_timeout = I_MAX(60*5, lock_set->lock_timeout_secs), |
3972 | 0 | .lock_suffix = "", |
3973 | |
|
3974 | 0 | .use_excl_lock = mail_set->dotlock_use_excl, |
3975 | 0 | .nfs_flush = mail_set->mail_nfs_storage, |
3976 | 0 | .use_io_notify = TRUE, |
3977 | 0 | }; |
3978 | 0 | struct dotlock *dotlock; |
3979 | 0 | int ret = file_dotlock_create(&dotlock_set, lock_path, 0, &dotlock); |
3980 | 0 | if (ret <= 0) { |
3981 | 0 | *error_r = t_strdup_printf("file_dotlock_create(%s) failed: %m", |
3982 | 0 | lock_path); |
3983 | 0 | return ret; |
3984 | 0 | } |
3985 | 0 | *lock_r = file_lock_from_dotlock(&dotlock); |
3986 | 0 | return 1; |
3987 | 0 | } |
3988 | | |
3989 | | int mail_storage_lock_create(const char *lock_path, |
3990 | | const struct file_create_settings *lock_set, |
3991 | | const struct mail_storage_settings *mail_set, |
3992 | | struct file_lock **lock_r, const char **error_r) |
3993 | 0 | { |
3994 | 0 | struct file_create_settings lock_set_new = *lock_set; |
3995 | 0 | bool created; |
3996 | |
|
3997 | 0 | if (lock_set->lock_settings.lock_method == FILE_LOCK_METHOD_DOTLOCK) |
3998 | 0 | return mail_storage_dotlock_create(lock_path, lock_set, mail_set, lock_r, error_r); |
3999 | | |
4000 | 0 | lock_set_new.lock_settings.close_on_free = TRUE; |
4001 | 0 | lock_set_new.lock_settings.unlink_on_free = TRUE; |
4002 | 0 | if (file_create_locked(lock_path, &lock_set_new, lock_r, |
4003 | 0 | &created, error_r) == -1) { |
4004 | 0 | *error_r = t_strdup_printf("file_create_locked(%s) failed: %s", |
4005 | 0 | lock_path, *error_r); |
4006 | 0 | return errno == EAGAIN ? 0 : -1; |
4007 | 0 | } |
4008 | 0 | return 1; |
4009 | 0 | } |
4010 | | |
4011 | | int mailbox_lock_file_create(struct mailbox *box, const char *lock_fname, |
4012 | | unsigned int lock_secs, struct file_lock **lock_r, |
4013 | | const char **error_r) |
4014 | 0 | { |
4015 | 0 | const struct mailbox_permissions *perm; |
4016 | 0 | struct file_create_settings set; |
4017 | 0 | const char *lock_path; |
4018 | |
|
4019 | 0 | perm = mailbox_get_permissions(box); |
4020 | 0 | i_zero(&set); |
4021 | 0 | set.lock_timeout_secs = |
4022 | 0 | mail_storage_get_lock_timeout(box->storage, lock_secs); |
4023 | 0 | set.lock_settings.lock_method = box->storage->set->parsed_lock_method; |
4024 | 0 | set.mode = perm->file_create_mode; |
4025 | 0 | set.gid = perm->file_create_gid; |
4026 | 0 | set.gid_origin = perm->file_create_gid_origin; |
4027 | |
|
4028 | 0 | if (box->list->mail_set->mail_volatile_path[0] == '\0') |
4029 | 0 | lock_path = t_strdup_printf("%s/%s", box->index->dir, lock_fname); |
4030 | 0 | else { |
4031 | 0 | unsigned char box_name_sha1[SHA1_RESULTLEN]; |
4032 | 0 | string_t *str = t_str_new(128); |
4033 | | |
4034 | | /* Keep this simple: Use the lock_fname with a SHA1 of the |
4035 | | mailbox name as the suffix. The mailbox name itself could |
4036 | | be too large as a filename and creating the full directory |
4037 | | structure would be pretty troublesome. It would also make |
4038 | | it more difficult to perform the automated deletion of empty |
4039 | | lock directories. |
4040 | | The SHA1 is calculated from the namespace prefix concatenated |
4041 | | with the mailbox name (prefix first, then name) to avoid |
4042 | | collisions when multiple namespaces share the same |
4043 | | mail_volatile_path. */ |
4044 | 0 | str_printfa(str, "%s/%s.", box->list->mail_set->mail_volatile_path, |
4045 | 0 | lock_fname); |
4046 | 0 | struct sha1_ctxt ctx; |
4047 | 0 | sha1_init(&ctx); |
4048 | 0 | sha1_loop(&ctx, box->list->ns->prefix, box->list->ns->prefix_len); |
4049 | 0 | sha1_loop(&ctx, box->name, strlen(box->name)); |
4050 | 0 | sha1_result(&ctx, box_name_sha1); |
4051 | 0 | binary_to_hex_append(str, box_name_sha1, sizeof(box_name_sha1)); |
4052 | 0 | lock_path = str_c(str); |
4053 | 0 | set.mkdir_mode = 0700; |
4054 | 0 | } |
4055 | |
|
4056 | 0 | return mail_storage_lock_create(lock_path, &set, |
4057 | 0 | box->storage->set, lock_r, error_r); |
4058 | 0 | } |
4059 | | |
4060 | | void mailbox_sync_notify(struct mailbox *box, uint32_t uid, |
4061 | | enum mailbox_sync_type sync_type) |
4062 | 0 | { |
4063 | 0 | if (box->v.sync_notify != NULL) T_BEGIN { |
4064 | 0 | box->v.sync_notify(box, uid, sync_type); |
4065 | 0 | } T_END; |
4066 | | |
4067 | | /* Send an event for expunged mail. */ |
4068 | 0 | if (sync_type == MAILBOX_SYNC_TYPE_EXPUNGE) { |
4069 | 0 | e_debug(event_create_passthrough(box->event)-> |
4070 | 0 | set_name("mail_expunged")-> |
4071 | 0 | add_int("uid", uid)->event(), |
4072 | 0 | "UID %u: Mail expunged", uid); |
4073 | 0 | } |
4074 | 0 | } |