#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <ctype.h>
#include <syslog.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include "acl.h"
#include "annotate.h"
#include "auth.h"
#include "glob.h"
#include "assert.h"
#include "global.h"
#include "cyrusdb.h"
#include "util.h"
#include "mailbox.h"
#include "exitcodes.h"
#include "imap_err.h"
#include "xmalloc.h"
#include "xstrlcpy.h"
#include "xstrlcat.h"
#include "mboxname.h"
#include "mupdate-client.h"
#include "mboxlist.h"
#include "quota.h"
#define DB config_mboxlist_db
#define SUBDB config_subscription_db
cyrus_acl_canonproc_t mboxlist_ensureOwnerRights;
struct db *mbdb;
static int mboxlist_dbopen = 0;
static int mboxlist_opensubs();
static void mboxlist_closesubs();
static int mboxlist_rmquota(const char *name, int matchlen, int maycreate,
void *rock);
static int mboxlist_changequota(const char *name, int matchlen, int maycreate,
void *rock);
struct change_rock {
struct quota *quota;
struct txn **tid;
};
#define FNAME_SUBSSUFFIX ".sub"
int mboxlist_getpath(const char *partition, const char *name,
char **pathp, char **mpathp)
{
static char pathresult[MAX_MAILBOX_PATH+1];
static char mpathresult[MAX_MAILBOX_PATH+1];
const char *root;
assert(partition && pathp);
root = config_partitiondir(partition);
if (!root) return IMAP_PARTITION_UNKNOWN;
mailbox_hash_mbox(pathresult, sizeof(pathresult), root, name);
*pathp = pathresult;
if (mpathp) {
root = config_metapartitiondir(partition);
if (!root) *mpathp = NULL;
else {
mailbox_hash_mbox(mpathresult, sizeof(mpathresult), root, name);
*mpathp = mpathresult;
}
}
return 0;
}
char *mboxlist_makeentry(int mbtype, const char *part, const char *acl)
{
char *mboxent = (char *) xmalloc(sizeof(char) *
(30 + strlen(acl) + strlen(part)));
sprintf(mboxent, "%d %s %s", mbtype, part, acl);
return mboxent;
}
static int mboxlist_mylookup(const char *name, int *typep,
char **pathp, char **mpathp,
char **partp, char **aclp,
struct txn **tid, int wrlock)
{
int acllen;
static char partition[MAX_PARTITION_LEN+HOSTNAME_SIZE+2];
static char *aclresult;
static int aclresultalloced;
int r;
const char *data;
char *p, *q;
int datalen;
int namelen;
int mbtype;
namelen = strlen(name);
if (namelen == 0) {
return IMAP_MAILBOX_NONEXISTENT;
}
if (wrlock) {
r = DB->fetchlock(mbdb, name, namelen, &data, &datalen, tid);
} else {
r = DB->fetch(mbdb, name, namelen, &data, &datalen, tid);
}
switch (r) {
case CYRUSDB_OK:
mbtype = strtol(data, &p, 10);
if (typep) *typep = mbtype;
if (*p == ' ') p++;
q = partition;
while (*p != ' ') {
*q++ = *p++;
}
*q = '\0';
p++;
if (partp) {
*partp = partition;
}
if (pathp) {
if (mbtype & MBTYPE_REMOTE) {
*pathp = partition;
if (mpathp) *mpathp = NULL;
} else if (mbtype & MBTYPE_MOVING) {
char *part = strchr(partition, '!');
if(!part) return IMAP_SYS_ERROR;
else part++;
r = mboxlist_getpath(part, name, pathp, mpathp);
if(r) return r;
} else {
r = mboxlist_getpath(partition, name, pathp, mpathp);
if(r) return r;
}
}
if (aclp) {
acllen = datalen - (p - data);
if (acllen >= aclresultalloced) {
aclresultalloced = acllen + 100;
aclresult = xrealloc(aclresult, aclresultalloced);
}
memcpy(aclresult, p, acllen);
aclresult[acllen] = '\0';
*aclp = aclresult;
}
break;
case CYRUSDB_AGAIN:
return IMAP_AGAIN;
break;
case CYRUSDB_NOTFOUND:
return IMAP_MAILBOX_NONEXISTENT;
break;
default:
syslog(LOG_ERR, "DBERROR: error fetching %s: %s",
name, cyrusdb_strerror(r));
return IMAP_IOERROR;
break;
}
return 0;
}
int mboxlist_lookup(const char *name, char **aclp, struct txn **tid)
{
return mboxlist_mylookup(name, NULL, NULL, NULL, NULL, aclp, tid, 0);
}
int mboxlist_detail(const char *name, int *typep, char **pathp, char **mpathp,
char **partp, char **aclp, struct txn **tid)
{
return mboxlist_mylookup(name, typep, pathp, mpathp, partp, aclp, tid, 0);
}
int mboxlist_findstage(const char *name, char *stagedir, size_t sd_len)
{
const char *root;
char *partition;
int r;
assert(stagedir != NULL);
r = mboxlist_mylookup(name, NULL, NULL, NULL, &partition, NULL, NULL, 0);
switch (r) {
case 0:
break;
default:
return r;
break;
}
root = config_partitiondir(partition);
if (!root) return IMAP_PARTITION_UNKNOWN;
snprintf(stagedir, sd_len, "%s/stage./", root);
return 0;
}
int mboxlist_update(char *name, int flags, const char *part, const char *acl,
int localonly)
{
int r = 0, r2 = 0;
char *mboxent = NULL;
struct txn *tid = NULL;
mboxent = mboxlist_makeentry(flags, part, acl);
r = DB->store(mbdb, name, strlen(name), mboxent, strlen(mboxent), &tid);
free(mboxent);
mboxent = NULL;
if(!r && !localonly && config_mupdate_server) {
mupdate_handle *mupdate_h = NULL;
char buf[MAX_PARTITION_LEN + HOSTNAME_SIZE + 2];
snprintf(buf, sizeof(buf), "%s!%s", config_servername, part);
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if(r) {
syslog(LOG_ERR,
"can not connect to mupdate server for update of '%s'",
name);
} else {
r = mupdate_activate(mupdate_h, name, buf, acl);
if(r) {
syslog(LOG_ERR,
"MUPDATE: can't update mailbox entry for '%s'",
name);
}
}
mupdate_disconnect(&mupdate_h);
}
if(tid) {
if(r) {
r2 = DB->abort(mbdb, tid);
} else {
r2 = DB->commit(mbdb, tid);
}
}
if(r2) {
syslog(LOG_ERR, "DBERROR: error %s txn in mboxlist_update: %s",
r ? "aborting" : "commiting", cyrusdb_strerror(r2));
}
return r;
}
static int
mboxlist_mycreatemailboxcheck(char *name,
int new_mbtype __attribute__((unused)),
char *partition,
int isadmin, char *userid,
struct auth_state *auth_state,
char **newacl, char **newpartition,
int RMW, int localonly, int force_user_create,
struct txn **tid)
{
int r;
char *mbox = name;
char *p;
char *acl;
char *defaultacl, *identifier, *rights;
char parent[MAX_MAILBOX_NAME+1];
unsigned long parentlen;
char *parentname = NULL;
char *parentpartition = NULL;
char *parentacl = NULL;
unsigned long parentpartitionlen = 0;
unsigned long parentacllen = 0;
int mbtype;
if (partition && strlen(partition) > MAX_PARTITION_LEN) {
return IMAP_PARTITION_UNKNOWN;
}
if (config_virtdomains && (p = strchr(name, '!'))) {
mbox = p + 1;
}
r = mboxname_policycheck(mbox);
if (r) return r;
if(!isadmin && localonly) return IMAP_PERMISSION_DENIED;
if(!isadmin && force_user_create) return IMAP_PERMISSION_DENIED;
if (mboxname_userownsmailbox(userid, name) && strchr(name+5, '.') &&
(config_implicitrights & ACL_ADMIN)) {
isadmin = 1;
}
r = mboxlist_mylookup(name, &mbtype, NULL, NULL, NULL, &acl, tid, RMW);
switch (r) {
case 0:
if(mbtype & MBTYPE_RESERVE)
r = IMAP_MAILBOX_RESERVED;
else
r = IMAP_MAILBOX_EXISTS;
if (!isadmin &&
!(cyrus_acl_myrights(auth_state, acl) & ACL_LOOKUP)) {
r = IMAP_PERMISSION_DENIED;
}
return r;
break;
case IMAP_MAILBOX_NONEXISTENT:
break;
default:
return r;
break;
}
strlcpy(parent, name, sizeof(parent));
parentlen = 0;
while ((parentlen==0) && (p = strrchr(parent, '.')) && !strchr(p, '!')) {
*p = '\0';
r = mboxlist_mylookup(parent, NULL, NULL, NULL, &parentpartition,
&parentacl, tid, 0);
switch (r) {
case 0:
parentlen = strlen(parent);
parentname = parent;
parentpartitionlen = strlen(parentpartition);
parentacllen = strlen(parentacl);
break;
case IMAP_MAILBOX_NONEXISTENT:
break;
default:
return r;
break;
}
}
if (parentlen != 0) {
if (!isadmin &&
!(cyrus_acl_myrights(auth_state, parentacl) & ACL_CREATE)) {
return IMAP_PERMISSION_DENIED;
}
if (partition == NULL) {
partition = xmalloc(parentpartitionlen + 1);
memcpy(partition, parentpartition, parentpartitionlen);
partition[parentpartitionlen] = '\0';
} else {
partition = xstrdup(partition);
}
acl = xmalloc(parentacllen + 1);
memcpy(acl, parentacl, parentacllen);
acl[parentacllen] = '\0';
strncpy(name, parent, strlen(parent));
} else {
if (!isadmin) {
return IMAP_PERMISSION_DENIED;
}
acl = xstrdup("");
if (!strncmp(mbox, "user.", 5)) {
char *firstdot = strchr(mbox+5, '.');
if (!force_user_create && firstdot) {
free(acl);
return IMAP_PERMISSION_DENIED;
}
if(firstdot) *firstdot = '\0';
identifier = xmalloc(mbox - name + strlen(mbox+5) + 1);
strcpy(identifier, mbox+5);
if(firstdot) *firstdot = '.';
if (config_getswitch(IMAPOPT_UNIXHIERARCHYSEP)) {
for (p = identifier; *p; p++) {
if (*p == DOTCHAR) *p = '.';
}
}
if (mbox != name) {
sprintf(identifier+strlen(identifier),
"@%.*s", mbox - name - 1, name);
}
cyrus_acl_set(&acl, identifier, ACL_MODE_SET, ACL_ALL,
(cyrus_acl_canonproc_t *)0, (void *)0);
free(identifier);
} else {
defaultacl = identifier =
xstrdup(config_getstring(IMAPOPT_DEFAULTACL));
for (;;) {
while (*identifier && isspace((int) *identifier)) identifier++;
rights = identifier;
while (*rights && !isspace((int) *rights)) rights++;
if (!*rights) break;
*rights++ = '\0';
while (*rights && isspace((int) *rights)) rights++;
if (!*rights) break;
p = rights;
while (*p && !isspace((int) *p)) p++;
if (*p) *p++ = '\0';
cyrus_acl_set(&acl, identifier, ACL_MODE_SET, cyrus_acl_strtomask(rights),
(cyrus_acl_canonproc_t *)0, (void *)0);
identifier = p;
}
free(defaultacl);
}
if (!partition) {
partition = (char *)config_defpartition;
if (strlen(partition) > MAX_PARTITION_LEN) {
fatal("name of default partition is too long", EC_CONFIG);
}
}
partition = xstrdup(partition);
}
if (newpartition) *newpartition = partition;
else free(partition);
if (newacl) *newacl = acl;
else free(acl);
return 0;
}
int
mboxlist_createmailboxcheck(char *name, int mbtype, char *partition,
int isadmin, char *userid,
struct auth_state *auth_state,
char **newacl, char **newpartition, int forceuser)
{
return mboxlist_mycreatemailboxcheck(name, mbtype, partition, isadmin,
userid, auth_state, newacl,
newpartition, 0, 0, forceuser, NULL);
}
int mboxlist_createmailbox(char *name, int mbtype, char *partition,
int isadmin, char *userid,
struct auth_state *auth_state,
int localonly, int forceuser, int dbonly)
{
int r;
char *acl = NULL;
char *newpartition = NULL;
struct txn *tid = NULL;
mupdate_handle *mupdate_h = NULL;
char *mboxent = NULL;
int newreserved = 0;
int madereserved = 0;
char buf[MAX_PARTITION_LEN + HOSTNAME_SIZE + 2];
retry:
tid = NULL;
r = mboxlist_mycreatemailboxcheck(name, mbtype, partition, isadmin,
userid, auth_state,
&acl, &newpartition, 1, localonly,
forceuser, &tid);
switch (r) {
case 0:
break;
case IMAP_AGAIN:
goto retry;
default:
goto done;
}
if(mbtype & (MBTYPE_MOVING | MBTYPE_RESERVE)) {
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
mboxent = mboxlist_makeentry(mbtype | MBTYPE_RESERVE,
newpartition, acl);
r = DB->store(mbdb, name, strlen(name),
mboxent, strlen(mboxent), &tid);
free(mboxent);
mboxent = NULL;
if(r) {
#ifdef APPLE_OS_X_SERVER
syslog(LOG_ERR, "Could not reserve mailbox %s during create (%s)", name, cyrusdb_strerror(r));
goto retry;
#else
syslog(LOG_ERR, "Could not reserve mailbox %s during create", name);
goto done;
#endif
} else {
DB->commit(mbdb, tid);
tid = NULL;
newreserved = 1;
}
if (config_mupdate_server && !localonly) {
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if(r) {
syslog(LOG_ERR,
"can not connect to mupdate server for reservation on '%s'",
name);
goto done;
}
snprintf(buf, sizeof(buf), "%s!%s", config_servername, newpartition);
r = mupdate_reserve(mupdate_h, name, buf);
if(r) {
syslog(LOG_ERR,
"MUPDATE: can't reserve mailbox entry for '%s'", name);
goto done;
}
}
madereserved = 1;
done:
if (!r && !dbonly && !(mbtype & MBTYPE_REMOTE)) {
r = mailbox_create(name, newpartition, acl, NULL,
((mbtype & MBTYPE_NETNEWS) ?
MAILBOX_FORMAT_NETNEWS :
MAILBOX_FORMAT_NORMAL),
NULL);
}
if (r) {
int r2 = 0;
if (tid) {
r2 = DB->abort(mbdb, tid);
tid = NULL;
}
if (r2) {
syslog(LOG_ERR, "DBERROR: can't abort: %s", cyrusdb_strerror(r2));
}
if(newreserved) {
r2 = DB->delete(mbdb, name, strlen(name), NULL, 0);
if(r2) {
syslog(LOG_ERR,
"DBERROR: can't remove RESERVE entry for %s (%s)",
name, cyrusdb_strerror(r2));
}
}
if (madereserved && config_mupdate_server) {
r2 = mupdate_delete(mupdate_h, name);
if(r2 > 0) {
syslog(LOG_WARNING,
"MUPDATE: lost connection, retrying");
mupdate_disconnect(&mupdate_h);
r2 = mupdate_connect(config_mupdate_server, NULL,
&mupdate_h, NULL);
if(!r2) {
r2 = mupdate_delete(mupdate_h, name);
}
}
if(r2) {
syslog(LOG_ERR,
"MUPDATE: can't unreserve mailbox entry '%s'",
name);
}
}
} else {
mboxent = mboxlist_makeentry(mbtype, newpartition, acl);
switch(r = DB->store(mbdb, name, strlen(name),
mboxent, strlen(mboxent), NULL)) {
case 0:
break;
default:
syslog(LOG_ERR, "DBERROR: failed on activation: %s",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
}
if (!r && config_mupdate_server && !localonly) {
snprintf(buf, sizeof(buf), "%s!%s", config_servername, newpartition);
r = mupdate_activate(mupdate_h, name, buf, acl);
if(r > 0) {
syslog(LOG_WARNING,
"MUPDATE: lost connection, retrying");
mupdate_disconnect(&mupdate_h);
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if(!r) {
r = mupdate_activate(mupdate_h, name, buf, acl);
}
}
if(r) {
syslog(LOG_ERR,
"MUPDATE: can't commit mailbox entry for '%s'", name);
}
}
if(config_mupdate_server && mupdate_h) mupdate_disconnect(&mupdate_h);
if (acl) free(acl);
if (newpartition) free(newpartition);
if (mboxent) free(mboxent);
return r;
}
int mboxlist_insertremote(const char *name, int mbtype,
const char *host, const char *acl,
struct txn **tid)
{
char *mboxent, *p;
int r = 0;
assert(name != NULL && host != NULL);
if ((p = strchr(host, '!'))) {
int len = (p - host);
if (config_mupdate_config == IMAP_ENUM_MUPDATE_CONFIG_UNIFIED &&
len == strlen(config_servername) &&
!strncasecmp(host, config_servername, len)) {
mbtype &= ~MBTYPE_REMOTE;
host += len + 1;
}
else {
mbtype |= MBTYPE_REMOTE;
}
}
mboxent = mboxlist_makeentry(mbtype, host, acl);
r = DB->store(mbdb, name, strlen(name), mboxent, strlen(mboxent), tid);
switch (r) {
case CYRUSDB_OK:
break;
case CYRUSDB_AGAIN:
abort();
break;
default:
syslog(LOG_ERR, "DBERROR: error updating database %s: %s",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
break;
}
free(mboxent);
return r;
}
int mboxlist_deleteremote(const char *name, struct txn **in_tid)
{
int r;
struct txn **tid;
struct txn *lcl_tid = NULL;
int mbtype;
char *part;
if(in_tid) {
tid = in_tid;
} else {
tid = &lcl_tid;
}
retry:
r = mboxlist_mylookup(name, &mbtype, NULL, NULL, &part, NULL, tid, 1);
switch (r) {
case 0:
break;
case IMAP_AGAIN:
goto retry;
break;
default:
goto done;
}
if((mbtype & MBTYPE_REMOTE) && !strchr(part, '!')) {
syslog(LOG_ERR,
"mboxlist_deleteremote called on non-remote mailbox: %s",
name);
goto done;
}
retry_del:
r = DB->delete(mbdb, name, strlen(name), tid, 0);
switch (r) {
case CYRUSDB_OK:
break;
case CYRUSDB_AGAIN:
goto retry_del;
default:
syslog(LOG_ERR, "DBERROR: error deleting %s: %s",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
if (!in_tid) {
r = DB->commit(mbdb, *tid);
if (r) {
syslog(LOG_ERR, "DBERROR: failed on commit: %s",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
tid = NULL;
}
done:
if(r && !in_tid) {
DB->abort(mbdb, *tid);
}
return r;
}
int mboxlist_deletemailbox(const char *name, int isadmin, char *userid,
struct auth_state *auth_state, int checkacl,
int local_only, int force)
{
int r;
char *acl;
long access;
struct mailbox mailbox;
int deletequotaroot = 0;
char *path, *mpath;
struct txn *tid = NULL;
int isremote = 0;
int mbtype;
const char *p;
mupdate_handle *mupdate_h = NULL;
if(!isadmin && force) return IMAP_PERMISSION_DENIED;
retry:
if ((p = mboxname_isusermailbox(name, 1))) {
if (userid) {
int len = config_virtdomains ? strcspn(userid, "@") : strlen(userid);
if ((len == strlen(p)) && !strncmp(p, userid, len)) {
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
}
if (!isadmin) { r = IMAP_PERMISSION_DENIED; goto done; }
}
r = mboxlist_mylookup(name, &mbtype, &path, &mpath, NULL, &acl, &tid, 1);
switch (r) {
case 0:
break;
case IMAP_AGAIN:
goto retry;
break;
default:
goto done;
}
isremote = mbtype & MBTYPE_REMOTE;
if(!isremote && (mbtype & MBTYPE_RESERVE) && !force) {
r = IMAP_MAILBOX_RESERVED;
goto done;
}
if(checkacl) {
access = cyrus_acl_myrights(auth_state, acl);
if(!(access & ACL_DELETEMBOX)) {
if (mboxname_userownsmailbox(userid, name) &&
(config_implicitrights & ACL_ADMIN)) {
isadmin = 1;
}
r = (isadmin || (access & ACL_LOOKUP)) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
goto done;
}
}
if(!r && !isremote) {
r = mailbox_open_locked(name, path, mpath, acl, 0, &mailbox, 0);
if(r && !force) goto done;
}
r = DB->delete(mbdb, name, strlen(name), &tid, 0);
switch (r) {
case CYRUSDB_OK:
break;
case CYRUSDB_AGAIN:
goto retry;
default:
syslog(LOG_ERR, "DBERROR: error deleting %s: %s",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
if(!force) goto done;
}
if (!r || force) {
r = DB->commit(mbdb, tid);
if (r) {
syslog(LOG_ERR, "DBERROR: failed on commit: %s",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
tid = NULL;
}
if ((!r || force)
&& !isremote && !local_only && config_mupdate_server) {
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if(r) {
syslog(LOG_ERR,
"can not connect to mupdate server for delete of '%s'",
name);
goto done;
}
r = mupdate_delete(mupdate_h, name);
if(r) {
syslog(LOG_ERR,
"MUPDATE: can't delete mailbox entry '%s'", name);
}
mupdate_disconnect(&mupdate_h);
}
if ((r && !force) || isremote) goto done;
if (!r || force) r = mailbox_delete(&mailbox, deletequotaroot);
if (!r && mailbox.quota.root != NULL) {
}
done:
if(r && tid && !force) {
DB->abort(mbdb, tid);
} else if(tid && force) {
int delerr;
DB->commit(mbdb, tid);
delerr = annotatemore_delete(name);
if(delerr) {
syslog(LOG_ERR,
"Failed to delete annotations with mailbox '%s': %s",
name, error_message(delerr));
}
}
return r;
}
int mboxlist_renamemailbox(char *oldname, char *newname, char *partition,
int isadmin, char *userid,
struct auth_state *auth_state, int forceuser)
{
int r;
long access;
int isusermbox = 0;
int partitionmove = 0;
int mbtype;
char *oldpath = NULL, *oldmpath = NULL;
int oldopen = 0, newopen = 0, newreserved = 0;
struct mailbox oldmailbox;
struct mailbox newmailbox;
char *oldacl = NULL, *newacl = NULL;
const char *root = NULL;
struct txn *tid = NULL;
char *newpartition = NULL;
char *mboxent = NULL;
char *p;
mupdate_handle *mupdate_h = NULL;
int madenew = 0;
retry:
r = mboxlist_mylookup(oldname, &mbtype, &oldpath, &oldmpath,
NULL, &oldacl, &tid, 1);
switch (r) {
case 0:
break;
case IMAP_AGAIN:
goto retry;
default:
goto done;
}
if(mbtype & MBTYPE_RESERVE) {
r = IMAP_MAILBOX_RESERVED;
goto done;
}
newacl = xstrdup(oldacl);
if (!strcmp(oldname, newname) && !(mbtype & MBTYPE_REMOTE)) {
if (!isadmin) {
r = IMAP_PERMISSION_DENIED;
goto done;
} else if (!partition) {
r = IMAP_PARTITION_UNKNOWN;
goto done;
}
partitionmove = 1;
root = config_partitiondir(partition);
if (!root) {
r = IMAP_PARTITION_UNKNOWN;
goto done;
}
if (!strncmp(root, oldpath, strlen(root)) &&
oldpath[strlen(root)] == '/') {
r = IMAP_MAILBOX_EXISTS;
goto done;
}
} else if ((p = mboxname_isusermailbox(oldname, 1))) {
if (!strncmp(p, userid, config_virtdomains ? strcspn(userid, "@") :
strlen(userid))) {
access = cyrus_acl_myrights(auth_state, oldacl);
if (!(access & ACL_DELETEMBOX)) {
r = IMAP_PERMISSION_DENIED;
goto done;
}
isusermbox = 1;
} else if (config_getswitch(IMAPOPT_ALLOWUSERMOVES) &&
mboxname_isusermailbox(newname, 1)) {
access = cyrus_acl_myrights(auth_state, oldacl);
if (!(access & ACL_DELETEMBOX) && !isadmin) {
r = (isadmin || (access & ACL_LOOKUP)) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
goto done;
}
} else {
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
} else {
access = cyrus_acl_myrights(auth_state, oldacl);
if (!(access & ACL_DELETEMBOX) && !isadmin) {
r = (isadmin || (access & ACL_LOOKUP)) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
goto done;
}
}
if(!r && (mbtype & MBTYPE_MOVING)) {
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
if (!partitionmove) {
if (mboxname_isusermailbox(newname, 1)) {
if (config_getswitch(IMAPOPT_ALLOWUSERMOVES) &&
mboxname_isusermailbox(oldname, 1)) {
if (!isadmin) {
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
} else {
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
}
r = mboxlist_mycreatemailboxcheck(newname, 0, partition, isadmin,
userid, auth_state, NULL,
&newpartition, 1, 0, forceuser, &tid);
switch (r) {
case 0:
break;
case IMAP_AGAIN:
goto retry;
break;
default:
goto done;
break;
}
} else {
newpartition = xstrdup(partition);
}
if(!r && !partitionmove) {
mboxent = mboxlist_makeentry(mbtype | MBTYPE_RESERVE,
newpartition, newacl);
r = DB->store(mbdb, newname, strlen(newname),
mboxent, strlen(mboxent), &tid);
free(mboxent);
mboxent = NULL;
}
if(r) {
syslog(LOG_ERR, "Could not reserve mailbox %s during rename", oldname);
goto done;
} else {
DB->commit(mbdb, tid);
tid = NULL;
if(!partitionmove) newreserved = 1;
}
if(!r && config_mupdate_server) {
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if(r) {
syslog(LOG_ERR,
"can not connect to mupdate server for rename of '%s'",
newname);
goto done;
}
if (!partitionmove) {
char buf[MAX_PARTITION_LEN + HOSTNAME_SIZE + 2];
snprintf(buf, sizeof(buf), "%s!%s",
config_servername, newpartition);
r = mupdate_reserve(mupdate_h, newname, buf);
if(r) {
syslog(LOG_ERR,
"MUPDATE: can't reserve mailbox entry for '%s'",
newname);
goto done;
}
madenew = 1;
}
}
if(!r) {
r = mailbox_open_locked(oldname, oldpath, oldmpath, oldacl, auth_state,
&oldmailbox, 0);
oldopen = 1;
}
if (!r && !(mbtype & MBTYPE_REMOTE)) {
r = mailbox_rename_copy(&oldmailbox, newname, newpartition,
NULL, NULL, &newmailbox);
if (r) {
goto done;
} else {
newopen = 1;
}
}
if (!isusermbox) {
r = DB->delete(mbdb, oldname, strlen(oldname), &tid, 0);
switch (r) {
case 0:
break;
case CYRUSDB_AGAIN:
goto retry;
break;
default:
syslog(LOG_ERR, "DBERROR: error deleting %s: %s",
oldname, cyrusdb_strerror(r));
r = IMAP_IOERROR;
mailbox_close(&newmailbox);
goto done;
break;
}
}
mboxent = mboxlist_makeentry(mbtype, newpartition, newacl);
r = DB->store(mbdb, newname, strlen(newname),
mboxent, strlen(mboxent), &tid);
switch (r) {
case 0:
break;
case CYRUSDB_AGAIN:
goto retry;
default:
syslog(LOG_ERR, "DBERROR: error renaming %s: %s",
newname, cyrusdb_strerror(r));
r = IMAP_IOERROR;
goto done;
}
done:
if (r != 0) {
int r2 = 0;
if (tid) {
r2 = DB->abort(mbdb, tid);
tid = NULL;
}
if (r2) {
syslog(LOG_ERR, "DBERROR: can't abort: %s", cyrusdb_strerror(r2));
}
if(newreserved) {
r2 = DB->delete(mbdb, newname, strlen(newname), NULL, 0);
if(r2) {
syslog(LOG_ERR,
"DBERROR: can't remove RESERVE entry for %s (%s)",
newname, cyrusdb_strerror(r2));
}
}
if (madenew && config_mupdate_server) {
r2 = mupdate_delete(mupdate_h, newname);
if(r2 > 0) {
syslog(LOG_WARNING,
"MUPDATE: lost connection, retrying");
mupdate_disconnect(&mupdate_h);
r2 = mupdate_connect(config_mupdate_server, NULL,
&mupdate_h, NULL);
if(!r2) {
r2 = mupdate_delete(mupdate_h, newname);
}
}
if(r2) {
syslog(LOG_ERR,
"MUPDATE: can't unreserve mailbox entry '%s'",
newname);
}
}
} else {
switch (r = DB->commit(mbdb, tid)) {
case 0:
break;
default:
syslog(LOG_ERR, "DBERROR: failed on commit: %s",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
break;
}
}
if (!r && config_mupdate_server) {
char buf[MAX_PARTITION_LEN + HOSTNAME_SIZE + 2];
snprintf(buf, sizeof(buf), "%s!%s",
config_servername, newpartition);
r = mupdate_activate(mupdate_h, newname, buf, newacl);
if(r > 0) {
syslog(LOG_WARNING,
"MUPDATE: lost connection, retrying");
mupdate_disconnect(&mupdate_h);
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if(!r) {
r = mupdate_activate(mupdate_h, newname, buf, newacl);
}
}
if(r) {
syslog(LOG_ERR,
"MUPDATE: can't commit mailbox entry for '%s'",
newname);
}
}
if (!r && !partitionmove && !isusermbox && config_mupdate_server) {
r = mupdate_delete(mupdate_h, oldname);
if(r > 0) {
syslog(LOG_WARNING,
"MUPDATE: lost connection, retrying");
mupdate_disconnect(&mupdate_h);
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if(!r) {
r = mupdate_delete(mupdate_h, oldname);
}
}
if(r) {
syslog(LOG_ERR,
"MUPDATE: can't delete mailbox entry '%s'", oldname);
}
}
if(newopen) mailbox_close(&newmailbox);
if(config_mupdate_server) mupdate_disconnect(&mupdate_h);
if(oldopen) {
if(!r)
mailbox_rename_cleanup(&oldmailbox,isusermbox);
mailbox_close(&oldmailbox);
}
if (newacl) free(newacl);
if (newpartition) free(newpartition);
if (mboxent) free(mboxent);
return r;
}
int mboxlist_setacl(const char *name, const char *identifier,
const char *rights,
int isadmin, const char *userid,
struct auth_state *auth_state)
{
int useridlen = strlen(userid), domainlen = 0;
char *cp, ident[256];
const char *domain = NULL;
int r;
int access;
int mode = ACL_MODE_SET;
int isusermbox = 0, anyoneuseracl = 1;
struct mailbox mailbox;
int mailbox_open = 0;
char *acl, *newacl = NULL;
char *partition, *path, *mpath;
char *mboxent = NULL;
int mbtype;
struct txn *tid = NULL;
if (config_virtdomains) {
if ((cp = strchr(userid, '@'))) {
useridlen = cp - userid;
}
if ((cp = strchr(name, '!'))) {
domain = name;
domainlen = cp - name + 1;
}
if ((cp = strchr(identifier, '@'))) {
if (rights &&
((domain && strncasecmp(cp+1, domain, strlen(cp+1))) ||
(!domain && (!config_defdomain ||
strcasecmp(config_defdomain, cp+1))))) {
return IMAP_INVALID_IDENTIFIER;
}
if ((config_defdomain && !strcasecmp(config_defdomain, cp+1)) ||
!strcmp(identifier, "anonymous") ||
!strcmp(identifier, "anyone")) {
snprintf(ident, sizeof(ident),
"%.*s", cp - identifier, identifier);
} else {
strlcpy(ident, identifier, sizeof(ident));
}
} else {
strlcpy(ident, identifier, sizeof(ident));
if (domain && !isadmin &&
strcmp(ident, "anonymous") && strcmp(ident, "anyone")) {
snprintf(ident+strlen(ident), sizeof(ident)-strlen(ident),
"@%.*s",
domainlen ? domainlen-1 : (int) strlen(domain), domain);
}
}
identifier = ident;
}
if (!strncmp(name+domainlen, "user.", 5) &&
(!(cp = strchr(userid, '.')) || (cp - userid) > useridlen) &&
!strncmp(name+domainlen+5, userid, useridlen) &&
(name[domainlen+5+useridlen] == '\0' ||
name[domainlen+5+useridlen] == '.')) {
isusermbox = 1;
}
anyoneuseracl = config_getswitch(IMAPOPT_ANYONEUSERACL);
do {
r = mboxlist_mylookup(name, &mbtype, &path, &mpath,
&partition, &acl, &tid, 1);
} while(r == IMAP_AGAIN);
if(!r && mbtype & (MBTYPE_MOVING | MBTYPE_RESERVE)) {
r = IMAP_MAILBOX_NOTSUPPORTED;
}
if (!r && !(mbtype & MBTYPE_REMOTE)) {
DB->abort(mbdb, tid);
tid = NULL;
r = mailbox_open_header_path(name, path, mpath,
acl, NULL, &mailbox, 0);
if (!r) {
mailbox_open = 1;
r = mailbox_lock_header(&mailbox);
}
if(!r) {
do {
r = mboxlist_mylookup(name, &mbtype, &path, NULL,
&partition, &acl, &tid, 1);
} while( r == IMAP_AGAIN );
}
if(r) goto done;
}
if (!r && !isadmin) {
access = cyrus_acl_myrights(auth_state, acl);
if (!(access & ACL_ADMIN)) {
r = (access & ACL_LOOKUP) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
goto done;
}
}
if (!r && !isadmin && !anyoneuseracl && !strncmp(identifier, "anyone", 6)) {
r = IMAP_PERMISSION_DENIED;
goto done;
}
if(!r) {
newacl = xstrdup(acl);
if (rights) {
mode = ACL_MODE_SET;
if (*rights == '+') {
rights++;
mode = ACL_MODE_ADD;
}
else if (*rights == '-') {
rights++;
mode = ACL_MODE_REMOVE;
}
if (cyrus_acl_set(&newacl, identifier, mode,
cyrus_acl_strtomask(rights),
isusermbox ? mboxlist_ensureOwnerRights : 0,
(void *)userid)) {
r = IMAP_INVALID_IDENTIFIER;
}
} else {
if (cyrus_acl_remove(&newacl, identifier,
isusermbox ? mboxlist_ensureOwnerRights : 0,
(void *)userid)) {
r = IMAP_INVALID_IDENTIFIER;
}
}
}
if(!r) {
mboxent = mboxlist_makeentry(mbtype, partition, newacl);
do {
r = DB->store(mbdb, name, strlen(name),
mboxent, strlen(mboxent), &tid);
} while(r == CYRUSDB_AGAIN);
if(r) {
syslog(LOG_ERR, "DBERROR: error updating acl %s: %s",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
}
if (!r && !(mbtype & MBTYPE_REMOTE)) {
if(mailbox.acl) free(mailbox.acl);
mailbox.acl = xstrdup(newacl);
r = mailbox_write_header(&mailbox);
}
if (!r) {
if((r = DB->commit(mbdb, tid)) != 0) {
syslog(LOG_ERR, "DBERROR: failed on commit: %s",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
tid = NULL;
}
if (!r && config_mupdate_server) {
mupdate_handle *mupdate_h = NULL;
char buf[MAX_PARTITION_LEN + HOSTNAME_SIZE + 2];
snprintf(buf, sizeof(buf), "%s!%s", config_servername, partition);
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if(r) {
syslog(LOG_ERR,
"can not connect to mupdate server for reservation on '%s'",
name);
} else {
r = mupdate_activate(mupdate_h, name, buf, newacl);
if(r) {
syslog(LOG_ERR,
"MUPDATE: can't update mailbox entry for '%s'",
name);
}
}
mupdate_disconnect(&mupdate_h);
}
done:
if (r && tid) {
int r2 = DB->abort(mbdb, tid);
if(r2) {
syslog(LOG_ERR,
"DBERROR: error aborting txn in mboxlist_setacl: %s",
cyrusdb_strerror(r2));
}
}
if (mailbox_open) mailbox_close(&mailbox);
if (mboxent) free(mboxent);
if (newacl) free(newacl);
return r;
}
int
mboxlist_sync_setacls(char *name, char *newacl)
{
int r;
struct mailbox mailbox;
int mailbox_open = 0;
char *acl;
char *partition, *path, *mpath;
char *mboxent = NULL;
int mbtype;
struct txn *tid = NULL;
do {
r = mboxlist_mylookup(name, &mbtype, &path, &mpath,
&partition, &acl, &tid, 1);
} while(r == IMAP_AGAIN);
if(!r && mbtype & (MBTYPE_MOVING | MBTYPE_RESERVE)) {
r = IMAP_MAILBOX_NOTSUPPORTED;
}
if (!r && !(mbtype & MBTYPE_REMOTE)) {
DB->abort(mbdb, tid);
tid = NULL;
r = mailbox_open_header_path(name, path, mpath,
acl, NULL, &mailbox, 0);
if (!r) {
mailbox_open = 1;
r = mailbox_lock_header(&mailbox);
}
if(!r) {
do {
r = mboxlist_mylookup(name, &mbtype, &path, &mpath,
&partition, &acl, &tid, 1);
} while( r == IMAP_AGAIN );
}
if(r) goto done;
}
if(!r) {
mboxent = mboxlist_makeentry(mbtype, partition, newacl);
do {
r = DB->store(mbdb, name, strlen(name),
mboxent, strlen(mboxent), &tid);
} while(r == CYRUSDB_AGAIN);
if(r) {
syslog(LOG_ERR, "DBERROR: error updating acl %s: %s",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
}
if (!r && !(mbtype & MBTYPE_REMOTE)) {
if(mailbox.acl) free(mailbox.acl);
mailbox.acl = xstrdup(newacl);
r = mailbox_write_header(&mailbox);
}
if (!r) {
if((r = DB->commit(mbdb, tid)) != 0) {
syslog(LOG_ERR, "DBERROR: failed on commit: %s",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
tid = NULL;
}
if (!r && config_mupdate_server) {
mupdate_handle *mupdate_h = NULL;
char buf[MAX_PARTITION_LEN + HOSTNAME_SIZE + 2];
sprintf(buf, "%s!%s", config_servername, partition);
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if(r) {
syslog(LOG_ERR,
"can not connect to mupdate server for reservation on '%s'",
name);
} else {
r = mupdate_activate(mupdate_h, name, buf, newacl);
if(r) {
syslog(LOG_ERR,
"MUPDATE: can't update mailbox entry for '%s'",
name);
}
}
mupdate_disconnect(&mupdate_h);
}
done:
if (r && tid) {
int r2 = DB->abort(mbdb, tid);
if(r2) {
syslog(LOG_ERR,
"DBERROR: error aborting txn in mboxlist_setacl: %s",
cyrusdb_strerror(r2));
}
}
if (mailbox_open) mailbox_close(&mailbox);
if (mboxent) free(mboxent);
return r;
}
struct find_rock {
struct glob *g;
struct namespace *namespace;
int find_namespace;
int domainlen;
int inboxoffset;
const char *inboxcase;
const char *usermboxname;
int usermboxnamelen;
int checkmboxlist;
int checkshared;
int isadmin;
struct auth_state *auth_state;
int (*proc)(char *, int, int, void *rock);
void *procrock;
};
static int find_p(void *rockp,
const char *key, int keylen,
const char *data, int datalen)
{
struct find_rock *rock = (struct find_rock *) rockp;
long minmatch;
struct glob *g = rock->g;
long matchlen;
if (!rock->domainlen && !rock->isadmin && strchr(key, '!')) return 0;
minmatch = 0;
if (rock->inboxoffset) {
char namebuf[MAX_MAILBOX_NAME+1];
if(keylen >= sizeof(namebuf)) {
syslog(LOG_ERR, "oversize keylen in mboxlist.c:find_p()");
return 0;
}
memcpy(namebuf, key, keylen);
namebuf[keylen] = '\0';
if (rock->inboxoffset) {
namebuf[rock->inboxoffset] = rock->inboxcase[0];
namebuf[rock->inboxoffset+1] = rock->inboxcase[1];
namebuf[rock->inboxoffset+2] = rock->inboxcase[2];
namebuf[rock->inboxoffset+3] = rock->inboxcase[3];
namebuf[rock->inboxoffset+4] = rock->inboxcase[4];
}
matchlen = glob_test(g, namebuf+rock->inboxoffset,
keylen-rock->inboxoffset, &minmatch);
} else {
matchlen = glob_test(g, key, keylen, &minmatch);
}
if(matchlen == -1) return 0;
if (rock->find_namespace != NAMESPACE_INBOX &&
rock->usermboxname &&
keylen >= rock->usermboxnamelen &&
(keylen == rock->usermboxnamelen ||
key[rock->usermboxnamelen] == '.') &&
!strncmp(key, rock->usermboxname, rock->usermboxnamelen)) {
return 0;
}
if (rock->find_namespace == NAMESPACE_SHARED &&
rock->namespace && rock->namespace->isalt &&
!strncmp(key+rock->domainlen, "user", 4) &&
(key[rock->domainlen+4] == '\0' || key[rock->domainlen+4] == '.')) {
return 0;
}
if (!rock->isadmin) {
const char *p, *acl;
int rights;
int acllen;
static char *aclbuf = NULL;
static int aclbufsz = 0;
p = strchr(data, ' ');
if (!p) {
syslog(LOG_ERR, "%s: can't find partition", key);
return 0;
}
p++;
acl = strchr(p, ' ');
if (!acl) {
syslog(LOG_ERR, "%s: can't find acl", key);
return 0;
}
acl++;
acllen = datalen - (acl - data);
if (acllen >= aclbufsz) {
aclbufsz = acllen + 500;
aclbuf = xrealloc(aclbuf, aclbufsz);
}
memcpy(aclbuf, acl, acllen);
aclbuf[acllen] = '\0';
rights = cyrus_acl_myrights(rock->auth_state, aclbuf);
if (!(rights & ACL_LOOKUP)) {
return 0;
}
}
return 1;
}
static int find_cb(void *rockp,
const char *key, int keylen,
const char *data __attribute__((unused)),
int datalen __attribute__((unused)))
{
char namebuf[MAX_MAILBOX_NAME+1];
struct find_rock *rock = (struct find_rock *) rockp;
int r = 0;
long minmatch;
struct glob *g = rock->g;
minmatch = 0;
while (minmatch >= 0) {
long matchlen;
if(keylen >= sizeof(namebuf)) {
syslog(LOG_ERR, "oversize keylen in mboxlist.c:find_cb()");
return 0;
}
memcpy(namebuf, key, keylen);
namebuf[keylen] = '\0';
if (rock->find_namespace != NAMESPACE_INBOX &&
rock->usermboxname &&
!strncmp(namebuf, rock->usermboxname, rock->usermboxnamelen)
&& (keylen == rock->usermboxnamelen ||
namebuf[rock->usermboxnamelen] == '.')) {
return 0;
}
if (rock->checkmboxlist) {
r = mboxlist_lookup(namebuf, NULL, NULL);
} else {
r = 0;
}
if (!r && rock->inboxoffset) {
namebuf[rock->inboxoffset] = rock->inboxcase[0];
namebuf[rock->inboxoffset+1] = rock->inboxcase[1];
namebuf[rock->inboxoffset+2] = rock->inboxcase[2];
namebuf[rock->inboxoffset+3] = rock->inboxcase[3];
namebuf[rock->inboxoffset+4] = rock->inboxcase[4];
}
matchlen = glob_test(g, namebuf+rock->inboxoffset,
keylen-rock->inboxoffset, &minmatch);
if (matchlen == -1) {
r = 0;
break;
}
switch (r) {
case 0:
if (rock->find_namespace == NAMESPACE_SHARED &&
rock->checkshared && rock->namespace) {
r = (*rock->proc)(rock->namespace->prefix[NAMESPACE_SHARED],
strlen(rock->namespace->prefix[NAMESPACE_SHARED])-1,
1, rock->procrock);
if (rock->checkshared > 1) {
return CYRUSDB_DONE;
}
}
rock->checkshared = 0;
r = (*rock->proc)(namebuf+rock->inboxoffset, matchlen,
1, rock->procrock);
break;
case IMAP_MAILBOX_NONEXISTENT:
r = 0;
break;
default:
break;
}
if (r) break;
}
return r;
}
int mboxlist_findall(struct namespace *namespace __attribute__((unused)),
const char *pattern, int isadmin, char *userid,
struct auth_state *auth_state, int (*proc)(), void *rock)
{
struct find_rock cbrock;
char usermboxname[MAX_MAILBOX_NAME+1];
int usermboxnamelen = 0;
const char *data;
int datalen;
int r = 0;
char *p;
int prefixlen;
int userlen = userid ? strlen(userid) : 0, domainlen = 0;
char domainpat[MAX_MAILBOX_NAME+1] = "";
char *pat = NULL;
if (config_virtdomains) {
char *domain;
if (userid && (domain = strrchr(userid, '@'))) {
userlen = domain - userid;
domainlen = strlen(domain);
if ((p = strchr(pattern , '!'))) {
if ((p-pattern != domainlen-1) ||
strncmp(pattern, domain+1, domainlen-1)) {
return IMAP_MAILBOX_BADNAME;
}
pattern = p+1;
}
snprintf(domainpat, sizeof(domainpat), "%s!%s", domain+1, pattern);
}
if ((p = strrchr(pattern, '@'))) {
if (domainlen) {
return IMAP_MAILBOX_BADNAME;
}
if (!(config_defdomain && !strcasecmp(config_defdomain, p+1))) {
snprintf(domainpat, sizeof(domainpat), "%s!", p+1);
domainlen = strlen(p);
}
snprintf(domainpat+domainlen, sizeof(domainpat)-domainlen,
"%.*s", p - pattern, pattern);
}
}
if (domainpat[0] == '\0')
strlcpy(domainpat, pattern, sizeof(domainpat));
cbrock.g = glob_init(pattern, GLOB_HIERARCHY|GLOB_INBOXCASE);
cbrock.namespace = NULL;
cbrock.domainlen = domainlen;
cbrock.inboxcase = glob_inboxcase(cbrock.g);
cbrock.isadmin = isadmin;
cbrock.auth_state = auth_state;
cbrock.checkmboxlist = 0;
cbrock.checkshared = 0;
cbrock.proc = proc;
cbrock.procrock = rock;
if (userid && (!(p = strchr(userid, '.')) || ((p - userid) > userlen)) &&
strlen(userid)+5 < MAX_MAILBOX_NAME) {
if (domainlen)
snprintf(usermboxname, sizeof(usermboxname),
"%s!", userid+userlen+1);
snprintf(usermboxname+domainlen, sizeof(usermboxname)-domainlen,
"user.%.*s", userlen, userid);
usermboxnamelen = strlen(usermboxname);
}
else {
userid = NULL;
}
if (userid) {
if (GLOB_TEST(cbrock.g, "INBOX") != -1) {
r = DB->fetch(mbdb, usermboxname, usermboxnamelen,
&data, &datalen, NULL);
if (!r && data) {
r = (*proc)(cbrock.inboxcase, 5, 1, rock);
}
else if (r == CYRUSDB_NOTFOUND) r = 0;
}
else if (!strncmp(pattern,
usermboxname+domainlen, usermboxnamelen-domainlen) &&
GLOB_TEST(cbrock.g, usermboxname+domainlen) != -1) {
r = DB->fetch(mbdb, usermboxname, usermboxnamelen,
&data, &datalen, NULL);
if (!r && data) {
r = (*proc)(usermboxname, usermboxnamelen, 1, rock);
}
else if (r == CYRUSDB_NOTFOUND) r = 0;
}
strlcat(usermboxname, ".", sizeof(usermboxname));
usermboxnamelen++;
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
} else {
cbrock.usermboxname = NULL;
cbrock.usermboxnamelen = 0;
}
if (r) goto done;
pattern = pat = xstrdup(pattern);
for (p = pat; *p; p++) {
if (*p == '*' || *p == '%' || *p == '?' || *p == '@') break;
}
prefixlen = p - pattern;
*p = '\0';
if (userid &&
(!strncmp(usermboxname+domainlen, pattern, usermboxnamelen-domainlen-1) ||
!strncasecmp("inbox.", pattern, prefixlen < 6 ? prefixlen : 6))) {
if (!strncmp(usermboxname+domainlen, pattern, usermboxnamelen-domainlen-1)) {
glob_free(&cbrock.g);
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
cbrock.inboxoffset = 0;
}
else {
cbrock.inboxoffset = domainlen + userlen;
}
cbrock.find_namespace = NAMESPACE_INBOX;
r = DB->foreach(mbdb,
usermboxname, usermboxnamelen,
&find_p, &find_cb, &cbrock,
NULL);
}
if(!r) {
cbrock.find_namespace = NAMESPACE_USER;
glob_free(&cbrock.g);
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
cbrock.inboxoffset = 0;
if (usermboxnamelen) {
usermboxname[--usermboxnamelen] = '\0';
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
}
r = DB->foreach(mbdb,
domainpat, domainlen + prefixlen,
&find_p, &find_cb, &cbrock,
NULL);
}
done:
glob_free(&cbrock.g);
if (pat) free(pat);
return r;
}
int mboxlist_findall_alt(struct namespace *namespace,
const char *pattern, int isadmin, char *userid,
struct auth_state *auth_state, int (*proc)(),
void *rock)
{
struct find_rock cbrock;
char usermboxname[MAX_MAILBOX_NAME+1], patbuf[MAX_MAILBOX_NAME+1];
int usermboxnamelen = 0;
const char *data;
int datalen;
int r = 0;
char *p;
int prefixlen, len;
int userlen = userid ? strlen(userid) : 0, domainlen = 0;
char domainpat[MAX_MAILBOX_NAME+1];
char *pat = NULL;
if (config_virtdomains && userid && (p = strchr(userid, '@'))) {
userlen = p - userid;
domainlen = strlen(p);
snprintf(domainpat, sizeof(domainpat), "%s!", p+1);
}
else
domainpat[0] = '\0';
cbrock.g = glob_init(pattern, GLOB_HIERARCHY|GLOB_INBOXCASE);
cbrock.namespace = namespace;
cbrock.domainlen = domainlen;
cbrock.inboxcase = glob_inboxcase(cbrock.g);
cbrock.isadmin = isadmin;
cbrock.auth_state = auth_state;
cbrock.checkmboxlist = 0;
cbrock.checkshared = 0;
cbrock.proc = proc;
cbrock.procrock = rock;
if (userid && (!(p = strchr(userid, '.')) || ((p - userid) > userlen)) &&
strlen(userid)+5 < MAX_MAILBOX_NAME) {
if (domainlen)
snprintf(usermboxname, sizeof(usermboxname),
"%s!", userid+userlen+1);
snprintf(usermboxname+domainlen, sizeof(usermboxname)-domainlen,
"user.%.*s", userlen, userid);
usermboxnamelen = strlen(usermboxname);
}
else {
userid = 0;
}
if (userid) {
if (GLOB_TEST(cbrock.g, "INBOX") != -1) {
r = DB->fetch(mbdb, usermboxname, usermboxnamelen,
&data, &datalen, NULL);
if (!r && data) {
r = (*proc)(cbrock.inboxcase, 5, 0, rock);
}
else if (r == CYRUSDB_NOTFOUND) r = 0;
}
strlcat(usermboxname, ".", sizeof(usermboxname));
usermboxnamelen++;
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
} else {
cbrock.usermboxname = NULL;
cbrock.usermboxnamelen = 0;
}
if (r) goto done;
glob_free(&cbrock.g);
pattern = pat = xstrdup(pattern);
for (p = pat; *p; p++) {
if (*p == '*' || *p == '%' || *p == '?' || *p == '@') break;
}
prefixlen = p - pattern;
if (userid) {
strlcpy(patbuf, "INBOX.", sizeof(patbuf));
strlcat(patbuf, pattern, sizeof(patbuf));
cbrock.g = glob_init(patbuf, GLOB_HIERARCHY|GLOB_INBOXCASE);
cbrock.inboxcase = glob_inboxcase(cbrock.g);
cbrock.inboxoffset = domainlen+userlen;
cbrock.find_namespace = NAMESPACE_INBOX;
DB->foreach(mbdb,
usermboxname, usermboxnamelen,
&find_p, &find_cb, &cbrock,
NULL);
glob_free(&cbrock.g);
}
if (usermboxnamelen) {
usermboxname[--usermboxnamelen] = '\0';
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
}
len = strlen(namespace->prefix[NAMESPACE_USER]);
if(len>0) len--;
if (!strncmp(namespace->prefix[NAMESPACE_USER], pattern,
prefixlen < len ? prefixlen : len)) {
if (prefixlen < len) {
strlcpy(domainpat+domainlen, pattern+prefixlen,
sizeof(domainpat)-domainlen);
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
}
else {
strlcpy(domainpat+domainlen, "user", sizeof(domainpat)-domainlen);
strlcat(domainpat, pattern+len, sizeof(domainpat));
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
}
cbrock.find_namespace = NAMESPACE_USER;
cbrock.inboxoffset = 0;
strlcpy(domainpat+domainlen, "user", sizeof(domainpat)-domainlen);
DB->foreach(mbdb,
domainpat, strlen(domainpat),
&find_p, &find_cb, &cbrock,
NULL);
glob_free(&cbrock.g);
}
len = strlen(namespace->prefix[NAMESPACE_SHARED]);
if(len>0) len--;
if (!strncmp(namespace->prefix[NAMESPACE_SHARED], pattern,
prefixlen < len ? prefixlen : len)) {
cbrock.find_namespace = NAMESPACE_SHARED;
cbrock.inboxoffset = 0;
if (prefixlen <= len) {
for (p = pat+prefixlen; *p; p++) {
if (*p == '%') continue;
else if (*p == '.') p++;
break;
}
if (*pattern && !strchr(pattern, '.') &&
pattern[strlen(pattern)-1] == '%') {
cbrock.checkshared = 1;
}
if ((cbrock.checkshared || prefixlen == len) && !*p) {
strlcpy(domainpat+domainlen, "*", sizeof(domainpat)-domainlen);
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
cbrock.checkshared = 2;
}
else {
strlcpy(domainpat+domainlen, p, sizeof(domainpat)-domainlen);
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
}
domainpat[domainlen] = '\0';
DB->foreach(mbdb,
domainpat, domainlen,
&find_p, &find_cb, &cbrock,
NULL);
}
else if (pattern[len] == '.') {
strlcpy(domainpat+domainlen, pattern+len+1,
sizeof(domainpat)-domainlen);
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
DB->foreach(mbdb,
domainpat, domainlen+prefixlen-(len+1),
&find_p, &find_cb, &cbrock,
NULL);
}
}
done:
glob_free(&cbrock.g);
if (pat) free(pat);
return r;
}
static int child_cb(char *name,
int matchlen __attribute__((unused)),
int maycreate __attribute__((unused)),
void *rock)
{
if (!name) return 0;
return (*((int *) rock) = 1);
}
int mboxlist_setquota(const char *root, int newquota, int force)
{
char pattern[MAX_MAILBOX_PATH+1];
struct quota quota;
int have_mailbox = 1;
int r, t;
struct txn *tid = NULL;
struct change_rock crock;
if (!root[0] || root[0] == '.' || strchr(root, '/')
|| strchr(root, '*') || strchr(root, '%') || strchr(root, '?')) {
return IMAP_MAILBOX_BADNAME;
}
memset("a, 0, sizeof(struct quota));
quota.root = (char *) root;
r = quota_read("a, &tid, 1);
if (!r) {
quota.limit = newquota;
r = quota_write("a, &tid);
if (!r) quota_commit(&tid);
return r;
}
if (r != IMAP_QUOTAROOT_NONEXISTENT) return r;
strlcpy(pattern, quota.root, sizeof(pattern));
if (config_virtdomains && quota.root[strlen(quota.root)-1] == '!') {
have_mailbox = 0;
strlcat(pattern, "*", sizeof(pattern));
}
else {
strlcat(pattern, ".*", sizeof(pattern));
r = mboxlist_detail(quota.root, &t, NULL, NULL, NULL, NULL, NULL);
if (r) {
if (!force && r == IMAP_MAILBOX_NONEXISTENT) {
mboxlist_findall(NULL, pattern, 1, NULL, NULL,
child_cb, (void *) &force);
}
if(!force) return r;
else {
have_mailbox = 0;
t = 0;
}
}
if(t & (MBTYPE_REMOTE | MBTYPE_MOVING)) {
return IMAP_MAILBOX_NOTSUPPORTED;
}
}
quota.used = 0;
quota.limit = newquota;
r = quota_write("a, &tid);
if (r) return r;
crock.quota = "a;
crock.tid = &tid;
if(have_mailbox)
mboxlist_changequota(quota.root, 0, 0, &crock);
mboxlist_findall(NULL, pattern, 1, 0, 0, mboxlist_changequota, &crock);
r = quota_write("a, &tid);
if (!r) quota_commit(&tid);
return r;
}
int mboxlist_unsetquota(const char *root)
{
char pattern[MAX_MAILBOX_PATH+1];
struct quota quota;
int r=0;
if (!root[0] || root[0] == '.' || strchr(root, '/')
|| strchr(root, '*') || strchr(root, '%') || strchr(root, '?')) {
return IMAP_MAILBOX_BADNAME;
}
quota.root = (char *) root;
r = quota_read("a, NULL, 0);
if (r == IMAP_QUOTAROOT_NONEXISTENT) {
return 0;
}
else if (r) return r;
strlcpy(pattern, root, sizeof(pattern));
if (config_virtdomains && root[strlen(root)-1] == '!') {
strlcat(pattern, "*", sizeof(pattern));
}
else
strlcat(pattern, ".*", sizeof(pattern));
mboxlist_rmquota(root, 0, 0, (void *)root);
mboxlist_findall(NULL, pattern, 1, 0, 0, mboxlist_rmquota, (void *)root);
r = quota_delete("a, NULL);
return r;
}
void mboxlist_getinternalstuff(const char **listfnamep __attribute__((unused)),
const char **newlistfnamep __attribute__((unused)),
const char **basep __attribute__((unused)),
unsigned long * sizep __attribute__((unused)))
{
printf("yikes! don't reconstruct me!\n");
abort();
}
int mboxlist_ensureOwnerRights(rock, identifier, access)
void *rock;
const char *identifier;
int access;
{
char *owner = (char *)rock;
if (strcmp(identifier, owner) != 0) return access;
return access|config_implicitrights;
}
static int mboxlist_rmquota(const char *name,
int matchlen __attribute__((unused)),
int maycreate __attribute__((unused)),
void *rock)
{
int r;
struct mailbox mailbox;
const char *oldroot = (const char *) rock;
assert(rock != NULL);
r = mailbox_open_header(name, 0, &mailbox);
if (r) goto error_noclose;
r = mailbox_lock_header(&mailbox);
if (r) goto error;
r = mailbox_open_index(&mailbox);
if (r) goto error;
r = mailbox_lock_index(&mailbox);
if (r) goto error;
if (mailbox.quota.root) {
if (strlen(mailbox.quota.root) != strlen(oldroot)
|| strcmp(mailbox.quota.root, oldroot)) {
mailbox_close(&mailbox);
return 0;
}
free(mailbox.quota.root);
mailbox.quota.root = NULL;
r = mailbox_write_header(&mailbox);
if(r) goto error;
}
mailbox_close(&mailbox);
return 0;
error:
mailbox_close(&mailbox);
error_noclose:
syslog(LOG_ERR, "LOSTQUOTA: unable to remove quota root %s for %s: %s",
oldroot, name, error_message(r));
return 0;
}
static int mboxlist_changequota(const char *name,
int matchlen __attribute__((unused)),
int maycreate __attribute__((unused)),
void *rock)
{
int r;
struct mailbox mailbox;
struct change_rock *crock = (struct change_rock *) rock;
struct quota *mboxlist_newquota = crock->quota;
struct txn **tid = crock->tid;
assert(rock != NULL);
r = mailbox_open_header(name, 0, &mailbox);
if (r) goto error_noclose;
r = mailbox_lock_header(&mailbox);
if (r) goto error;
r = mailbox_open_index(&mailbox);
if (r) goto error;
r = mailbox_lock_index(&mailbox);
if (r) goto error;
if (mailbox.quota.root) {
if (strlen(mailbox.quota.root) >= strlen(mboxlist_newquota->root)) {
mailbox_close(&mailbox);
return 0;
}
r = quota_read(&mailbox.quota, tid, 1);
if (r) goto error;
if (mailbox.quota.used >= mailbox.quota_mailbox_used) {
mailbox.quota.used -= mailbox.quota_mailbox_used;
}
else {
mailbox.quota.used = 0;
}
r = quota_write(&mailbox.quota, tid);
if (r) {
syslog(LOG_ERR,
"LOSTQUOTA: unable to record free of " UQUOTA_T_FMT " bytes in quota %s",
mailbox.quota_mailbox_used, mailbox.quota.root);
}
free(mailbox.quota.root);
}
mailbox.quota.root = xstrdup(mboxlist_newquota->root);
r = mailbox_write_header(&mailbox);
if (r) goto error;
mboxlist_newquota->used += mailbox.quota_mailbox_used;
mailbox_close(&mailbox);
return 0;
error:
mailbox_close(&mailbox);
error_noclose:
syslog(LOG_ERR, "LOSTQUOTA: unable to change quota root for %s to %s: %s",
name, mboxlist_newquota->root, error_message(r));
return 0;
}
void mboxlist_init(int myflags)
{
int r;
if (myflags & MBOXLIST_SYNC) {
r = DB->sync();
}
}
void mboxlist_open(char *fname)
{
int ret, flags;
char *tofree = NULL;
if (!fname) {
size_t fname_len = strlen(config_dir)+strlen(FNAME_MBOXLIST)+1;
fname = xmalloc(fname_len);
tofree = fname;
strlcpy(fname, config_dir, fname_len);
strlcat(fname, FNAME_MBOXLIST, fname_len);
}
flags = CYRUSDB_CREATE;
if (config_getswitch(IMAPOPT_IMPROVED_MBOXLIST_SORT)) {
flags |= CYRUSDB_MBOXSORT;
}
ret = DB->open(fname, flags, &mbdb);
if (ret != 0) {
syslog(LOG_ERR, "DBERROR: opening %s: %s", fname,
cyrusdb_strerror(ret));
fatal("can't read mailboxes file", EC_TEMPFAIL);
}
if (tofree) free(tofree);
mboxlist_dbopen = 1;
}
void mboxlist_close(void)
{
int r;
if (mboxlist_dbopen) {
r = DB->close(mbdb);
if (r) {
syslog(LOG_ERR, "DBERROR: error closing mailboxes: %s",
cyrusdb_strerror(r));
}
mboxlist_dbopen = 0;
}
}
void mboxlist_done(void)
{
}
char *mboxlist_hash_usersubs(const char *userid)
{
char *fname = xmalloc(strlen(config_dir) + sizeof(FNAME_DOMAINDIR) +
sizeof(FNAME_USERDIR) + strlen(userid) +
sizeof(FNAME_SUBSSUFFIX) + 10);
char c, *domain;
if (config_virtdomains && (domain = strchr(userid, '@'))) {
char d = (char) dir_hash_c(domain+1);
*domain = '\0';
c = (char) dir_hash_c(userid);
sprintf(fname, "%s%s%c/%s%s%c/%s%s", config_dir, FNAME_DOMAINDIR, d,
domain+1, FNAME_USERDIR, c, userid, FNAME_SUBSSUFFIX);
*domain = '@';
}
else {
c = (char) dir_hash_c(userid);
sprintf(fname, "%s%s%c/%s%s", config_dir, FNAME_USERDIR, c, userid,
FNAME_SUBSSUFFIX);
}
return fname;
}
static int
mboxlist_opensubs(const char *userid,
struct db **ret)
{
int r = 0,flags;
char *subsfname;
subsfname = mboxlist_hash_usersubs(userid);
flags = CYRUSDB_CREATE;
if (config_getswitch(IMAPOPT_IMPROVED_MBOXLIST_SORT)) {
flags |= CYRUSDB_MBOXSORT;
}
r = SUBDB->open(subsfname, flags, ret);
if (r != CYRUSDB_OK) {
r = IMAP_IOERROR;
}
free(subsfname);
return r;
}
static void mboxlist_closesubs(struct db *sub)
{
SUBDB->close(sub);
}
int mboxlist_findsub(struct namespace *namespace __attribute__((unused)),
const char *pattern, int isadmin __attribute__((unused)),
char *userid, struct auth_state *auth_state,
int (*proc)(), void *rock, int force)
{
struct db *subs = NULL;
struct find_rock cbrock;
char usermboxname[MAX_MAILBOX_NAME+1];
int usermboxnamelen = 0;
const char *data;
int datalen;
int r = 0;
char *p;
int prefixlen;
int userlen = userid ? strlen(userid) : 0, domainlen = 0;
char domainpat[MAX_MAILBOX_NAME+1];
char *pat = NULL;
if (config_virtdomains && userid && (p = strchr(userid, '@'))) {
userlen = p - userid;
domainlen = strlen(p);
snprintf(domainpat, sizeof(domainpat), "%s!%s", p+1, pattern);
}
else
strncpy(domainpat, pattern, sizeof(domainpat));
cbrock.g = glob_init(pattern, GLOB_HIERARCHY|GLOB_INBOXCASE);
cbrock.namespace = NULL;
cbrock.domainlen = domainlen;
cbrock.inboxcase = glob_inboxcase(cbrock.g);
cbrock.isadmin = 1;
cbrock.auth_state = auth_state;
cbrock.checkmboxlist = !force;
cbrock.checkshared = 0;
cbrock.proc = proc;
cbrock.procrock = rock;
if ((r = mboxlist_opensubs(userid, &subs)) != 0) {
goto done;
}
if (userid && (!(p = strchr(userid, '.')) || ((p - userid) > userlen)) &&
strlen(userid)+5 < MAX_MAILBOX_NAME) {
if (domainlen)
snprintf(usermboxname, sizeof(usermboxname),
"%s!", userid+userlen+1);
snprintf(usermboxname+domainlen, sizeof(usermboxname)-domainlen,
"user.%.*s", userlen, userid);
usermboxnamelen = strlen(usermboxname);
}
else {
userid = 0;
}
if (userid) {
if (GLOB_TEST(cbrock.g, "INBOX") != -1) {
r = SUBDB->fetch(subs, usermboxname, usermboxnamelen,
&data, &datalen, NULL);
if (!r && data) {
r = (*proc)(cbrock.inboxcase, 5, 1, rock);
}
else if (r == CYRUSDB_NOTFOUND) r = 0;
}
else if (!strncmp(pattern,
usermboxname+domainlen, usermboxnamelen-domainlen) &&
GLOB_TEST(cbrock.g, usermboxname+domainlen) != -1) {
r = SUBDB->fetch(subs, usermboxname, usermboxnamelen,
&data, &datalen, NULL);
if (!r && data) {
r = (*proc)(usermboxname, usermboxnamelen, 1, rock);
}
else if (r == CYRUSDB_NOTFOUND) r = 0;
}
strlcat(usermboxname, ".", sizeof(usermboxname));
usermboxnamelen++;
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
}
if (r) goto done;
pattern = pat = xstrdup(pattern);
for (p = pat; *p; p++) {
if (*p == '*' || *p == '%' || *p == '?' || *p == '@') break;
}
prefixlen = p - pattern;
*p = '\0';
if (userid &&
(!strncmp(usermboxname+domainlen, pattern, usermboxnamelen-domainlen-1) ||
!strncasecmp("inbox.", pattern, prefixlen < 6 ? prefixlen : 6))) {
if (!strncmp(usermboxname+domainlen, pattern, usermboxnamelen-domainlen-1)) {
glob_free(&cbrock.g);
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
cbrock.inboxoffset = 0;
}
else {
cbrock.inboxoffset = strlen(userid);
}
cbrock.find_namespace = NAMESPACE_INBOX;
SUBDB->foreach(subs,
usermboxname, usermboxnamelen,
&find_p, &find_cb, &cbrock,
NULL);
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
} else {
cbrock.usermboxname = NULL;
cbrock.usermboxnamelen = 0;
}
cbrock.find_namespace = NAMESPACE_USER;
glob_free(&cbrock.g);
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
cbrock.inboxoffset = 0;
if (usermboxnamelen) {
usermboxname[--usermboxnamelen] = '\0';
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
}
SUBDB->foreach(subs, domainpat, domainlen + prefixlen,
&find_p, &find_cb, &cbrock, NULL);
done:
if (subs) mboxlist_closesubs(subs);
glob_free(&cbrock.g);
if (pat) free(pat);
return r;
}
int mboxlist_findsub_alt(struct namespace *namespace,
const char *pattern, int isadmin __attribute__((unused)),
char *userid, struct auth_state *auth_state,
int (*proc)(), void *rock, int force)
{
struct db *subs = NULL;
struct find_rock cbrock;
char usermboxname[MAX_MAILBOX_NAME+1], patbuf[MAX_MAILBOX_NAME+1];
int usermboxnamelen = 0;
const char *data;
int datalen;
int r = 0;
char *p;
int prefixlen, len;
int userlen = userid ? strlen(userid) : 0, domainlen = 0;
char domainpat[MAX_MAILBOX_NAME+1];
char *pat = NULL;
if (config_virtdomains && userid && (p = strchr(userid, '@'))) {
userlen = p - userid;
domainlen = strlen(p);
snprintf(domainpat, sizeof(domainpat), "%s!", p+1);
}
else
domainpat[0] = '\0';
cbrock.g = glob_init(pattern, GLOB_HIERARCHY|GLOB_INBOXCASE);
cbrock.namespace = namespace;
cbrock.domainlen = domainlen;
cbrock.inboxcase = glob_inboxcase(cbrock.g);
cbrock.isadmin = 1;
cbrock.auth_state = auth_state;
cbrock.checkmboxlist = !force;
cbrock.checkshared = 0;
cbrock.proc = proc;
cbrock.procrock = rock;
if ((r = mboxlist_opensubs(userid, &subs)) != 0) {
goto done;
}
if (userid && (!(p = strchr(userid, '.')) || ((p - userid) > userlen)) &&
strlen(userid)+5 < MAX_MAILBOX_NAME) {
if (domainlen)
snprintf(usermboxname, sizeof(usermboxname),
"%s!", userid+userlen+1);
snprintf(usermboxname+domainlen, sizeof(usermboxname)-domainlen,
"user.%.*s", userlen, userid);
usermboxnamelen = strlen(usermboxname);
}
else {
userid = 0;
}
if (userid) {
if (GLOB_TEST(cbrock.g, "INBOX") != -1) {
r = SUBDB->fetch(subs, usermboxname, usermboxnamelen,
&data, &datalen, NULL);
if (!r && data) {
r = (*proc)(cbrock.inboxcase, 5, 0, rock);
}
else if (r == CYRUSDB_NOTFOUND) r = 0;
}
strlcat(usermboxname, ".", sizeof(usermboxname));
usermboxnamelen++;
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
}
if (r) goto done;
glob_free(&cbrock.g);
pattern = pat = xstrdup(pattern);
for (p = pat; *p; p++) {
if (*p == '*' || *p == '%' || *p == '?' || *p == '@') break;
}
prefixlen = p - pattern;
if (userid) {
strlcpy(patbuf, "INBOX.", sizeof(patbuf));
strlcat(patbuf, pattern, sizeof(patbuf));
cbrock.g = glob_init(patbuf, GLOB_HIERARCHY|GLOB_INBOXCASE);
cbrock.inboxcase = glob_inboxcase(cbrock.g);
cbrock.inboxoffset = domainlen+userlen;
cbrock.find_namespace = NAMESPACE_INBOX;
SUBDB->foreach(subs,
usermboxname, usermboxnamelen,
&find_p, &find_cb, &cbrock,
NULL);
glob_free(&cbrock.g);
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
} else {
cbrock.usermboxname = NULL;
cbrock.usermboxnamelen = 0;
}
if (usermboxnamelen) {
usermboxname[--usermboxnamelen] = '\0';
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
}
len = strlen(namespace->prefix[NAMESPACE_USER]);
if(len>0) len--;
if (!strncmp(namespace->prefix[NAMESPACE_USER], pattern,
prefixlen < len ? prefixlen : len)) {
if (prefixlen < len) {
strlcpy(domainpat+domainlen, pattern+prefixlen,
sizeof(domainpat)-domainlen);
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
}
else {
strlcpy(domainpat+domainlen, "user",
sizeof(domainpat)-domainlen);
strlcat(domainpat, pattern+len, sizeof(domainpat));
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
}
cbrock.find_namespace = NAMESPACE_USER;
cbrock.inboxoffset = 0;
strlcpy(domainpat+domainlen, "user", sizeof(domainpat)-domainlen);
SUBDB->foreach(subs,
domainpat, strlen(domainpat),
&find_p, &find_cb, &cbrock,
NULL);
glob_free(&cbrock.g);
}
len = strlen(namespace->prefix[NAMESPACE_SHARED]);
if(len>0) len--;
if (!strncmp(namespace->prefix[NAMESPACE_SHARED], pattern,
prefixlen < len ? prefixlen : len)) {
cbrock.find_namespace = NAMESPACE_SHARED;
cbrock.inboxoffset = 0;
if (prefixlen <= len) {
for (p = pat+prefixlen; *p; p++) {
if (*p == '%') continue;
else if (*p == '.') p++;
break;
}
if (*pattern && !strchr(pattern, '.') &&
pattern[strlen(pattern)-1] == '%') {
cbrock.checkshared = 1;
}
if ((cbrock.checkshared || prefixlen == len) && !*p) {
strlcpy(domainpat+domainlen, "*", sizeof(domainpat)-domainlen);
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
cbrock.checkshared = 2;
}
else {
strlcpy(domainpat+domainlen, p, sizeof(domainpat)-domainlen);
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
}
domainpat[domainlen] = '\0';
SUBDB->foreach(subs,
domainpat, domainlen,
&find_p, &find_cb, &cbrock,
NULL);
}
else if (pattern[len] == '.') {
strlcpy(domainpat+domainlen, pattern+len+1,
sizeof(domainpat)-domainlen);
cbrock.g = glob_init(domainpat, GLOB_HIERARCHY);
SUBDB->foreach(subs,
domainpat, domainlen+prefixlen-(len+1),
&find_p, &find_cb, &cbrock,
NULL);
}
}
done:
if (subs) mboxlist_closesubs(subs);
glob_free(&cbrock.g);
if (pat) free(pat);
return r;
}
int mboxlist_changesub(const char *name, const char *userid,
struct auth_state *auth_state, int add, int force)
{
int r;
char *acl;
struct db *subs;
if ((r = mboxlist_opensubs(userid, &subs)) != 0) {
return r;
}
if (add && !force) {
if ((r = mboxlist_lookup(name, &acl, NULL))!=0) {
mboxlist_closesubs(subs);
return r;
}
if ((cyrus_acl_myrights(auth_state, acl) & ACL_LOOKUP) == 0) {
mboxlist_closesubs(subs);
return IMAP_MAILBOX_NONEXISTENT;
}
}
if (add) {
r = SUBDB->store(subs, name, strlen(name), "", 0, NULL);
} else {
r = SUBDB->delete(subs, name, strlen(name), NULL, 0);
if (r == CYRUSDB_EXISTS) r = CYRUSDB_OK;
}
switch (r) {
case CYRUSDB_OK:
r = 0;
break;
default:
r = IMAP_IOERROR;
break;
}
mboxlist_closesubs(subs);
return r;
}
int mboxlist_commit(struct txn *tid)
{
assert(tid);
return DB->commit(mbdb, tid);
}
int mboxlist_abort(struct txn *tid)
{
assert(tid);
return DB->abort(mbdb, tid);
}