~ubuntu-branches/ubuntu/trusty/dovecot/trusty-updates

« back to all changes in this revision

Viewing changes to src/lib-storage/mailbox-list.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-08 09:35:49 UTC
  • mfrom: (1.15.3) (96.1.1 trusty-proposed)
  • Revision ID: package-import@ubuntu.com-20140108093549-814nkqdcxfbvgktg
Tags: 1:2.2.9-1ubuntu1
* Merge from Debian unstable, remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
  + Use the autotools-dev dh addon to update config.guess/config.sub for
    arm64.
* Dropped changes, included in Debian:
  - Update Dovecot name to reflect distribution in login greeting.
  - Update Drac plugin for >= 2.0.0 support.
* d/control: Drop dovecot-postfix package as its no longer required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (c) 2006-2012 Dovecot authors, see the included COPYING file */
 
1
/* Copyright (c) 2006-2013 Dovecot authors, see the included COPYING file */
2
2
 
3
3
#include "lib.h"
4
4
#include "array.h"
 
5
#include "abspath.h"
5
6
#include "ioloop.h"
6
7
#include "mkdir-parents.h"
7
8
#include "str.h"
11
12
#include "time-util.h"
12
13
#include "unichar.h"
13
14
#include "settings-parser.h"
 
15
#include "iostream-ssl.h"
 
16
#include "fs-api.h"
14
17
#include "imap-utf7.h"
15
18
#include "mailbox-log.h"
16
19
#include "mailbox-tree.h"
17
 
#include "mail-storage.h"
 
20
#include "mail-storage-private.h"
18
21
#include "mail-storage-hooks.h"
19
22
#include "mailbox-list-private.h"
20
23
 
34
37
 
35
38
struct mailbox_list_module_register mailbox_list_module_register = { 0 };
36
39
 
37
 
static ARRAY_DEFINE(mailbox_list_drivers, const struct mailbox_list *);
 
40
static ARRAY(const struct mailbox_list *) mailbox_list_drivers;
38
41
 
39
42
void mailbox_lists_init(void)
40
43
{
138
141
        list->ns = ns;
139
142
        list->mail_set = ns->mail_set;
140
143
        list->flags = flags;
141
 
        list->file_create_mode = (mode_t)-1;
142
 
        list->dir_create_mode = (mode_t)-1;
143
 
        list->file_create_gid = (gid_t)-1;
 
144
        list->root_permissions.file_create_mode = (mode_t)-1;
 
145
        list->root_permissions.dir_create_mode = (mode_t)-1;
 
146
        list->root_permissions.file_create_gid = (gid_t)-1;
144
147
        list->changelog_timestamp = (time_t)-1;
145
148
 
146
149
        /* copy settings */
149
152
                list->set.index_dir = set->index_dir == NULL ||
150
153
                        strcmp(set->index_dir, set->root_dir) == 0 ? NULL :
151
154
                        p_strdup(list->pool, set->index_dir);
 
155
                list->set.index_pvt_dir = set->index_pvt_dir == NULL ||
 
156
                        strcmp(set->index_pvt_dir, set->root_dir) == 0 ? NULL :
 
157
                        p_strdup(list->pool, set->index_pvt_dir);
152
158
                list->set.control_dir = set->control_dir == NULL ||
153
159
                        strcmp(set->control_dir, set->root_dir) == 0 ? NULL :
154
160
                        p_strdup(list->pool, set->control_dir);
162
168
        list->set.mailbox_dir_name =
163
169
                p_strdup(list->pool, set->mailbox_dir_name);
164
170
        list->set.alt_dir = p_strdup(list->pool, set->alt_dir);
 
171
        list->set.alt_dir_nocheck = set->alt_dir_nocheck;
 
172
        list->set.index_control_use_maildir_name =
 
173
                set->index_control_use_maildir_name;
165
174
 
166
175
        if (*set->mailbox_dir_name == '\0')
167
176
                list->set.mailbox_dir_name = "";
174
183
        }
175
184
        list->set.utf8 = set->utf8;
176
185
 
 
186
        if (list->v.init != NULL) {
 
187
                if (list->v.init(list, error_r) < 0) {
 
188
                        list->v.deinit(list);
 
189
                        return -1;
 
190
                }
 
191
        }
 
192
 
