mail-namespace.c revision 142476eefd60e583ffa540e70dfe408f58650b59
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "lib.h"
49e358eebea107aad9919dcc4bd88cee8519ba2eTimo Sirainen#include "array.h"
49e358eebea107aad9919dcc4bd88cee8519ba2eTimo Sirainen#include "str.h"
49e358eebea107aad9919dcc4bd88cee8519ba2eTimo Sirainen#include "file-lock.h"
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include "mail-storage-private.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "mail-storage-settings.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "mail-namespace.h"
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen#include <stdlib.h>
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainenvoid (*hook_mail_namespaces_created)(struct mail_namespace *namespaces);
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainenvoid mail_namespace_add_storage(struct mail_namespace *ns,
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen struct mail_storage *storage)
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen{
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen /* currently we support only a single storage */
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen i_assert(ns->storage == NULL);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ns->storage = storage;
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (storage->v.add_list != NULL)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen storage->v.add_list(storage, ns->list);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen}
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainenvoid mail_namespace_finish_list_init(struct mail_namespace *ns,
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen struct mailbox_list *list)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen{
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen ns->list = list;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ns->real_sep = mailbox_list_get_hierarchy_sep(list);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen ns->prefix_len = strlen(ns->prefix);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (ns->set->separator != NULL)
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen ns->sep = *ns->set->separator;
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen if (ns->sep == '\0')
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi ns->sep = ns->real_sep;
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen if (ns->sep == '"' || ns->sep == '\\') {
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen ns->sep_str[0] = '\\';
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen ns->sep_str[1] = ns->sep;
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen } else {
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen ns->sep_str[0] = ns->sep;
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen }
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen}
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainenstatic void mail_namespace_free(struct mail_namespace *ns)
212a34c06ff45952c008ae9eec387ced783de6cfPhil Carmody{
212a34c06ff45952c008ae9eec387ced783de6cfPhil Carmody if (ns->storage != NULL)
212a34c06ff45952c008ae9eec387ced783de6cfPhil Carmody mail_storage_unref(&ns->storage);
212a34c06ff45952c008ae9eec387ced783de6cfPhil Carmody if (ns->list != NULL)
10c96a244935de4add8213ba0b894178dfb889a5Timo Sirainen mailbox_list_destroy(&ns->list);
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen if (ns->owner != ns->user && ns->owner != NULL)
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen mail_user_unref(&ns->owner);
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen i_free(ns->prefix);
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen i_free(ns);
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainenstatic int
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainennamespace_add(struct mail_user *user,
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen struct mail_namespace_settings *ns_set,
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen const struct mail_storage_settings *mail_set,
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen struct mail_namespace *prev_namespaces,
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen struct mail_namespace **ns_p, const char **error_r)
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen{
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen struct mail_namespace *ns;
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen const char *driver, *error;
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ns = i_new(struct mail_namespace, 1);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ns->user = user;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (strncmp(ns_set->type, "private", 7) == 0) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen ns->owner = user;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen ns->type = NAMESPACE_PRIVATE;
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen } else if (strncmp(ns_set->type, "shared", 6) == 0)
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen ns->type = NAMESPACE_SHARED;
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen else if (strncmp(ns_set->type, "public", 6) == 0)
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen ns->type = NAMESPACE_PUBLIC;
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen else {
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen *error_r = t_strdup_printf("Unknown namespace type: %s",
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainen ns_set->type);
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainen mail_namespace_free(ns);
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen return -1;
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen }
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (strcmp(ns_set->list, "children") == 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ns->flags |= NAMESPACE_FLAG_LIST_CHILDREN;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen else if (strcmp(ns_set->list, "yes") == 0)
46ce4d9273e6df12ef1912bbdb1c8b84b104f394Timo Sirainen ns->flags |= NAMESPACE_FLAG_LIST_PREFIX;
46ce4d9273e6df12ef1912bbdb1c8b84b104f394Timo Sirainen else if (strcmp(ns_set->list, "no") != 0) {
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen *error_r = t_strdup_printf("Invalid list setting value: %s",
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen ns_set->list);
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen mail_namespace_free(ns);
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen return -1;
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen if (ns_set->inbox)
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen ns->flags |= NAMESPACE_FLAG_INBOX;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (ns_set->hidden)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen ns->flags |= NAMESPACE_FLAG_HIDDEN;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (ns_set->subscriptions)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen ns->flags |= NAMESPACE_FLAG_SUBSCRIPTIONS;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen if (ns_set->alias_for != NULL) {
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen ns->alias_for = mail_namespace_find_prefix(prev_namespaces,
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen ns_set->alias_for);
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen if (ns->alias_for == NULL) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *error_r = t_strdup_printf("Invalid namespace alias_for: %s",
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen ns_set->alias_for);
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen mail_namespace_free(ns);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen return -1;
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen }
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen if (ns->alias_for->alias_for != NULL) {
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen *error_r = t_strdup_printf("Chained namespace alias_for: %s",
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen ns_set->alias_for);
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen mail_namespace_free(ns);
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen return -1;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen }
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen ns->alias_chain_next = ns->alias_for->alias_chain_next;
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen ns->alias_for->alias_chain_next = ns;
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen }
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen if (mail_set->mail_debug) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_info("Namespace: type=%s, prefix=%s, sep=%s, "
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "inbox=%s, hidden=%s, list=%s, subscriptions=%s",
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ns_set->type, ns_set->prefix,
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen ns_set->separator == NULL ? "" : ns_set->separator,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ns_set->inbox ? "yes" : "no",
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ns_set->hidden ? "yes" : "no",
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen ns_set->list ? "yes" : "no",
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ns_set->subscriptions ? "yes" : "no");
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen }
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (*ns_set->location == '\0')
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen ns_set->location = mail_set->mail_location;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen ns->set = ns_set;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen ns->mail_set = mail_set;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen ns->prefix = i_strdup(ns_set->prefix);
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (ns->type == NAMESPACE_SHARED && strchr(ns->prefix, '%') != NULL) {
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen /* dynamic shared namespace */
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen ns->flags |= NAMESPACE_FLAG_NOQUOTA | NAMESPACE_FLAG_NOACL;
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen driver = "shared";
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen } else {
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen driver = NULL;
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen }
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if (mail_storage_create(ns, driver, 0, &error) < 0) {
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen *error_r = t_strdup_printf("Namespace '%s': %s",
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen ns->prefix, error);
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen mail_namespace_free(ns);
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen return -1;
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen }
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen *ns_p = ns;
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen return 0;
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen}
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainenstatic bool
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainennamespaces_check(struct mail_namespace *namespaces, const char **error_r)
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen{
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen struct mail_namespace *ns, *inbox_ns = NULL;
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen unsigned int subscriptions_count = 0;
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen char list_sep = '\0';
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen for (ns = namespaces; ns != NULL; ns = ns->next) {
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen if ((ns->flags & NAMESPACE_FLAG_INBOX) != 0) {
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen if (inbox_ns != NULL) {
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen *error_r = "namespace configuration error: "
6657aee0bb6c603b4ee5111388b93c1a8a9ad680Martti Rannanjärvi "There can be only one namespace with "
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen "inbox=yes";
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen return FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen inbox_ns = ns;
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen }
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if (*ns->prefix != '\0' &&
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen (ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0 &&
699fdc186f982f70d990820796eaa0f12133e27cTimo Sirainen ns->prefix[strlen(ns->prefix)-1] != ns->sep) {
699fdc186f982f70d990820796eaa0f12133e27cTimo Sirainen *error_r = t_strdup_printf(
699fdc186f982f70d990820796eaa0f12133e27cTimo Sirainen "namespace configuration error: "
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen "list=yes requires prefix=%s "
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen "to end with separator", ns->prefix);
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody return FALSE;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody }
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody NAMESPACE_FLAG_LIST_CHILDREN)) != 0) {
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody if (list_sep == '\0')
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody list_sep = ns->sep;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody else if (list_sep != ns->sep) {
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody *error_r = "namespace configuration error: "
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody "All list=yes namespaces must use "
573424407a2d3c1453638a643583a7cf10c129e1Phil Carmody "the same separator";
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody return FALSE;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody }
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody }
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody if ((ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0)
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody subscriptions_count++;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody }
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody if (inbox_ns == NULL) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *error_r = "namespace configuration error: "
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen "inbox=yes namespace missing";
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen return FALSE;
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen }
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen if (list_sep == '\0') {
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen *error_r = "namespace configuration error: "
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen "no list=yes namespaces";
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen return FALSE;
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen }
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen if (subscriptions_count == 0) {
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen *error_r = "namespace configuration error: "
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen "no subscriptions=yes namespaces";
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return FALSE;
}
return TRUE;
}
int mail_namespaces_init(struct mail_user *user, const char **error_r)
{
const struct mail_storage_settings *mail_set;
struct mail_namespace_settings *const *ns_set;
struct mail_namespace *namespaces, *ns, **ns_p;
struct mail_namespace_settings *inbox_set;
const char *error, *driver, *env;
unsigned int i, count;
i_assert(user->initialized);
namespaces = NULL; ns_p = &namespaces;
mail_set = mail_user_set_get_storage_set(user->set);
if (array_is_created(&user->set->namespaces))
ns_set = array_get(&user->set->namespaces, &count);
else {
ns_set = NULL;
count = 0;
}
for (i = 0; i < count; i++) {
if (namespace_add(user, ns_set[i], mail_set, namespaces,
ns_p, error_r) < 0)
return -1;
ns_p = &(*ns_p)->next;
}
if (namespaces != NULL) {
if (!namespaces_check(namespaces, error_r)) {
while (namespaces != NULL) {
ns = namespaces;
namespaces = ns->next;
mail_namespace_free(ns);
}
return -1;
}
mail_user_add_namespace(user, &namespaces);
if (hook_mail_namespaces_created != NULL) {
T_BEGIN {
hook_mail_namespaces_created(namespaces);
} T_END;
}
return 0;
}
/* fallback to using environment variables */
ns = i_new(struct mail_namespace, 1);
ns->type = NAMESPACE_PRIVATE;
ns->flags = NAMESPACE_FLAG_INBOX | NAMESPACE_FLAG_LIST_PREFIX |
NAMESPACE_FLAG_SUBSCRIPTIONS;
ns->owner = user;
inbox_set = p_new(user->pool, struct mail_namespace_settings, 1);
*inbox_set = mail_namespace_default_settings;
inbox_set->inbox = TRUE;
driver = NULL;
env = "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";
env = "MAILDIR";
}
}
ns->set = inbox_set;
ns->mail_set = mail_set;
ns->prefix = i_strdup(ns->set->prefix);
ns->user = user;
if (mail_storage_create(ns, driver, 0, &error) < 0) {
if (*inbox_set->location != '\0') {
*error_r = t_strdup_printf(
"Initializing mail storage from environment %s "
"failed: %s", env, error);
} else {
*error_r = t_strdup_printf("mail_location not set and "
"autodetection failed: %s", error);
}
mail_namespace_free(ns);
return -1;
}
user->namespaces = ns;
if (hook_mail_namespaces_created != NULL) {
T_BEGIN {
hook_mail_namespaces_created(ns);
} T_END;
}
return 0;
}
struct mail_namespace *mail_namespaces_init_empty(struct mail_user *user)
{
struct mail_namespace *ns;
ns = i_new(struct mail_namespace, 1);
ns->user = user;
ns->owner = user;
ns->prefix = i_strdup("");
ns->flags = NAMESPACE_FLAG_INBOX | NAMESPACE_FLAG_LIST_PREFIX |
NAMESPACE_FLAG_SUBSCRIPTIONS;
ns->mail_set = mail_user_set_get_storage_set(user->set);
user->namespaces = ns;
return ns;
}
void mail_namespaces_deinit(struct mail_namespace **_namespaces)
{
struct mail_namespace *ns, *namespaces = *_namespaces;
*_namespaces = NULL;
while (namespaces != NULL) {
ns = namespaces;
namespaces = namespaces->next;
mail_namespace_free(ns);
}
}
void mail_namespaces_set_storage_callbacks(struct mail_namespace *namespaces,
struct mail_storage_callbacks *callbacks,
void *context)
{
struct mail_namespace *ns;
for (ns = namespaces; ns != NULL; ns = ns->next)
mail_storage_set_callbacks(ns->storage, callbacks, context);
}
void mail_namespace_destroy(struct mail_namespace *ns)
{
struct mail_namespace **nsp;
/* remove from user's namespaces list */
for (nsp = &ns->user->namespaces; *nsp != NULL; nsp = &(*nsp)->next) {
if (*nsp == ns) {
*nsp = ns->next;
break;
}
}
mail_namespace_free(ns);
}
const char *mail_namespace_fix_sep(struct mail_namespace *ns, const char *name)
{
char *ret, *p;
if (ns->sep == ns->real_sep)
return name;
if (ns->type == NAMESPACE_SHARED &&
(ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) {
/* shared namespace root. the backend storage's hierarchy
separator isn't known yet, so do nothing. */
return name;
}
ret = p_strdup(unsafe_data_stack_pool, name);
for (p = ret; *p != '\0'; p++) {
if (*p == ns->sep)
*p = ns->real_sep;
}
return ret;
}
const char *mail_namespace_get_storage_name(struct mail_namespace *ns,
const char *name)
{
unsigned int len = strlen(ns->prefix);
if (len > 0) {
if (strncmp(ns->prefix, name, len) == 0)
name += len;
else {
i_assert(strcasecmp(name, "INBOX") == 0);
}
}
return mail_namespace_fix_sep(ns, name);
}
const char *mail_namespace_get_vname(struct mail_namespace *ns, string_t *dest,
const char *name)
{
str_truncate(dest, 0);
if ((ns->flags & NAMESPACE_FLAG_INBOX) == 0 ||
strcasecmp(name, "INBOX") != 0 ||
ns->user != ns->owner)
str_append(dest, ns->prefix);
for (; *name != '\0'; name++) {
if (*name == ns->real_sep)
str_append_c(dest, ns->sep);
else
str_append_c(dest, *name);
}
return str_c(dest);
}
struct mail_storage *
mail_namespace_get_default_storage(struct mail_namespace *ns)
{
/* currently we don't support more than one storage per namespace */
return ns->storage;
}
char mail_namespaces_get_root_sep(const struct mail_namespace *namespaces)
{
while ((namespaces->flags & NAMESPACE_FLAG_LIST_PREFIX) == 0)
namespaces = namespaces->next;
return namespaces->sep;
}
static struct mail_namespace *
mail_namespace_find_mask(struct mail_namespace *namespaces,
const char **mailbox,
enum namespace_flags flags,
enum namespace_flags mask)
{
struct mail_namespace *ns = namespaces;
const char *box = *mailbox;
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 */
*mailbox = "INBOX";
while (ns != NULL) {
if ((ns->flags & NAMESPACE_FLAG_INBOX) != 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 &&
(strncmp(ns->prefix, box, ns->prefix_len) == 0 ||
(inbox && strncmp(ns->prefix, "INBOX", 5) == 0 &&
strncmp(ns->prefix+5, box+5, ns->prefix_len-5) == 0)) &&
(ns->flags & mask) == flags) {
best = ns;
best_len = ns->prefix_len;
}
}
if (best != NULL) {
if (best_len > 0)
*mailbox += best_len;
else if (inbox && (box[5] == best->sep || box[5] == '\0'))
*mailbox = t_strconcat("INBOX", box+5, NULL);
*mailbox = mail_namespace_fix_sep(best, *mailbox);
}
return best;
}
struct mail_namespace *
mail_namespace_find(struct mail_namespace *namespaces, const char **mailbox)
{
return mail_namespace_find_mask(namespaces, mailbox, 0, 0);
}
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)
{
while ((namespaces->flags & NAMESPACE_FLAG_INBOX) == 0)
namespaces = namespaces->next;
return namespaces;
}
bool mail_namespace_update_name(const struct mail_namespace *ns,
const char **mailbox)
{
struct mail_namespace tmp_ns = *ns;
/* FIXME: a bit kludgy.. */
tmp_ns.next = NULL;
return mail_namespace_find_mask(&tmp_ns, mailbox, 0, 0) != NULL;
}
struct mail_namespace *
mail_namespace_find_prefix(struct mail_namespace *namespaces,
const char *prefix)
{
struct mail_namespace *ns;
unsigned int 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;
unsigned int 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] == ns->sep)
return ns;
}
return NULL;
}