diff options
Diffstat (limited to 'src/lib-storage/mail-namespace.c')
-rw-r--r-- | src/lib-storage/mail-namespace.c | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/src/lib-storage/mail-namespace.c b/src/lib-storage/mail-namespace.c new file mode 100644 index 0000000..0b3d458 --- /dev/null +++ b/src/lib-storage/mail-namespace.c @@ -0,0 +1,866 @@ +/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "file-lock.h" +#include "settings-parser.h" +#include "mailbox-list-private.h" +#include "mail-storage-private.h" +#include "mail-storage-settings.h" +#include "mail-namespace.h" + + +static struct mail_namespace_settings prefixless_ns_unexpanded_set = { + .name = "", + .type = "private", + .separator = "", + .prefix = "0", + .location = "0fail::LAYOUT=none", + .alias_for = NULL, + + .inbox = FALSE, + .hidden = TRUE, + .list = "no", + .subscriptions = FALSE, + .ignore_on_failure = FALSE, + .disabled = FALSE, + + .mailboxes = ARRAY_INIT +}; +static struct mail_namespace_settings prefixless_ns_set; + +void mail_namespace_add_storage(struct mail_namespace *ns, + struct mail_storage *storage) +{ + if (ns->storage == NULL) + ns->storage = storage; + array_push_back(&ns->all_storages, &storage); + + if (storage->v.add_list != NULL) + storage->v.add_list(storage, ns->list); + hook_mail_namespace_storage_added(ns); +} + +void mail_namespace_finish_list_init(struct mail_namespace *ns, + struct mailbox_list *list) +{ + ns->list = list; + ns->prefix_len = strlen(ns->prefix); +} + +static void mail_namespace_free(struct mail_namespace *ns) +{ + struct mail_storage *storage; + + if (array_is_created(&ns->all_storages)) { + array_foreach_elem(&ns->all_storages, storage) + mail_storage_unref(&storage); + array_free(&ns->all_storages); + } + if (ns->list != NULL) + mailbox_list_destroy(&ns->list); + + if (ns->owner != ns->user && ns->owner != NULL) + mail_user_unref(&ns->owner); + i_free(ns->prefix); + i_free(ns); +} + +static bool +namespace_has_special_use_mailboxes(struct mail_namespace_settings *ns_set) +{ + struct mailbox_settings *box_set; + + if (!array_is_created(&ns_set->mailboxes)) + return FALSE; + + array_foreach_elem(&ns_set->mailboxes, box_set) { + if (box_set->special_use[0] != '\0') + return TRUE; + } + return FALSE; +} + +int mail_namespace_alloc(struct mail_user *user, + void *user_all_settings, + struct mail_namespace_settings *ns_set, + struct mail_namespace_settings *unexpanded_set, + struct mail_namespace **ns_r, + const char **error_r) +{ + struct mail_namespace *ns; + + ns = i_new(struct mail_namespace, 1); + ns->refcount = 1; + ns->user = user; + ns->prefix = i_strdup(ns_set->prefix); + ns->set = ns_set; + ns->unexpanded_set = unexpanded_set; + ns->user_set = user_all_settings; + ns->mail_set = mail_user_set_get_driver_settings(user->set_info, + ns->user_set, MAIL_STORAGE_SET_DRIVER_NAME); + i_array_init(&ns->all_storages, 2); + + if (strcmp(ns_set->type, "private") == 0) { + ns->owner = user; + ns->type = MAIL_NAMESPACE_TYPE_PRIVATE; + } else if (strcmp(ns_set->type, "shared") == 0) + ns->type = MAIL_NAMESPACE_TYPE_SHARED; + else if (strcmp(ns_set->type, "public") == 0) + ns->type = MAIL_NAMESPACE_TYPE_PUBLIC; + else { + *error_r = t_strdup_printf("Unknown namespace type: %s", + ns_set->type); + mail_namespace_free(ns); + return -1; + } + + if (strcmp(ns_set->list, "children") == 0) + ns->flags |= NAMESPACE_FLAG_LIST_CHILDREN; + else if (strcmp(ns_set->list, "yes") == 0) + ns->flags |= NAMESPACE_FLAG_LIST_PREFIX; + else if (strcmp(ns_set->list, "no") != 0) { + *error_r = t_strdup_printf("Invalid list setting value: %s", + ns_set->list); + mail_namespace_free(ns); + return -1; + } + + if (ns_set->inbox) { + ns->flags |= NAMESPACE_FLAG_INBOX_USER | + NAMESPACE_FLAG_INBOX_ANY; + } + if (ns_set->hidden) + ns->flags |= NAMESPACE_FLAG_HIDDEN; + if (ns_set->subscriptions) + ns->flags |= NAMESPACE_FLAG_SUBSCRIPTIONS; + + *ns_r = ns; + + return 0; +} + +int mail_namespaces_init_add(struct mail_user *user, + struct mail_namespace_settings *ns_set, + struct mail_namespace_settings *unexpanded_ns_set, + struct mail_namespace **ns_p, const char **error_r) +{ + const struct mail_storage_settings *mail_set = + mail_user_set_get_storage_set(user); + struct mail_namespace *ns; + const char *driver, *error; + int ret; + + if (*ns_set->location == '\0') + ns_set->location = mail_set->mail_location; + + e_debug(user->event, "Namespace %s: type=%s, prefix=%s, sep=%s, " + "inbox=%s, hidden=%s, list=%s, subscriptions=%s " + "location=%s", + ns_set->name, ns_set->type, ns_set->prefix, + ns_set->separator == NULL ? "" : ns_set->separator, + ns_set->inbox ? "yes" : "no", + ns_set->hidden ? "yes" : "no", + ns_set->list, + ns_set->subscriptions ? "yes" : "no", ns_set->location); + + if ((ret = mail_namespace_alloc(user, user->set, + ns_set, unexpanded_ns_set, + &ns, error_r)) < 0) + return ret; + + if (ns_set == &prefixless_ns_set) { + /* autocreated prefix="" namespace */ + ns->flags |= NAMESPACE_FLAG_UNUSABLE | + NAMESPACE_FLAG_AUTOCREATED; + } + + ns->special_use_mailboxes = namespace_has_special_use_mailboxes(ns_set); + + if (ns->type == MAIL_NAMESPACE_TYPE_SHARED && + (strchr(ns->prefix, '%') != NULL || + strchr(ns->set->location, '%') != NULL)) { + /* dynamic shared namespace. the above check catches wrong + mixed %% usage, but still allows for specifying a shared + namespace to an explicit location without any %% */ + ns->flags |= NAMESPACE_FLAG_NOQUOTA | NAMESPACE_FLAG_NOACL; + driver = MAIL_SHARED_STORAGE_NAME; + } else { + driver = NULL; + } + + if (mail_storage_create(ns, driver, 0, &error) < 0) { + *error_r = t_strdup_printf("Namespace '%s': %s", + ns->prefix, error); + mail_namespace_free(ns); + return -1; + } + + *ns_p = ns; + return 0; +} + +static bool namespace_is_valid_alias_storage(struct mail_namespace *ns, + const char **error_r) +{ + if (strcmp(ns->storage->name, ns->alias_for->storage->name) != 0) { + *error_r = t_strdup_printf( + "Namespace %s can't have alias_for=%s " + "to a different storage type (%s vs %s)", + ns->prefix, ns->alias_for->prefix, + ns->storage->name, ns->alias_for->storage->name); + return FALSE; + } + + if ((ns->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) != 0 && + ns->storage != ns->alias_for->storage) { + *error_r = t_strdup_printf( + "Namespace %s can't have alias_for=%s " + "to a different storage (different root dirs)", + ns->prefix, ns->alias_for->prefix); + return FALSE; + } + return TRUE; +} + +static int +namespace_set_alias_for(struct mail_namespace *ns, + struct mail_namespace *all_namespaces, + const char **error_r) +{ + if (ns->set->alias_for != NULL) { + ns->alias_for = mail_namespace_find_prefix(all_namespaces, + ns->set->alias_for); + if (ns->alias_for == NULL) { + *error_r = t_strdup_printf("Invalid namespace alias_for: %s", + ns->set->alias_for); + return -1; + } + if (ns->alias_for->alias_for != NULL) { + *error_r = t_strdup_printf("Chained namespace alias_for: %s", + ns->set->alias_for); + return -1; + } + if (!namespace_is_valid_alias_storage(ns, error_r)) + return -1; + + if ((ns->alias_for->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { + /* copy inbox=yes */ + ns->flags |= NAMESPACE_FLAG_INBOX_USER; + } + + ns->alias_chain_next = ns->alias_for->alias_chain_next; + ns->alias_for->alias_chain_next = ns; + } + return 0; +} + +static bool get_listindex_path(struct mail_namespace *ns, const char **path_r) +{ + const char *root; + + if (ns->list->set.list_index_fname[0] == '\0' || + !mailbox_list_get_root_path(ns->list, + MAILBOX_LIST_PATH_TYPE_LIST_INDEX, + &root)) + return FALSE; + + *path_r = t_strconcat(root, "/", ns->list->set.list_index_fname, NULL); + return TRUE; +} + +static bool +namespace_has_duplicate_listindex(struct mail_namespace *ns, + const char **error_r) +{ + struct mail_namespace *ns2; + const char *ns_list_index_path, *ns_mailboxes_root; + const char *ns2_list_index_path, *ns2_mailboxes_root; + + if (!ns->mail_set->mailbox_list_index) { + /* mailbox list indexes not in use */ + return FALSE; + } + + if (!get_listindex_path(ns, &ns_list_index_path) || + !mailbox_list_get_root_path(ns->list, MAILBOX_LIST_PATH_TYPE_MAILBOX, + &ns_mailboxes_root)) + return FALSE; + + for (ns2 = ns->next; ns2 != NULL; ns2 = ns2->next) { + if (!get_listindex_path(ns2, &ns2_list_index_path) || + !mailbox_list_get_root_path(ns2->list, MAILBOX_LIST_PATH_TYPE_MAILBOX, + &ns2_mailboxes_root)) + continue; + + if (strcmp(ns_list_index_path, ns2_list_index_path) == 0 && + strcmp(ns_mailboxes_root, ns2_mailboxes_root) != 0) { + *error_r = t_strdup_printf( + "Namespaces '%s' and '%s' have different mailboxes paths, but duplicate LISTINDEX path. " + "Add a unique LISTINDEX=<fname>", + ns->prefix, ns2->prefix); + return TRUE; + } + } + return FALSE; +} + +static bool +namespaces_check(struct mail_namespace *namespaces, const char **error_r) +{ + struct mail_namespace *ns, *inbox_ns = NULL; + unsigned int subscriptions_count = 0; + bool visible_namespaces = FALSE, have_list_yes = FALSE; + char ns_sep, list_sep = '\0'; + + for (ns = namespaces; ns != NULL; ns = ns->next) { + ns_sep = mail_namespace_get_sep(ns); + if (mail_namespace_find_prefix(ns->next, ns->prefix) != NULL) { + *error_r = t_strdup_printf( + "Duplicate namespace prefix: \"%s\"", + ns->prefix); + return FALSE; + } + if ((ns->flags & NAMESPACE_FLAG_HIDDEN) == 0) + visible_namespaces = TRUE; + /* check the inbox=yes status before alias_for changes it */ + if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { + if (inbox_ns != NULL) { + *error_r = "There can be only one namespace with " + "inbox=yes"; + return FALSE; + } + inbox_ns = ns; + } + if (namespace_set_alias_for(ns, namespaces, error_r) < 0) + return FALSE; + if (namespace_has_duplicate_listindex(ns, error_r)) + return FALSE; + + if (*ns->prefix != '\0' && + (ns->flags & (NAMESPACE_FLAG_LIST_PREFIX | + NAMESPACE_FLAG_LIST_CHILDREN)) != 0 && + ns->prefix[strlen(ns->prefix)-1] != ns_sep) { + *error_r = t_strdup_printf( + "list=yes requires prefix=%s " + "to end with separator %c", ns->prefix, ns_sep); + return FALSE; + } + if (*ns->prefix != '\0' && + (ns->flags & (NAMESPACE_FLAG_LIST_PREFIX | + NAMESPACE_FLAG_LIST_CHILDREN)) != 0 && + ns->prefix[0] == ns_sep) { + *error_r = t_strdup_printf( + "list=yes requires prefix=%s " + "not to start with separator", ns->prefix); + return FALSE; + } + if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX | + NAMESPACE_FLAG_LIST_CHILDREN)) != 0) { + if ((ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0) + have_list_yes = TRUE; + if (list_sep == '\0') + list_sep = ns_sep; + else if (list_sep != ns_sep) { + *error_r = "All list=yes namespaces must use " + "the same separator"; + return FALSE; + } + } + if ((ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0) + subscriptions_count++; + } + + if (inbox_ns == NULL) { + *error_r = "inbox=yes namespace missing"; + return FALSE; + } + if (!have_list_yes) { + *error_r = "list=yes namespace missing"; + return FALSE; + } + if (!visible_namespaces) { + *error_r = "hidden=no namespace missing"; + return FALSE; + } + if (subscriptions_count == 0) { + *error_r = "subscriptions=yes namespace missing"; + return FALSE; + } + return TRUE; +} + +int mail_namespaces_init_finish(struct mail_namespace *namespaces, + const char **error_r) +{ + struct mail_namespace *ns; + bool prefixless_found = FALSE; + + i_assert(namespaces != NULL); + + for (ns = namespaces; ns != NULL; ns = ns->next) { + if (ns->prefix_len == 0) + prefixless_found = TRUE; + } + if (!prefixless_found) { + prefixless_ns_set = prefixless_ns_unexpanded_set; + /* a pretty evil way to expand the values */ + prefixless_ns_set.prefix++; + prefixless_ns_set.location++; + + if (mail_namespaces_init_add(namespaces->user, + &prefixless_ns_set, + &prefixless_ns_unexpanded_set, + &ns, error_r) < 0) + i_unreached(); + ns->next = namespaces; + namespaces = ns; + } + if (namespaces->user->autocreated) { + /* e.g. raw user - don't check namespaces' validity */ + } else if (!namespaces_check(namespaces, error_r)) { + namespaces->user->error = + t_strconcat("namespace configuration error: ", + *error_r, NULL); + } + + if (namespaces->user->error == NULL) { + mail_user_add_namespace(namespaces->user, &namespaces); + T_BEGIN { + hook_mail_namespaces_created(namespaces); + } T_END; + } + + /* allow namespace hooks to return failure via the user error */ + if (namespaces->user->error != NULL) { + namespaces->user->namespaces = NULL; + *error_r = t_strdup(namespaces->user->error); + while (namespaces != NULL) { + ns = namespaces; + namespaces = ns->next; + mail_namespace_free(ns); + } + return -1; + } + + namespaces->user->namespaces_created = TRUE; + return 0; +} + +int mail_namespaces_init(struct mail_user *user, const char **error_r) +{ + struct mail_namespace_settings *const *ns_set; + struct mail_namespace_settings *const *unexpanded_ns_set; + struct mail_namespace *namespaces, **ns_p; + unsigned int i, count, count2; + + i_assert(user->initialized); + + namespaces = NULL; ns_p = &namespaces; + + if (array_is_created(&user->set->namespaces)) { + ns_set = array_get(&user->set->namespaces, &count); + unexpanded_ns_set = + array_get(&user->unexpanded_set->namespaces, &count2); + i_assert(count == count2); + } else { + ns_set = unexpanded_ns_set = NULL; + count = 0; + } + for (i = 0; i < count; i++) { + if (ns_set[i]->disabled) + continue; + + if (mail_namespaces_init_add(user, ns_set[i], + unexpanded_ns_set[i], + ns_p, error_r) < 0) { + if (!ns_set[i]->ignore_on_failure) { + mail_namespaces_deinit(&namespaces); + return -1; + } + e_debug(user->event, "Skipping namespace %s: %s", + ns_set[i]->prefix, *error_r); + } else { + ns_p = &(*ns_p)->next; + } + } + + if (namespaces == NULL) { + /* no namespaces defined, create a default one */ + return mail_namespaces_init_location(user, NULL, error_r); + } + return mail_namespaces_init_finish(namespaces, error_r); +} + +int mail_namespaces_init_location(struct mail_user *user, const char *location, + const char **error_r) +{ + struct mail_namespace_settings *inbox_set, *unexpanded_inbox_set; + struct mail_namespace *ns; + const struct mail_storage_settings *mail_set; + const char *error, *driver, *location_source; + bool default_location = FALSE; + int ret; + + i_assert(location == NULL || *location != '\0'); + + inbox_set = p_new(user->pool, struct mail_namespace_settings, 1); + *inbox_set = mail_namespace_default_settings; + inbox_set->inbox = TRUE; + /* enums must be changed */ + inbox_set->type = "private"; + inbox_set->list = "yes"; + + unexpanded_inbox_set = p_new(user->pool, struct mail_namespace_settings, 1); + *unexpanded_inbox_set = *inbox_set; + + driver = NULL; + mail_set = mail_user_set_get_storage_set(user); + if (location != NULL) { + inbox_set->location = p_strdup(user->pool, location); + location_source = "mail_location parameter"; + } else if (*mail_set->mail_location != '\0') { + location_source = "mail_location setting"; + inbox_set->location = mail_set->mail_location; + default_location = TRUE; + } else { + location_source = "environment MAIL"; + inbox_set->location = getenv("MAIL"); + } + if (inbox_set->location == NULL) { + /* support also maildir-specific environment */ + inbox_set->location = getenv("MAILDIR"); + if (inbox_set->location == NULL) + inbox_set->location = ""; + else { + driver = "maildir"; + location_source = "environment MAILDIR"; + } + } + if (default_location) { + /* treat this the same as if a namespace was created with + default settings. dsync relies on finding a namespace + without explicit location setting. */ + unexpanded_inbox_set->location = SETTING_STRVAR_UNEXPANDED; + } else { + unexpanded_inbox_set->location = + p_strconcat(user->pool, SETTING_STRVAR_EXPANDED, + inbox_set->location, NULL); + } + + if ((ret = mail_namespace_alloc(user, user->set, + inbox_set, unexpanded_inbox_set, + &ns, error_r)) < 0) + return ret; + + if (mail_storage_create(ns, driver, 0, &error) < 0) { + if (*inbox_set->location != '\0') { + *error_r = t_strdup_printf( + "Initializing mail storage from %s " + "failed: %s", location_source, error); + } else { + *error_r = t_strdup_printf("mail_location not set and " + "autodetection failed: %s", error); + } + mail_namespace_free(ns); + return -1; + } + return mail_namespaces_init_finish(ns, error_r); +} + +struct mail_namespace *mail_namespaces_init_empty(struct mail_user *user) +{ + struct mail_namespace *ns; + + ns = i_new(struct mail_namespace, 1); + ns->refcount = 1; + ns->user = user; + ns->owner = user; + ns->prefix = i_strdup(""); + ns->flags = NAMESPACE_FLAG_INBOX_USER | NAMESPACE_FLAG_INBOX_ANY | + NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_SUBSCRIPTIONS; + ns->user_set = user->set; + ns->mail_set = mail_user_set_get_storage_set(user); + i_array_init(&ns->all_storages, 2); + return ns; +} + +void mail_namespaces_deinit(struct mail_namespace **_namespaces) +{ + struct mail_namespace *ns, *next; + + /* update *_namespaces as needed, instead of immediately setting it + to NULL. for example mdbox_storage.destroy() wants to go through + user's namespaces. */ + while (*_namespaces != NULL) { + ns = *_namespaces; + next = ns->next; + + mail_namespace_free(ns); + *_namespaces = next; + } +} + +void mail_namespaces_set_storage_callbacks(struct mail_namespace *namespaces, + struct mail_storage_callbacks *callbacks, + void *context) +{ + struct mail_namespace *ns; + struct mail_storage *storage; + + for (ns = namespaces; ns != NULL; ns = ns->next) { + array_foreach_elem(&ns->all_storages, storage) + mail_storage_set_callbacks(storage, callbacks, context); + } +} + +void mail_namespace_ref(struct mail_namespace *ns) +{ + i_assert(ns->refcount > 0); + + ns->refcount++; +} + +void mail_namespace_unref(struct mail_namespace **_ns) +{ + struct mail_namespace *ns = *_ns; + + i_assert(ns->refcount > 0); + + *_ns = NULL; + + if (--ns->refcount > 0) + return; + + i_assert(ns->destroyed); + mail_namespace_free(ns); +} + +void mail_namespace_destroy(struct mail_namespace *ns) +{ + struct mail_namespace **nsp; + + i_assert(!ns->destroyed); + + /* remove from user's namespaces list */ + for (nsp = &ns->user->namespaces; *nsp != NULL; nsp = &(*nsp)->next) { + if (*nsp == ns) { + *nsp = ns->next; + break; + } + } + ns->destroyed = TRUE; + + mail_namespace_unref(&ns); +} + +struct mail_storage * +mail_namespace_get_default_storage(struct mail_namespace *ns) +{ + return ns->storage; +} + +char mail_namespace_get_sep(struct mail_namespace *ns) +{ + return *ns->set->separator != '\0' ? *ns->set->separator : + mailbox_list_get_hierarchy_sep(ns->list); +} + +char mail_namespaces_get_root_sep(struct mail_namespace *namespaces) +{ + while ((namespaces->flags & NAMESPACE_FLAG_LIST_PREFIX) == 0) + namespaces = namespaces->next; + return mail_namespace_get_sep(namespaces); +} + +static bool mail_namespace_is_usable_prefix(struct mail_namespace *ns, + const char *mailbox, bool inbox) +{ + if (strncmp(ns->prefix, mailbox, ns->prefix_len) == 0) { + /* true exact prefix match */ + return TRUE; + } + + if (inbox && str_begins(ns->prefix, "INBOX") && + strncmp(ns->prefix+5, mailbox+5, ns->prefix_len-5) == 0) { + /* we already checked that mailbox begins with case-insensitive + INBOX. this namespace also begins with INBOX and the rest + of the prefix matches too. */ + return TRUE; + } + + if (strncmp(ns->prefix, mailbox, ns->prefix_len-1) == 0 && + mailbox[ns->prefix_len-1] == '\0' && + ns->prefix[ns->prefix_len-1] == mail_namespace_get_sep(ns)) { + /* we're trying to access the namespace prefix itself */ + return TRUE; + } + return FALSE; +} + +static struct mail_namespace * +mail_namespace_find_mask(struct mail_namespace *namespaces, const char *box, + enum namespace_flags flags, + enum namespace_flags mask) +{ + struct mail_namespace *ns = namespaces; + struct mail_namespace *best = NULL; + size_t best_len = 0; + bool inbox; + + inbox = strncasecmp(box, "INBOX", 5) == 0; + if (inbox && box[5] == '\0') { + /* find the INBOX namespace */ + while (ns != NULL) { + if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 && + (ns->flags & mask) == flags) + return ns; + if (*ns->prefix == '\0') + best = ns; + ns = ns->next; + } + return best; + } + + for (; ns != NULL; ns = ns->next) { + if (ns->prefix_len >= best_len && (ns->flags & mask) == flags && + mail_namespace_is_usable_prefix(ns, box, inbox)) { + best = ns; + best_len = ns->prefix_len; + } + } + return best; +} + +static struct mail_namespace * +mail_namespace_find_shared(struct mail_namespace *ns, const char *mailbox) +{ + struct mailbox_list *list = ns->list; + struct mail_storage *storage; + + if (mailbox_list_get_storage(&list, mailbox, &storage) < 0) + return ns; + + return mailbox_list_get_namespace(list); +} + +struct mail_namespace * +mail_namespace_find(struct mail_namespace *namespaces, const char *mailbox) +{ + struct mail_namespace *ns; + + ns = mail_namespace_find_mask(namespaces, mailbox, 0, 0); + i_assert(ns != NULL); + + if (mail_namespace_is_shared_user_root(ns)) { + /* see if we need to autocreate a namespace for shared user */ + if (strchr(mailbox, mail_namespace_get_sep(ns)) != NULL) + return mail_namespace_find_shared(ns, mailbox); + } + return ns; +} + +struct mail_namespace * +mail_namespace_find_unalias(struct mail_namespace *namespaces, + const char **mailbox) +{ + struct mail_namespace *ns; + const char *storage_name; + + ns = mail_namespace_find(namespaces, *mailbox); + if (ns->alias_for != NULL) { + storage_name = + mailbox_list_get_storage_name(ns->list, *mailbox); + ns = ns->alias_for; + *mailbox = mailbox_list_get_vname(ns->list, storage_name); + } + return ns; +} + +struct mail_namespace * +mail_namespace_find_visible(struct mail_namespace *namespaces, + const char *mailbox) +{ + return mail_namespace_find_mask(namespaces, mailbox, 0, + NAMESPACE_FLAG_HIDDEN); +} + +struct mail_namespace * +mail_namespace_find_subscribable(struct mail_namespace *namespaces, + const char *mailbox) +{ + return mail_namespace_find_mask(namespaces, mailbox, + NAMESPACE_FLAG_SUBSCRIPTIONS, + NAMESPACE_FLAG_SUBSCRIPTIONS); +} + +struct mail_namespace * +mail_namespace_find_unsubscribable(struct mail_namespace *namespaces, + const char *mailbox) +{ + return mail_namespace_find_mask(namespaces, mailbox, + 0, NAMESPACE_FLAG_SUBSCRIPTIONS); +} + +struct mail_namespace * +mail_namespace_find_inbox(struct mail_namespace *namespaces) +{ + i_assert(namespaces != NULL); + + /* there should always be an INBOX */ + while ((namespaces->flags & NAMESPACE_FLAG_INBOX_USER) == 0) { + namespaces = namespaces->next; + i_assert(namespaces != NULL); + } + return namespaces; +} + +struct mail_namespace * +mail_namespace_find_prefix(struct mail_namespace *namespaces, + const char *prefix) +{ + struct mail_namespace *ns; + size_t len = strlen(prefix); + + for (ns = namespaces; ns != NULL; ns = ns->next) { + if (ns->prefix_len == len && + strcmp(ns->prefix, prefix) == 0) + return ns; + } + return NULL; +} + +struct mail_namespace * +mail_namespace_find_prefix_nosep(struct mail_namespace *namespaces, + const char *prefix) +{ + struct mail_namespace *ns; + size_t len = strlen(prefix); + + for (ns = namespaces; ns != NULL; ns = ns->next) { + if (ns->prefix_len == len + 1 && + strncmp(ns->prefix, prefix, len) == 0 && + ns->prefix[len] == mail_namespace_get_sep(ns)) + return ns; + } + return NULL; +} + +bool mail_namespace_is_shared_user_root(struct mail_namespace *ns) +{ + struct mail_storage *storage; + + if (ns->type != MAIL_NAMESPACE_TYPE_SHARED) + return FALSE; + if ((ns->flags & NAMESPACE_FLAG_AUTOCREATED) != 0) { + /* child of the shared root */ + return FALSE; + } + /* if we have driver=shared storage, we're a real shared root */ + array_foreach_elem(&ns->all_storages, storage) { + if (strcmp(storage->name, MAIL_SHARED_STORAGE_NAME) == 0) + return TRUE; + } + return FALSE; +} |