@@ -2127,8 +2127,8 @@ struct find_rock {
ptrarray_t globs;
struct namespace *namespace;
const char *userid;
const char *domain;
int find_namespace;
int domainlen;
int is_the_inbox;
int checkmboxlist;
int issubs;
@@ -2137,6 +2137,9 @@ struct find_rock {
struct db *db;
int isadmin;
const struct auth_state *auth_state;
mbname_t *mbname;
mbentry_t *mbentry;
long intmatchlen;
findall_cb *proc;
void *procrock;
};
@@ -2147,42 +2150,43 @@ static int find_p(void *rockp,
const char *data, size_t datalen)
{
struct find_rock *rock = (struct find_rock *) rockp;
mbentry_t *mbentry = NULL;
int ret = 0;
char intname[MAX_MAILBOX_PATH+1];
int i;

memcpy(intname, key, keylen);
intname[keylen] = 0;

/* don't list mailboxes outside of the default domain */
if (!rock->domainlen && !rock->isadmin && strchr(intname, '!')) return 0;
assert(!rock->mbname);
rock->mbname = mbname_from_intname(intname);

/* don't list "user" by itself */
if (!strcmp(intname+rock->domainlen, "user")) return 0;
if (!rock->isadmin && !config_getswitch(IMAPOPT_CROSSDOMAINS)) {
/* don't list mailboxes outside of the default domain */
if (strcmpsafe(rock->domain, mbname_domain(rock->mbname)))
goto nomatch;
}

int isuserspace = !strncmp(intname+rock->domainlen, "user.", 5);
/* XXX - skip 'user' by itself - is that even a thing? */

if (rock->find_namespace == NAMESPACE_INBOX) {
if (!isuserspace) return 0;
if (rock->isadmin) return 0;
if (rock->isadmin) goto nomatch;
if (!mbname_localpart(rock->mbname)) goto nomatch;
}
else if (rock->find_namespace == NAMESPACE_USER) {
/* this would've been output with the inbox stuff, so skip it */
if (!isuserspace) return 0;
if (!rock->isadmin && mboxname_userownsmailbox(rock->userid, intname)) return 0;
if (!mbname_localpart(rock->mbname)) goto nomatch;
if (!rock->isadmin && !strcmp(rock->userid, mbname_userid(rock->mbname))) goto nomatch;
}
else {
/* this would've been output with the user stuff, so skip it */
if (isuserspace) return 0;
if (!mbname_isdeleted(rock->mbname) && mbname_localpart(rock->mbname)) goto nomatch;
}

/* NOTE: this will all be cleaned up to be much more efficient sooner or later, with
* a mbname_t being kept inside the mbentry, and the extname cached all the way to
* final use. For now, we pay the cost of re-calculating for simplicity of the
* changes to mbname_t itself */
char *extname = mboxname_to_external(intname, rock->namespace, rock->userid);
if (!extname) return 0;
const char *extname = mbname_extname(rock->mbname, rock->namespace, rock->userid);
if (!extname) goto nomatch;

long matchlen = -1;
for (i = 0; i < rock->globs.count; i++) {
@@ -2191,112 +2195,99 @@ static int find_p(void *rockp,
if (thismatch > matchlen) matchlen = thismatch;
}

free(extname);

/* If its not a match, skip it -- partial matches are ok. */
if (matchlen == -1) return 0;
if (matchlen == -1) goto nomatch;

/* we need to recalculate matchlen against the internal mailbox name.
* There are various possiblities, but simplest is just to assume the
* same number of characters from the end is the same point */
rock->intmatchlen = matchlen - strlen(extname) + keylen;

/* subs DB has empty keys */
if (rock->issubs)
return 1;

/* ignore entirely deleted records */
if (mboxlist_parse_entry(&mbentry, key, keylen, data, datalen))
return 0;
if (mboxlist_parse_entry(&rock->mbentry, key, keylen, data, datalen))
goto nomatch;

/* nobody sees tombstones */
if (mbentry->mbtype & MBTYPE_DELETED)
goto done;
if (rock->mbentry->mbtype & MBTYPE_DELETED)
goto nomatch;

/* check acl */
if (!rock->isadmin) {
/* always suppress deleted for non-admin */
if (mboxname_isdeletedmailbox(mbentry->name, NULL)) goto done;
if (mbname_isdeleted(rock->mbname)) goto nomatch;

/* check the acls */
if (!(cyrus_acl_myrights(rock->auth_state, mbentry->acl) & ACL_LOOKUP)) goto done;
if (!(cyrus_acl_myrights(rock->auth_state, rock->mbentry->acl) & ACL_LOOKUP)) goto nomatch;
}

/* if we get here, close enough for us to spend the time
acting interested */
ret = 1;

done:
mboxlist_entry_free(&mbentry);
return ret;
return 1;

nomatch:
mboxlist_entry_free(&rock->mbentry);
mbname_free(&rock->mbname);
return 0;
}

static int find_cb(void *rockp,
const char *key, size_t keylen,
/* XXX - confirm these are the same? - nah */
const char *key __attribute__((unused)),
size_t keylen __attribute__((unused)),
const char *data __attribute__((unused)),
size_t datalen __attribute__((unused)))
{
struct find_rock *rock = (struct find_rock *) rockp;
char intname[MAX_MAILBOX_PATH+1];
int i;

/* we passed find_p, so we're a valid thing to output */

memcpy(intname, key, keylen);
intname[keylen] = 0;
int r = 0;

if (rock->checkmboxlist) {
int r = mboxlist_lookup(intname, NULL, NULL);
if (r == IMAP_MAILBOX_NONEXISTENT) return 0;
if (r) return r;
}

/* XXX - cache in the rock? */
char *extname = mboxname_to_external(intname, rock->namespace, rock->userid);
if (!extname) return 0;
int extlen = strlen(extname);

/* re-check the match */
long matchlen = -1;
for (i = 0; i < rock->globs.count; i++) {
glob *g = ptrarray_nth(&rock->globs, i);
long thismatch = glob_test(g, extname);
if (thismatch > matchlen) matchlen = thismatch;
r = mboxlist_lookup(mbname_intname(rock->mbname), NULL, NULL);
if (r) {
if (r == IMAP_MAILBOX_NONEXISTENT) r = 0;
goto done;
}
}

free(extname);

if (matchlen == -1) return 0;

if (rock->find_namespace == NAMESPACE_USER && rock->checkuser) {
/* special case: LIST "" *% -- output prefix */
int r = (*rock->proc)("user", 4, 1, rock->procrock);
if (r) return r;
r = (*rock->proc)("user", 4, 1, rock->procrock);
if (r) goto done;

if (rock->checkuser > 1) {
/* special case: LIST "" % -- output prefix only */
/* short-circuit the foreach - one mailbox is sufficient */
return CYRUSDB_DONE;
r = CYRUSDB_DONE;
goto done;
}
rock->checkuser = 0;
}

/* found an entry; output it */
if (rock->find_namespace == NAMESPACE_SHARED && rock->checkshared) {
/* special case: LIST "" *% -- output shared prefix */
int r = (*rock->proc)("", 0, 1, rock->procrock);
if (r) return r;
r = (*rock->proc)("", 0, 1, rock->procrock);
if (r) goto done;

if (rock->checkshared > 1) {
/* special case: LIST "" % -- output prefix only */
/* short-circuit the foreach - one mailbox is sufficient */
return CYRUSDB_DONE;
r = CYRUSDB_DONE;
goto done;
}
rock->checkshared = 0;
}

/* we need to recalculate matchlen against the internal mailbox name.
* There are various possiblities, but simplest is just to assume the
* same number of characters from the end is the same point */

long intmatchlen = matchlen - extlen + strlen(intname);
r = (*rock->proc)(mbname_intname(rock->mbname), rock->intmatchlen, !rock->is_the_inbox, rock->procrock);

return (*rock->proc)(intname, intmatchlen, !rock->is_the_inbox, rock->procrock);
done:
mboxlist_entry_free(&rock->mbentry);
mbname_free(&rock->mbname);
return r;
}

struct allmb_rock {
@@ -2459,9 +2450,11 @@ static int mboxlist_do_find(struct find_rock *rock, const strarray_t *patterns)
const char *userid = rock->userid;
int isadmin = rock->isadmin;

int crossdomains = config_getswitch(IMAPOPT_CROSSDOMAINS);
char inbox[MAX_MAILBOX_BUFFER];
size_t inboxlen = 0;
size_t prefixlen, len;
size_t domainlen = 0;
size_t userlen = userid ? strlen(userid) : 0;
char domainpat[MAX_MAILBOX_BUFFER]; /* do intra-domain fetches only */
char commonpat[MAX_MAILBOX_BUFFER];
@@ -2476,7 +2469,7 @@ static int mboxlist_do_find(struct find_rock *rock, const strarray_t *patterns)

if (config_virtdomains && userid && (p = strchr(userid, '@'))) {
userlen = p - userid;
rock->domainlen = strlen(p); /* includes separator */
domainlen = strlen(p); /* includes separator */
snprintf(domainpat, sizeof(domainpat), "%s!", p+1);
}
else
@@ -2485,9 +2478,9 @@ static int mboxlist_do_find(struct find_rock *rock, const strarray_t *patterns)
/* calculate the inbox */
if (userid && (!(p = strchr(userid, '.')) || ((p - userid) > (int)userlen)) &&
strlen(userid)+5 < MAX_MAILBOX_BUFFER) {
if (rock->domainlen)
if (domainlen)
snprintf(inbox, sizeof(inbox), "%s!", userid+userlen+1);
snprintf(inbox+rock->domainlen, sizeof(inbox)-rock->domainlen,
snprintf(inbox+domainlen, sizeof(inbox)-domainlen,
"user.%.*s", (int)userlen, userid);
inboxlen = strlen(inbox);
}
@@ -2549,18 +2542,21 @@ static int mboxlist_do_find(struct find_rock *rock, const strarray_t *patterns)
if (!strncmp(rock->namespace->prefix[NAMESPACE_USER], commonpat, MIN(len, prefixlen))) {
if (prefixlen < len) {
/* we match all users */
strlcpy(domainpat+rock->domainlen, "user.", sizeof(domainpat)-rock->domainlen);
strlcpy(domainpat+domainlen, "user.", sizeof(domainpat)-domainlen);
}
else {
/* just those in this prefix */
strlcpy(domainpat+rock->domainlen, "user.", sizeof(domainpat)-rock->domainlen);
strlcpy(domainpat+rock->domainlen+5, commonpat+len+1, sizeof(domainpat)-rock->domainlen-5);
strlcpy(domainpat+domainlen, "user.", sizeof(domainpat)-domainlen);
strlcpy(domainpat+domainlen+5, commonpat+len+1, sizeof(domainpat)-domainlen-5);
}

rock->find_namespace = NAMESPACE_USER;

/* because of how domains work, with crossdomains or admin you can't prefix at all :( */
size_t thislen = (isadmin || crossdomains) ? 0 : strlen(domainpat);

/* iterate through all the other user folders on the server */
r = cyrusdb_foreach(rock->db, domainpat, strlen(domainpat), &find_p, &find_cb, rock, NULL);
r = cyrusdb_foreach(rock->db, domainpat, thislen, &find_p, &find_cb, rock, NULL);
if (r == CYRUSDB_DONE) r = 0;
if (r) goto done;
}
@@ -2601,7 +2597,7 @@ static int mboxlist_do_find(struct find_rock *rock, const strarray_t *patterns)
}

/* iterate through all the non-user folders on the server */
r = cyrusdb_foreach(rock->db, domainpat, rock->domainlen, &find_p, &find_cb, rock, NULL);
r = cyrusdb_foreach(rock->db, domainpat, domainlen, &find_p, &find_cb, rock, NULL);
if (r == CYRUSDB_DONE) r = 0;
if (r) goto done;
}
@@ -2636,6 +2632,10 @@ EXPORTED int mboxlist_findallmulti(struct namespace *namespace,
cbrock.proc = proc;
cbrock.procrock = rock;
cbrock.userid = userid;
if (userid) {
const char *domp = strchr(userid, '@');
if (domp) cbrock.domain = domp + 1;
}

r = mboxlist_do_find(&cbrock, patterns);

@@ -3112,6 +3112,10 @@ EXPORTED int mboxlist_findsubmulti(struct namespace *namespace,
cbrock.proc = proc;
cbrock.procrock = rock;
cbrock.userid = userid;
if (userid) {
const char *domp = strchr(userid, '@');
if (domp) cbrock.domain = domp + 1;
}

r = mboxlist_do_find(&cbrock, patterns);

@@ -469,14 +469,14 @@ EXPORTED mbname_t *mbname_from_intname(const char *intname)
if (!strarray_size(mbname->boxes))
return mbname;

if (strarray_size(mbname->boxes) > 2 && !strcmp(strarray_nth(mbname->boxes, 0), dp)) {
if (strarray_size(mbname->boxes) > 2 && !strcmpsafe(strarray_nth(mbname->boxes, 0), dp)) {
free(strarray_shift(mbname->boxes));
char *delval = strarray_pop(mbname->boxes);
mbname->is_deleted = strtoul(delval, NULL, 16);
free(delval);
}

if (strarray_size(mbname->boxes) > 1 && !strcmp(strarray_nth(mbname->boxes, 0), "user")) {
if (strarray_size(mbname->boxes) > 1 && !strcmpsafe(strarray_nth(mbname->boxes, 0), "user")) {
free(strarray_shift(mbname->boxes));
mbname->localpart = strarray_shift(mbname->boxes);
}
@@ -486,6 +486,10 @@ EXPORTED mbname_t *mbname_from_intname(const char *intname)

EXPORTED mbname_t *mbname_from_extname(const char *extname, const struct namespace *ns, const char *userid)
{
int crossdomains = config_getswitch(IMAPOPT_CROSSDOMAINS) && !ns->isadmin;
/* old-school virtdomains requires admin to be a different domain than the userid */
int admindomains = config_virtdomains && ns->isadmin;

/* specialuse magic */
if (extname && extname[0] == '\\') {
char *intname = mboxlist_find_specialuse(extname, userid);
@@ -496,7 +500,7 @@ EXPORTED mbname_t *mbname_from_extname(const char *extname, const struct namespa

mbname_t *mbname = xzmalloc(sizeof(mbname_t));
char sepstr[2];
const char *domain = NULL;
char *p = NULL;

if (!extname)
return mbname;
@@ -508,23 +512,28 @@ EXPORTED mbname_t *mbname_from_extname(const char *extname, const struct namespa

mbname->extname = xstrdup(extname); // may as well cache it

sepstr[0] = ns->hier_sep;
sepstr[1] = '\0';

char *p = config_virtdomains ? strchr(mbname->extname, '@') : NULL;
if (p) {
if (!ns->isadmin) goto done; /* only admins can do domains */
if (ns->isalt) goto done; /* no domains in altnamespace */
domain = p+1;
if (!strcmpsafe(domain, config_defdomain))
domain = NULL;
*p = '\0'; /* temporary */
if (admindomains) {
p = strchr(mbname->extname, '@');
if (p) {
*p = '\0';
if (strcmpsafe(p+1, config_defdomain))
mbname->domain = xstrdup(p+1);
}
else {
// domain admin?
mbname->domain = xstrdupnull(mbname_domain(userparts));
}
}
else if (!crossdomains) {
// non-crossdomains, we're always in the user's domain
mbname->domain = xstrdupnull(mbname_domain(userparts));
}

mbname->domain = xstrdupnull(domain ? domain : mbname_domain(userparts));
sepstr[0] = ns->hier_sep;
sepstr[1] = '\0';

mbname->boxes = strarray_split(mbname->extname, sepstr, 0);
if (p) *p = '@'; /* repair */
if (p) *p = '@'; // rebuild extname for later use

if (!strarray_size(mbname->boxes))
goto done;
@@ -535,25 +544,38 @@ EXPORTED mbname_t *mbname_from_extname(const char *extname, const struct namespa
const char *up = config_getstring(IMAPOPT_USERPREFIX);
const char *sp = config_getstring(IMAPOPT_SHAREDPREFIX);

if (!strcmp(strarray_nth(mbname->boxes, 0), up)) {
if (!strcmpsafe(strarray_nth(mbname->boxes, 0), up)) {
/* other user namespace */
free(strarray_shift(mbname->boxes));
/* XXX - cross domain support. For now, it's always in the
* userid's domain, if any */
mbname->localpart = strarray_shift(mbname->boxes);
if (crossdomains) {
char *p = strchr(mbname->localpart, '@');
if (p) {
*p = '\0';
if (strcmpsafe(p+1, config_defdomain))
mbname->domain = xstrdup(p+1);
}
/* otherwise it must be in defdomain. Domains are
* always specified in crossdomains */
}
goto done;
}

if (!strcmp(strarray_nth(mbname->boxes, 0), sp)) {
if (!strcmpsafe(strarray_nth(mbname->boxes, 0), sp)) {
/* shared namespace, no user */
free(strarray_shift(mbname->boxes));
/* unless it's admindomains, we need to copy the user's domain */
if ((config_virtdomains && !ns->isadmin) || crossdomains)
mbname->domain = xstrdupnull(mbname_domain(userparts));
goto done;
}

/* everything else belongs to the userid */
mbname->localpart = xstrdupnull(mbname_localpart(userparts));
/* otherwise it was done above */
if (crossdomains) mbname->domain = xstrdupnull(mbname_domain(userparts));
/* special case pure inbox with case, because horrible */
if (strarray_size(mbname->boxes) == 1 && !strcasecmp(strarray_nth(mbname->boxes, 0), "INBOX"))
if (strarray_size(mbname->boxes) == 1 && !strcasecmpsafe(strarray_nth(mbname->boxes, 0), "INBOX"))
free(strarray_shift(mbname->boxes));

goto done;
@@ -562,14 +584,16 @@ EXPORTED mbname_t *mbname_from_extname(const char *extname, const struct namespa
const char *dp = config_getstring(IMAPOPT_DELETEDPREFIX);

/* special inbox with insensitivity still, because horrible */
if (!strcasecmp(strarray_nth(mbname->boxes, 0), "INBOX")) {
if (!strcasecmpsafe(strarray_nth(mbname->boxes, 0), "INBOX")) {
free(strarray_shift(mbname->boxes));
mbname->localpart = xstrdupnull(mbname_localpart(userparts));
/* otherwise it was done above */
if (crossdomains) mbname->domain = xstrdupnull(mbname_domain(userparts));
goto done;
}

/* deleted prefix first */
if (!strcmp(strarray_nth(mbname->boxes, 0), dp)) {
if (ns->isadmin && !strcmpsafe(strarray_nth(mbname->boxes, 0), dp)) {
free(strarray_shift(mbname->boxes));
char *delval = strarray_pop(mbname->boxes);
if (!delval)
@@ -582,12 +606,24 @@ EXPORTED mbname_t *mbname_from_extname(const char *extname, const struct namespa
goto done;

/* now look for user */
if (!strcmp(strarray_nth(mbname->boxes, 0), "user")) {
if (!strcmpsafe(strarray_nth(mbname->boxes, 0), "user")) {
free(strarray_shift(mbname->boxes));
mbname->localpart = strarray_shift(mbname->boxes);
if (crossdomains) {
char *p = strchr(mbname->localpart, '@');
if (p) {
*p = '\0';
if (strcmpsafe(p+1, config_defdomain))
mbname->domain = xstrdup(p+1);
}
}
goto done;
}

/* the rest is just in boxes */
/* shared folders: are in user's domain unless admin */
if ((config_virtdomains && !ns->isadmin) || crossdomains)
mbname->domain = xstrdupnull(mbname_domain(userparts));

done:
mbname_free(&userparts);

@@ -677,7 +713,7 @@ EXPORTED const char *mbname_intname(const mbname_t *mbname)

for (i = 0; i < strarray_size(boxes); i++) {
if (sep) buf_putc(&buf, '.');
char *lp = xstrdup(strarray_nth(boxes, i));
char *lp = xstrdupnull(strarray_nth(boxes, i));
_rm_dots(lp);
buf_appendcstr(&buf, lp);
free(lp);
@@ -783,32 +819,50 @@ EXPORTED const char *mbname_recipient(const mbname_t *mbname, const struct names
*/
EXPORTED const char *mbname_extname(const mbname_t *mbname, const struct namespace *ns, const char *userid)
{
int crossdomains = config_getswitch(IMAPOPT_CROSSDOMAINS) && !ns->isadmin;
/* old-school virtdomains requires admin to be a different domain than the userid */
int admindomains = config_virtdomains && ns->isadmin;

/* gotta match up! */
if (mbname->extname && ns == mbname->extns && !strcmpsafe(userid, mbname->extuserid))
return mbname->extname;

struct buf buf = BUF_INITIALIZER;

/* have to zero out any existing value just in case we drop through */
mbname_t *backdoor = (mbname_t *)mbname;
if (backdoor->extname) {
free(backdoor->extname);
backdoor->extname = NULL;
backdoor->extns = ns;
free(backdoor->extuserid);
backdoor->extuserid = xstrdupnull(userid);
}

mbname_t *userparts = mbname_from_userid(userid);
strarray_t *boxes = strarray_dup(mbname_boxes(mbname));

struct buf buf = BUF_INITIALIZER;

if (ns->isalt) {
const char *up = config_getstring(IMAPOPT_USERPREFIX);
const char *sp = config_getstring(IMAPOPT_SHAREDPREFIX);

/* DELETED mailboxes have no extname in alt namespace.
* There's also no need to display domains, because admins
* are never in altnamespace, and only admins can see domains */
* There's also no need to display domains unless in crossdomains,
* because admins are never in altnamespace, and only admins can
* see domains in the admindomains space */
if (mbname->is_deleted)
goto done;

/* shared */
if (!mbname->localpart) {
if (strarray_size(boxes) == 1 && !strcmp(strarray_nth(boxes, 0), "user")) {
if (!mbname_localpart(mbname)) {
if (strarray_size(boxes) == 1 && !strcmpsafe(strarray_nth(boxes, 0), "user")) {
/* special case user all by itself */
buf_appendcstr(&buf, up);
goto end;
}
/* shared folders can ONLY be in the same domain in alt namespace */
if (strcmpsafe(mbname_domain(mbname), mbname_domain(userparts)))
goto done;
buf_appendcstr(&buf, sp);
int i;
for (i = 0; i < strarray_size(boxes); i++) {
@@ -822,7 +876,13 @@ EXPORTED const char *mbname_extname(const mbname_t *mbname, const struct namespa
if (strcmpsafe(mbname_userid(mbname), userid)) {
buf_appendcstr(&buf, up);
buf_putc(&buf, ns->hier_sep);
buf_appendcstr(&buf, mbname->localpart);
buf_appendcstr(&buf, mbname_localpart(mbname));
if (crossdomains) {
const char *domain = mbname_domain(mbname);
if (!domain) domain = config_defdomain;
buf_putc(&buf, '@');
buf_appendcstr(&buf, domain);
}
int i;
for (i = 0; i < strarray_size(boxes); i++) {
buf_putc(&buf, ns->hier_sep);
@@ -839,14 +899,14 @@ EXPORTED const char *mbname_extname(const mbname_t *mbname, const struct namespa

/* invalid names - anything exactly 'inbox' can't be displayed because
* select would be ambiguous */
if (strarray_size(boxes) == 1 && !strcasecmp(strarray_nth(boxes, 0), "INBOX"))
if (strarray_size(boxes) == 1 && !strcasecmpsafe(strarray_nth(boxes, 0), "INBOX"))
goto done;

/* likewise anything exactly matching the user or shared prefixes, both top level
* or with children */
if (!strcmp(strarray_nth(boxes, 0), up))
if (!strcmpsafe(strarray_nth(boxes, 0), up))
goto done;
if (!strcmp(strarray_nth(boxes, 0), sp))
if (!strcmpsafe(strarray_nth(boxes, 0), sp))
goto done;

int i;
@@ -864,27 +924,42 @@ EXPORTED const char *mbname_extname(const mbname_t *mbname, const struct namespa
}

/* shared */
if (!mbname->localpart) {
if (!mbname_localpart(mbname)) {
/* invalid names - not sure it's even possible, but hey */
if (!strarray_size(boxes))
goto done;
if (!strcasecmp(strarray_nth(boxes, 0), "INBOX"))
if (!strcasecmpsafe(strarray_nth(boxes, 0), "INBOX"))
goto done;

/* shared folders can ONLY be in the same domain except for admin */
if (!admindomains && strcmpsafe(mbname_domain(mbname), mbname_domain(userparts)))
goto done;

/* note "user" precisely appears here, but no need to special case it
* since the output is the same */
int i;
for (i = 0; i < strarray_size(boxes); i++) {
if (i) buf_putc(&buf, ns->hier_sep);
buf_appendcstr(&buf, strarray_nth(boxes, i));
}

goto end;
}

/* other users */
if (strcmpsafe(mbname_userid(mbname), userid)) {
buf_appendcstr(&buf, "user");
buf_putc(&buf, ns->hier_sep);
buf_appendcstr(&buf, mbname->localpart);
buf_appendcstr(&buf, mbname_localpart(mbname));
if (crossdomains) {
const char *domain = mbname_domain(mbname);
if (!domain) domain = config_defdomain;
buf_putc(&buf, '@');
buf_appendcstr(&buf, domain);
}
/* shared folders can ONLY be in the same domain except for admin */
else if (!admindomains && strcmpsafe(mbname_domain(mbname), mbname_domain(userparts)))
goto done;
int i;
for (i = 0; i < strarray_size(boxes); i++) {
buf_putc(&buf, ns->hier_sep);
@@ -908,17 +983,12 @@ EXPORTED const char *mbname_extname(const mbname_t *mbname, const struct namespa
buf_printf(&buf, "%X", (unsigned)mbname->is_deleted);
}

if (mbname->domain && strcmpsafe(mbname->domain, userparts->domain)) {
if (admindomains && mbname_domain(mbname)) {
buf_putc(&buf, '@');
buf_appendcstr(&buf, mbname->domain);
buf_appendcstr(&buf, mbname_domain(mbname));
}

mbname_t *backdoor = (mbname_t *)mbname;
free(backdoor->extname);
backdoor->extname = buf_release(&buf);
backdoor->extns = ns;
free(backdoor->extuserid);
backdoor->extuserid = xstrdupnull(userid);

done:

@@ -526,6 +526,10 @@ Blank lines and lines beginning with ``#'' are ignored.
information needed for receiving new messages in existing
conversations, in days. */

{ "crossdomains", 0, SWITCH }
/* Enable cross domain sharing. This works best with alt namespace and
unix hierarchy separators on, so you get Other Users/foo@example.com/... */

{ "dav_realm", NULL, STRING }
/* The realm to present for HTTP authentication of generic DAV
resources (principals). If not set (the default), the value of the