177
193
        if (ns->mail_set->mail_debug) {
178
 
                i_debug("%s: root=%s, index=%s, control=%s, inbox=%s, alt=%s",
 
194
                i_debug("%s: root=%s, index=%s, indexpvt=%s, control=%s, inbox=%s, alt=%s",
179
195
                        list->name,
180
196
                        list->set.root_dir == NULL ? "" : list->set.root_dir,
181
197
                        list->set.index_dir == NULL ? "" : list->set.index_dir,
 
198
                        list->set.index_pvt_dir == NULL ? "" : list->set.index_pvt_dir,
182
199
                        list->set.control_dir == NULL ?
183
200
                        "" : list->set.control_dir,
184
201
                        list->set.inbox_path == NULL ?
194
211
        return 0;
195
212
}
196
213
 
197
 
static int fix_path(struct mail_user *user, const char *path,
 
214
static int fix_path(struct mail_user *user, const char *path, bool expand_home,
198
215
                    const char **path_r, const char **error_r)
199
216
{
200
217
        size_t len = strlen(path);
201
218
 
202
219
        if (len > 1 && path[len-1] == '/')
203
220
                path = t_strndup(path, len-1);
204
 
        if (path[0] == '~' && path[1] != '/' && path[1] != '\0') {
 
221
        if (!expand_home) {
 
222
                /* no ~ expansion */
 
223
        } else if (path[0] == '~' && path[1] != '/' && path[1] != '\0') {
205
224
                /* ~otheruser/dir */
206
225
                if (home_try_expand(&path) < 0) {
207
226
                        *error_r = t_strconcat(
240
259
        return str;
241
260
}
242
261
 
243
 
int mailbox_list_settings_parse(struct mail_user *user, const char *data,
244
 
                                struct mailbox_list_settings *set_r,
245
 
                                const char **error_r)
 
262
static int
 
263
mailbox_list_settings_parse_full(struct mail_user *user, const char *data,
 
264
                                 bool expand_home,
 
265
                                 struct mailbox_list_settings *set_r,
 
266
                                 const char **error_r)
246
267
{
247
268
        const char *const *tmp, *key, *value, **dest, *str, *error;
248
269
 
258
279
        /* <root dir> */
259
280
        tmp = t_strsplit(data, ":");
260
281
        str = split_next_arg(&tmp);
261
 
        if (fix_path(user, str, &set_r->root_dir, &error) < 0) {
 
282
        if (fix_path(user, str, expand_home, &set_r->root_dir, &error) < 0) {
262
283
                *error_r = t_strconcat(error, "mail root dir in: ", data, NULL);
263
284
                return -1;
264
285
        }
289
310
                        dest = &set_r->inbox_path;
290
311
                else if (strcmp(key, "INDEX") == 0)
291
312
                        dest = &set_r->index_dir;
 
313
                else if (strcmp(key, "INDEXPVT") == 0)
 
314
                        dest = &set_r->index_pvt_dir;
292
315
                else if (strcmp(key, "CONTROL") == 0)
293
316
                        dest = &set_r->control_dir;
294
317
                else if (strcmp(key, "ALT") == 0)
295
318
                        dest = &set_r->alt_dir;
296
 
                else if (strcmp(key, "LAYOUT") == 0)
 
319
                else if (strcmp(key, "ALTNOCHECK") == 0) {
 
320
                        set_r->alt_dir_nocheck = TRUE;
 
321
                        continue;
 
322
                } else if (strcmp(key, "LAYOUT") == 0)
297
323
                        dest = &set_r->layout;
298
324
                else if (strcmp(key, "SUBSCRIPTIONS") == 0)
299
325
                        dest = &set_r->subscription_fname;
301
327
                        dest = &set_r->maildir_name;
302
328
                else if (strcmp(key, "MAILBOXDIR") == 0)
303
329
                        dest = &set_r->mailbox_dir_name;
304
 
                else {
 
330
                else if (strcmp(key, "FULLDIRNAME") == 0) {
 
331
                        set_r->index_control_use_maildir_name = TRUE;
 
332
                        dest = &set_r->maildir_name;
 
333
                } else {
305
334
                        *error_r = t_strdup_printf("Unknown setting: %s", key);
306
335
                        return -1;
307
336
                }
308
 
                if (fix_path(user, value, dest, &error) < 0) {
 
337
                if (fix_path(user, value, expand_home, dest, &error) < 0) {
309
338
                        *error_r = t_strconcat(error, key, " in: ", data, NULL);
310
339
                        return -1;
311
340
                }
316
345
        return 0;
317
346
}
318
347
 
 
348
int mailbox_list_settings_parse(struct mail_user *user, const char *data,
 
349
                                struct mailbox_list_settings *set_r,
 
350
                                const char **error_r)
 
351
{
 
352
        return mailbox_list_settings_parse_full(user, data, TRUE,
 
353
                                                set_r, error_r);
 
354
}
 
355
 
319
356
const char *mailbox_list_get_unexpanded_path(struct mailbox_list *list,
320
357
                                             enum mailbox_list_path_type type)
321
358
{
323
360
        const char *location = list->ns->unexpanded_set->location;
324
361
        struct mail_user *user = list->ns->user;
325
362
        struct mailbox_list_settings set;
326
 
        const char *p, *error;
 
363
        const char *p, *path, *error;
327
364
 
328
365
        if (*location == SETTING_STRVAR_EXPANDED[0]) {
329
366
                /* set using -o or userdb lookup. */
349
386
        if (p == NULL)
350
387
                return "";
351
388
 
352
 
        if (mailbox_list_settings_parse(user, p + 1, &set, &error) < 0)
353
 
                return "";
354
 
        return mailbox_list_get_root_path(&set, type);
 
389
        if (mailbox_list_settings_parse_full(user, p + 1, FALSE,
 
390
                                             &set, &error) < 0)
 
391
                return "";
 
392
        if (mailbox_list_set_get_root_path(&set, type, &path) <= 0)
 
393
                return "";
 
394
        return path;
 
395
}
 
396
 
 
397
static bool need_escape_dirstart(const char *vname, const char *maildir_name)
 
398
{
 
399
        unsigned int len;
 
400
 
 
401
        if (vname[0] == '.') {
 
402
                if (vname[1] == '\0' || vname[1] == '/')
 
403
                        return TRUE; /* "." */
 
404
                if (vname[1] == '.' && (vname[2] == '\0' || vname[2] == '/'))
 
405
                        return TRUE; /* ".." */
 
406
        }
 
407
        if (*maildir_name != '\0') {
 
408
                len = strlen(maildir_name);
 
409
                if (strncmp(maildir_name, vname, len) == 0 &&
 
410
                    (vname[len] == '\0' || vname[len] == '/'))
 
411
                        return TRUE; /* e.g. dbox-Mails */
 
412
        }
 
413
        return FALSE;
355
414
}
356
415
 
357
416
static const char *
360
419
        char ns_sep = mail_namespace_get_sep(list->ns);
361
420
        char list_sep = mailbox_list_get_hierarchy_sep(list);
362
421
        string_t *escaped_name = t_str_new(64);
 
422
        char dirstart = TRUE;
363
423
 
364
424
        /* no escaping of namespace prefix */
365
425
        if (strncmp(list->ns->prefix, vname, list->ns->prefix_len) == 0) {
372
432
                str_printfa(escaped_name, "%c%02x",
373
433
                            list->set.escape_char, *vname);
374
434
                vname++;
 
435
                dirstart = FALSE;
375
436
        }
376
437
        for (; *vname != '\0'; vname++) {
377
438
                if (*vname == ns_sep)
378
 
                        str_append_c(escaped_name, *vname);
 
439
                        str_append_c(escaped_name, list_sep);
379
440
                else if (*vname == list_sep ||
380
441
                         *vname == list->set.escape_char ||
381
 
                         *vname == '/') {
 
442
                         *vname == '/' ||
 
443
                         (dirstart &&
 
444
                          need_escape_dirstart(vname, list->set.maildir_name))) {
382
445
                        str_printfa(escaped_name, "%c%02x",
383
446
                                    list->set.escape_char, *vname);
384
447
                } else {
385
448
                        str_append_c(escaped_name, *vname);
386
449
                }
 
450
                dirstart = *vname == '/';
387
451
        }
388
452
        return str_c(escaped_name);
389
453
}
390
454
 
 
455
static int
 
456
mailbox_list_unescape_broken_chars(struct mailbox_list *list, char *name)
 
457
{
 
458
        char *src, *dest;
 
459
        unsigned char chr;
 
460
 
 
461
        if ((src = strchr(name, list->set.broken_char)) == NULL)
 
462
                return 0;
 
463
        dest = src;
 
464
 
 
465
        while (*src != '\0') {
 
466
                if (*src == list->set.broken_char) {
 
467
                        if (src[1] >= '0' && src[1] <= '9')
 
468
                                chr = (src[1]-'0') * 0x10;
 
469
                        else if (src[1] >= 'a' && src[1] <= 'f')
 
470
                                chr = (src[1]-'a' + 10) * 0x10;
 
471
                        else
 
472
                                return -1;
 
473
 
 
474
                        if (src[2] >= '0' && src[2] <= '9')
 
475
                                chr += src[2]-'0';
 
476
                        else if (src[2] >= 'a' && src[2] <= 'f')
 
477
                                chr += src[2]-'a' + 10;
 
478
                        else
 
479
                                return -1;
 
480
                        *dest++ = chr;
 
481
                        src += 3;
 
482
                } else {
 
483
                        *dest++ = *src++;
 
484
                }
 
485
        }
 
486
        *dest++ = '\0';
 
487
        return 0;
 
488
}
 
489
 
 
490
static char *mailbox_list_convert_sep(const char *storage_name, char src, char dest)
 
491
{
 
492
        char *ret, *p;
 
493
 
 
494
        ret = p_strdup(unsafe_data_stack_pool, storage_name);
 
495
        for (p = ret; *p != '\0'; p++) {
 
496
                if (*p == src)
 
497
                        *p = dest;
 
498
        }
 
499
        return ret;
 
500
}
 
501
 
391
502
const char *mailbox_list_default_get_storage_name(struct mailbox_list *list,
392
503
                                                  const char *vname)
393
504
{
395
506
        unsigned int prefix_len = strlen(ns->prefix);
396
507
        const char *storage_name = vname;
397
508
        string_t *str;
398
 
        char list_sep, ns_sep, *ret, *p;
 
509
        char list_sep, ns_sep, *ret;
399
510
 
400
 
        if (strcasecmp(storage_name, "INBOX") == 0)
 
511
        if (strcasecmp(storage_name, "INBOX") == 0 &&
 
512
            (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0)
401
513
                storage_name = "INBOX";
402
514
        else if (list->set.escape_char != '\0')
403
515
                storage_name = mailbox_list_escape_name(list, vname);
404
516
 
405
 
        if (prefix_len > 0 && strcmp(storage_name, "INBOX") != 0) {
 
517
        if (prefix_len > 0 && (strcmp(storage_name, "INBOX") != 0 ||
 
518
                               (ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0)) {
406
519
                /* skip namespace prefix, except if this is INBOX */
407
520
                if (strncmp(ns->prefix, storage_name, prefix_len) == 0)
408
521
                        storage_name += prefix_len;
409
522
                else if (strncmp(ns->prefix, storage_name, prefix_len-1) == 0 &&
 
523
                         strlen(storage_name) == prefix_len-1 &&
410
524
                         ns->prefix[prefix_len-1] == mail_namespace_get_sep(ns)) {
411
525
                        /* trying to access the namespace prefix itself */
412
526
                        storage_name = "";
427
541
        list_sep = mailbox_list_get_hierarchy_sep(list);
428
542
        ns_sep = mail_namespace_get_sep(ns);
429
543
 
430
 
        if (*storage_name == '\0' && ns->type == NAMESPACE_SHARED &&
 
544
        if (*storage_name == '\0' && ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
431
545
            (ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
432
546
            !list->mail_set->mail_shared_explicit_inbox) {
433
547
                /* opening shared/$user. it's the same as INBOX. */
434
548
                storage_name = "INBOX";
435
549
        }
436
550
 
437
 
        if (list_sep == ns_sep)
438
 
                return storage_name;
439
 
        if (ns->type == NAMESPACE_SHARED &&
440
 
            (ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) {
441
 
                /* shared namespace root. the backend storage's hierarchy
442
 
                   separator isn't known yet, so do nothing. */
443
 
                return storage_name;
 
551
        if (list_sep != ns_sep && list->set.escape_char == '\0') {
 
552
                if (ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
 
553
                    (ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) {
 
554
                        /* shared namespace root. the backend storage's
 
555
                           hierarchy separator isn't known yet, so do
 
556
                           nothing. */
 
557
                        return storage_name;
 
558
                }
 
559
 
 
560
                ret = mailbox_list_convert_sep(storage_name, ns_sep, list_sep);
 
561
        } else if (list->set.broken_char == '\0' ||
 
562
                   strchr(storage_name, list->set.broken_char) == NULL) {
 
563
                /* no need to convert broken chars */
 
564
                return storage_name;
 
565
        } else {
 
566
                ret = p_strdup(unsafe_data_stack_pool, storage_name);
444
567
        }
445
568
 
446
 
        ret = p_strdup(unsafe_data_stack_pool, storage_name);
447
 
        for (p = ret; *p != '\0'; p++) {
448
 
                if (*p == ns_sep)
449
 
                        *p = list_sep;
 
569
        if (list->set.broken_char != '\0') {
 
570
                if (mailbox_list_unescape_broken_chars(list, ret) < 0) {
 
571
                        ret = mailbox_list_convert_sep(storage_name,
 
572
                                                       ns_sep, list_sep);
 
573
                }
450
574
        }
451
575
        return ret;
452
576
}
493
617
        return str_c(dest);
494
618
}
495
619
 
 
620
static void
 
621
mailbox_list_escape_broken_chars(struct mailbox_list *list, string_t *str)
 
622
{
 
623
        unsigned int i;
 
624
        char buf[3];
 
625
 
 
626
        if (strchr(str_c(str), list->set.broken_char) == NULL)
 
627
                return;
 
628
 
 
629
        for (i = 0; i < str_len(str); i++) {
 
630
                if (str_c(str)[i] == list->set.broken_char) {
 
631
                        i_snprintf(buf, sizeof(buf), "%02x",
 
632
                                   list->set.broken_char);
 
633
                        str_insert(str, i+1, buf);
 
634
                        i += 2;
 
635
                }
 
636
        }
 
637
}
 
638
 
 
639
static void
 
640
mailbox_list_escape_broken_name(struct mailbox_list *list,
 
641
                                const char *vname, string_t *str)
 
642
{
 
643
        str_truncate(str, 0);
 
644
        for (; *vname != '\0'; vname++) {
 
645
                if (*vname == '&' || (unsigned char)*vname >= 0x80) {
 
646
                        str_printfa(str, "%c%02x", list->set.broken_char,
 
647
                                    (unsigned char)*vname);
 
648
                } else {
 
649
                        str_append_c(str, *vname);
 
650
                }
 
651
        }
 
652
}
 
653
 
496
654
const char *mailbox_list_default_get_vname(struct mailbox_list *list,
497
655
                                           const char *storage_name)
498
656
{
508
666
                   and <ns prefix>/inBox. */
509
667
                return vname;
510
668
        }
511
 
        if (strcmp(vname, "INBOX") == 0 && list->ns->type == NAMESPACE_SHARED &&
 
669
        if (strcmp(vname, "INBOX") == 0 &&
 
670
            list->ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
512
671
            (list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
513
672
            !list->mail_set->mail_shared_explicit_inbox) {
514
673
                /* convert to shared/$user, we don't really care about the
526
685
        } else if (!list->set.utf8) {
527
686
                /* mUTF-7 -> UTF-8 conversion */
528
687
                string_t *str = t_str_new(strlen(vname));
529
 
                if (imap_utf7_to_utf8(vname, str) == 0)
530
 
                        vname = str_c(str);
 
688
                if (imap_utf7_to_utf8(vname, str) == 0) {
 
689
                        if (list->set.broken_char != '\0')
 
690
                                mailbox_list_escape_broken_chars(list, str);
 
691
                        vname = str_c(str);
 
692
                } else if (list->set.broken_char != '\0') {
 
693
                        mailbox_list_escape_broken_name(list, vname, str);
 
694
                        vname = str_c(str);
 
695
                }
531
696
        }
532
697
 
533
698
        prefix_len = strlen(list->ns->prefix);
 
699
        if (list->set.escape_char != '\0') {
 
700
                vname = mailbox_list_unescape_name(list, vname);
 
701
                return prefix_len == 0 ? vname :
 
702
                        t_strconcat(list->ns->prefix, vname, NULL);
 
703
        }
 
704
 
534
705
        list_sep = mailbox_list_get_hierarchy_sep(list);
535
706
        ns_sep = mail_namespace_get_sep(list->ns);
536
707
 
546
717
                ret[i + prefix_len] = '\0';
547
718
                vname = ret;
548
719
        }
549
 
        if (list->set.escape_char != '\0')
550
 
                vname = mailbox_list_unescape_name(list, vname);
551
720
        return vname;
552
721
}
553
722
 
563
732
        *_list = NULL;
564
733
        i_free_and_null(list->error_string);
565
734
 
566
 
        if (list->guid_cache != NULL) {
 
735
        if (hash_table_is_created(list->guid_cache)) {
567
736
                hash_table_destroy(&list->guid_cache);
568
737
                pool_unref(&list->guid_cache_pool);
569
738
        }
580
749
        return list->name;
581
750
}
582
751
 
 
752
const struct mailbox_list_settings *
 
753
mailbox_list_get_settings(const struct mailbox_list *list)
 
754
{
 
755
        return &list->set;
 
756
}
 
757
 
583
758
enum mailbox_list_flags mailbox_list_get_flags(const struct mailbox_list *list)
584
759
{
585
760
        return list->flags;
606
781
        return list->ns->user;
607
782
}
608
783
 
 
784
static int
 
785
mailbox_list_get_storage_driver(struct mailbox_list *list, const char *driver,
 
786
                                struct mail_storage **storage_r)
 
787
{
 
788
        struct mail_storage *const *storagep;
 
789
        const char *error, *data;
 
790
 
 
791
        array_foreach(&list->ns->all_storages, storagep) {
 
792
                if (strcmp((*storagep)->name, driver) == 0) {
 
793
                        *storage_r = *storagep;
 
794
                        return 0;
 
795
                }
 
796
        }
 
797
 
 
798
        data = strchr(list->ns->set->location, ':');
 
799
        if (data == NULL)
 
800
                data = "";
 
801
        else
 
802
                data++;
 
803
        if (mail_storage_create_full(list->ns, driver, data, 0,
 
804
                                     storage_r, &error) < 0) {
 
805
                mailbox_list_set_critical(list,
 
806
                        "Namespace %s: Failed to create storage '%s': %s",
 
807
                        list->ns->prefix, driver, error);
 
808
                return -1;
 
809
        }
 
810
        return 0;
 
811
}
 
812
 
609
813
int mailbox_list_get_storage(struct mailbox_list **list, const char *vname,
610
814
                             struct mail_storage **storage_r)
611
815
{
 
816
        const struct mailbox_settings *set;
 
817
 
612
818
        if ((*list)->v.get_storage != NULL)
613
819
                return (*list)->v.get_storage(list, vname, storage_r);
614
 
        else {
615
 
                *storage_r = (*list)->ns->storage;
616
 
                return 0;
 
820
 
 
821
        set = mailbox_settings_find((*list)->ns->user, vname);
 
822
        if (set != NULL && set->driver != NULL && set->driver[0] != '\0') {
 
823
                return mailbox_list_get_storage_driver(*list, set->driver,
 
824
                                                       storage_r);
617
825
        }
 
826
        *storage_r = mail_namespace_get_default_storage((*list)->ns);
 
827
        return 0;
618
828
}
619
829
 
620
 
void mailbox_list_get_closest_storage(struct mailbox_list *list,
 
830
void mailbox_list_get_default_storage(struct mailbox_list *list,
621
831
                                      struct mail_storage **storage)
622
832
{
623
 
        *storage = list->ns->storage;
 
833
        *storage = mail_namespace_get_default_storage(list->ns);
624
834
}
625
835
 
626
836
char mailbox_list_get_hierarchy_sep(struct mailbox_list *list)
628
838
        return list->v.get_hierarchy_sep(list);
629
839
}
630
840
 
631
 
void mailbox_list_get_permissions(struct mailbox_list *list, const char *name,
632
 
                                  struct mailbox_permissions *permissions_r)
 
841
static void ATTR_NULL(2)
 
842
mailbox_list_get_permissions_internal(struct mailbox_list *list,
 
843
                                      const char *name,
 
844
                                      struct mailbox_permissions *permissions_r)
633
845
{
634
 
        const char *path, *parent_name, *p;
 
846
        const char *path, *parent_name, *parent_path, *p;
635
847
        struct stat st;
636
848
 
637
849
        memset(permissions_r, 0, sizeof(*permissions_r));
644
856
        permissions_r->file_create_gid = (gid_t)-1;
645
857
        permissions_r->file_create_gid_origin = "defaults";
646
858
 
647
 
        path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR);
648
 
        if (path == NULL) {
 
859
        if (name != NULL) {
 
860
                if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR,
 
861
                                          &path) < 0)
 
862
                        name = NULL;
 
863
        }
 
864
        if (name == NULL) {
 
865
                (void)mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_DIR,
 
866
                                                 &path);
 
867
        }
 
868
 
 
869
        if (path == NULL ||
 
870
            (list->flags & MAILBOX_LIST_FLAG_NO_MAIL_FILES) != 0) {
649
871
                /* no filesystem support in storage */
650
872
        } else if (stat(path, &st) < 0) {
651
 
                if (!ENOTFOUND(errno)) {
 
873
                if (errno == EACCES) {
 
874
                        mailbox_list_set_critical(list, "%s",
 
875
                                mail_error_eacces_msg("stat", path));
 
876
                } else if (!ENOTFOUND(errno)) {
652
877
                        mailbox_list_set_critical(list, "stat(%s) failed: %m",
653
878
                                                  path);
654
879
                } else if (list->mail_set->mail_debug) {
679
904
                permissions_r->file_create_mode = (st.st_mode & 0666) | 0600;
680
905
                permissions_r->dir_create_mode = (st.st_mode & 0777) | 0700;
681
906
                permissions_r->file_create_gid_origin = path;
 
907
                permissions_r->gid_origin_is_mailbox_path = name != NULL;
682
908
 
683
909
                if (!S_ISDIR(st.st_mode)) {
684
910
                        /* we're getting permissions from a file.
701
927
                } else {
702
928
                        permissions_r->file_create_gid = st.st_gid;
703
929
                }
 
930
                if (!S_ISDIR(st.st_mode) &&
 
931
                    permissions_r->file_create_gid != (gid_t)-1) {
 
932
                        /* we need to stat() the parent directory to see if
 
933
                           it has setgid-bit set */
 
934
                        p = strrchr(path, '/');
 
935
                        parent_path = p == NULL ? NULL :
 
936
                                t_strdup_until(path, p);
 
937
                        if (parent_path != NULL &&
 
938
                            stat(parent_path, &st) == 0 &&
 
939
                            (st.st_mode & S_ISGID) != 0) {
 
940
                                /* directory's GID is used automatically for
 
941
                                   new files */
 
942
                                permissions_r->file_create_gid = (gid_t)-1;
 
943
                        }
 
944
                }
704
945
        }
705
946
 
706
947
        if (name == NULL) {
707
 
                list->file_create_mode = permissions_r->file_create_mode;
708
 
                list->dir_create_mode = permissions_r->dir_create_mode;
709
 
                list->file_create_gid = permissions_r->file_create_gid;
710
 
                list->file_create_gid_origin =
 
948
                list->root_permissions = *permissions_r;
 
949
                list->root_permissions.file_create_gid_origin =
711
950
                        p_strdup(list->pool,
712
951
                                 permissions_r->file_create_gid_origin);
713
952
        }
714
953
 
715
954
        if (list->mail_set->mail_debug && name == NULL) {
716
955
                i_debug("Namespace %s: Using permissions from %s: "
717
 
                        "mode=0%o gid=%ld", list->ns->prefix,
 
956
                        "mode=0%o gid=%s", list->ns->prefix,
718
957
                        path != NULL ? path : "",
719
 
                        (int)list->dir_create_mode,
720
 
                        list->file_create_gid == (gid_t)-1 ? -1L :
721
 
                        (long)list->file_create_gid);
 
958
                        (int)permissions_r->dir_create_mode,
 
959
                        permissions_r->file_create_gid == (gid_t)-1 ? "default" :
 
960
                        dec2str(permissions_r->file_create_gid));
722
961
        }
723
962
}
724
963
 
 
964
void mailbox_list_get_permissions(struct mailbox_list *list, const char *name,
 
965
                                  struct mailbox_permissions *permissions_r)
 
966
{
 
967
        mailbox_list_get_permissions_internal(list, name, permissions_r);
 
968
}
 
969
 
725
970
void mailbox_list_get_root_permissions(struct mailbox_list *list,
726
 
                                       mode_t *file_mode_r, mode_t *dir_mode_r,
727
 
                                       gid_t *gid_r, const char **gid_origin_r)
728
 
{
729
 
        struct mailbox_permissions perm;
730
 
 
731
 
        if (list->file_create_mode != (mode_t)-1) {
732
 
                *file_mode_r = list->file_create_mode;
733
 
                *dir_mode_r = list->dir_create_mode;
734
 
                *gid_r = list->file_create_gid;
735
 
                *gid_origin_r = list->file_create_gid_origin;
736
 
        } else {
737
 
                mailbox_list_get_permissions(list, NULL, &perm);
738
 
 
739
 
                *file_mode_r = perm.file_create_mode;
740
 
                *dir_mode_r = perm.dir_create_mode;
741
 
                *gid_r = perm.file_create_gid;
742
 
                *gid_origin_r = perm.file_create_gid_origin;
743
 
        }
744
 
}
745
 
 
746
 
static int
747
 
mailbox_list_stat_parent(const char *path, const char **root_dir_r,
748
 
                         struct stat *st_r, const char **error_r)
749
 
{
750
 
        const char *p;
751
 
 
752
 
        while (stat(path, st_r) < 0) {
753
 
                if (errno != ENOENT || strcmp(path, "/") == 0) {
754
 
                        *error_r = t_strdup_printf("stat(%s) failed: %m", path);
755
 
                        return -1;
756
 
                }
757
 
                p = strrchr(path, '/');
758
 
                if (p == NULL)
759
 
                        path = "/";
760
 
                else
761
 
                        path = t_strdup_until(path, p);
762
 
        }
763
 
        *root_dir_r = path;
764
 
        return 0;
 
971
                                       struct mailbox_permissions *permissions_r)
 
972
{
 
973
        if (list->root_permissions.file_create_mode != (mode_t)-1)
 
974
                *permissions_r = list->root_permissions;
 
975
        else {
 
976
                mailbox_list_get_permissions_internal(list, NULL,
 
977
                                                      permissions_r);
 
978
        }
765
979
}
766
980
 
767
981
static const char *
806
1020
        return ret;
807
1021
}
808
1022
 
809
 
int mailbox_list_mkdir_root(struct mailbox_list *list, const char *path,
810
 
                            enum mailbox_list_path_type type,
811
 
                            const char **error_r)
 
1023
static int
 
1024
mailbox_list_try_mkdir_root_parent(struct mailbox_list *list,
 
1025
                                   enum mailbox_list_path_type type,
 
1026
                                   struct mailbox_permissions *perm,
 
1027
                                   const char **error_r)
812
1028
{
813
 
        const char *expanded, *unexpanded, *root_dir, *p, *origin, *error;
 
1029
        const char *expanded, *unexpanded, *root_dir, *p;
814
1030
        struct stat st;
815
 
        mode_t file_mode, dir_mode;
816
 
        gid_t gid;
817
 
 
818
 
        if (stat(path, &st) == 0) {
819
 
                /* looks like it already exists, don't bother checking
820
 
                   further. */
821
 
                return 0;
822
 
        }
823
 
 
824
 
        if (!mail_user_is_path_mounted(list->ns->user, path, &error)) {
825
 
                *error_r = t_strdup_printf(
826
 
                        "Can't create mailbox root dir %s: %s", path, error);
827
 
                return -1;
828
 
        }
829
 
 
830
 
        mailbox_list_get_root_permissions(list, &file_mode, &dir_mode,
831
 
                                          &gid, &origin);
 
1031
        bool home = FALSE;
832
1032
 
833
1033
        /* get the directory path up to last %variable. for example
834
1034
           unexpanded path may be "/var/mail/%d/%2n/%n/Maildir", and we want
835
1035
           to get expanded="/var/mail/domain/nn" */
836
1036
        unexpanded = mailbox_list_get_unexpanded_path(list, type);
837
1037
        p = strrchr(unexpanded, '%');
838
 
        if (p == NULL)
839
 
                expanded = "";
840
 
        else {
 
1038
        if ((p == unexpanded && p[1] == 'h') ||
 
1039
            (p == NULL && unexpanded[0] == '~')) {
 
1040
                /* home directory used */
 
1041
                if (!mailbox_list_get_root_path(list, type, &expanded))
 
1042
                        i_unreached();
 
1043
                home = TRUE;
 
1044
        } else if (p == NULL) {
 
1045
                return 0;
 
1046
        } else {
841
1047
                while (p != unexpanded && *p != '/') p--;
842
1048
                if (p == unexpanded)
843
 
                        expanded = "";
844
 
                else {
845
 
                        expanded = mailbox_list_get_path(list, NULL, type);
846
 
                        expanded = get_expanded_path(unexpanded, p, expanded);
847
 
                }
848
 
        }
849
 
 
850
 
        if (*expanded != '\0') {
851
 
                /* up to this directory get the permissions from the first
852
 
                   parent directory that exists, if it has setgid bit
853
 
                   enabled. */
854
 
                if (mailbox_list_stat_parent(expanded, &root_dir, &st,
855
 
                                             error_r) < 0)
856
 
                        return -1;
857
 
                if ((st.st_mode & S_ISGID) != 0 && root_dir != expanded) {
 
1049
                        return 0;
 
1050
 
 
1051
                if (!mailbox_list_get_root_path(list, type, &expanded))
 
1052
                        i_unreached();
 
1053
                expanded = get_expanded_path(unexpanded, p, expanded);
 
1054
                if (*expanded == '\0')
 
1055
                        return 0;
 
1056
        }
 
1057
 
 
1058
        /* get the first existing parent directory's permissions */
 
1059
        if (stat_first_parent(expanded, &root_dir, &st) < 0) {
 
1060
                *error_r = errno == EACCES ?
 
1061
                        mail_error_eacces_msg("stat", root_dir) :
 
1062
                        t_strdup_printf("stat(%s) failed: %m", root_dir);
 
1063
                return -1;
 
1064
        }
 
1065
 
 
1066
        /* if the parent directory doesn't have setgid-bit enabled, we don't
 
1067
           copy any permissions from it. */
 
1068
        if ((st.st_mode & S_ISGID) == 0)
 
1069
                return 0;
 
1070
 
 
1071
        if (!home) {
 
1072
                /* assuming we have e.g. /var/vmail/%d/%n directory, here we
 
1073
                   want to create up to /var/vmail/%d with permissions from
 
1074
                   the parent directory. we never want to create the %n
 
1075
                   directory itself. */
 
1076
                if (root_dir == expanded) {
 
1077
                        /* this is the %n directory */
 
1078
                } else {
858
1079
                        if (mkdir_parents_chgrp(expanded, st.st_mode,
859
1080
                                                (gid_t)-1, root_dir) < 0 &&
860
1081
                            errno != EEXIST) {
863
1084
                                return -1;
864
1085
                        }
865
1086
                }
866
 
                if (gid == (gid_t)-1 && (dir_mode & S_ISGID) == 0) {
 
1087
                if (perm->file_create_gid == (gid_t)-1 &&
 
1088
                    (perm->dir_create_mode & S_ISGID) == 0) {
867
1089
                        /* change the group for user directories */
868
 
                        gid = getegid();
 
1090
                        perm->dir_create_mode |= S_ISGID;
 
1091
                        perm->file_create_gid = getegid();
 
1092
                        perm->file_create_gid_origin = "egid";
 
1093
                        perm->gid_origin_is_mailbox_path = FALSE;
869
1094
                }
 
1095
        } else {
 
1096
                /* when using %h and the parent has setgid-bit,
 
1097
                   copy the permissions from it for the home we're creating */
 
1098
                perm->file_create_mode = st.st_mode & 0666;
 
1099
                perm->dir_create_mode = st.st_mode;
 
1100
                perm->file_create_gid = (gid_t)-1;
 
1101
                perm->file_create_gid_origin = "parent";
 
1102
                perm->gid_origin_is_mailbox_path = FALSE;
 
1103
        }
 
1104
        return 0;
 
1105
}
 
1106
 
 
1107
int mailbox_list_try_mkdir_root(struct mailbox_list *list, const char *path,
 
1108
                                enum mailbox_list_path_type type,
 
1109
                                const char **error_r)
 
1110
{
 
1111
        const char *root_dir, *error;
 
1112
        struct stat st;
 
1113
        struct mailbox_permissions perm;
 
1114
 
 
1115
        if (stat(path, &st) == 0) {
 
1116
                /* looks like it already exists, don't bother checking
 
1117
                   further. */
 
1118
                return 0;
 
1119
        }
 
1120
 
 
1121
        mailbox_list_get_root_permissions(list, &perm);
 
1122
 
 
1123
        if (!mailbox_list_get_root_path(list, type, &root_dir))
 
1124
                i_unreached();
 
1125
        i_assert(strncmp(root_dir, path, strlen(root_dir)) == 0);
 
1126
        if (strcmp(root_dir, path) != 0 && stat(root_dir, &st) == 0) {
 
1127
                /* creating a subdirectory under an already existing root dir.
 
1128
                   use the root's permissions */
 
1129
        } else if (mail_user_is_path_mounted(list->ns->user, path, &error)) {
 
1130
                if (mailbox_list_try_mkdir_root_parent(list, type,
 
1131
                                                       &perm, error_r) < 0)
 
1132
                        return -1;
 
1133
        } else {
 
1134
                *error_r = t_strdup_printf(
 
1135
                        "Can't create mailbox root dir %s: %s", path, error);
 
1136
                return -1;
870
1137
        }
871
1138
 
872
1139
        /* the rest of the directories exist only for one user. create them
873
1140
           with default directory permissions */
874
 
        if (mkdir_parents_chgrp(path, dir_mode, gid, origin) < 0 &&
 
1141
        if (mkdir_parents_chgrp(path, perm.dir_create_mode,
 
1142
                                perm.file_create_gid,
 
1143
                                perm.file_create_gid_origin) < 0 &&
875
1144
            errno != EEXIST) {
876
1145
                if (errno == EACCES)
877
1146
                        *error_r = mail_error_create_eacces_msg("mkdir", path);
882
1151
        return 0;
883
1152
}
884
1153
 
885
 
bool mailbox_list_is_valid_pattern(struct mailbox_list *list,
886
 
                                   const char *pattern)
 
1154
int mailbox_list_mkdir_root(struct mailbox_list *list, const char *path,
 
1155
                            enum mailbox_list_path_type type)
887
1156
{
888
 
        bool ret;
 
1157
        const char *error;
889
1158
 
890
 
        T_BEGIN {
891
 
                ret = list->v.is_valid_pattern(list, pattern);
892
 
        } T_END;
893
 
        return ret;
 
1159
        if (mailbox_list_try_mkdir_root(list, path, type, &error) < 0) {
 
1160
                mailbox_list_set_critical(list, "%s", error);
 
1161
                return -1;
 
1162
        }
 
1163
        if (type == MAILBOX_LIST_PATH_TYPE_INDEX)
 
1164
                list->index_root_dir_created = TRUE;
 
1165
        return 0;
894
1166
}
895
1167
 
896
 
bool mailbox_list_is_valid_existing_name(struct mailbox_list *list,
897
 
                                         const char *name)
 
1168
static bool
 
1169
mailbox_list_is_valid_fs_name(struct mailbox_list *list, const char *name,
 
1170
                              const char **error_r)
898
1171
{
899
 
        bool ret;
900
 
 
901
 
        if (*name == '\0' && *list->ns->prefix != '\0') {
902
 
                /* an ugly way to get to mailbox root (e.g. Maildir/ when
903
 
                   it's not the INBOX) */
 
1172
        bool ret, allow_internal_dirs;
 
1173
 
 
1174
        *error_r = NULL;
 
1175
 
 
1176
        if (list->mail_set->mail_full_filesystem_access)
904
1177
                return TRUE;
905
 
        }
906
 
 
 
1178
 
 
1179
        /* make sure it's not absolute path */
 
1180
        if (*name == '/') {
 
1181
                *error_r = "Begins with '/'";
 
1182
                return FALSE;
 
1183
        }
 
1184
        if (*name == '~') {
 
1185
                *error_r = "Begins with '~'";
 
1186
                return FALSE;
 
1187
        }
 
1188
 
 
1189
        /* make sure the mailbox name doesn't contain any foolishness:
 
1190
           "../" could give access outside the mailbox directory.
 
1191
           "./" and "//" could fool ACL checks.
 
1192
 
 
1193
           some mailbox formats have reserved directory names, such as
 
1194
           Maildir's cur/new/tmp. if any of those would conflict with the
 
1195
           mailbox directory name, it's not valid. maildir++ is kludged here as
 
1196
           a special case because all of its mailbox dirs begin with "." */
 
1197
        allow_internal_dirs = list->v.is_internal_name == NULL ||
 
1198
                *list->set.maildir_name != '\0' ||
 
1199
                strcmp(list->name, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) == 0;
907
1200
        T_BEGIN {
908
 
                ret = list->v.is_valid_existing_name(list, name);
 
1201
                const char *const *names;
 
1202
 
 
1203
                names = t_strsplit(name, "/");
 
1204
                for (; *names != NULL; names++) {
 
1205
                        const char *n = *names;
 
1206
 
 
1207
                        if (*n == '\0') {
 
1208
                                *error_r = "Has adjacent '/' chars";
 
1209
                                break; /* // */
 
1210
                        }
 
1211
                        if (*n == '.') {
 
1212
                                if (n[1] == '\0') {
 
1213
                                        *error_r = "Contains '.' part";
 
1214
                                        break; /* ./ */
 
1215
                                }
 
1216
                                if (n[1] == '.' && n[2] == '\0') {
 
1217
                                        *error_r = "Contains '..' part";
 
1218
                                        break; /* ../ */
 
1219
                                }
 
1220
                        }
 
1221
                        if (*list->set.maildir_name != '\0' &&
 
1222
                            strcmp(list->set.maildir_name, n) == 0) {
 
1223
                                /* don't allow maildir_name to be used as part
 
1224
                                   of the mailbox name */
 
1225
                                *error_r = "Contains reserved name";
 
1226
                                break;
 
1227
                        }
 
1228
                        if (!allow_internal_dirs &&
 
1229
                            list->v.is_internal_name(list, n)) {
 
1230
                                *error_r = "Contains reserved name";
 
1231
                                break;
 
1232
                        }
 
1233
                }
 
1234
                ret = *names == NULL;
909
1235
        } T_END;
 
1236
 
910
1237
        return ret;
911
1238
}
912
1239
 
913
 
bool mailbox_list_is_valid_create_name(struct mailbox_list *list,
914
 
                                       const char *name)
 
1240
 
 
1241
bool mailbox_list_is_valid_name(struct mailbox_list *list,
 
1242
                                const char *name, const char **error_r)
915
1243
{
916
 
        const char *p;
917
 
        int ret;
918
 
 
919
 
        /* safer to just disallow all control characters */
920
 
        for (p = name; *p != '\0'; p++) {
921
 
                if ((unsigned char)*p < ' ')
922
 
                        return FALSE;
 
1244
        if (*name == '\0') {
 
1245
                if (*list->ns->prefix != '\0') {
 
1246
                        /* an ugly way to get to mailbox root (e.g. Maildir/
 
1247
                           when it's not the INBOX) */
 
1248
                        return TRUE;
 
1249
                }
 
1250
                *error_r = "Name is empty";
 
1251
                return FALSE;
923
1252
        }
924
1253
 
925
 
        if (list->set.utf8)
926
 
                ret = uni_utf8_str_is_valid(name) ? 0 : -1;
927
 
        else T_BEGIN {
928
 
                string_t *str = t_str_new(256);
929
 
                ret = imap_utf7_to_utf8(name, str);
930
 
        } T_END;
931
 
        return ret < 0 ? FALSE :
932
 
                list->v.is_valid_create_name(list, name);
933
 
}
934
 
 
935
 
const char *mailbox_list_get_path(struct mailbox_list *list, const char *name,
936
 
                                  enum mailbox_list_path_type type)
937
 
{
938
 
        return list->v.get_path(list, name, type);
939
 
}
940
 
 
941
 
const char *
942
 
mailbox_list_get_root_path(const struct mailbox_list_settings *set,
943
 
                           enum mailbox_list_path_type type)
 
1254
        return mailbox_list_is_valid_fs_name(list, name, error_r);
 
1255
}
 
1256
 
 
1257
int mailbox_list_get_path(struct mailbox_list *list, const char *name,
 
1258
                          enum mailbox_list_path_type type,
 
1259
                          const char **path_r)
 
1260
{
 
1261
        int ret;
 
1262
 
 
1263
        if ((ret = list->v.get_path(list, name, type, path_r)) <= 0)
 
1264
                *path_r = NULL;
 
1265
        else
 
1266
                i_assert(*path_r != NULL);
 
1267
        return ret;
 
1268
}
 
1269
 
 
1270
bool mailbox_list_get_root_path(struct mailbox_list *list,
 
1271
                                enum mailbox_list_path_type type,
 
1272
                                const char **path_r)
 
1273
{
 
1274
        int ret;
 
1275
 
 
1276
        if ((ret = list->v.get_path(list, NULL, type, path_r)) < 0)
 
1277
                i_unreached();
 
1278
        if (ret == 0)
 
1279
                *path_r = NULL;
 
1280
        else
 
1281
                i_assert(*path_r != NULL);
 
1282
        return ret > 0;
 
1283
}
 
1284
 
 
1285
const char *mailbox_list_get_root_forced(struct mailbox_list *list,
 
1286
                                         enum mailbox_list_path_type type)
944
1287
{
945
1288
        const char *path;
946
1289
 
 
1290
        if (!mailbox_list_get_root_path(list, type, &path))
 
1291
                i_unreached();
 
1292
        return path;
 
1293
}
 
1294
 
 
1295
bool mailbox_list_set_get_root_path(const struct mailbox_list_settings *set,
 
1296
                                    enum mailbox_list_path_type type,
 
1297
                                    const char **path_r)
 
1298
{
 
1299
        const char *path = NULL;
 
1300
 
947
1301
        switch (type) {
948
1302
        case MAILBOX_LIST_PATH_TYPE_DIR:
949
 
                return set->root_dir;
 
1303
                path = set->root_dir;
 
1304
                break;
950
1305
        case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
951
 
                return set->alt_dir;
 
1306
                path = set->alt_dir;
 
1307
                break;
952
1308
        case MAILBOX_LIST_PATH_TYPE_MAILBOX:
953
1309
                if (*set->mailbox_dir_name == '\0')
954
 
                        return set->root_dir;
955
 
                path = t_strconcat(set->root_dir, "/",
956
 
                                   set->mailbox_dir_name, NULL);
957
 
                return t_strndup(path, strlen(path)-1);
 
1310
                        path = set->root_dir;
 
1311
                else {
 
1312
                        path = t_strconcat(set->root_dir, "/",
 
1313
                                           set->mailbox_dir_name, NULL);
 
1314
                        path = t_strndup(path, strlen(path)-1);
 
1315
                }
 
1316
                break;
958
1317
        case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
959
1318
                if (*set->mailbox_dir_name == '\0')
960
 
                        return set->root_dir;
961
 
                path = t_strconcat(set->alt_dir, "/",
962
 
                                   set->mailbox_dir_name, NULL);
963
 
                return path == NULL ? NULL : t_strndup(path, strlen(path)-1);
 
1319
                        path = set->root_dir;
 
1320
                else if (set->alt_dir != NULL) {
 
1321
                        path = t_strconcat(set->alt_dir, "/",
 
1322
                                           set->mailbox_dir_name, NULL);
 
1323
                        path = t_strndup(path, strlen(path)-1);
 
1324
                }
 
1325
                break;
964
1326
        case MAILBOX_LIST_PATH_TYPE_CONTROL:
965
 
                return set->control_dir != NULL ?
 
1327
                path = set->control_dir != NULL ?
966
1328
                        set->control_dir : set->root_dir;
 
1329
                break;
967
1330
        case MAILBOX_LIST_PATH_TYPE_INDEX:
968
 
                return set->index_dir != NULL ?
969
 
                        set->index_dir : set->root_dir;
 
1331
                if (set->index_dir != NULL) {
 
1332
                        if (set->index_dir[0] == '\0') {
 
1333
                                /* in-memory indexes */
 
1334
                                return 0;
 
1335
                        }
 
1336
                        path = set->index_dir;
 
1337
                } else {
 
1338
                        path = set->root_dir;
 
1339
                }
 
1340
                break;
 
1341
        case MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE:
 
1342
                path = set->index_pvt_dir;
 
1343
                break;
970
1344
        }
971
 
        i_unreached();
 
1345
        *path_r = path;
 
1346
        return path != NULL;
972
1347
}
973
1348
 
974
1349
const char *mailbox_list_get_temp_prefix(struct mailbox_list *list)
1065
1440
                return mailbox_list_iter_deinit(&iter);
1066
1441
        }
1067
1442
 
1068
 
        rootdir = mailbox_list_get_path(list, NULL,
1069
 
                                        MAILBOX_LIST_PATH_TYPE_MAILBOX);
1070
 
        i_assert(rootdir != NULL);
1071
 
        path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR);
 
1443
        rootdir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_MAILBOX);
 
1444
        if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR, &path) <= 0)
 
1445
                i_unreached();
1072
1446
 
1073
1447
        fname = strrchr(path, '/');
1074
1448
        if (fname == NULL) {
1094
1468
            (list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
1095
1469
                /* if INBOX is in e.g. ~/Maildir, it shouldn't be possible to
1096
1470
                   access it also via namespace prefix. */
1097
 
                inbox = mailbox_list_get_path(list, "INBOX",
1098
 
                                              MAILBOX_LIST_PATH_TYPE_MAILBOX);
 
1471
                if (mailbox_list_get_path(list, "INBOX",
 
1472
                                          MAILBOX_LIST_PATH_TYPE_MAILBOX,
 
1473
                                          &inbox) <= 0)
 
1474
                        i_unreached();
1099
1475
                if (strcmp(inbox, dir) == 0) {
1100
1476
                        *flags_r |= MAILBOX_NONEXISTENT;
1101
1477
                        return 0;
1108
1484
 
1109
1485
static bool mailbox_list_init_changelog(struct mailbox_list *list)
1110
1486
{
 
1487
        struct mailbox_permissions perm;
1111
1488
        const char *path;
1112
 
        mode_t file_mode, dir_mode;
1113
 
        gid_t gid;
1114
 
        const char *gid_origin;
1115
1489
 
1116
1490
        if (list->changelog != NULL)
1117
1491
                return TRUE;
1118
1492
 
1119
1493
        /* don't do this in mailbox_list_create(), because _get_path() might be
1120
1494
           overridden by storage (mbox). */
1121
 
        path = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_INDEX);
1122
 
        if (*path == '\0')
 
1495
        if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX, &path))
1123
1496
                return FALSE;
1124
1497
 
1125
1498
        path = t_strconcat(path, "/"MAILBOX_LOG_FILE_NAME, NULL);
1126
1499
        list->changelog = mailbox_log_alloc(path);
1127
1500
 
1128
 
        mailbox_list_get_root_permissions(list, &file_mode, &dir_mode,
1129
 
                                          &gid, &gid_origin);
1130
 
        mailbox_log_set_permissions(list->changelog, dir_mode, gid, gid_origin);
 
1501
        mailbox_list_get_root_permissions(list, &perm);
 
1502
        mailbox_log_set_permissions(list->changelog, perm.file_create_mode,
 
1503
                                    perm.file_create_gid,
 
1504
                                    perm.file_create_gid_origin);
1131
1505
        return TRUE;
1132
1506
}
1133
1507
 
 
1508
int mailbox_list_mkdir_missing_index_root(struct mailbox_list *list)
 
1509
{
 
1510
        const char *root_dir, *index_dir;
 
1511
        int ret;
 
1512
 
 
1513
        if (list->index_root_dir_created)
 
1514
                return 1;
 
1515
 
 
1516
        /* if index root dir hasn't been created yet, do it now */
 
1517
        ret = mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX,
 
1518
                                         &index_dir);
 
1519
        if (ret <= 0)
 
1520
                return ret;
 
1521
        ret = mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_MAILBOX,
 
1522
                                         &root_dir);
 
1523
        if (ret <= 0)
 
1524
                return ret;
 
1525
 
 
1526
        if (strcmp(root_dir, index_dir) != 0) {
 
1527
                if (mailbox_list_mkdir_root(list, index_dir,
 
1528
                                            MAILBOX_LIST_PATH_TYPE_INDEX) < 0)
 
1529
                        return -1;
 
1530
        }
 
1531
        list->index_root_dir_created = TRUE;
 
1532
        return 1;
 
1533
}
 
1534
 
1134
1535
void mailbox_list_add_change(struct mailbox_list *list,
1135
1536
                             enum mailbox_log_record_type type,
1136
1537
                             const guid_128_t mailbox_guid)
1142
1543
            guid_128_is_empty(mailbox_guid))
1143
1544
                return;
1144
1545
 
1145
 
        if (!list->index_root_dir_created) {
1146
 
                if (mailbox_list_create_missing_index_dir(list, NULL) < 0)
1147
 
                        return;
1148
 
        }
 
1546
        if (mailbox_list_mkdir_missing_index_root(list) <= 0)
 
1547
                return;
1149
1548
 
1150
1549
        stamp = list->changelog_timestamp != (time_t)-1 ?
1151
1550
                list->changelog_timestamp : ioloop_time;
1160
1559
int mailbox_list_set_subscribed(struct mailbox_list *list,
1161
1560
                                const char *name, bool set)
1162
1561
{
1163
 
        guid_128_t guid;
1164
1562
        int ret;
1165
1563
 
1166
1564
        /* make sure we'll refresh the file on next list */
1168
1566
 
1169
1567
        if ((ret = list->v.set_subscribed(list, name, set)) <= 0)
1170
1568
                return ret;
1171
 
 
1172
 
        /* subscriptions are about names, not about mailboxes. it's possible
1173
 
           to have a subscription to nonexistent mailbox. renames also don't
1174
 
           change subscriptions. so instead of using actual GUIDs, we'll use
1175
 
           hash of the name. */
1176
 
        mailbox_name_get_sha128(name, guid);
1177
 
        mailbox_list_add_change(list, set ? MAILBOX_LOG_RECORD_SUBSCRIBE :
1178
 
                                MAILBOX_LOG_RECORD_UNSUBSCRIBE, guid);
1179
1569
        return 0;
1180
1570
}
1181
1571
 
1182
 
int mailbox_list_create_dir(struct mailbox_list *list, const char *name)
1183
 
{
1184
 
        if (!mailbox_list_is_valid_create_name(list, name) || *name == '\0') {
1185
 
                mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
1186
 
                                       "Invalid mailbox name");
1187
 
                return -1;
1188
 
        }
1189
 
        return list->v.create_mailbox_dir(list, name,
1190
 
                                          MAILBOX_DIR_CREATE_TYPE_ONLY_NOSELECT);
1191
 
}
1192
 
 
1193
1572
int mailbox_list_delete_dir(struct mailbox_list *list, const char *name)
1194
1573
{
1195
 
        if (!mailbox_list_is_valid_existing_name(list, name) || *name == '\0') {
 
1574
        const char *error;
 
1575
 
 
1576
        if (!mailbox_list_is_valid_name(list, name, &error) || *name == '\0') {
1196
1577
                mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
1197
1578
                                       "Invalid mailbox name");
1198
1579
                return -1;
1202
1583
 
1203
1584
int mailbox_list_delete_symlink(struct mailbox_list *list, const char *name)
1204
1585
{
1205
 
        if (!mailbox_list_is_valid_existing_name(list, name) || *name == '\0') {
 
1586
        const char *error;
 
1587
 
 
1588
        if (!mailbox_list_is_valid_name(list, name, &error) || *name == '\0') {
1206
1589
                mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
1207
1590
                                       "Invalid mailbox name");
1208
1591
                return -1;
1280
1663
        return type;
1281
1664
}
1282
1665
 
 
1666
int mailbox_list_dirent_is_alias_symlink(struct mailbox_list *list,
 
1667
                                         const char *dir_path,
 
1668
                                         const struct dirent *d)
 
1669
{
 
1670
        struct stat st;
 
1671
        int ret;
 
1672
 
 
1673
        if (mailbox_list_get_file_type(d) == MAILBOX_LIST_FILE_TYPE_SYMLINK)
 
1674
                return 1;
 
1675
 
 
1676
        T_BEGIN {
 
1677
                const char *path, *linkpath;
 
1678
 
 
1679
                path = t_strconcat(dir_path, "/", d->d_name, NULL);
 
1680
                if (lstat(path, &st) < 0) {
 
1681
                        mailbox_list_set_critical(list,
 
1682
                                                  "lstat(%s) failed: %m", path);
 
1683
                        ret = -1;
 
1684
                } else if (!S_ISLNK(st.st_mode)) {
 
1685
                        ret = 0;
 
1686
                } else if (t_readlink(path, &linkpath) < 0) {
 
1687
                        i_error("readlink(%s) failed: %m", path);
 
1688
                        ret = -1;
 
1689
                } else {
 
1690
                        /* it's an alias only if it points to the same
 
1691
                           directory */
 
1692
                        ret = strchr(linkpath, '/') == NULL ? 1 : 0;
 
1693
                }
 
1694
        } T_END;
 
1695
        return ret;
 
1696
}
 
1697
 
 
1698
 
1283
1699
static bool
1284
1700
mailbox_list_try_get_home_path(struct mailbox_list *list, const char **name)
1285
1701
{
1317
1733
 
1318
1734
        /* okay, we have an absolute path now. but check first if it points to
1319
1735
           same directory as one of our regular mailboxes. */
1320
 
        root_dir = mailbox_list_get_path(list, NULL,
1321
 
                                         MAILBOX_LIST_PATH_TYPE_MAILBOX);
 
1736
        root_dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_MAILBOX);
1322
1737
        len = strlen(root_dir);
1323
1738
        if (strncmp(root_dir, *name, len) == 0 && (*name)[len] == '/') {
1324
1739
                mailbox_name = *name + len + 1;
1325
 
                path = mailbox_list_get_path(list, mailbox_name,
1326
 
                                             MAILBOX_LIST_PATH_TYPE_MAILBOX);
 
1740
                if (mailbox_list_get_path(list, mailbox_name,
 
1741
                                          MAILBOX_LIST_PATH_TYPE_MAILBOX,
 
1742
                                          &path) <= 0)
 
1743
                        return FALSE;
1327
1744
                if (strcmp(path, *name) == 0) {
1328
1745
                        /* yeah, we can replace the full path with mailbox
1329
1746
                           name. this way we can use indexes. */
1334
1751
        return TRUE;
1335
1752
}
1336
1753
 
1337
 
int mailbox_list_mkdir(struct mailbox_list *list,
1338
 
                       const char *mailbox, const char *path)
1339
 
{
1340
 
        struct mailbox_permissions perm;
1341
 
 
1342
 
        mailbox_list_get_permissions(list, mailbox, &perm);
1343
 
        if (mkdir_parents_chgrp(path, perm.dir_create_mode,
1344
 
                                perm.file_create_gid,
1345
 
                                perm.file_create_gid_origin) < 0 &&
1346
 
            errno != EEXIST) {
1347
 
                mailbox_list_set_critical(list, "mkdir_parents(%s) failed: %m",
1348
 
                                          path);
1349
 
                return -1;
1350
 
        }
1351
 
        return 0;
1352
 
}
1353
 
 
1354
 
int mailbox_list_mkdir_parent(struct mailbox_list *list,
1355
 
                              const char *mailbox, const char *path)
1356
 
{
1357
 
        const char *p;
1358
 
 
1359
 
        p = strrchr(path, '/');
1360
 
        if (p == NULL)
1361
 
                return 0;
1362
 
 
1363
 
        return mailbox_list_mkdir(list, mailbox, t_strdup_until(path, p));
1364
 
}
1365
 
 
1366
 
int mailbox_list_create_missing_index_dir(struct mailbox_list *list,
1367
 
                                          const char *name)
1368
 
{
1369
 
        const char *root_dir, *index_dir, *parent_dir, *p, *error;
1370
 
        struct mailbox_permissions perm;
1371
 
        unsigned int n = 0;
1372
 
 
1373
 
        list->index_root_dir_created = TRUE;
1374
 
        root_dir = mailbox_list_get_path(list, name,
1375
 
                                         MAILBOX_LIST_PATH_TYPE_MAILBOX);
1376
 
        index_dir = mailbox_list_get_path(list, name,
1377
 
                                          MAILBOX_LIST_PATH_TYPE_INDEX);
1378
 
        if (*index_dir == '\0')
1379
 
                return 0;
1380
 
        if (strcmp(index_dir, root_dir) == 0) {
1381
 
                if ((list->props & MAILBOX_LIST_PROP_AUTOCREATE_DIRS) == 0)
1382
 
                        return 0;
1383
 
                /* the directory might not have been created yet */
1384
 
        }
1385
 
 
1386
 
        if (name == NULL) {
1387
 
                if (mailbox_list_mkdir_root(list, index_dir,
1388
 
                                            MAILBOX_LIST_PATH_TYPE_INDEX,
1389
 
                                            &error) < 0) {
1390
 
                        mailbox_list_set_critical(list,
1391
 
                                "Couldn't create index root dir %s: %s",
1392
 
                                index_dir, error);
1393
 
                        return -1;
1394
 
                }
1395
 
                return 0;
1396
 
        }
1397
 
 
1398
 
        mailbox_list_get_permissions(list, name, &perm);
1399
 
        while (mkdir_chgrp(index_dir, perm.dir_create_mode,
1400
 
                           perm.file_create_gid,
1401
 
                           perm.file_create_gid_origin) < 0) {
1402
 
                if (errno == EEXIST)
1403
 
                        break;
1404
 
 
1405
 
                p = strrchr(index_dir, '/');
1406
 
                if (errno != ENOENT || p == NULL || ++n == 2) {
1407
 
                        mailbox_list_set_critical(list,
1408
 
                                "mkdir(%s) failed: %m", index_dir);
1409
 
                        if (p == NULL || errno != EPERM ||
1410
 
                            perm.dir_create_mode == 0700)
1411
 
                                return -1;
1412
 
                        /* we can't use the GID. allow it anyway with more
1413
 
                           restricted permissions. */
1414
 
                        perm.file_create_gid = (gid_t)-1;
1415
 
                        perm.dir_create_mode = 0700;
1416
 
                        continue;
1417
 
                }
1418
 
                /* create the parent directory first */
1419
 
                parent_dir = t_strdup_until(index_dir, p);
1420
 
                if (mailbox_list_mkdir_root(list, parent_dir,
1421
 
                                            MAILBOX_LIST_PATH_TYPE_INDEX,
1422
 
                                            &error) < 0) {
1423
 
                        mailbox_list_set_critical(list,
1424
 
                                "Couldn't create index dir %s: %s",
1425
 
                                parent_dir, error);
1426
 
                        return -1;
1427
 
                }
1428
 
        }
1429
 
        return 0;
1430
 
}
1431
 
 
1432
1754
const char *mailbox_list_get_last_error(struct mailbox_list *list,
1433
1755
                                        enum mail_error *error_r)
1434
1756
{
1490
1812
        mailbox_list_set_error(list, error, error_string);
1491
1813
        return TRUE;
1492
1814
}
 
1815
 
 
1816
int mailbox_list_init_fs(struct mailbox_list *list, const char *driver,
 
1817
                         const char *args, const char *root_dir,
 
1818
                         struct fs **fs_r, const char **error_r)
 
1819
{
 
1820
        struct fs_settings fs_set;
 
1821
        struct ssl_iostream_settings ssl_set;
 
1822
 
 
1823
        memset(&ssl_set, 0, sizeof(ssl_set));
 
1824
        ssl_set.ca_dir = list->mail_set->ssl_client_ca_dir;
 
1825
        ssl_set.ca_file = list->mail_set->ssl_client_ca_file;
 
1826
 
 
1827
        memset(&fs_set, 0, sizeof(fs_set));
 
1828
        fs_set.temp_file_prefix = mailbox_list_get_global_temp_prefix(list);
 
1829
        fs_set.base_dir = list->ns->user->set->base_dir;
 
1830
        fs_set.temp_dir = list->ns->user->set->mail_temp_dir;
 
1831
        fs_set.ssl_client_set = &ssl_set;
 
1832
        fs_set.root_path = root_dir;
 
1833
        fs_set.debug = list->ns->user->mail_debug;
 
1834
 
 
1835
        return fs_init(driver, args, &fs_set, fs_r, error_r);
 
1836
}