1 /* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "path-util.h"
6 #include "ioloop.h"
7 #include "file-create-locked.h"
8 #include "mkdir-parents.h"
9 #include "hex-binary.h"
10 #include "str.h"
11 #include "sha1.h"
12 #include "hash.h"
13 #include "home-expand.h"
14 #include "time-util.h"
15 #include "unichar.h"
16 #include "settings-parser.h"
17 #include "iostream-ssl.h"
18 #include "fs-api-private.h"
19 #include "imap-utf7.h"
20 #include "mailbox-log.h"
21 #include "mailbox-tree.h"
22 #include "mail-storage-private.h"
23 #include "mail-storage-hooks.h"
24 #include "mailbox-list-private.h"
25
26 #include <time.h>
27 #include <ctype.h>
28 #include <unistd.h>
29 #include <dirent.h>
30 #include <sys/stat.h>
31
32 #define MAILBOX_LIST_LOCK_FNAME "mailboxes.lock"
33 #define MAILBOX_LIST_LOCK_SECS 60
34
35 #define MAILBOX_LIST_FS_CONTEXT(obj) \
36 MODULE_CONTEXT(obj, mailbox_list_fs_module)
37
38 struct mailbox_list_fs_context {
39 union fs_api_module_context module_ctx;
40 struct mailbox_list *list;
41 };
42
43 struct mailbox_list_module_register mailbox_list_module_register = { 0 };
44
45 static ARRAY(const struct mailbox_list *) mailbox_list_drivers;
46 static MODULE_CONTEXT_DEFINE_INIT(mailbox_list_fs_module,
47 &fs_api_module_register);
48
mailbox_lists_init(void)49 void mailbox_lists_init(void)
50 {
51 i_array_init(&mailbox_list_drivers, 4);
52 }
53
mailbox_lists_deinit(void)54 void mailbox_lists_deinit(void)
55 {
56 array_free(&mailbox_list_drivers);
57 }
58
mailbox_list_driver_find(const char * name,unsigned int * idx_r)59 static bool mailbox_list_driver_find(const char *name, unsigned int *idx_r)
60 {
61 const struct mailbox_list *const *drivers;
62 unsigned int i, count;
63
64 drivers = array_get(&mailbox_list_drivers, &count);
65 for (i = 0; i < count; i++) {
66 if (strcasecmp(drivers[i]->name, name) == 0) {
67 *idx_r = i;
68 return TRUE;
69 }
70 }
71 return FALSE;
72 }
73
mailbox_list_register(const struct mailbox_list * list)74 void mailbox_list_register(const struct mailbox_list *list)
75 {
76 unsigned int idx;
77
78 if (mailbox_list_driver_find(list->name, &idx)) {
79 i_fatal("mailbox_list_register(%s): duplicate driver",
80 list->name);
81 }
82
83 array_push_back(&mailbox_list_drivers, &list);
84 }
85
mailbox_list_unregister(const struct mailbox_list * list)86 void mailbox_list_unregister(const struct mailbox_list *list)
87 {
88 unsigned int idx;
89
90 if (!mailbox_list_driver_find(list->name, &idx)) {
91 i_fatal("mailbox_list_unregister(%s): unknown driver",
92 list->name);
93 }
94 array_delete(&mailbox_list_drivers, idx, 1);
95 }
96
97 const struct mailbox_list *
mailbox_list_find_class(const char * driver)98 mailbox_list_find_class(const char *driver)
99 {
100 unsigned int idx;
101
102 if (!mailbox_list_driver_find(driver, &idx))
103 return NULL;
104
105 return array_idx_elem(&mailbox_list_drivers, idx);
106 }
107
mailbox_list_create(const char * driver,struct mail_namespace * ns,const struct mailbox_list_settings * set,enum mailbox_list_flags flags,struct mailbox_list ** list_r,const char ** error_r)108 int mailbox_list_create(const char *driver, struct mail_namespace *ns,
109 const struct mailbox_list_settings *set,
110 enum mailbox_list_flags flags,
111 struct mailbox_list **list_r, const char **error_r)
112 {
113 const struct mailbox_list *class;
114 struct mailbox_list *list;
115
116 i_assert(ns->list == NULL ||
117 (flags & MAILBOX_LIST_FLAG_SECONDARY) != 0);
118
119 i_assert(set->subscription_fname == NULL ||
120 *set->subscription_fname != '\0');
121
122 if ((class = mailbox_list_find_class(driver)) == NULL) {
123 *error_r = "Unknown driver name";
124 return -1;
125 }
126
127 if ((class->props & MAILBOX_LIST_PROP_NO_MAILDIR_NAME) != 0 &&
128 *set->maildir_name != '\0') {
129 *error_r = "maildir_name not supported by this driver";
130 return -1;
131 }
132 if ((class->props & MAILBOX_LIST_PROP_NO_ALT_DIR) != 0 &&
133 set->alt_dir != NULL) {
134 *error_r = "alt_dir not supported by this driver";
135 return -1;
136 }
137
138 i_assert(set->root_dir == NULL || *set->root_dir != '\0' ||
139 (class->props & MAILBOX_LIST_PROP_NO_ROOT) != 0);
140
141 list = class->v.alloc();
142 array_create(&list->module_contexts, list->pool, sizeof(void *), 5);
143
144 list->ns = ns;
145 list->mail_set = ns->mail_set;
146 list->flags = flags;
147 list->root_permissions.file_create_mode = (mode_t)-1;
148 list->root_permissions.dir_create_mode = (mode_t)-1;
149 list->root_permissions.file_create_gid = (gid_t)-1;
150 list->changelog_timestamp = (time_t)-1;
151 if (set->no_noselect)
152 list->props |= MAILBOX_LIST_PROP_NO_NOSELECT;
153
154 /* copy settings */
155 if (set->root_dir != NULL) {
156 list->set.root_dir = p_strdup(list->pool, set->root_dir);
157 list->set.index_dir = set->index_dir == NULL ||
158 strcmp(set->index_dir, set->root_dir) == 0 ? NULL :
159 p_strdup(list->pool, set->index_dir);
160 list->set.index_pvt_dir = set->index_pvt_dir == NULL ||
161 strcmp(set->index_pvt_dir, set->root_dir) == 0 ? NULL :
162 p_strdup(list->pool, set->index_pvt_dir);
163 list->set.index_cache_dir = set->index_cache_dir == NULL ||
164 strcmp(set->index_cache_dir, set->root_dir) == 0 ? NULL :
165 p_strdup(list->pool, set->index_cache_dir);
166 list->set.control_dir = set->control_dir == NULL ||
167 strcmp(set->control_dir, set->root_dir) == 0 ? NULL :
168 p_strdup(list->pool, set->control_dir);
169 }
170
171 list->set.inbox_path = p_strdup(list->pool, set->inbox_path);
172 list->set.subscription_fname =
173 p_strdup(list->pool, set->subscription_fname);
174 list->set.list_index_fname =
175 p_strdup(list->pool, set->list_index_fname);
176 list->set.list_index_dir =
177 p_strdup(list->pool, set->list_index_dir);
178 list->set.maildir_name =
179 p_strdup(list->pool, set->maildir_name);
180 list->set.mailbox_dir_name =
181 p_strdup(list->pool, set->mailbox_dir_name);
182 list->set.alt_dir = p_strdup(list->pool, set->alt_dir);
183 list->set.alt_dir_nocheck = set->alt_dir_nocheck;
184 list->set.volatile_dir = p_strdup(list->pool, set->volatile_dir);
185 list->set.index_control_use_maildir_name =
186 set->index_control_use_maildir_name;
187 list->set.iter_from_index_dir = set->iter_from_index_dir;
188 list->set.no_noselect = set->no_noselect;
189 list->set.no_fs_validation = set->no_fs_validation;
190
191 if (*set->mailbox_dir_name == '\0')
192 list->set.mailbox_dir_name = "";
193 else if (set->mailbox_dir_name[strlen(set->mailbox_dir_name)-1] == '/') {
194 list->set.mailbox_dir_name =
195 p_strdup(list->pool, set->mailbox_dir_name);
196 } else {
197 list->set.mailbox_dir_name =
198 p_strconcat(list->pool, set->mailbox_dir_name, "/", NULL);
199 }
200 if (set->storage_name_escape_char != '\0')
201 list->set.storage_name_escape_char = set->storage_name_escape_char;
202 list->set.vname_escape_char = set->vname_escape_char;
203 list->set.utf8 = set->utf8;
204
205 if (list->v.init != NULL) {
206 if (list->v.init(list, error_r) < 0) {
207 list->v.deinit(list);
208 return -1;
209 }
210 }
211
212 e_debug(ns->user->event,
213 "%s: root=%s, index=%s, indexpvt=%s, control=%s, inbox=%s, alt=%s",
214 list->name,
215 list->set.root_dir == NULL ? "" : list->set.root_dir,
216 list->set.index_dir == NULL ? "" : list->set.index_dir,
217 list->set.index_pvt_dir == NULL ? "" : list->set.index_pvt_dir,
218 list->set.control_dir == NULL ?
219 "" : list->set.control_dir,
220 list->set.inbox_path == NULL ?
221 "" : list->set.inbox_path,
222 list->set.alt_dir == NULL ? "" : list->set.alt_dir);
223 if ((flags & MAILBOX_LIST_FLAG_SECONDARY) == 0)
224 mail_namespace_finish_list_init(ns, list);
225
226 *list_r = list;
227
228 hook_mailbox_list_created(list);
229 return 0;
230 }
231
fix_path(struct mail_user * user,const char * path,bool expand_home,const char ** path_r,const char ** error_r)232 static int fix_path(struct mail_user *user, const char *path, bool expand_home,
233 const char **path_r, const char **error_r)
234 {
235 size_t len = strlen(path);
236
237 if (len > 1 && path[len-1] == '/')
238 path = t_strndup(path, len-1);
239 if (!expand_home) {
240 /* no ~ expansion */
241 } else if (path[0] == '~' && path[1] != '/' && path[1] != '\0') {
242 /* ~otheruser/dir */
243 if (home_try_expand(&path) < 0) {
244 *error_r = t_strconcat(
245 "No home directory for system user. "
246 "Can't expand ", t_strcut(path, '/'),
247 " for ", NULL);
248 return -1;
249 }
250 } else {
251 if (mail_user_try_home_expand(user, &path) < 0) {
252 *error_r = "Home directory not set for user. "
253 "Can't expand ~/ for ";
254 return -1;
255 }
256 }
257 *path_r = path;
258 return 0;
259 }
260
split_next_arg(const char * const ** _args)261 static const char *split_next_arg(const char *const **_args)
262 {
263 const char *const *args = *_args;
264 const char *str = args[0];
265
266 args++;
267 while (*args != NULL && **args == '\0') {
268 args++;
269 if (*args == NULL) {
270 /* string ends with ":", just ignore it. */
271 break;
272 }
273 str = t_strconcat(str, ":", *args, NULL);
274 args++;
275 }
276 *_args = args;
277 return str;
278 }
279
mailbox_list_settings_init_defaults(struct mailbox_list_settings * set_r)280 void mailbox_list_settings_init_defaults(struct mailbox_list_settings *set_r)
281 {
282 i_zero(set_r);
283 set_r->mailbox_dir_name = "";
284 set_r->maildir_name = "";
285 set_r->list_index_fname = MAILBOX_LIST_INDEX_DEFAULT_PREFIX;
286 }
287
288 static int
mailbox_list_settings_parse_full(struct mail_user * user,const char * data,bool expand_home,struct mailbox_list_settings * set_r,const char ** error_r)289 mailbox_list_settings_parse_full(struct mail_user *user, const char *data,
290 bool expand_home,
291 struct mailbox_list_settings *set_r,
292 const char **error_r)
293 {
294 const char *const *tmp, *key, *value, **dest, *str, *fname, *error;
295
296 *error_r = NULL;
297
298 mailbox_list_settings_init_defaults(set_r);
299 if (*data == '\0')
300 return 0;
301
302 /* <root dir> */
303 tmp = t_strsplit(data, ":");
304 str = split_next_arg(&tmp);
305 if (fix_path(user, str, expand_home, &set_r->root_dir, &error) < 0) {
306 *error_r = t_strconcat(error, "mail root dir in: ", data, NULL);
307 return -1;
308 }
309 if (str_begins(set_r->root_dir, "INBOX=")) {
310 /* probably mbox user trying to avoid root_dir */
311 *error_r = t_strconcat("Mail root directory not given: ",
312 data, NULL);
313 return -1;
314 }
315
316 while (*tmp != NULL) {
317 str = split_next_arg(&tmp);
318 if (strcmp(str, "UTF-8") == 0) {
319 set_r->utf8 = TRUE;
320 continue;
321 }
322
323 value = strchr(str, '=');
324 if (value == NULL) {
325 key = str;
326 value = "";
327 } else {
328 key = t_strdup_until(str, value);
329 value++;
330 }
331
332 if (strcmp(key, "INBOX") == 0)
333 dest = &set_r->inbox_path;
334 else if (strcmp(key, "INDEX") == 0)
335 dest = &set_r->index_dir;
336 else if (strcmp(key, "INDEXPVT") == 0)
337 dest = &set_r->index_pvt_dir;
338 else if (strcmp(key, "INDEXCACHE") == 0)
339 dest = &set_r->index_cache_dir;
340 else if (strcmp(key, "CONTROL") == 0)
341 dest = &set_r->control_dir;
342 else if (strcmp(key, "ALT") == 0)
343 dest = &set_r->alt_dir;
344 else if (strcmp(key, "ALTNOCHECK") == 0) {
345 set_r->alt_dir_nocheck = TRUE;
346 continue;
347 } else if (strcmp(key, "LAYOUT") == 0)
348 dest = &set_r->layout;
349 else if (strcmp(key, "SUBSCRIPTIONS") == 0)
350 dest = &set_r->subscription_fname;
351 else if (strcmp(key, "DIRNAME") == 0)
352 dest = &set_r->maildir_name;
353 else if (strcmp(key, "MAILBOXDIR") == 0)
354 dest = &set_r->mailbox_dir_name;
355 else if (strcmp(key, "VOLATILEDIR") == 0)
356 dest = &set_r->volatile_dir;
357 else if (strcmp(key, "LISTINDEX") == 0)
358 dest = &set_r->list_index_fname;
359 else if (strcmp(key, "FULLDIRNAME") == 0) {
360 set_r->index_control_use_maildir_name = TRUE;
361 dest = &set_r->maildir_name;
362 } else if (strcmp(key, "BROKENCHAR") == 0) {
363 if (strlen(value) != 1) {
364 *error_r = "BROKENCHAR value must be a single character";
365 return -1;
366 }
367 set_r->vname_escape_char = value[0];
368 continue;
369 } else if (strcmp(key, "ITERINDEX") == 0) {
370 set_r->iter_from_index_dir = TRUE;
371 continue;
372 } else if (strcmp(key, "NO-NOSELECT") == 0) {
373 set_r->no_noselect = TRUE;
374 continue;
375 } else if (strcmp(key, "NO-FS-VALIDATION") == 0) {
376 set_r->no_fs_validation = TRUE;
377 continue;
378 } else {
379 *error_r = t_strdup_printf("Unknown setting: %s", key);
380 return -1;
381 }
382 if (fix_path(user, value, expand_home, dest, &error) < 0) {
383 *error_r = t_strconcat(error, key, " in: ", data, NULL);
384 return -1;
385 }
386 }
387
388 if (set_r->index_dir != NULL && strcmp(set_r->index_dir, "MEMORY") == 0)
389 set_r->index_dir = "";
390 if (set_r->iter_from_index_dir &&
391 (set_r->index_dir == NULL || set_r->index_dir[0] == '\0')) {
392 *error_r = "ITERINDEX requires INDEX to be explicitly set";
393 return -1;
394 }
395 if (set_r->list_index_fname != NULL &&
396 (fname = strrchr(set_r->list_index_fname, '/')) != NULL) {
397 /* non-default LISTINDEX directory */
398 set_r->list_index_dir =
399 t_strdup_until(set_r->list_index_fname, fname);
400 set_r->list_index_fname = fname+1;
401 if (set_r->list_index_dir[0] != '/' &&
402 set_r->index_dir != NULL && set_r->index_dir[0] == '\0') {
403 *error_r = "LISTINDEX directory is relative but INDEX=MEMORY";
404 return -1;
405 }
406 }
407 return 0;
408 }
409
mailbox_list_settings_parse(struct mail_user * user,const char * data,struct mailbox_list_settings * set_r,const char ** error_r)410 int mailbox_list_settings_parse(struct mail_user *user, const char *data,
411 struct mailbox_list_settings *set_r,
412 const char **error_r)
413 {
414 return mailbox_list_settings_parse_full(user, data, TRUE,
415 set_r, error_r);
416 }
417
mailbox_list_get_unexpanded_path(struct mailbox_list * list,enum mailbox_list_path_type type)418 const char *mailbox_list_get_unexpanded_path(struct mailbox_list *list,
419 enum mailbox_list_path_type type)
420 {
421 const struct mail_storage_settings *mail_set;
422 const char *location = list->ns->unexpanded_set->location;
423 struct mail_user *user = list->ns->user;
424 struct mailbox_list_settings set;
425 const char *p, *path, *error;
426
427 if (*location == SETTING_STRVAR_EXPANDED[0]) {
428 /* set using -o or userdb lookup. */
429 return "";
430 }
431
432 i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
433 location++;
434
435 if (*location == '\0') {
436 mail_set = mail_user_set_get_driver_settings(user->set_info,
437 user->unexpanded_set, MAIL_STORAGE_SET_DRIVER_NAME);
438 i_assert(mail_set != NULL);
439 location = mail_set->mail_location;
440 if (*location == SETTING_STRVAR_EXPANDED[0])
441 return "";
442 i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
443 location++;
444 }
445
446 /* type:settings */
447 p = strchr(location, ':');
448 if (p == NULL)
449 return "";
450
451 if (mailbox_list_settings_parse_full(user, p + 1, FALSE,
452 &set, &error) < 0)
453 return "";
454 if (!mailbox_list_set_get_root_path(&set, type, &path))
455 return "";
456 return path;
457 }
458
need_escape_dirstart(const char * vname,const char * maildir_name)459 static bool need_escape_dirstart(const char *vname, const char *maildir_name)
460 {
461 size_t len;
462
463 if (vname[0] == '.') {
464 if (vname[1] == '\0' || vname[1] == '/')
465 return TRUE; /* "." */
466 if (vname[1] == '.' && (vname[2] == '\0' || vname[2] == '/'))
467 return TRUE; /* ".." */
468 }
469 if (*maildir_name != '\0') {
470 len = strlen(maildir_name);
471 if (str_begins(vname, maildir_name) &&
472 (vname[len] == '\0' || vname[len] == '/'))
473 return TRUE; /* e.g. dbox-Mails */
474 }
475 return FALSE;
476 }
477
478 const char *
mailbox_list_escape_name_params(const char * vname,const char * ns_prefix,char ns_sep,char list_sep,char escape_char,const char * maildir_name)479 mailbox_list_escape_name_params(const char *vname, const char *ns_prefix,
480 char ns_sep, char list_sep, char escape_char,
481 const char *maildir_name)
482 {
483 size_t ns_prefix_len = strlen(ns_prefix);
484 string_t *escaped_name = t_str_new(64);
485 bool dirstart = TRUE;
486
487 i_assert(escape_char != '\0');
488
489 /* no escaping of namespace prefix */
490 if (str_begins(vname, ns_prefix)) {
491 str_append_data(escaped_name, vname, ns_prefix_len);
492 vname += ns_prefix_len;
493 }
494
495 /* escape the mailbox name */
496 if (*vname == '~') {
497 str_printfa(escaped_name, "%c%02x", escape_char, *vname);
498 vname++;
499 dirstart = FALSE;
500 }
501 for (; *vname != '\0'; vname++) {
502 if (*vname == ns_sep)
503 str_append_c(escaped_name, list_sep);
504 else if (*vname == list_sep ||
505 *vname == escape_char ||
506 *vname == '/' ||
507 (dirstart &&
508 need_escape_dirstart(vname, maildir_name))) {
509 str_printfa(escaped_name, "%c%02x",
510 escape_char, *vname);
511 } else {
512 str_append_c(escaped_name, *vname);
513 }
514 dirstart = *vname == '/';
515 }
516 return str_c(escaped_name);
517 }
518
mailbox_list_name_unescape(const char ** _name,char escape_char)519 void mailbox_list_name_unescape(const char **_name, char escape_char)
520 {
521 const char *p, *name = *_name;
522 unsigned char chr;
523
524 if ((p = strchr(name, escape_char)) == NULL)
525 return;
526
527 string_t *str = t_str_new(strlen(name)*2);
528 str_append_data(str, name, p - name);
529 while (*p != '\0') {
530 if (*p == escape_char &&
531 imap_escaped_utf8_hex_to_char(p+1, &chr) == 0) {
532 str_append_c(str, chr);
533 p += 3;
534 } else {
535 str_append_c(str, *p++);
536 }
537 }
538 *_name = str_c(str);
539 }
540
541 static bool
mailbox_list_vname_prepare(struct mailbox_list * list,const char ** _vname)542 mailbox_list_vname_prepare(struct mailbox_list *list, const char **_vname)
543 {
544 struct mail_namespace *ns = list->ns;
545 const char *vname = *_vname;
546
547 if (strcasecmp(vname, "INBOX") == 0 &&
548 (list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
549 /* INBOX is case sensitive. Normalize it into "INBOX". */
550 vname = "INBOX";
551 } else if (ns->prefix_len > 0) {
552 /* skip namespace prefix, except if this is INBOX */
553 if (strncmp(ns->prefix, vname, ns->prefix_len) == 0) {
554 vname += ns->prefix_len;
555 if (strcmp(vname, "INBOX") == 0 &&
556 (list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
557 list->set.storage_name_escape_char != '\0') {
558 /* prefix/INBOX - this is troublesome, because
559 it ends up conflicting with the INBOX name.
560 Handle this in a bit kludgy way by escaping
561 the initial "I" character. */
562 *_vname = t_strdup_printf("%c49NBOX",
563 list->set.storage_name_escape_char);
564 return TRUE;
565 }
566 } else if (strncmp(ns->prefix, vname, ns->prefix_len-1) == 0 &&
567 strlen(vname) == ns->prefix_len-1 &&
568 ns->prefix[ns->prefix_len-1] == mail_namespace_get_sep(ns)) {
569 /* trying to access the namespace prefix itself */
570 vname = "";
571 } else {
572 /* we're converting a nonexistent mailbox name,
573 such as a LIST pattern. */
574 }
575 }
576 if (*vname == '\0' && ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
577 (ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
578 !list->mail_set->mail_shared_explicit_inbox) {
579 /* opening shared/$user. it's the same as INBOX. */
580 vname = "INBOX";
581 }
582 *_vname = vname;
583 return FALSE;
584 }
585
586 static const char *
mailbox_list_default_get_storage_name_part(struct mailbox_list * list,const char * vname_part)587 mailbox_list_default_get_storage_name_part(struct mailbox_list *list,
588 const char *vname_part)
589 {
590 const char *storage_name = vname_part;
591 string_t *str;
592
593 if (!list->set.utf8) {
594 /* UTF-8 -> mUTF-7 conversion */
595 str = t_str_new(strlen(storage_name)*2);
596 if (imap_escaped_utf8_to_utf7(storage_name,
597 list->set.vname_escape_char,
598 str) < 0)
599 i_panic("Mailbox name not UTF-8: %s", vname_part);
600 storage_name = str_c(str);
601 } else if (list->set.vname_escape_char != '\0') {
602 mailbox_list_name_unescape(&storage_name,
603 list->set.vname_escape_char);
604 }
605 if (list->set.storage_name_escape_char != '\0') {
606 storage_name = mailbox_list_escape_name_params(storage_name,
607 list->ns->prefix,
608 '\0', /* no separator conversion */
609 mailbox_list_get_hierarchy_sep(list),
610 list->set.storage_name_escape_char,
611 list->set.maildir_name);
612 }
613 return storage_name;
614 }
615
mailbox_list_default_get_storage_name(struct mailbox_list * list,const char * vname)616 const char *mailbox_list_default_get_storage_name(struct mailbox_list *list,
617 const char *vname)
618 {
619 const char *prepared_name = vname;
620 const char list_sep = mailbox_list_get_hierarchy_sep(list);
621 const char ns_sep = mail_namespace_get_sep(list->ns);
622
623 if (mailbox_list_vname_prepare(list, &prepared_name))
624 return prepared_name;
625 if (list->ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
626 (list->ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0 &&
627 list_sep != ns_sep &&
628 list->set.storage_name_escape_char == '\0') {
629 /* Accessing shared namespace root. This is just the initial
630 lookup that ends up as parameter to
631 shared_storage_get_namespace(). That then finds/creates the
632 actual shared namespace, which gets used to generate the
633 proper storage_name. So the only thing that's really
634 necessary here is to just skip over the shared namespace
635 prefix and leave the rest of the name untouched. The only
636 exception is if there is a storage_name_escape_char set, in
637 this case the storage name must be handled. */
638 return prepared_name;
639 }
640
641 const char sep[] = { ns_sep, '\0' };
642 const char *const *parts = t_strsplit(prepared_name, sep);
643 string_t *storage_name = t_str_new(128);
644 for (unsigned int i = 0; parts[i] != NULL; i++) {
645 if (i > 0)
646 str_append_c(storage_name, list_sep);
647 str_append(storage_name,
648 mailbox_list_default_get_storage_name_part(list, parts[i]));
649 }
650 return str_c(storage_name);
651 }
652
mailbox_list_get_storage_name(struct mailbox_list * list,const char * vname)653 const char *mailbox_list_get_storage_name(struct mailbox_list *list,
654 const char *vname)
655 {
656 return list->v.get_storage_name(list, vname);
657 }
658
659 const char *
mailbox_list_unescape_name_params(const char * src,const char * ns_prefix,char ns_sep,char list_sep,char escape_char)660 mailbox_list_unescape_name_params(const char *src, const char *ns_prefix,
661 char ns_sep, char list_sep, char escape_char)
662 {
663 size_t ns_prefix_len = strlen(ns_prefix);
664 string_t *dest = t_str_new(strlen(src));
665 unsigned int num;
666
667 if (str_begins(src, ns_prefix)) {
668 str_append_data(dest, src, ns_prefix_len);
669 src += ns_prefix_len;
670 }
671
672 for (; *src != '\0'; src++) {
673 if (*src == escape_char &&
674 i_isxdigit(src[1]) && i_isxdigit(src[2])) {
675 if (src[1] >= '0' && src[1] <= '9')
676 num = src[1] - '0';
677 else
678 num = i_toupper(src[1]) - 'A' + 10;
679 num *= 16;
680 if (src[2] >= '0' && src[2] <= '9')
681 num += src[2] - '0';
682 else
683 num += i_toupper(src[2]) - 'A' + 10;
684
685 str_append_c(dest, num);
686 src += 2;
687 } else if (*src == list_sep)
688 str_append_c(dest, ns_sep);
689 else
690 str_append_c(dest, *src);
691 }
692 return str_c(dest);
693 }
694
695 static bool
mailbox_list_storage_name_prepare(struct mailbox_list * list,const char ** _storage_name)696 mailbox_list_storage_name_prepare(struct mailbox_list *list,
697 const char **_storage_name)
698 {
699 const char *name = *_storage_name;
700
701 if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
702 strcmp(name, "INBOX") == 0 &&
703 list->ns->user == list->ns->owner) {
704 /* user's INBOX - use as-is. NOTE: don't do case-insensitive
705 comparison, otherwise we can't differentiate between INBOX
706 and <ns prefix>/inBox. */
707 return TRUE;
708 }
709 if (strcmp(name, "INBOX") == 0 &&
710 list->ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
711 (list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
712 !list->mail_set->mail_shared_explicit_inbox) {
713 /* convert to shared/$user, we don't really care about the
714 INBOX suffix here. */
715 name = "";
716 }
717 if (name[0] == '\0') {
718 /* return namespace prefix without the separator */
719 if (list->ns->prefix_len == 0)
720 *_storage_name = list->ns->prefix;
721 else {
722 *_storage_name =
723 t_strndup(list->ns->prefix,
724 list->ns->prefix_len - 1);
725 }
726 return TRUE;
727 }
728 return FALSE;
729 }
730
mailbox_list_name_escape(const char * name,const char * escape_chars,string_t * dest)731 void mailbox_list_name_escape(const char *name, const char *escape_chars,
732 string_t *dest)
733 {
734 for (unsigned int i = 0; name[i] != '\0'; i++) {
735 if (strchr(escape_chars, name[i]) != NULL)
736 str_printfa(dest, "%c%02x", escape_chars[0], name[i]);
737 else
738 str_append_c(dest, name[i]);
739 }
740 }
741
742 static const char *
mailbox_list_default_get_vname_part(struct mailbox_list * list,const char * storage_name_part)743 mailbox_list_default_get_vname_part(struct mailbox_list *list,
744 const char *storage_name_part)
745 {
746 const char *vname = storage_name_part;
747 char escape_chars[] = {
748 list->set.vname_escape_char,
749 mail_namespace_get_sep(list->ns),
750 '\0'
751 };
752
753 if (list->set.storage_name_escape_char != '\0') {
754 vname = mailbox_list_unescape_name_params(vname,
755 list->ns->prefix,
756 '\0', '\0', /* no separator conversion */
757 list->set.storage_name_escape_char);
758 }
759
760 if (!list->set.utf8) {
761 /* mUTF-7 -> UTF-8 conversion */
762 string_t *str = t_str_new(strlen(vname));
763 if (escape_chars[0] != '\0') {
764 imap_utf7_to_utf8_escaped(vname, escape_chars, str);
765 vname = str_c(str);
766 } else if (imap_utf7_to_utf8(vname, str) == 0)
767 vname = str_c(str);
768 else {
769 /* Invalid mUTF7, but no escape character. This mailbox
770 can't be accessible, so just return it as the
771 original mUTF7 name. */
772 }
773 } else if (list->set.vname_escape_char != '\0') {
774 string_t *str = t_str_new(strlen(vname));
775 mailbox_list_name_escape(vname, escape_chars, str);
776 vname = str_c(str);
777 }
778 return vname;
779 }
780
mailbox_list_default_get_vname(struct mailbox_list * list,const char * storage_name)781 const char *mailbox_list_default_get_vname(struct mailbox_list *list,
782 const char *storage_name)
783 {
784 if (mailbox_list_storage_name_prepare(list, &storage_name))
785 return storage_name;
786
787 char ns_sep = mail_namespace_get_sep(list->ns);
788 char sep[] = { mailbox_list_get_hierarchy_sep(list), '\0' };
789 const char *const *parts = t_strsplit(storage_name, sep);
790 string_t *vname = t_str_new(128);
791 str_append(vname, list->ns->prefix);
792 for (unsigned int i = 0; parts[i] != NULL; i++) {
793 if (i > 0)
794 str_append_c(vname, ns_sep);
795 str_append(vname,
796 mailbox_list_default_get_vname_part(list, parts[i]));
797 }
798 return str_c(vname);
799 }
800
mailbox_list_get_vname(struct mailbox_list * list,const char * name)801 const char *mailbox_list_get_vname(struct mailbox_list *list, const char *name)
802 {
803 return list->v.get_vname(list, name);
804 }
805
mailbox_list_destroy(struct mailbox_list ** _list)806 void mailbox_list_destroy(struct mailbox_list **_list)
807 {
808 struct mailbox_list *list = *_list;
809
810 *_list = NULL;
811 i_free_and_null(list->error_string);
812 i_free(list->last_internal_error);
813
814 if (hash_table_is_created(list->guid_cache)) {
815 hash_table_destroy(&list->guid_cache);
816 pool_unref(&list->guid_cache_pool);
817 }
818
819 if (list->subscriptions != NULL)
820 mailbox_tree_deinit(&list->subscriptions);
821 if (list->changelog != NULL)
822 mailbox_log_free(&list->changelog);
823
824 if (array_is_created(&list->error_stack)) {
825 i_assert(array_count(&list->error_stack) == 0);
826 array_free(&list->error_stack);
827 }
828 list->v.deinit(list);
829 }
830
mailbox_list_get_driver_name(const struct mailbox_list * list)831 const char *mailbox_list_get_driver_name(const struct mailbox_list *list)
832 {
833 return list->name;
834 }
835
836 const struct mailbox_list_settings *
mailbox_list_get_settings(const struct mailbox_list * list)837 mailbox_list_get_settings(const struct mailbox_list *list)
838 {
839 return &list->set;
840 }
841
mailbox_list_get_flags(const struct mailbox_list * list)842 enum mailbox_list_flags mailbox_list_get_flags(const struct mailbox_list *list)
843 {
844 return list->flags;
845 }
846
847 struct mail_namespace *
mailbox_list_get_namespace(const struct mailbox_list * list)848 mailbox_list_get_namespace(const struct mailbox_list *list)
849 {
850 return list->ns;
851 }
852
get_dir_mode(mode_t mode)853 static mode_t get_dir_mode(mode_t mode)
854 {
855 /* add the execute bit if either read or write bit is set */
856 if ((mode & 0600) != 0) mode |= 0100;
857 if ((mode & 0060) != 0) mode |= 0010;
858 if ((mode & 0006) != 0) mode |= 0001;
859 return mode;
860 }
861
862 struct mail_user *
mailbox_list_get_user(const struct mailbox_list * list)863 mailbox_list_get_user(const struct mailbox_list *list)
864 {
865 return list->ns->user;
866 }
867
868 static int
mailbox_list_get_storage_driver(struct mailbox_list * list,const char * driver,struct mail_storage ** storage_r)869 mailbox_list_get_storage_driver(struct mailbox_list *list, const char *driver,
870 struct mail_storage **storage_r)
871 {
872 struct mail_storage *storage;
873 const char *error, *data;
874
875 array_foreach_elem(&list->ns->all_storages, storage) {
876 if (strcmp(storage->name, driver) == 0) {
877 *storage_r = storage;
878 return 0;
879 }
880 }
881
882 data = i_strchr_to_next(list->ns->set->location, ':');
883 if (data == NULL)
884 data = "";
885 if (mail_storage_create_full(list->ns, driver, data, 0,
886 storage_r, &error) < 0) {
887 mailbox_list_set_critical(list,
888 "Namespace %s: Failed to create storage '%s': %s",
889 list->ns->prefix, driver, error);
890 return -1;
891 }
892 return 0;
893 }
894
mailbox_list_get_storage(struct mailbox_list ** list,const char * vname,struct mail_storage ** storage_r)895 int mailbox_list_get_storage(struct mailbox_list **list, const char *vname,
896 struct mail_storage **storage_r)
897 {
898 const struct mailbox_settings *set;
899
900 if ((*list)->v.get_storage != NULL)
901 return (*list)->v.get_storage(list, vname, storage_r);
902
903 set = mailbox_settings_find((*list)->ns, vname);
904 if (set != NULL && set->driver != NULL && set->driver[0] != '\0') {
905 return mailbox_list_get_storage_driver(*list, set->driver,
906 storage_r);
907 }
908 *storage_r = mail_namespace_get_default_storage((*list)->ns);
909 return 0;
910 }
911
mailbox_list_get_default_storage(struct mailbox_list * list,struct mail_storage ** storage)912 void mailbox_list_get_default_storage(struct mailbox_list *list,
913 struct mail_storage **storage)
914 {
915 *storage = mail_namespace_get_default_storage(list->ns);
916 }
917
mailbox_list_get_hierarchy_sep(struct mailbox_list * list)918 char mailbox_list_get_hierarchy_sep(struct mailbox_list *list)
919 {
920 /* the current API doesn't allow returning an error, so imap code
921 looks at the list's last error. make sure the error is cleared
922 so the error-check doesn't return something irrelevant */
923 mailbox_list_clear_error(list);
924 return list->v.get_hierarchy_sep(list);
925 }
926
927 static bool
mailbox_list_get_permissions_stat(struct mailbox_list * list,const char * path,struct mailbox_permissions * permissions_r)928 mailbox_list_get_permissions_stat(struct mailbox_list *list, const char *path,
929 struct mailbox_permissions *permissions_r)
930 {
931 struct stat st;
932
933 if (stat(path, &st) < 0) {
934 if (errno == EACCES) {
935 mailbox_list_set_critical(list, "%s",
936 mail_error_eacces_msg("stat", path));
937 } else if (!ENOTFOUND(errno)) {
938 mailbox_list_set_critical(list, "stat(%s) failed: %m",
939 path);
940 } else {
941 e_debug(list->ns->user->event,
942 "Namespace %s: %s doesn't exist yet, "
943 "using default permissions",
944 list->ns->prefix, path);
945 }
946 return FALSE;
947 }
948
949 permissions_r->file_uid = st.st_uid;
950 permissions_r->file_gid = st.st_gid;
951 permissions_r->file_create_mode = (st.st_mode & 0666) | 0600;
952 permissions_r->dir_create_mode = (st.st_mode & 0777) | 0700;
953 permissions_r->file_create_gid_origin = path;
954
955 if (!S_ISDIR(st.st_mode)) {
956 /* we're getting permissions from a file.
957 apply +x modes as necessary. */
958 permissions_r->dir_create_mode =
959 get_dir_mode(permissions_r->dir_create_mode);
960 }
961
962 if (S_ISDIR(st.st_mode) && (st.st_mode & S_ISGID) != 0) {
963 /* directory's GID is used automatically for new files */
964 permissions_r->file_create_gid = (gid_t)-1;
965 } else if ((st.st_mode & 0070) >> 3 == (st.st_mode & 0007)) {
966 /* group has same permissions as world, so don't bother
967 changing it */
968 permissions_r->file_create_gid = (gid_t)-1;
969 } else if (getegid() == st.st_gid) {
970 /* using our own gid, no need to change it */
971 permissions_r->file_create_gid = (gid_t)-1;
972 } else {
973 permissions_r->file_create_gid = st.st_gid;
974 }
975 if (!S_ISDIR(st.st_mode) &&
976 permissions_r->file_create_gid != (gid_t)-1) {
977 /* we need to stat() the parent directory to see if
978 it has setgid-bit set */
979 const char *p = strrchr(path, '/');
980 const char *parent_path = p == NULL ? NULL :
981 t_strdup_until(path, p);
982 if (parent_path != NULL &&
983 stat(parent_path, &st) == 0 &&
984 (st.st_mode & S_ISGID) != 0) {
985 /* directory's GID is used automatically for
986 new files */
987 permissions_r->file_create_gid = (gid_t)-1;
988 }
989 }
990 return TRUE;
991 }
992
993 static void ATTR_NULL(2)
mailbox_list_get_permissions_internal(struct mailbox_list * list,const char * name,struct mailbox_permissions * permissions_r)994 mailbox_list_get_permissions_internal(struct mailbox_list *list,
995 const char *name,
996 struct mailbox_permissions *permissions_r)
997 {
998 const char *path = NULL, *parent_name, *p;
999
1000 i_zero(permissions_r);
1001
1002 /* use safe defaults */
1003 permissions_r->file_uid = (uid_t)-1;
1004 permissions_r->file_gid = (gid_t)-1;
1005 permissions_r->file_create_mode = 0600;
1006 permissions_r->dir_create_mode = 0700;
1007 permissions_r->file_create_gid = (gid_t)-1;
1008 permissions_r->file_create_gid_origin = "defaults";
1009
1010 if (list->set.iter_from_index_dir ||
1011 (list->flags & MAILBOX_LIST_FLAG_NO_MAIL_FILES) != 0) {
1012 /* a) iterating from index dir. Use the index dir's permissions
1013 as well, since they might be in a faster storage.
1014
1015 b) mail files don't exist in storage, but index files
1016 might. */
1017 (void)mailbox_list_get_path(list, name,
1018 MAILBOX_LIST_PATH_TYPE_INDEX, &path);
1019 }
1020
1021 if (name != NULL && path == NULL) {
1022 if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR,
1023 &path) < 0)
1024 name = NULL;
1025 }
1026 if (name == NULL && path == NULL) {
1027 (void)mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_DIR,
1028 &path);
1029 }
1030
1031 if (path == NULL) {
1032 /* no filesystem support in storage */
1033 } else if (mailbox_list_get_permissions_stat(list, path, permissions_r)) {
1034 /* got permissions from the given path */
1035 permissions_r->gid_origin_is_mailbox_path = name != NULL;
1036 } else if (name != NULL) {
1037 /* path couldn't be stat()ed, try parent mailbox */
1038 p = strrchr(name, mailbox_list_get_hierarchy_sep(list));
1039 if (p == NULL) {
1040 /* return root defaults */
1041 parent_name = NULL;
1042 } else {
1043 parent_name = t_strdup_until(name, p);
1044 }
1045 mailbox_list_get_permissions(list, parent_name,
1046 permissions_r);
1047 return;
1048 } else {
1049 /* assume current defaults for mailboxes that don't exist or
1050 can't be looked up for some other reason */
1051 permissions_r->file_uid = geteuid();
1052 permissions_r->file_gid = getegid();
1053 }
1054 if (name == NULL) {
1055 mailbox_permissions_copy(&list->root_permissions, permissions_r,
1056 list->pool);
1057 }
1058
1059 if (name == NULL) {
1060 e_debug(list->ns->user->event,
1061 "Namespace %s: Using permissions from %s: "
1062 "mode=0%o gid=%s", list->ns->prefix,
1063 path != NULL ? path : "",
1064 (int)permissions_r->dir_create_mode,
1065 permissions_r->file_create_gid == (gid_t)-1 ? "default" :
1066 dec2str(permissions_r->file_create_gid));
1067 }
1068 }
1069
mailbox_list_get_permissions(struct mailbox_list * list,const char * name,struct mailbox_permissions * permissions_r)1070 void mailbox_list_get_permissions(struct mailbox_list *list, const char *name,
1071 struct mailbox_permissions *permissions_r)
1072 {
1073 mailbox_list_get_permissions_internal(list, name, permissions_r);
1074 }
1075
mailbox_list_get_root_permissions(struct mailbox_list * list,struct mailbox_permissions * permissions_r)1076 void mailbox_list_get_root_permissions(struct mailbox_list *list,
1077 struct mailbox_permissions *permissions_r)
1078 {
1079 if (list->root_permissions.file_create_mode != (mode_t)-1)
1080 *permissions_r = list->root_permissions;
1081 else {
1082 mailbox_list_get_permissions_internal(list, NULL,
1083 permissions_r);
1084 }
1085 }
1086
mailbox_permissions_copy(struct mailbox_permissions * dest,const struct mailbox_permissions * src,pool_t pool)1087 void mailbox_permissions_copy(struct mailbox_permissions *dest,
1088 const struct mailbox_permissions *src,
1089 pool_t pool)
1090 {
1091 *dest = *src;
1092 dest->file_create_gid_origin =
1093 p_strdup(pool, src->file_create_gid_origin);
1094 }
1095
1096 static const char *
get_expanded_path(const char * unexpanded_start,const char * unexpanded_stop,const char * expanded_full)1097 get_expanded_path(const char *unexpanded_start, const char *unexpanded_stop,
1098 const char *expanded_full)
1099 {
1100 const char *ret;
1101 unsigned int i, slash_count = 0, slash2_count = 0;
1102
1103 /* get the expanded path up to the same amount of '/' characters.
1104 if there isn't the same amount of '/' characters, it means %variable
1105 expansion added more of them and we can't handle this. */
1106 for (i = 0; unexpanded_start+i != unexpanded_stop; i++) {
1107 if (unexpanded_start[i] == '/')
1108 slash_count++;
1109 }
1110 for (; unexpanded_start[i] != '\0'; i++) {
1111 if (unexpanded_start[i] == '/')
1112 slash2_count++;
1113 }
1114
1115 for (i = 0; expanded_full[i] != '\0'; i++) {
1116 if (expanded_full[i] == '/') {
1117 if (slash_count == 0)
1118 break;
1119 slash_count--;
1120 }
1121 }
1122 if (slash_count != 0)
1123 return "";
1124
1125 ret = t_strndup(expanded_full, i);
1126 for (; expanded_full[i] != '\0'; i++) {
1127 if (expanded_full[i] == '/') {
1128 if (slash2_count == 0)
1129 return "";
1130 slash2_count--;
1131 }
1132 }
1133 if (slash2_count != 0)
1134 return "";
1135 return ret;
1136 }
1137
1138 static int
mailbox_list_try_mkdir_root_parent(struct mailbox_list * list,enum mailbox_list_path_type type,struct mailbox_permissions * perm,const char ** error_r)1139 mailbox_list_try_mkdir_root_parent(struct mailbox_list *list,
1140 enum mailbox_list_path_type type,
1141 struct mailbox_permissions *perm,
1142 const char **error_r)
1143 {
1144 const char *expanded, *unexpanded, *root_dir, *p;
1145 struct stat st;
1146 bool home = FALSE;
1147
1148 /* get the directory path up to last %variable. for example
1149 unexpanded path may be "/var/mail/%d/%2n/%n/Maildir", and we want
1150 to get expanded="/var/mail/domain/nn" */
1151 unexpanded = mailbox_list_get_unexpanded_path(list, type);
1152 p = strrchr(unexpanded, '%');
1153 if ((p == unexpanded && p[1] == 'h') ||
1154 (p == NULL && unexpanded[0] == '~')) {
1155 /* home directory used */
1156 if (!mailbox_list_get_root_path(list, type, &expanded))
1157 i_unreached();
1158 home = TRUE;
1159 } else if (p == NULL) {
1160 return 0;
1161 } else {
1162 while (p != unexpanded && *p != '/') p--;
1163 if (p == unexpanded)
1164 return 0;
1165
1166 if (!mailbox_list_get_root_path(list, type, &expanded))
1167 i_unreached();
1168 expanded = get_expanded_path(unexpanded, p, expanded);
1169 if (*expanded == '\0')
1170 return 0;
1171 }
1172
1173 /* get the first existing parent directory's permissions */
1174 if (stat_first_parent(expanded, &root_dir, &st) < 0) {
1175 *error_r = errno == EACCES ?
1176 mail_error_eacces_msg("stat", root_dir) :
1177 t_strdup_printf("stat(%s) failed: %m", root_dir);
1178 return -1;
1179 }
1180
1181 /* if the parent directory doesn't have setgid-bit enabled, we don't
1182 copy any permissions from it. */
1183 if ((st.st_mode & S_ISGID) == 0)
1184 return 0;
1185
1186 if (!home) {
1187 /* assuming we have e.g. /var/vmail/%d/%n directory, here we
1188 want to create up to /var/vmail/%d with permissions from
1189 the parent directory. we never want to create the %n
1190 directory itself. */
1191 if (root_dir == expanded) {
1192 /* this is the %n directory */
1193 } else {
1194 if (mkdir_parents_chgrp(expanded, st.st_mode,
1195 (gid_t)-1, root_dir) < 0 &&
1196 errno != EEXIST) {
1197 *error_r = t_strdup_printf(
1198 "mkdir(%s) failed: %m", expanded);
1199 return -1;
1200 }
1201 }
1202 if (perm->file_create_gid == (gid_t)-1 &&
1203 (perm->dir_create_mode & S_ISGID) == 0) {
1204 /* change the group for user directories */
1205 perm->dir_create_mode |= S_ISGID;
1206 perm->file_create_gid = getegid();
1207 perm->file_create_gid_origin = "egid";
1208 perm->gid_origin_is_mailbox_path = FALSE;
1209 }
1210 } else {
1211 /* when using %h and the parent has setgid-bit,
1212 copy the permissions from it for the home we're creating */
1213 perm->file_create_mode = st.st_mode & 0666;
1214 perm->dir_create_mode = st.st_mode;
1215 perm->file_create_gid = (gid_t)-1;
1216 perm->file_create_gid_origin = "parent";
1217 perm->gid_origin_is_mailbox_path = FALSE;
1218 }
1219 return 0;
1220 }
1221
mailbox_list_try_mkdir_root(struct mailbox_list * list,const char * path,enum mailbox_list_path_type type,const char ** error_r)1222 int mailbox_list_try_mkdir_root(struct mailbox_list *list, const char *path,
1223 enum mailbox_list_path_type type,
1224 const char **error_r)
1225 {
1226 const char *root_dir;
1227 struct stat st;
1228 struct mailbox_permissions perm;
1229
1230 if (stat(path, &st) == 0) {
1231 /* looks like it already exists, don't bother checking
1232 further. */
1233 if (!S_ISDIR(st.st_mode)) {
1234 *error_r = t_strdup_printf(
1235 "Root directory is a file: %s", path);
1236 return -1;
1237 }
1238 return 0;
1239 }
1240
1241 mailbox_list_get_root_permissions(list, &perm);
1242
1243 if (!mailbox_list_get_root_path(list, type, &root_dir))
1244 i_unreached();
1245 i_assert(str_begins(path, root_dir));
1246 if (strcmp(root_dir, path) != 0 && stat(root_dir, &st) == 0) {
1247 /* creating a subdirectory under an already existing root dir.
1248 use the root's permissions */
1249 } else {
1250 if (mailbox_list_try_mkdir_root_parent(list, type,
1251 &perm, error_r) < 0)
1252 return -1;
1253 }
1254
1255 /* the rest of the directories exist only for one user. create them
1256 with default directory permissions */
1257 if (mkdir_parents_chgrp(path, perm.dir_create_mode,
1258 perm.file_create_gid,
1259 perm.file_create_gid_origin) < 0 &&
1260 errno != EEXIST) {
1261 if (errno == EACCES)
1262 *error_r = mail_error_create_eacces_msg("mkdir", path);
1263 else
1264 *error_r = t_strdup_printf("mkdir(%s) failed: %m", path);
1265 return -1;
1266 }
1267 return 0;
1268 }
1269
mailbox_list_mkdir_root(struct mailbox_list * list,const char * path,enum mailbox_list_path_type type)1270 int mailbox_list_mkdir_root(struct mailbox_list *list, const char *path,
1271 enum mailbox_list_path_type type)
1272 {
1273 const char *error;
1274
1275 if (mailbox_list_try_mkdir_root(list, path, type, &error) < 0) {
1276 mailbox_list_set_critical(list, "%s", error);
1277 return -1;
1278 }
1279 if (type == MAILBOX_LIST_PATH_TYPE_INDEX)
1280 list->index_root_dir_created = TRUE;
1281 return 0;
1282 }
1283
1284 static bool
mailbox_list_is_valid_fs_name(struct mailbox_list * list,const char * name,const char ** error_r)1285 mailbox_list_is_valid_fs_name(struct mailbox_list *list, const char *name,
1286 const char **error_r)
1287 {
1288 bool ret, allow_internal_dirs;
1289
1290 *error_r = NULL;
1291
1292 if (list->mail_set->mail_full_filesystem_access ||
1293 list->set.no_fs_validation)
1294 return TRUE;
1295
1296 /* either the list backend uses '/' as the hierarchy separator or
1297 it doesn't use filesystem at all (PROP_NO_ROOT) */
1298 if ((list->props & MAILBOX_LIST_PROP_NO_ROOT) == 0 &&
1299 mailbox_list_get_hierarchy_sep(list) != '/' &&
1300 strchr(name, '/') != NULL) {
1301 *error_r = "Name must not have '/' characters";
1302 return FALSE;
1303 }
1304
1305 /* make sure it's not absolute path */
1306 if (*name == '/') {
1307 *error_r = "Begins with '/'";
1308 return FALSE;
1309 }
1310 if (*name == '~') {
1311 *error_r = "Begins with '~'";
1312 return FALSE;
1313 }
1314
1315 /* make sure the mailbox name doesn't contain any foolishness:
1316 "../" could give access outside the mailbox directory.
1317 "./" and "//" could fool ACL checks.
1318
1319 some mailbox formats have reserved directory names, such as
1320 Maildir's cur/new/tmp. if any of those would conflict with the
1321 mailbox directory name, it's not valid. */
1322 allow_internal_dirs = list->v.is_internal_name == NULL ||
1323 *list->set.maildir_name != '\0' ||
1324 (list->props & MAILBOX_LIST_PROP_NO_INTERNAL_NAMES) != 0;
1325 T_BEGIN {
1326 const char *const *names;
1327
1328 names = t_strsplit(name, "/");
1329 for (; *names != NULL; names++) {
1330 const char *n = *names;
1331
1332 if (*n == '\0') {
1333 *error_r = "Has adjacent '/' chars";
1334 break; /* // */
1335 }
1336 if (*n == '.') {
1337 if (n[1] == '\0') {
1338 *error_r = "Contains '.' part";
1339 break; /* ./ */
1340 }
1341 if (n[1] == '.' && n[2] == '\0') {
1342 *error_r = "Contains '..' part";
1343 break; /* ../ */
1344 }
1345 }
1346 if (*list->set.maildir_name != '\0' &&
1347 strcmp(list->set.maildir_name, n) == 0) {
1348 /* don't allow maildir_name to be used as part
1349 of the mailbox name */
1350 *error_r = "Contains reserved name";
1351 break;
1352 }
1353 if (!allow_internal_dirs &&
1354 list->v.is_internal_name(list, n)) {
1355 *error_r = "Contains reserved name";
1356 break;
1357 }
1358 }
1359 ret = *names == NULL;
1360 } T_END;
1361
1362 return ret;
1363 }
1364
1365
mailbox_list_is_valid_name(struct mailbox_list * list,const char * name,const char ** error_r)1366 bool mailbox_list_is_valid_name(struct mailbox_list *list,
1367 const char *name, const char **error_r)
1368 {
1369 if (*name == '\0') {
1370 if (*list->ns->prefix != '\0') {
1371 /* an ugly way to get to mailbox root (e.g. Maildir/
1372 when it's not the INBOX) */
1373 return TRUE;
1374 }
1375 *error_r = "Name is empty";
1376 return FALSE;
1377 }
1378
1379 return mailbox_list_is_valid_fs_name(list, name, error_r);
1380 }
1381
mailbox_list_get_path(struct mailbox_list * list,const char * name,enum mailbox_list_path_type type,const char ** path_r)1382 int mailbox_list_get_path(struct mailbox_list *list, const char *name,
1383 enum mailbox_list_path_type type,
1384 const char **path_r)
1385 {
1386 int ret;
1387
1388 if ((ret = list->v.get_path(list, name, type, path_r)) <= 0)
1389 *path_r = NULL;
1390 else
1391 i_assert(*path_r != NULL);
1392 return ret;
1393 }
1394
mailbox_list_get_root_path(struct mailbox_list * list,enum mailbox_list_path_type type,const char ** path_r)1395 bool mailbox_list_get_root_path(struct mailbox_list *list,
1396 enum mailbox_list_path_type type,
1397 const char **path_r)
1398 {
1399 int ret;
1400
1401 if ((ret = list->v.get_path(list, NULL, type, path_r)) < 0)
1402 i_unreached();
1403 if (ret == 0)
1404 *path_r = NULL;
1405 else
1406 i_assert(*path_r != NULL);
1407 return ret > 0;
1408 }
1409
mailbox_list_get_root_forced(struct mailbox_list * list,enum mailbox_list_path_type type)1410 const char *mailbox_list_get_root_forced(struct mailbox_list *list,
1411 enum mailbox_list_path_type type)
1412 {
1413 const char *path;
1414
1415 if (!mailbox_list_get_root_path(list, type, &path))
1416 i_unreached();
1417 return path;
1418 }
1419
mailbox_list_set_get_root_path(const struct mailbox_list_settings * set,enum mailbox_list_path_type type,const char ** path_r)1420 bool mailbox_list_set_get_root_path(const struct mailbox_list_settings *set,
1421 enum mailbox_list_path_type type,
1422 const char **path_r)
1423 {
1424 const char *path = NULL;
1425
1426 switch (type) {
1427 case MAILBOX_LIST_PATH_TYPE_DIR:
1428 path = set->root_dir;
1429 break;
1430 case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
1431 path = set->alt_dir;
1432 break;
1433 case MAILBOX_LIST_PATH_TYPE_MAILBOX:
1434 if (*set->mailbox_dir_name == '\0')
1435 path = set->root_dir;
1436 else {
1437 path = t_strconcat(set->root_dir, "/",
1438 set->mailbox_dir_name, NULL);
1439 path = t_strndup(path, strlen(path)-1);
1440 }
1441 break;
1442 case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
1443 if (*set->mailbox_dir_name == '\0')
1444 path = set->alt_dir;
1445 else if (set->alt_dir != NULL) {
1446 path = t_strconcat(set->alt_dir, "/",
1447 set->mailbox_dir_name, NULL);
1448 path = t_strndup(path, strlen(path)-1);
1449 }
1450 break;
1451 case MAILBOX_LIST_PATH_TYPE_CONTROL:
1452 path = set->control_dir != NULL ?
1453 set->control_dir : set->root_dir;
1454 break;
1455 case MAILBOX_LIST_PATH_TYPE_LIST_INDEX:
1456 if (set->list_index_dir != NULL) {
1457 if (set->list_index_dir[0] == '/') {
1458 path = set->list_index_dir;
1459 break;
1460 }
1461 /* relative path */
1462 if (!mailbox_list_set_get_root_path(set,
1463 MAILBOX_LIST_PATH_TYPE_INDEX, &path))
1464 i_unreached();
1465 path = t_strconcat(path, "/", set->list_index_dir, NULL);
1466 break;
1467 }
1468 /* fall through - default to index directory */
1469 case MAILBOX_LIST_PATH_TYPE_INDEX_CACHE:
1470 if (set->index_cache_dir != NULL &&
1471 type == MAILBOX_LIST_PATH_TYPE_INDEX_CACHE) {
1472 path = set->index_cache_dir;
1473 break;
1474 }
1475 /* fall through */
1476 case MAILBOX_LIST_PATH_TYPE_INDEX:
1477 if (set->index_dir != NULL) {
1478 if (set->index_dir[0] == '\0') {
1479 /* in-memory indexes */
1480 return 0;
1481 }
1482 path = set->index_dir;
1483 } else {
1484 path = set->root_dir;
1485 }
1486 break;
1487 case MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE:
1488 path = set->index_pvt_dir;
1489 break;
1490 }
1491 *path_r = path;
1492 return path != NULL;
1493 }
1494
mailbox_list_get_temp_prefix(struct mailbox_list * list)1495 const char *mailbox_list_get_temp_prefix(struct mailbox_list *list)
1496 {
1497 return list->v.get_temp_prefix(list, FALSE);
1498 }
1499
mailbox_list_get_global_temp_prefix(struct mailbox_list * list)1500 const char *mailbox_list_get_global_temp_prefix(struct mailbox_list *list)
1501 {
1502 return list->v.get_temp_prefix(list, TRUE);
1503 }
1504
mailbox_list_join_refpattern(struct mailbox_list * list,const char * ref,const char * pattern)1505 const char *mailbox_list_join_refpattern(struct mailbox_list *list,
1506 const char *ref, const char *pattern)
1507 {
1508 if (list->v.join_refpattern != NULL)
1509 return list->v.join_refpattern(list, ref, pattern);
1510
1511 /* the default implementation: */
1512 if (*ref != '\0') {
1513 /* merge reference and pattern */
1514 pattern = t_strconcat(ref, pattern, NULL);
1515 }
1516 return pattern;
1517 }
1518
mailbox_has_children(struct mailbox_list * list,const char * name)1519 int mailbox_has_children(struct mailbox_list *list, const char *name)
1520 {
1521 struct mailbox_list_iterate_context *iter;
1522 const char *pattern;
1523 int ret;
1524
1525 pattern = t_strdup_printf("%s%c%%", name,
1526 mail_namespace_get_sep(list->ns));
1527 iter = mailbox_list_iter_init(list, pattern,
1528 MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
1529 ret = mailbox_list_iter_next(iter) != NULL ? 1 : 0;
1530 if (mailbox_list_iter_deinit(&iter) < 0)
1531 ret = -1;
1532 return ret;
1533 }
1534
mailbox_list_mailbox(struct mailbox_list * list,const char * name,enum mailbox_info_flags * flags_r)1535 int mailbox_list_mailbox(struct mailbox_list *list, const char *name,
1536 enum mailbox_info_flags *flags_r)
1537 {
1538 const char *path, *fname, *rootdir, *dir, *inbox;
1539 size_t len;
1540
1541 *flags_r = 0;
1542
1543 if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
1544 strcasecmp(name, "INBOX") == 0) {
1545 /* special handling for INBOX, mainly because with Maildir++
1546 layout it needs to check if the cur/ directory exists,
1547 which the Maildir++ layout backend itself can't do.. */
1548 struct mailbox *box;
1549 enum mailbox_existence existence;
1550 int ret;
1551
1552 /* kludge: with imapc backend we can get here with
1553 list=Maildir++ (for indexes), but list->ns->list=imapc */
1554 box = mailbox_alloc(list->ns->list, "INBOX", 0);
1555 ret = mailbox_exists(box, FALSE, &existence);
1556 if (ret < 0) {
1557 const char *errstr;
1558 enum mail_error error;
1559
1560 /* internal error or with imapc we can get here with
1561 login failures */
1562 errstr = mailbox_get_last_error(box, &error);
1563 mailbox_list_set_error(list, error, errstr);
1564 }
1565 mailbox_free(&box);
1566 if (ret < 0)
1567 return -1;
1568 switch (existence) {
1569 case MAILBOX_EXISTENCE_NONE:
1570 case MAILBOX_EXISTENCE_NOSELECT:
1571 *flags_r |= MAILBOX_NONEXISTENT;
1572 return 0;
1573 case MAILBOX_EXISTENCE_SELECT:
1574 break;
1575 }
1576 return 1;
1577 }
1578
1579 if (list->v.get_mailbox_flags == NULL) {
1580 /* can't do this optimized. do it the slow way. */
1581 struct mailbox_list_iterate_context *iter;
1582 const struct mailbox_info *info;
1583 const char *vname;
1584
1585 vname = mailbox_list_get_vname(list, name);
1586 iter = mailbox_list_iter_init(list, vname, 0);
1587 info = mailbox_list_iter_next(iter);
1588 if (info == NULL)
1589 *flags_r = MAILBOX_NONEXISTENT;
1590 else
1591 *flags_r = info->flags;
1592 return mailbox_list_iter_deinit(&iter);
1593 }
1594
1595 if (!list->set.iter_from_index_dir) {
1596 rootdir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_MAILBOX);
1597 if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR, &path) <= 0)
1598 i_unreached();
1599 } else {
1600 rootdir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_INDEX);
1601 if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_INDEX, &path) <= 0)
1602 i_unreached();
1603 }
1604
1605 fname = strrchr(path, '/');
1606 if (fname == NULL) {
1607 fname = path;
1608 dir = "/";
1609 } else {
1610 dir = t_strdup_until(path, fname);
1611 fname++;
1612 }
1613
1614 len = strlen(rootdir);
1615 if (str_begins(path, rootdir) && path[len] == '/') {
1616 /* looking up a regular mailbox under mail root dir */
1617 } else if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
1618 strcasecmp(name, "INBOX") == 0) {
1619 /* looking up INBOX that's elsewhere */
1620 } else {
1621 /* looking up the root dir itself */
1622 dir = path;
1623 fname = "";
1624 }
1625 if (*fname == '\0' && *name == '\0' &&
1626 (list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
1627 /* if INBOX is in e.g. ~/Maildir, it shouldn't be possible to
1628 access it also via namespace prefix. */
1629 if (mailbox_list_get_path(list, "INBOX",
1630 MAILBOX_LIST_PATH_TYPE_MAILBOX,
1631 &inbox) <= 0)
1632 i_unreached();
1633 if (strcmp(inbox, dir) == 0) {
1634 *flags_r |= MAILBOX_NONEXISTENT;
1635 return 0;
1636 }
1637 }
1638 return list->v.get_mailbox_flags(list, dir, fname,
1639 MAILBOX_LIST_FILE_TYPE_UNKNOWN,
1640 flags_r);
1641 }
1642
mailbox_list_init_changelog(struct mailbox_list * list)1643 static bool mailbox_list_init_changelog(struct mailbox_list *list)
1644 {
1645 struct mailbox_permissions perm;
1646 const char *path;
1647
1648 if (list->changelog != NULL)
1649 return TRUE;
1650
1651 /* don't do this in mailbox_list_create(), because _get_path() might be
1652 overridden by storage (mbox). */
1653 if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX, &path))
1654 return FALSE;
1655
1656 path = t_strconcat(path, "/"MAILBOX_LOG_FILE_NAME, NULL);
1657 list->changelog = mailbox_log_alloc(list->ns->user->event, path);
1658
1659 mailbox_list_get_root_permissions(list, &perm);
1660 mailbox_log_set_permissions(list->changelog, perm.file_create_mode,
1661 perm.file_create_gid,
1662 perm.file_create_gid_origin);
1663 return TRUE;
1664 }
1665
mailbox_list_mkdir_missing_index_root(struct mailbox_list * list)1666 int mailbox_list_mkdir_missing_index_root(struct mailbox_list *list)
1667 {
1668 const char *index_dir;
1669
1670 if (list->index_root_dir_created)
1671 return 1;
1672
1673 /* If index root dir hasn't been created yet, do it now.
1674 Do this here even if the index directory is the same as mail root
1675 directory, because it may not have been created elsewhere either. */
1676 if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX,
1677 &index_dir))
1678 return 0;
1679
1680 if (mailbox_list_mkdir_root(list, index_dir,
1681 MAILBOX_LIST_PATH_TYPE_INDEX) < 0)
1682 return -1;
1683 list->index_root_dir_created = TRUE;
1684 return 1;
1685 }
1686
mailbox_list_mkdir_missing_list_index_root(struct mailbox_list * list)1687 int mailbox_list_mkdir_missing_list_index_root(struct mailbox_list *list)
1688 {
1689 const char *index_dir;
1690
1691 if (list->set.list_index_dir == NULL)
1692 return mailbox_list_mkdir_missing_index_root(list);
1693
1694 /* LISTINDEX points outside the index root directory */
1695 if (list->list_index_root_dir_created)
1696 return 1;
1697
1698 if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_LIST_INDEX,
1699 &index_dir))
1700 return 0;
1701 if (mailbox_list_mkdir_root(list, index_dir,
1702 MAILBOX_LIST_PATH_TYPE_LIST_INDEX) < 0)
1703 return -1;
1704 list->list_index_root_dir_created = TRUE;
1705 return 1;
1706 }
1707
mailbox_list_add_change(struct mailbox_list * list,enum mailbox_log_record_type type,const guid_128_t mailbox_guid)1708 void mailbox_list_add_change(struct mailbox_list *list,
1709 enum mailbox_log_record_type type,
1710 const guid_128_t mailbox_guid)
1711 {
1712 struct mailbox_log_record rec;
1713 time_t stamp;
1714
1715 if (!mailbox_list_init_changelog(list) ||
1716 guid_128_is_empty(mailbox_guid))
1717 return;
1718
1719 if (mailbox_list_mkdir_missing_index_root(list) <= 0)
1720 return;
1721
1722 stamp = list->changelog_timestamp != (time_t)-1 ?
1723 list->changelog_timestamp : ioloop_time;
1724
1725 i_zero(&rec);
1726 rec.type = type;
1727 memcpy(rec.mailbox_guid, mailbox_guid, sizeof(rec.mailbox_guid));
1728 mailbox_log_record_set_timestamp(&rec, stamp);
1729 (void)mailbox_log_append(list->changelog, &rec);
1730 }
1731
mailbox_list_set_subscribed(struct mailbox_list * list,const char * name,bool set)1732 int mailbox_list_set_subscribed(struct mailbox_list *list,
1733 const char *name, bool set)
1734 {
1735 int ret;
1736
1737 /* make sure we'll refresh the file on next list */
1738 list->subscriptions_mtime = (time_t)-1;
1739
1740 if ((ret = list->v.set_subscribed(list, name, set)) <= 0)
1741 return ret;
1742 return 0;
1743 }
1744
mailbox_list_delete_dir(struct mailbox_list * list,const char * name)1745 int mailbox_list_delete_dir(struct mailbox_list *list, const char *name)
1746 {
1747 const char *error;
1748
1749 if (!mailbox_list_is_valid_name(list, name, &error) || *name == '\0') {
1750 mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
1751 "Invalid mailbox name");
1752 return -1;
1753 }
1754 return list->v.delete_dir(list, name);
1755 }
1756
mailbox_list_delete_symlink(struct mailbox_list * list,const char * name)1757 int mailbox_list_delete_symlink(struct mailbox_list *list, const char *name)
1758 {
1759 const char *error;
1760
1761 if (!mailbox_list_is_valid_name(list, name, &error) || *name == '\0') {
1762 mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
1763 "Invalid mailbox name");
1764 return -1;
1765 }
1766 return list->v.delete_symlink(list, name);
1767 }
1768
mailbox_name_get_sha128(const char * name,guid_128_t guid_128_r)1769 void mailbox_name_get_sha128(const char *name, guid_128_t guid_128_r)
1770 {
1771 unsigned char sha[SHA1_RESULTLEN];
1772
1773 sha1_get_digest(name, strlen(name), sha);
1774 memcpy(guid_128_r, sha, I_MIN(GUID_128_SIZE, sizeof(sha)));
1775 }
1776
mailbox_list_get_changelog(struct mailbox_list * list)1777 struct mailbox_log *mailbox_list_get_changelog(struct mailbox_list *list)
1778 {
1779 return !mailbox_list_init_changelog(list) ? NULL : list->changelog;
1780 }
1781
mailbox_list_set_changelog_timestamp(struct mailbox_list * list,time_t stamp)1782 void mailbox_list_set_changelog_timestamp(struct mailbox_list *list,
1783 time_t stamp)
1784 {
1785 list->changelog_timestamp = stamp;
1786 }
1787
1788 enum mailbox_list_file_type
mailbox_list_get_file_type(const struct dirent * d ATTR_UNUSED)1789 mailbox_list_get_file_type(const struct dirent *d ATTR_UNUSED)
1790 {
1791 enum mailbox_list_file_type type;
1792
1793 #ifdef HAVE_DIRENT_D_TYPE
1794 switch (d->d_type) {
1795 case DT_UNKNOWN:
1796 type = MAILBOX_LIST_FILE_TYPE_UNKNOWN;
1797 break;
1798 case DT_REG:
1799 type = MAILBOX_LIST_FILE_TYPE_FILE;
1800 break;
1801 case DT_DIR:
1802 type = MAILBOX_LIST_FILE_TYPE_DIR;
1803 break;
1804 case DT_LNK:
1805 type = MAILBOX_LIST_FILE_TYPE_SYMLINK;
1806 break;
1807 default:
1808 type = MAILBOX_LIST_FILE_TYPE_OTHER;
1809 break;
1810 }
1811 #else
1812 type = MAILBOX_LIST_FILE_TYPE_UNKNOWN;
1813 #endif
1814 return type;
1815 }
1816
mailbox_list_dirent_is_alias_symlink(struct mailbox_list * list,const char * dir_path,const struct dirent * d)1817 int mailbox_list_dirent_is_alias_symlink(struct mailbox_list *list,
1818 const char *dir_path,
1819 const struct dirent *d)
1820 {
1821 struct stat st;
1822 int ret;
1823
1824 if (mailbox_list_get_file_type(d) == MAILBOX_LIST_FILE_TYPE_SYMLINK)
1825 return 1;
1826
1827 T_BEGIN {
1828 const char *path, *linkpath, *error;
1829
1830 path = t_strconcat(dir_path, "/", d->d_name, NULL);
1831 if (lstat(path, &st) < 0) {
1832 mailbox_list_set_critical(list,
1833 "lstat(%s) failed: %m", path);
1834 ret = -1;
1835 } else if (!S_ISLNK(st.st_mode)) {
1836 ret = 0;
1837 } else if (t_readlink(path, &linkpath, &error) < 0) {
1838 e_error(list->ns->user->event,
1839 "t_readlink(%s) failed: %s", path, error);
1840 ret = -1;
1841 } else {
1842 /* it's an alias only if it points to the same
1843 directory */
1844 ret = strchr(linkpath, '/') == NULL ? 1 : 0;
1845 }
1846 } T_END;
1847 return ret;
1848 }
1849
1850
1851 static bool
mailbox_list_try_get_home_path(struct mailbox_list * list,const char ** name)1852 mailbox_list_try_get_home_path(struct mailbox_list *list, const char **name)
1853 {
1854 if ((*name)[1] == '/') {
1855 /* ~/dir - use the configured home directory */
1856 if (mail_user_try_home_expand(list->ns->user, name) < 0)
1857 return FALSE;
1858 } else {
1859 /* ~otheruser/dir - assume we're using system users */
1860 if (home_try_expand(name) < 0)
1861 return FALSE;
1862 }
1863 return TRUE;
1864 }
1865
mailbox_list_try_get_absolute_path(struct mailbox_list * list,const char ** name)1866 bool mailbox_list_try_get_absolute_path(struct mailbox_list *list,
1867 const char **name)
1868 {
1869 const char *root_dir, *path, *mailbox_name;
1870 size_t len;
1871
1872 if (!list->mail_set->mail_full_filesystem_access)
1873 return FALSE;
1874
1875 if (**name == '~') {
1876 /* try to expand home directory */
1877 if (!mailbox_list_try_get_home_path(list, name)) {
1878 /* fallback to using actual "~name" mailbox */
1879 return FALSE;
1880 }
1881 } else {
1882 if (**name != '/')
1883 return FALSE;
1884 }
1885
1886 /* okay, we have an absolute path now. but check first if it points to
1887 same directory as one of our regular mailboxes. */
1888 root_dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_MAILBOX);
1889 len = strlen(root_dir);
1890 if (str_begins(*name, root_dir) && (*name)[len] == '/') {
1891 mailbox_name = *name + len + 1;
1892 if (mailbox_list_get_path(list, mailbox_name,
1893 MAILBOX_LIST_PATH_TYPE_MAILBOX,
1894 &path) <= 0)
1895 return FALSE;
1896 if (strcmp(path, *name) == 0) {
1897 /* yeah, we can replace the full path with mailbox
1898 name. this way we can use indexes. */
1899 *name = mailbox_name;
1900 return FALSE;
1901 }
1902 }
1903 return TRUE;
1904 }
1905
mailbox_list_get_last_error(struct mailbox_list * list,enum mail_error * error_r)1906 const char *mailbox_list_get_last_error(struct mailbox_list *list,
1907 enum mail_error *error_r)
1908 {
1909 if (list->error == MAIL_ERROR_NONE) {
1910 if (error_r != NULL)
1911 *error_r = MAIL_ERROR_TEMP;
1912 return list->error_string != NULL ? list->error_string :
1913 "BUG: Unknown internal list error";
1914 }
1915
1916 if (list->error_string == NULL) {
1917 /* This shouldn't happen.. */
1918 list->error_string =
1919 i_strdup_printf("BUG: Unknown 0x%x list error",
1920 list->error);
1921 }
1922
1923 if (error_r != NULL)
1924 *error_r = list->error;
1925 return list->error_string;
1926 }
1927
mailbox_list_get_last_mail_error(struct mailbox_list * list)1928 enum mail_error mailbox_list_get_last_mail_error(struct mailbox_list *list)
1929 {
1930 return list->error;
1931 }
1932
mailbox_list_get_last_internal_error(struct mailbox_list * list,enum mail_error * error_r)1933 const char *mailbox_list_get_last_internal_error(struct mailbox_list *list,
1934 enum mail_error *error_r)
1935 {
1936 if (error_r != NULL)
1937 *error_r = list->error;
1938 if (list->last_error_is_internal) {
1939 i_assert(list->last_internal_error != NULL);
1940 return list->last_internal_error;
1941 }
1942 return mailbox_list_get_last_error(list, error_r);
1943 }
1944
mailbox_list_clear_error(struct mailbox_list * list)1945 void mailbox_list_clear_error(struct mailbox_list *list)
1946 {
1947 i_free_and_null(list->error_string);
1948
1949 i_free(list->last_internal_error);
1950 list->last_error_is_internal = FALSE;
1951 list->error = MAIL_ERROR_NONE;
1952 }
1953
mailbox_list_set_error(struct mailbox_list * list,enum mail_error error,const char * string)1954 void mailbox_list_set_error(struct mailbox_list *list,
1955 enum mail_error error, const char *string)
1956 {
1957 if (list->error_string != string) {
1958 i_free(list->error_string);
1959 list->error_string = i_strdup(string);
1960 }
1961
1962 list->last_error_is_internal = FALSE;
1963 list->error = error;
1964 }
1965
mailbox_list_set_internal_error(struct mailbox_list * list)1966 void mailbox_list_set_internal_error(struct mailbox_list *list)
1967 {
1968 const char *str;
1969
1970 str = t_strflocaltime(MAIL_ERRSTR_CRITICAL_MSG_STAMP, ioloop_time);
1971 i_free(list->error_string);
1972 list->error_string = i_strdup(str);
1973 list->error = MAIL_ERROR_TEMP;
1974
1975 /* this function doesn't set last_internal_error, so
1976 last_error_is_internal can't be TRUE. */
1977 list->last_error_is_internal = FALSE;
1978 i_free(list->last_internal_error);
1979 }
1980
mailbox_list_set_critical(struct mailbox_list * list,const char * fmt,...)1981 void mailbox_list_set_critical(struct mailbox_list *list, const char *fmt, ...)
1982 {
1983 char *old_error = list->error_string;
1984 char *old_internal_error = list->last_internal_error;
1985 va_list va;
1986
1987 list->error_string = NULL;
1988 list->last_internal_error = NULL;
1989 /* critical errors may contain sensitive data, so let user
1990 see only "Internal error" with a timestamp to make it
1991 easier to look from log files the actual error message. */
1992 mailbox_list_set_internal_error(list);
1993
1994 va_start(va, fmt);
1995 list->last_internal_error = i_strdup_vprintf(fmt, va);
1996 va_end(va);
1997 list->last_error_is_internal = TRUE;
1998 e_error(list->ns->user->event, "%s", list->last_internal_error);
1999
2000 /* free the old_error and old_internal_error only after the new error
2001 is generated, because they may be one of the parameters. */
2002 i_free(old_error);
2003 i_free(old_internal_error);
2004 }
2005
mailbox_list_set_error_from_errno(struct mailbox_list * list)2006 bool mailbox_list_set_error_from_errno(struct mailbox_list *list)
2007 {
2008 const char *error_string;
2009 enum mail_error error;
2010
2011 if (!mail_error_from_errno(&error, &error_string))
2012 return FALSE;
2013
2014 mailbox_list_set_error(list, error, error_string);
2015 return TRUE;
2016 }
2017
mailbox_list_last_error_push(struct mailbox_list * list)2018 void mailbox_list_last_error_push(struct mailbox_list *list)
2019 {
2020 struct mail_storage_error *err;
2021
2022 if (!array_is_created(&list->error_stack))
2023 i_array_init(&list->error_stack, 2);
2024 err = array_append_space(&list->error_stack);
2025 err->error_string = i_strdup(list->error_string);
2026 err->error = list->error;
2027 err->last_error_is_internal = list->last_error_is_internal;
2028 if (err->last_error_is_internal)
2029 err->last_internal_error = i_strdup(list->last_internal_error);
2030 }
2031
mailbox_list_last_error_pop(struct mailbox_list * list)2032 void mailbox_list_last_error_pop(struct mailbox_list *list)
2033 {
2034 unsigned int count = array_count(&list->error_stack);
2035 const struct mail_storage_error *err =
2036 array_idx(&list->error_stack, count-1);
2037
2038 i_free(list->error_string);
2039 i_free(list->last_internal_error);
2040 list->error_string = err->error_string;
2041 list->error = err->error;
2042 list->last_error_is_internal = err->last_error_is_internal;
2043 list->last_internal_error = err->last_internal_error;
2044 array_delete(&list->error_stack, count-1, 1);
2045 }
2046
mailbox_list_init_fs(struct mailbox_list * list,struct event * event_parent,const char * driver,const char * args,const char * root_dir,struct fs ** fs_r,const char ** error_r)2047 int mailbox_list_init_fs(struct mailbox_list *list, struct event *event_parent,
2048 const char *driver,
2049 const char *args, const char *root_dir,
2050 struct fs **fs_r, const char **error_r)
2051 {
2052 struct fs_settings fs_set;
2053 struct ssl_iostream_settings ssl_set;
2054 struct mailbox_list_fs_context *ctx;
2055 struct fs *parent_fs;
2056
2057 i_zero(&fs_set);
2058 mail_user_init_fs_settings(list->ns->user, &fs_set, &ssl_set);
2059 /* fs_set.event_parent points to user->event by default */
2060 if (event_parent != NULL)
2061 fs_set.event_parent = event_parent;
2062 fs_set.root_path = root_dir;
2063 fs_set.temp_file_prefix = mailbox_list_get_global_temp_prefix(list);
2064
2065 if (fs_init(driver, args, &fs_set, fs_r, error_r) < 0)
2066 return -1;
2067
2068 /* add mailbox_list context to the parent fs, which allows
2069 mailbox_list_fs_get_list() to work */
2070 for (parent_fs = *fs_r; parent_fs->parent != NULL;
2071 parent_fs = parent_fs->parent) ;
2072
2073 ctx = p_new(list->pool, struct mailbox_list_fs_context, 1);
2074 ctx->list = list;
2075 MODULE_CONTEXT_SET(parent_fs, mailbox_list_fs_module, ctx);
2076
2077 /* a bit kludgy notification to the fs that we're now finished setting
2078 up the module context. */
2079 (void)fs_get_properties(*fs_r);
2080 return 0;
2081 }
2082
mailbox_list_fs_get_list(struct fs * fs)2083 struct mailbox_list *mailbox_list_fs_get_list(struct fs *fs)
2084 {
2085 struct mailbox_list_fs_context *ctx;
2086
2087 while (fs->parent != NULL)
2088 fs = fs->parent;
2089
2090 ctx = MAILBOX_LIST_FS_CONTEXT(fs);
2091 return ctx == NULL ? NULL : ctx->list;
2092 }
2093
mailbox_list_lock(struct mailbox_list * list)2094 int mailbox_list_lock(struct mailbox_list *list)
2095 {
2096 struct mailbox_permissions perm;
2097 struct file_create_settings set;
2098 const char *lock_dir, *lock_fname, *lock_path, *error;
2099
2100 if (list->lock_refcount > 0) {
2101 list->lock_refcount++;
2102 return 0;
2103 }
2104
2105 mailbox_list_get_root_permissions(list, &perm);
2106 i_zero(&set);
2107 set.lock_timeout_secs = list->mail_set->mail_max_lock_timeout == 0 ?
2108 MAILBOX_LIST_LOCK_SECS :
2109 I_MIN(MAILBOX_LIST_LOCK_SECS, list->mail_set->mail_max_lock_timeout);
2110 set.lock_settings.lock_method = list->mail_set->parsed_lock_method;
2111 set.mode = perm.file_create_mode;
2112 set.gid = perm.file_create_gid;
2113 set.gid_origin = perm.file_create_gid_origin;
2114
2115 lock_fname = MAILBOX_LIST_LOCK_FNAME;
2116 if (list->set.volatile_dir != NULL) {
2117 /* Use VOLATILEDIR. It's shared with all mailbox_lists, so use
2118 hash of the namespace prefix as a way to make this lock name
2119 unique across the namespaces. */
2120 unsigned char ns_prefix_hash[SHA1_RESULTLEN];
2121 sha1_get_digest(list->ns->prefix, list->ns->prefix_len,
2122 ns_prefix_hash);
2123 lock_fname = t_strconcat(MAILBOX_LIST_LOCK_FNAME,
2124 binary_to_hex(ns_prefix_hash, sizeof(ns_prefix_hash)), NULL);
2125 lock_dir = list->set.volatile_dir;
2126 set.mkdir_mode = 0700;
2127 } else if (mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX,
2128 &lock_dir)) {
2129 /* use index root directory */
2130 if (mailbox_list_mkdir_missing_index_root(list) < 0)
2131 return -1;
2132 } else if (mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_DIR,
2133 &lock_dir)) {
2134 /* use mailbox root directory */
2135 if (mailbox_list_mkdir_root(list, lock_dir,
2136 MAILBOX_LIST_PATH_TYPE_DIR) < 0)
2137 return -1;
2138 } else {
2139 /* No filesystem used by mailbox list (e.g. imapc).
2140 Just assume it's locked */
2141 list->lock_refcount = 1;
2142 return 0;
2143 }
2144 lock_path = t_strdup_printf("%s/%s", lock_dir, lock_fname);
2145 if (mail_storage_lock_create(lock_path, &set, list->mail_set,
2146 &list->lock, &error) <= 0) {
2147 mailbox_list_set_critical(list,
2148 "Couldn't create mailbox list lock %s: %s",
2149 lock_path, error);
2150 return -1;
2151 }
2152
2153 list->lock_refcount = 1;
2154 return 0;
2155 }
2156
mailbox_list_unlock(struct mailbox_list * list)2157 void mailbox_list_unlock(struct mailbox_list *list)
2158 {
2159 i_assert(list->lock_refcount > 0);
2160 if (--list->lock_refcount > 0)
2161 return;
2162
2163 file_lock_free(&list->lock);
2164 }
2165