Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
322 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/imap/ctl_cyrusdb.c b/imap/ctl_cyrusdb.c
index 168b437a8..5e8e4a2a0 100644
--- a/imap/ctl_cyrusdb.c
+++ b/imap/ctl_cyrusdb.c
@@ -1,413 +1,412 @@
/* ctl_cyrusdb.c -- Program to perform operations common to all cyrus DBs
*
* Copyright (c) 1994-2008 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Carnegie Mellon University
* Center for Technology Transfer and Enterprise Creation
* 4615 Forbes Avenue
* Suite 302
* Pittsburgh, PA 15213
* (412) 268-7393, fax: (412) 268-7395
* innovation@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sysexits.h>
#include <syslog.h>
#include <errno.h>
#if HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#include "annotate.h"
#include "cyrusdb.h"
#include "duplicate.h"
#include "global.h"
#include "libcyr_cfg.h"
#include "mboxlist.h"
#include "seen.h"
#include "statuscache.h"
#include "tls.h"
#include "util.h"
#include "xmalloc.h"
#include "xstrlcpy.h"
#ifdef ENABLE_BACKUP
#include "backup/backup.h"
#endif
#define N(a) (sizeof(a) / sizeof(a[0]))
static struct cyrusdb {
const char *name;
const char **configptr;
cyrusdb_archiver *archiver;
int doarchive;
} dblist[] = {
{ FNAME_MBOXLIST, &config_mboxlist_db, NULL, 1 },
{ FNAME_QUOTADB, &config_quota_db, NULL, 1 },
{ FNAME_GLOBALANNOTATIONS, &config_annotation_db, NULL, 1 },
#ifdef ENABLE_BACKUP
{ FNAME_BACKUPDB, &config_backup_db, NULL, 1 },
#endif
{ FNAME_DELIVERDB, &config_duplicate_db, NULL, 0 },
{ FNAME_TLSSESSIONS, &config_tls_sessions_db,NULL, 0 },
{ FNAME_PTSDB, &config_ptscache_db, NULL, 0 },
{ FNAME_STATUSCACHEDB, &config_statuscache_db, NULL, 0 },
{ NULL, NULL, NULL, 0 }
};
static int compdb(const void *v1, const void *v2)
{
struct cyrusdb *db1 = (struct cyrusdb *) v1;
struct cyrusdb *db2 = (struct cyrusdb *) v2;
/* compare archive pointers for sort */
return ((char *)db1->archiver - (char *)db2->archiver);
}
static void usage(void)
{
fprintf(stderr, "ctl_cyrusdb [-C <altconfig>] -c\n");
fprintf(stderr, "ctl_cyrusdb [-C <altconfig>] -r [-x]\n");
exit(-1);
}
/* Callback for use by process_mboxlist */
static int fixmbox(const mbentry_t *mbentry,
void *rock __attribute__((unused)))
{
int r;
/* if MBTYPE_RESERVED, unset it & call mboxlist_delete */
if (mbentry->mbtype & MBTYPE_RESERVE) {
r = mboxlist_deletemailboxlock(mbentry->name, 1, NULL, NULL, NULL,
MBOXLIST_DELETE_FORCE);
if (r) {
/* log the error */
syslog(LOG_ERR,
"could not remove reserved mailbox '%s': %s",
mbentry->name, error_message(r));
} else {
syslog(LOG_NOTICE,
"removed reserved mailbox '%s'",
mbentry->name);
}
return 0;
}
/* clean out any legacy specialuse */
if (mbentry->legacy_specialuse) {
char *userid = mboxname_to_userid(mbentry->name);
if (userid) {
struct buf buf = BUF_INITIALIZER;
buf_setcstr(&buf, mbentry->legacy_specialuse);
annotatemore_rawwrite(mbentry->name, "/specialuse", userid, &buf);
buf_free(&buf);
free(userid);
}
mbentry_t *copy = mboxlist_entry_copy(mbentry);
/* XXX - const correctness */
free(copy->legacy_specialuse);
copy->legacy_specialuse = NULL;
mboxlist_update(copy, /*localonly*/1);
mboxlist_entry_free(&copy);
}
r = mboxlist_update_intermediaries(mbentry->name, mbentry->mbtype, /*modseq*/0);
if (r) {
syslog(LOG_ERR,
"failed to update intermediaries to mailboxes list for %s: %s",
mbentry->name, cyrusdb_strerror(r));
}
return 0;
}
static void process_mboxlist(void)
{
/* build a list of mailboxes - we're using internal names here */
mboxlist_allmbox(NULL, fixmbox, NULL, MBOXTREE_INTERMEDIATES);
/* enable or disable RACLs per config */
mboxlist_set_racls(config_getswitch(IMAPOPT_REVERSEACLS));
- mboxlist_set_runiqueid(config_getswitch(IMAPOPT_REVERSEUNIQUEIDS));
}
static const char *dbfname(struct cyrusdb *db)
{
static char buf[MAX_MAILBOX_PATH];
const char *fname = NULL;
/* find absolute path to db files in configuration */
if (!strcmp(db->name, FNAME_MBOXLIST))
fname = config_getstring(IMAPOPT_MBOXLIST_DB_PATH);
else if (!strcmp(db->name, FNAME_QUOTADB))
fname = config_getstring(IMAPOPT_QUOTA_DB_PATH);
else if (!strcmp(db->name, FNAME_GLOBALANNOTATIONS))
fname = config_getstring(IMAPOPT_ANNOTATION_DB_PATH);
#ifdef ENABLE_BACKUP
else if (!strcmp(db->name, FNAME_BACKUPDB))
fname = config_getstring(IMAPOPT_BACKUP_DB_PATH);
#endif
else if (!strcmp(db->name, FNAME_DELIVERDB))
fname = config_getstring(IMAPOPT_DUPLICATE_DB_PATH);
else if (!strcmp(db->name, FNAME_TLSSESSIONS))
fname = config_getstring(IMAPOPT_TLS_SESSIONS_DB_PATH);
else if (!strcmp(db->name, FNAME_PTSDB))
fname = config_getstring(IMAPOPT_PTSCACHE_DB_PATH);
else if (!strcmp(db->name, FNAME_STATUSCACHEDB))
fname = config_getstring(IMAPOPT_STATUSCACHE_DB_PATH);
/* use default if no special path was found */
if (!fname)
snprintf(buf, MAX_MAILBOX_PATH, "%s%s", config_dir, db->name);
else
snprintf(buf, MAX_MAILBOX_PATH, "%s", fname);
return buf;
}
static void check_convert(struct cyrusdb *db, const char *fname)
{
const char *detectname = cyrusdb_detect(fname);
char backendbuf[100];
char *p;
int r;
/* unable to detect current type, assume all is good */
if (!detectname) return;
/* strip the -nosync from the name if present */
xstrncpy(backendbuf, *db->configptr, 100);
p = strstr(backendbuf, "-nosync");
if (p) *p = '\0';
/* ignore files that are already the right type */
if (!strcmp(backendbuf, detectname)) return;
/* otherwise we need to upgrade! */
syslog(LOG_NOTICE, "converting %s from %s to %s",
fname, detectname, *db->configptr);
r = cyrusdb_convert(fname, fname, detectname, *db->configptr);
if (r)
syslog(LOG_NOTICE, "conversion failed %s", fname);
}
int main(int argc, char *argv[])
{
extern char *optarg;
int opt, r, r2;
char *alt_config = NULL;
int reserve_flag = 1;
enum { RECOVER, CHECKPOINT, NONE } op = NONE;
char *dirname = NULL, *backup1 = NULL, *backup2 = NULL;
strarray_t files = STRARRAY_INITIALIZER;
char *msg = "";
int i, rotated = 0;
r = r2 = 0;
while ((opt = getopt(argc, argv, "C:rxc")) != EOF) {
switch (opt) {
case 'C': /* alt config file */
alt_config = optarg;
break;
case 'r':
libcyrus_config_setint(CYRUSOPT_DB_INIT_FLAGS, CYRUSDB_RECOVER);
msg = "recovering cyrus databases";
if (op == NONE) op = RECOVER;
else usage();
break;
case 'c':
msg = "checkpointing cyrus databases";
if (op == NONE) op = CHECKPOINT;
else usage();
break;
case 'x':
reserve_flag = 0;
break;
default:
usage();
break;
}
}
if (op == NONE || (op != RECOVER && !reserve_flag)) {
usage();
/* NOTREACHED */
}
cyrus_init(alt_config, "ctl_cyrusdb", 0, 0);
/* create the name of the db directory */
/* (used by backup directory names) */
dirname = strconcat(config_dir, FNAME_DBDIR, (char *)NULL);
/* create the names of the backup directories */
backup1 = strconcat(dirname, ".backup1", (char *)NULL);
backup2 = strconcat(dirname, ".backup2", (char *)NULL);
syslog(LOG_NOTICE, "%s", msg);
/* detect backends */
for (i = 0; dblist[i].name != NULL; i++)
dblist[i].archiver = cyrusdb_getarchiver(*dblist[i].configptr);
/* sort dbenvs */
qsort(dblist, N(dblist)-1, sizeof(struct cyrusdb), &compdb);
for (i = 0; dblist[i].name; i++) {
const char *fname = dbfname(&dblist[i]);
if (op == RECOVER)
check_convert(&dblist[i], fname);
/* if we need to archive this db, add it to the list */
if (dblist[i].doarchive)
strarray_add(&files, fname);
/* deal with each dbenv once */
if (dblist[i+1].archiver == dblist[i].archiver)
continue;
r = r2 = 0;
switch (op) {
case RECOVER:
break;
case CHECKPOINT:
r2 = cyrusdb_sync(*dblist[i].configptr);
if (r2) {
syslog(LOG_ERR, "DBERROR: sync %s: %s", dirname,
cyrusdb_strerror(r2));
fprintf(stderr,
"ctl_cyrusdb: unable to sync environment\n");
}
/* ARCHIVE */
r2 = 0;
if (!rotated) {
/* rotate the backup directories -- ONE time only */
char *file;
DIR *dirp;
struct dirent *dirent;
/* remove db.backup2 */
dirp = opendir(backup2);
if (dirp) {
while ((dirent = readdir(dirp)) != NULL) {
if (dirent->d_name[0] == '.') continue;
file = strconcat(backup2, "/", dirent->d_name, (char *)NULL);
unlink(file);
free(file);
}
closedir(dirp);
}
r2 = rmdir(backup2);
/* move db.backup1 to db.backup2 */
if (r2 == 0 || errno == ENOENT)
r2 = rename(backup1, backup2);
/* make a new db.backup1 */
if (r2 == 0 || errno == ENOENT)
r2 = mkdir(backup1, 0755);
rotated = 1;
}
/* do the archive */
if (r2 == 0)
r2 = dblist[i].archiver(&files, backup1);
if (r2) {
syslog(LOG_ERR, "DBERROR: archive %s: %s", dirname,
cyrusdb_strerror(r2));
fprintf(stderr,
"ctl_cyrusdb: unable to archive environment\n");
}
break;
default:
break;
}
strarray_truncate(&files, 0);
}
strarray_fini(&files);
if(op == RECOVER && reserve_flag)
process_mboxlist();
free(dirname);
free(backup1);
free(backup2);
cyrus_done();
syslog(LOG_NOTICE, "done %s", msg);
exit(r || r2);
}
diff --git a/imap/mboxlist.c b/imap/mboxlist.c
index 8ae855487..66d8de1f7 100644
--- a/imap/mboxlist.c
+++ b/imap/mboxlist.c
@@ -1,4764 +1,4665 @@
/* mboxlist.c -- Mailbox list manipulation routines
*
* Copyright (c) 1994-2008 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Carnegie Mellon University
* Center for Technology Transfer and Enterprise Creation
* 4615 Forbes Avenue
* Suite 302
* Pittsburgh, PA 15213
* (412) 268-7393, fax: (412) 268-7395
* innovation@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <sysexits.h>
#include <syslog.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include "acl.h"
#include "annotate.h"
#include "bsearch.h"
#include "glob.h"
#include "assert.h"
#include "global.h"
#include "cyrusdb.h"
#include "util.h"
#include "mailbox.h"
#include "mboxevent.h"
#include "xmalloc.h"
#include "xstrlcpy.h"
#include "partlist.h"
#include "xstrlcat.h"
#include "user.h"
/* generated headers are not necessarily in current directory */
#include "imap/imap_err.h"
#include "mboxname.h"
#include "mupdate-client.h"
#include "mboxlist.h"
#include "quota.h"
#include "sync_log.h"
#define DB config_mboxlist_db
#define SUBDB config_subscription_db
cyrus_acl_canonproc_t mboxlist_ensureOwnerRights;
static struct db *mbdb;
static int mboxlist_dbopen = 0;
static int mboxlist_initialized = 0;
static int have_racl = 0;
static int have_runq = 0;
static int mboxlist_opensubs(const char *userid, struct db **ret);
static void mboxlist_closesubs(struct db *sub);
static int mboxlist_rmquota(const mbentry_t *mbentry, void *rock);
static int mboxlist_changequota(const mbentry_t *mbentry, void *rock);
static void init_internal();
EXPORTED mbentry_t *mboxlist_entry_create(void)
{
mbentry_t *ret = xzmalloc(sizeof(mbentry_t));
/* xxx - initialiser functions here? */
return ret;
}
EXPORTED mbentry_t *mboxlist_entry_copy(const mbentry_t *src)
{
mbentry_t *copy = mboxlist_entry_create();
copy->name = xstrdupnull(src->name);
copy->ext_name = xstrdupnull(src->ext_name);
copy->mtime = src->mtime;
copy->uidvalidity = src->uidvalidity;
copy->mbtype = src->mbtype;
copy->createdmodseq = src->createdmodseq;
copy->foldermodseq = src->foldermodseq;
copy->partition = xstrdupnull(src->partition);
copy->server = xstrdupnull(src->server);
copy->acl = xstrdupnull(src->acl);
copy->uniqueid = xstrdupnull(src->uniqueid);
copy->legacy_specialuse = xstrdupnull(src->legacy_specialuse);
return copy;
}
EXPORTED void mboxlist_entry_free(mbentry_t **mbentryptr)
{
mbentry_t *mbentry = *mbentryptr;
/* idempotent */
if (!mbentry) return;
free(mbentry->name);
free(mbentry->ext_name);
free(mbentry->partition);
free(mbentry->server);
free(mbentry->acl);
free(mbentry->uniqueid);
free(mbentry->legacy_specialuse);
free(mbentry);
*mbentryptr = NULL;
}
static void _write_acl(struct dlist *dl, const char *aclstr)
{
const char *p, *q;
struct dlist *al = dlist_newkvlist(dl, "A");
p = aclstr;
while (p && *p) {
char *name,*val;
q = strchr(p, '\t');
if (!q) break;
name = xstrndup(p, q-p);
q++;
p = strchr(q, '\t');
if (p) {
val = xstrndup(q, p-q);
p++;
}
else
val = xstrdup(q);
dlist_setatom(al, name, val);
free(name);
free(val);
}
}
EXPORTED const char *mboxlist_mbtype_to_string(uint32_t mbtype)
{
static struct buf buf = BUF_INITIALIZER;
buf_reset(&buf);
if (mbtype & MBTYPE_DELETED)
buf_putc(&buf, 'd');
if (mbtype & MBTYPE_MOVING)
buf_putc(&buf, 'm');
if (mbtype & MBTYPE_NETNEWS)
buf_putc(&buf, 'n');
if (mbtype & MBTYPE_REMOTE)
buf_putc(&buf, 'r');
if (mbtype & MBTYPE_RESERVE)
buf_putc(&buf, 'z');
if (mbtype & MBTYPE_CALENDAR)
buf_putc(&buf, 'c');
if (mbtype & MBTYPE_COLLECTION)
buf_putc(&buf, 'b');
if (mbtype & MBTYPE_ADDRESSBOOK)
buf_putc(&buf, 'a');
if (mbtype & MBTYPE_INTERMEDIATE)
buf_putc(&buf, 'i');
if (mbtype & MBTYPE_SUBMISSION)
buf_putc(&buf, 's');
if (mbtype & MBTYPE_PUSHSUBSCRIPTION)
buf_putc(&buf, 'p');
if (mbtype & MBTYPE_JMAPNOTIFICATION)
buf_putc(&buf, 'j');
/* make sure we didn't forget to set a character for every interesting bit */
if (mbtype) assert(buf_len(&buf));
return buf_cstring(&buf);
}
static char *mboxlist_entry_cstring(const mbentry_t *mbentry)
{
struct buf buf = BUF_INITIALIZER;
struct dlist *dl = dlist_newkvlist(NULL, mbentry->name);
if (mbentry->acl)
_write_acl(dl, mbentry->acl);
if (mbentry->uniqueid)
dlist_setatom(dl, "I", mbentry->uniqueid);
if (mbentry->partition)
dlist_setatom(dl, "P", mbentry->partition);
if (mbentry->server)
dlist_setatom(dl, "S", mbentry->server);
if (mbentry->mbtype)
dlist_setatom(dl, "T", mboxlist_mbtype_to_string(mbentry->mbtype));
if (mbentry->uidvalidity)
dlist_setnum32(dl, "V", mbentry->uidvalidity);
if (mbentry->createdmodseq)
dlist_setnum64(dl, "C", mbentry->createdmodseq);
if (mbentry->foldermodseq)
dlist_setnum64(dl, "F", mbentry->foldermodseq);
dlist_setdate(dl, "M", time(NULL));
dlist_printbuf(dl, 0, &buf);
dlist_free(&dl);
return buf_release(&buf);
}
EXPORTED char *mbentry_metapath(const struct mboxlist_entry *mbentry, int metatype, int isnew)
{
return mboxname_metapath(mbentry->partition,
mbentry->name,
mbentry->uniqueid,
metatype,
isnew);
}
EXPORTED char *mbentry_datapath(const struct mboxlist_entry *mbentry, uint32_t uid)
{
return mboxname_datapath(mbentry->partition,
mbentry->name,
mbentry->uniqueid,
uid);
}
/*
* read a single record from the mailboxes.db and return a pointer to it
*/
static int mboxlist_read(const char *name, const char **dataptr, size_t *datalenptr,
struct txn **tid, int wrlock)
{
int namelen = strlen(name);
int r;
if (!namelen)
return IMAP_MAILBOX_NONEXISTENT;
if (wrlock) {
r = cyrusdb_fetchlock(mbdb, name, namelen, dataptr, datalenptr, tid);
} else {
r = cyrusdb_fetch(mbdb, name, namelen, dataptr, datalenptr, tid);
}
switch (r) {
case CYRUSDB_OK:
/* no entry required, just checking if it exists */
return 0;
break;
case CYRUSDB_AGAIN:
return IMAP_AGAIN;
break;
case CYRUSDB_NOTFOUND:
return IMAP_MAILBOX_NONEXISTENT;
break;
default:
xsyslog(LOG_ERR, "DBERROR: error fetching mboxlist",
"mailbox=<%s> error=<%s>",
name, cyrusdb_strerror(r));
return IMAP_IOERROR;
break;
}
/* never get here */
}
EXPORTED uint32_t mboxlist_string_to_mbtype(const char *string)
{
uint32_t mbtype = 0;
if (!string) return 0; /* null just means default */
for (; *string; string++) {
switch (*string) {
case 'a':
mbtype |= MBTYPE_ADDRESSBOOK;
break;
case 'b':
mbtype |= MBTYPE_COLLECTION;
break;
case 'c':
mbtype |= MBTYPE_CALENDAR;
break;
case 'd':
mbtype |= MBTYPE_DELETED;
break;
case 'i':
mbtype |= MBTYPE_INTERMEDIATE;
break;
case 'j':
mbtype |= MBTYPE_JMAPNOTIFICATION;
break;
case 'm':
mbtype |= MBTYPE_MOVING;
break;
case 'n':
mbtype |= MBTYPE_NETNEWS;
break;
case 'r':
mbtype |= MBTYPE_REMOTE;
break;
case 'p':
mbtype |= MBTYPE_PUSHSUBSCRIPTION;
break;
case 's':
mbtype |= MBTYPE_SUBMISSION;
break;
case 'z':
mbtype |= MBTYPE_RESERVE;
break;
}
}
/* make sure we didn't forget to set a character for every interesting bit */
assert(mbtype);
return mbtype;
}
struct parseentry_rock {
struct mboxlist_entry *mbentry;
struct buf *aclbuf;
int doingacl;
};
int parseentry_cb(int type, struct dlistsax_data *d)
{
struct parseentry_rock *rock = (struct parseentry_rock *)d->rock;
switch(type) {
case DLISTSAX_KVLISTSTART:
if (!strcmp(buf_cstring(&d->kbuf), "A")) {
rock->doingacl = 1;
}
break;
case DLISTSAX_KVLISTEND:
rock->doingacl = 0;
break;
case DLISTSAX_STRING:
if (rock->doingacl) {
buf_append(rock->aclbuf, &d->kbuf);
buf_putc(rock->aclbuf, '\t');
buf_appendcstr(rock->aclbuf, d->data);
buf_putc(rock->aclbuf, '\t');
}
else {
const char *key = buf_cstring(&d->kbuf);
if (!strcmp(key, "C")) {
rock->mbentry->createdmodseq = atomodseq_t(d->data);
}
else if (!strcmp(key, "F")) {
rock->mbentry->foldermodseq = atomodseq_t(d->data);
}
else if (!strcmp(key, "I")) {
rock->mbentry->uniqueid = xstrdupnull(d->data);
}
else if (!strcmp(key, "M")) {
rock->mbentry->mtime = atoi(d->data);
}
else if (!strcmp(key, "P")) {
rock->mbentry->partition = xstrdupnull(d->data);
}
else if (!strcmp(key, "S")) {
rock->mbentry->server = xstrdupnull(d->data);
}
else if (!strcmp(key, "T")) {
rock->mbentry->mbtype = mboxlist_string_to_mbtype(d->data);
}
else if (!strcmp(key, "V")) {
rock->mbentry->uidvalidity = atol(d->data);
}
}
}
return 0;
}
/*
* parse a record read from the mailboxes.db into its parts.
*
* full dlist format is:
* A: _a_cl
* C _c_reatedmodseq
* F: _f_oldermodseq
* I: unique_i_d
* M: _m_time
* P: _p_artition
* S: _s_erver
* T: _t_ype
* V: uid_v_alidity
*/
EXPORTED int mboxlist_parse_entry(mbentry_t **mbentryptr,
const char *name, size_t namelen,
const char *data, size_t datalen)
{
static struct buf aclbuf;
int r = IMAP_MAILBOX_BADFORMAT;
char *freeme = NULL;
char **target;
char *p, *q;
mbentry_t *mbentry = mboxlist_entry_create();
if (!datalen)
goto done;
/* copy name */
if (namelen)
mbentry->name = xstrndup(name, namelen);
else
mbentry->name = xstrdup(name);
/* check for DLIST mboxlist */
if (*data == '%') {
struct parseentry_rock rock;
memset(&rock, 0, sizeof(struct parseentry_rock));
rock.mbentry = mbentry;
rock.aclbuf = &aclbuf;
aclbuf.len = 0;
r = dlist_parsesax(data, datalen, 0, parseentry_cb, &rock);
if (!r) mbentry->acl = buf_newcstring(&aclbuf);
goto done;
}
/* copy data */
freeme = p = xstrndup(data, datalen);
/* check for extended mboxlist entry */
if (*p == '(') {
int last = 0;
p++; /* past leading '(' */
while (!last) {
target = NULL;
q = p;
while (*q && *q != ' ' && *q != ')') q++;
if (*q != ' ') break;
*q++ = '\0';
if (!strcmp(p, "uniqueid")) target = &mbentry->uniqueid;
if (!strcmp(p, "specialuse")) target = &mbentry->legacy_specialuse;
p = q;
while (*q && *q != ' ' && *q != ')') q++;
if (*q != ' ') last = 1;
if (*q) *q++ = '\0';
if (target) *target = xstrdup(p);
p = q;
}
if (*p == ' ') p++; /* past trailing ' ' */
}
/* copy out interesting parts */
mbentry->mbtype = strtol(p, &p, 10);
if (*p == ' ') p++;
q = p;
while (*q && *q != ' ' && *q != '!') q++;
if (*q == '!') {
*q++ = '\0';
mbentry->server = xstrdup(p);
p = q;
while (*q && *q != ' ') q++;
}
if (*q) *q++ = '\0';
mbentry->partition = xstrdup(p);
mbentry->acl = xstrdup(q);
r = 0;
done:
if (!r && mbentryptr)
*mbentryptr = mbentry;
else mboxlist_entry_free(&mbentry);
free(freeme);
return r;
}
/* read a record and parse into parts */
static int mboxlist_mylookup(const char *name,
mbentry_t **mbentryptr,
struct txn **tid, int wrlock)
{
int r;
const char *data;
size_t datalen;
init_internal();
r = mboxlist_read(name, &data, &datalen, tid, wrlock);
if (r) return r;
return mboxlist_parse_entry(mbentryptr, name, 0, data, datalen);
}
/*
* Lookup 'name' in the mailbox list, ignoring reserved records
*/
EXPORTED int mboxlist_lookup(const char *name, mbentry_t **entryptr,
struct txn **tid)
{
mbentry_t *entry = NULL;
int r;
init_internal();
r = mboxlist_mylookup(name, &entry, tid, 0);
if (r) return r;
/* Ignore "reserved" entries, like they aren't there */
if (entry->mbtype & MBTYPE_RESERVE) {
mboxlist_entry_free(&entry);
return IMAP_MAILBOX_RESERVED;
}
/* Ignore "deleted" entries, like they aren't there */
if (entry->mbtype & MBTYPE_DELETED) {
mboxlist_entry_free(&entry);
return IMAP_MAILBOX_NONEXISTENT;
}
/* Ignore "intermediate" entries, like they aren't there */
if (entry->mbtype & MBTYPE_INTERMEDIATE) {
mboxlist_entry_free(&entry);
return IMAP_MAILBOX_NONEXISTENT;
}
if (entryptr) *entryptr = entry;
else mboxlist_entry_free(&entry);
return 0;
}
EXPORTED int mboxlist_lookup_allow_all(const char *name,
mbentry_t **entryptr,
struct txn **tid)
{
return mboxlist_mylookup(name, entryptr, tid, 0);
}
struct _find_specialuse_data {
const char *use;
const char *userid;
char *mboxname;
};
static int _find_specialuse(const mbentry_t *mbentry, void *rock)
{
struct _find_specialuse_data *d = (struct _find_specialuse_data *)rock;
struct buf attrib = BUF_INITIALIZER;
annotatemore_lookup(mbentry->name, "/specialuse", d->userid, &attrib);
if (attrib.len) {
strarray_t *uses = strarray_split(buf_cstring(&attrib), NULL, 0);
if (strarray_find_case(uses, d->use, 0) >= 0)
d->mboxname = xstrdup(mbentry->name);
strarray_free(uses);
}
buf_free(&attrib);
if (d->mboxname) return CYRUSDB_DONE;
return 0;
}
EXPORTED char *mboxlist_find_specialuse(const char *use, const char *userid)
{
init_internal();
assert(userid);
/* \\Inbox is magical */
if (!strcasecmp(use, "\\Inbox"))
return mboxname_user_mbox(userid, NULL);
struct _find_specialuse_data rock = { use, userid, NULL };
mboxlist_usermboxtree(userid, NULL, _find_specialuse, &rock, MBOXTREE_SKIP_ROOT);
return rock.mboxname;
}
struct _find_uniqueid_data {
const char *uniqueid;
mbentry_t **entryptr;
};
static int _find_uniqueid(const mbentry_t *mbentry, void *rock) {
struct _find_uniqueid_data *d = (struct _find_uniqueid_data *) rock;
int r = 0;
if (!strcmpsafe(d->uniqueid, mbentry->uniqueid)) {
*(d->entryptr) = mboxlist_entry_copy(mbentry);
r = CYRUSDB_DONE;
}
return r;
}
// calling this function without a userid is fine, it will scan the entire server!
static int _mboxlist_find_uniqueid(const char *uniqueid, const char *userid,
const struct auth_state *auth_state,
mbentry_t **entryptr)
{
struct _find_uniqueid_data rock = { uniqueid, entryptr };
int flags = MBOXTREE_INTERMEDIATES|MBOXTREE_PLUS_RACL;
init_internal();
if (userid)
return mboxlist_usermboxtree(userid, auth_state, _find_uniqueid, &rock, flags);
else
return mboxlist_allmbox("", _find_uniqueid, &rock, flags);
}
EXPORTED char *mboxlist_find_uniqueid(const char *uniqueid, const char *userid,
const struct auth_state *auth_state)
{
mbentry_t *mbentry = NULL;
char *mboxname = NULL;
_mboxlist_find_uniqueid(uniqueid, userid, auth_state, &mbentry);
if (mbentry) {
mboxname = xstrdup(mbentry->name);
mboxlist_entry_free(&mbentry);
}
return mboxname;
}
EXPORTED int mboxlist_lookup_by_uniqueid(const char *uniqueid, mbentry_t **entryptr)
{
return _mboxlist_find_uniqueid(uniqueid, NULL, NULL, entryptr);
}
/* given a mailbox name, find the staging directory. XXX - this should
* require more locking, and staging directories should be by pid */
HIDDEN int mboxlist_findstage(const char *name, char *stagedir, size_t sd_len)
{
const char *root;
mbentry_t *mbentry = NULL;
int r;
init_internal();
assert(stagedir != NULL);
/* Find mailbox */
r = mboxlist_lookup(name, &mbentry, NULL);
if (r) return r;
root = config_partitiondir(mbentry->partition);
mboxlist_entry_free(&mbentry);
if (!root) return IMAP_PARTITION_UNKNOWN;
snprintf(stagedir, sd_len, "%s/stage./", root);
return 0;
}
static void mboxlist_racl_key(int isuser, const char *keyuser, const char *mbname, struct buf *buf)
{
buf_setcstr(buf, "$RACL$");
buf_putc(buf, isuser ? 'U' : 'S');
buf_putc(buf, '$');
if (keyuser) {
buf_appendcstr(buf, keyuser);
buf_putc(buf, '$');
}
if (mbname) {
buf_appendcstr(buf, mbname);
}
}
static int user_can_read(const strarray_t *aclbits, const char *user)
{
int i;
if (!aclbits) return 0;
for (i = 0; i+1 < strarray_size(aclbits); i+=2) {
// skip ACLs without read bit
if (!strchr(strarray_nth(aclbits, i+1), 'r')) continue;
if (!strcmp(strarray_nth(aclbits, i), user)) return 1;
}
return 0;
}
static int mboxlist_update_raclmodseq(const char *acluser)
{
char *aclusermbox = mboxname_user_mbox(acluser, NULL);
mbentry_t *raclmbentry = NULL;
if (mboxlist_lookup(aclusermbox, &raclmbentry, NULL) == 0) {
mboxname_nextraclmodseq(aclusermbox, 0);
sync_log_mailbox(aclusermbox);
}
mboxlist_entry_free(&raclmbentry);
free(aclusermbox);
return 0;
}
static int mboxlist_update_racl(const char *name, const mbentry_t *oldmbentry, const mbentry_t *newmbentry, struct txn **txn)
{
static strarray_t *admins = NULL;
struct buf buf = BUF_INITIALIZER;
char *userid = mboxname_to_userid(name);
strarray_t *oldusers = NULL;
strarray_t *newusers = NULL;
int i;
int r = 0;
if (!admins) admins = strarray_split(config_getstring(IMAPOPT_ADMINS), NULL, 0);
if (oldmbentry && !(oldmbentry->mbtype & MBTYPE_DELETED))
oldusers = strarray_split(oldmbentry->acl, "\t", 0);
if (newmbentry && !(newmbentry->mbtype & MBTYPE_DELETED))
newusers = strarray_split(newmbentry->acl, "\t", 0);
if (oldusers) {
for (i = 0; i+1 < strarray_size(oldusers); i+=2) {
const char *acluser = strarray_nth(oldusers, i);
if (!strchr(strarray_nth(oldusers, i+1), 'r')) continue;
if (!strcmpsafe(userid, acluser)) continue;
if (strarray_find(admins, acluser, 0) >= 0) continue;
if (user_can_read(newusers, acluser)) continue;
mboxlist_racl_key(!!userid, acluser, name, &buf);
r = cyrusdb_delete(mbdb, buf.s, buf.len, txn, /*force*/1);
if (r) goto done;
mboxlist_update_raclmodseq(acluser);
}
}
if (newusers) {
for (i = 0; i+1 < strarray_size(newusers); i+=2) {
const char *acluser = strarray_nth(newusers, i);
if (!strchr(strarray_nth(newusers, i+1), 'r')) continue;
if (!strcmpsafe(userid, acluser)) continue;
if (strarray_find(admins, acluser, 0) >= 0) continue;
if (user_can_read(oldusers, acluser)) continue;
mboxlist_racl_key(!!userid, acluser, name, &buf);
r = cyrusdb_store(mbdb, buf.s, buf.len, "", 0, txn);
if (r) goto done;
mboxlist_update_raclmodseq(acluser);
}
}
done:
strarray_free(oldusers);
strarray_free(newusers);
free(userid);
buf_free(&buf);
return r;
}
-static void mboxlist_runiqueid_key(const char *uniqueid, const char *mbname, struct buf *buf)
-{
- buf_setcstr(buf, "$RUNQ$");
- buf_appendcstr(buf, uniqueid);
- buf_putc(buf, '$');
- if (mbname) {
- buf_appendcstr(buf, mbname);
- }
-}
-
-static int mboxlist_update_runiqueid(const char *name, const mbentry_t *oldmbentry, const mbentry_t *newmbentry, struct txn **txn)
-{
- struct buf buf = BUF_INITIALIZER;
- strarray_t oldids = STRARRAY_INITIALIZER;
- strarray_t newids = STRARRAY_INITIALIZER;
- const mbentry_t *old;
- const mbentry_t *new;
- int i;
- int r = 0;
-
- for (old = oldmbentry; old; old = old->deletedentry)
- if (old->uniqueid) strarray_add(&oldids, old->uniqueid);
-
- for (new = newmbentry; new; new = new->deletedentry)
- if (new->uniqueid) strarray_add(&newids, new->uniqueid);
-
- strarray_subtract_complement(&oldids, &newids);
- strarray_subtract_complement(&newids, &oldids);
-
- for (i = 0; i < strarray_size(&oldids); i++) {
- mboxlist_runiqueid_key(strarray_nth(&oldids, i), name, &buf);
- r = cyrusdb_delete(mbdb, buf.s, buf.len, txn, /*force*/1);
- if (r) goto done;
- }
- for (i = 0; i < strarray_size(&newids); i++) {
- mboxlist_runiqueid_key(strarray_nth(&newids, i), name, &buf);
- r = cyrusdb_store(mbdb, buf.s, buf.len, "", 0, txn);
- if (r) goto done;
- }
-
- done:
- strarray_fini(&oldids);
- strarray_fini(&newids);
- buf_free(&buf);
- return r;
-}
-
static int mboxlist_update_entry(const char *name, const mbentry_t *mbentry, struct txn **txn)
{
mbentry_t *old = NULL;
int r = 0;
mboxlist_mylookup(name, &old, txn, 0); // ignore errors, it will be NULL
if (have_racl) {
r = mboxlist_update_racl(name, old, mbentry, txn);
- if (r) goto done;
- }
-
- if (have_runq) {
- r = mboxlist_update_runiqueid(name, old, mbentry, txn);
- if (r) goto done;
+ /* XXX return value here is discarded? */
}
if (mbentry) {
char *mboxent = mboxlist_entry_cstring(mbentry);
r = cyrusdb_store(mbdb, name, strlen(name), mboxent, strlen(mboxent), txn);
free(mboxent);
if (!r && config_auditlog) {
/* XXX is there a difference between "" and NULL? */
xsyslog(LOG_NOTICE, "auditlog: acl",
"sessionid=<%s> "
"mailbox=<%s> uniqueid=<%s> mbtype=<%s> "
"oldacl=<%s> acl=<%s> foldermodseq=<%llu>",
session_id(),
name, mbentry->uniqueid, mboxlist_mbtype_to_string(mbentry->mbtype),
old ? old->acl : "NONE", mbentry->acl, mbentry->foldermodseq);
}
}
else {
r = cyrusdb_delete(mbdb, name, strlen(name), txn, /*force*/1);
}
- done:
mboxlist_entry_free(&old);
return r;
}
EXPORTED int mboxlist_delete(const char *name)
{
return mboxlist_update_entry(name, NULL, NULL);
}
EXPORTED int mboxlist_update(mbentry_t *mbentry, int localonly)
{
int r = 0, r2 = 0;
struct txn *tid = NULL;
init_internal();
r = mboxlist_update_entry(mbentry->name, mbentry, &tid);
if (!r)
mboxname_setmodseq(mbentry->name, mbentry->foldermodseq, mbentry->mbtype,
MBOXMODSEQ_ISFOLDER);
/* commit the change to mupdate */
if (!r && !localonly && config_mupdate_server) {
mupdate_handle *mupdate_h = NULL;
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if (r) {
syslog(LOG_ERR,
"cannot connect to mupdate server for update of '%s'",
mbentry->name);
} else {
char *location = strconcat(config_servername, "!",
mbentry->partition, (char *)NULL);
r = mupdate_activate(mupdate_h, mbentry->name,
location, mbentry->acl);
free(location);
if (r) {
syslog(LOG_ERR,
"MUPDATE: can't update mailbox entry for '%s'",
mbentry->name);
}
}
mupdate_disconnect(&mupdate_h);
}
if (tid) {
if (r) {
r2 = cyrusdb_abort(mbdb, tid);
if (r2)
xsyslog(LOG_ERR, "DBERROR: error aborting transaction",
"error=<%s>", cyrusdb_strerror(r2));
} else {
r2 = cyrusdb_commit(mbdb, tid);
if (r2)
xsyslog(LOG_ERR, "DBERROR: error committing transaction",
"error=<%s>", cyrusdb_strerror(r2));
}
}
return r;
}
static void assert_namespacelocked(const char *mboxname)
{
char *userid = mboxname_to_userid(mboxname);
assert(user_isnamespacelocked(userid));
free(userid);
}
static int _findparent(const char *mboxname, mbentry_t **mbentryp, int allow_all)
{
mbentry_t *mbentry = NULL;
mbname_t *mbname = mbname_from_intname(mboxname);
int r = IMAP_MAILBOX_NONEXISTENT;
init_internal();
while (strarray_size(mbname_boxes(mbname))) {
free(mbname_pop_boxes(mbname));
/* skip exactly INBOX, since it's not a real intermediate folder,
* and the parent of INBOX.INBOX.foo is INBOX */
if (strarray_size(mbname_boxes(mbname)) == 1 &&
!strcmp(strarray_nth(mbname_boxes(mbname), 0), "INBOX")) {
free(mbname_pop_boxes(mbname));
}
mboxlist_entry_free(&mbentry);
if (allow_all)
r = mboxlist_lookup_allow_all(mbname_intname(mbname), &mbentry, NULL);
else
r = mboxlist_lookup(mbname_intname(mbname), &mbentry, NULL);
if (r != IMAP_MAILBOX_NONEXISTENT)
break;
}
if (r)
mboxlist_entry_free(&mbentry);
else
*mbentryp = mbentry;
mbname_free(&mbname);
return r;
}
EXPORTED int mboxlist_findparent(const char *mboxname,
mbentry_t **mbentryp)
{
return _findparent(mboxname, mbentryp, 0);
}
EXPORTED int mboxlist_findparent_allow_all(const char *mboxname,
mbentry_t **mbentryp)
{
return _findparent(mboxname, mbentryp, 1);
}
static int mboxlist_create_partition(const char *mboxname,
const char *part,
char **out)
{
mbentry_t *parent = NULL;
if (!part) {
int r = mboxlist_findparent(mboxname, &parent);
if (!r) part = parent->partition;
}
/* use defaultpartition if specified */
if (!part && config_defpartition)
part = config_defpartition;
/* look for most fitting partition */
if (!part)
part = partlist_local_select();
/* Configuration error */
if (!part || (strlen(part) > MAX_PARTITION_LEN))
goto err;
if (!config_partitiondir(part))
goto err;
*out = xstrdupnull(part);
mboxlist_entry_free(&parent);
return 0;
err:
mboxlist_entry_free(&parent);
return IMAP_PARTITION_UNKNOWN;
}
/*
* Check if a mailbox can be created. There is no other setup at this
* stage, just the check!
*/
static int mboxlist_create_namecheck(const char *mboxname,
const char *userid,
const struct auth_state *auth_state,
int isadmin, int force_subdirs)
{
mbentry_t *mbentry = NULL;
int r = 0;
/* policy first */
r = mboxname_policycheck(mboxname);
if (r) goto done;
/* is this the user's INBOX namespace? */
if (!isadmin && mboxname_userownsmailbox(userid, mboxname)) {
/* User has admin rights over their own mailbox namespace */
if (config_implicitrights & ACL_ADMIN)
isadmin = 1;
}
/* Check to see if mailbox already exists */
r = mboxlist_lookup(mboxname, &mbentry, NULL);
if (r != IMAP_MAILBOX_NONEXISTENT) {
if (!r) {
r = IMAP_MAILBOX_EXISTS;
/* Lie about error if privacy demands */
if (!isadmin &&
!(cyrus_acl_myrights(auth_state, mbentry->acl) & ACL_LOOKUP)) {
r = IMAP_PERMISSION_DENIED;
}
}
goto done;
}
mboxlist_entry_free(&mbentry);
/* look for a parent mailbox */
r = mboxlist_findparent(mboxname, &mbentry);
if (r == 0) {
/* found a parent */
char root[MAX_MAILBOX_NAME+1];
/* check acl */
if (!isadmin &&
!(cyrus_acl_myrights(auth_state, mbentry->acl) & ACL_CREATE)) {
r = IMAP_PERMISSION_DENIED;
goto done;
}
/* check quota */
if (quota_findroot(root, sizeof(root), mboxname)) {
quota_t qdiffs[QUOTA_NUMRESOURCES] = QUOTA_DIFFS_DONTCARE_INITIALIZER;
qdiffs[QUOTA_NUMFOLDERS] = 1;
r = quota_check_useds(root, qdiffs);
if (r) goto done;
}
/* make sure parent isn't forbidden from containing children */
if ((!isadmin || mboxname_userownsmailbox(userid, mboxname))
&& config_getstring(IMAPOPT_SPECIALUSE_NOCHILDREN))
{
struct buf attrib = BUF_INITIALIZER;
mbname_t *mbname;
mbname = mbname_from_intname(mbentry->name);
annotatemore_lookup(mbentry->name, "/specialuse",
mbname_userid(mbname), &attrib);
mbname_free(&mbname);
if (buf_len(&attrib)) {
strarray_t *uses = strarray_split(buf_cstring(&attrib), NULL, 0);
strarray_t *forbidden = strarray_split(
config_getstring(IMAPOPT_SPECIALUSE_NOCHILDREN),
NULL,
STRARRAY_TRIM
);
if (strarray_intersect(uses, forbidden))
r = IMAP_PERMISSION_DENIED;
strarray_free(forbidden);
strarray_free(uses);
}
buf_free(&attrib);
if (r) goto done;
}
}
else if (r == IMAP_MAILBOX_NONEXISTENT) {
/* no parent mailbox */
if (!isadmin) {
r = IMAP_PERMISSION_DENIED;
goto done;
}
if (!force_subdirs) {
mbname_t *mbname = mbname_from_intname(mboxname);
if (!mbname_isdeleted(mbname) && mbname_userid(mbname) && strarray_size(mbname_boxes(mbname))) {
/* Disallow creating user.X.* when no user.X */
r = IMAP_PERMISSION_DENIED;
goto done;
}
mbname_free(&mbname);
}
/* otherwise no parent is OK */
r = 0;
}
done:
mboxlist_entry_free(&mbentry);
return r;
}
static int mboxlist_create_acl(const char *mboxname, char **out)
{
mbentry_t *mbentry = NULL;
int r;
int mask;
char *defaultacl;
char *identifier;
char *rights;
char *p;
r = mboxlist_findparent(mboxname, &mbentry);
if (!r) {
*out = xstrdup(mbentry->acl);
mboxlist_entry_free(&mbentry);
return 0;
}
*out = xstrdup("");
char *owner = mboxname_to_userid(mboxname);
if (owner) {
/* owner gets full permission on own mailbox by default */
cyrus_acl_set(out, owner, ACL_MODE_SET, ACL_ALL,
(cyrus_acl_canonproc_t *)0, (void *)0);
free(owner);
return 0;
}
defaultacl = identifier = xstrdup(config_getstring(IMAPOPT_DEFAULTACL));
for (;;) {
while (*identifier && Uisspace(*identifier)) identifier++;
rights = identifier;
while (*rights && !Uisspace(*rights)) rights++;
if (!*rights) break;
*rights++ = '\0';
while (*rights && Uisspace(*rights)) rights++;
if (!*rights) break;
p = rights;
while (*p && !Uisspace(*p)) p++;
if (*p) *p++ = '\0';
cyrus_acl_strtomask(rights, &mask);
/* XXX and if strtomask fails? */
cyrus_acl_set(out, identifier, ACL_MODE_SET, mask,
(cyrus_acl_canonproc_t *)0, (void *)0);
identifier = p;
}
free(defaultacl);
return 0;
}
/* and this API just plain sucks */
EXPORTED int mboxlist_createmailboxcheck(const char *name, int mbtype __attribute__((unused)),
const char *partition,
int isadmin, const char *userid,
const struct auth_state *auth_state,
char **newacl, char **newpartition,
int forceuser)
{
char *part = NULL;
char *acl = NULL;
int r = 0;
init_internal();
r = mboxlist_create_namecheck(name, userid, auth_state,
isadmin, forceuser);
if (r) goto done;
if (newacl) {
r = mboxlist_create_acl(name, &acl);
if (r) goto done;
}
if (newpartition) {
r = mboxlist_create_partition(name, partition, &part);
if (r) goto done;
}
done:
if (r || !newacl) free(acl);
else *newacl = acl;
if (r || !newpartition) free(part);
else *newpartition = part;
return r;
}
/* PLEASE NOTE - ALWAYS CALL AFTER MAKING THE CHANGES, as this function
* will check for children when deciding whether to create or remove
* intermediate folders */
EXPORTED int mboxlist_update_intermediaries(const char *frommboxname,
int mbtype, modseq_t modseq)
{
mbentry_t *mbentry = NULL;
mbname_t *mbname = mbname_from_intname(frommboxname);
int r = 0;
/* only use intermediates for user mailboxes */
if (!mbname_userid(mbname))
goto out;
for (; strarray_size(mbname_boxes(mbname)); free(mbname_pop_boxes(mbname))) {
/* check for magic INBOX */
if (strarray_size(mbname_boxes(mbname)) == 1 &&
!strcmp(strarray_nth(mbname_boxes(mbname), 0), "INBOX")) {
/* don't generate magic INBOX intermediate, JMAP doesn't use it */
goto out;
}
const char *mboxname = mbname_intname(mbname);
mboxlist_entry_free(&mbentry);
r = mboxlist_mylookup(mboxname, &mbentry, NULL, 0);
if (r == IMAP_MAILBOX_NONEXISTENT) r = 0;
if (r) goto out;
if (mbentry) {
if (mbentry->mbtype & MBTYPE_DELETED) {
/* fall through to create a new intermediate */
}
else if (mbentry->mbtype & MBTYPE_INTERMEDIATE) {
/* existing intermediate - delete unless it still has children */
if (mboxlist_haschildren(mboxname))
continue;
/* bump modseq, we're removing a thing that can be seen */
if (!modseq)
modseq = mboxname_nextmodseq(mboxname, mbentry->foldermodseq,
mbtype, MBOXMODSEQ_ISFOLDER);
mbentry_t *newmbentry = mboxlist_entry_copy(mbentry);
newmbentry->mbtype = MBTYPE_DELETED;
newmbentry->foldermodseq = modseq;
syslog(LOG_NOTICE,
"mboxlist: deleting intermediate with no children: %s (%s)",
mboxname, mbentry->uniqueid);
r = mboxlist_update_entry(mboxname, newmbentry, NULL);
mboxlist_entry_free(&newmbentry);
if (r) goto out;
sync_log_mailbox(mboxname);
/* we've changed the type, we're done at this level */
continue;
}
else {
/* real mailbox, we're done at this level */
continue;
}
}
/* if there's no children, there's no need for intermediates */
if (!mboxlist_haschildren(mboxname))
continue;
/* bump modseq, we're adding a thing that can be seen */
if (!modseq)
modseq = mboxname_nextmodseq(mboxname,
mbentry ? mbentry->foldermodseq : 0,
mbtype, MBOXMODSEQ_ISFOLDER);
mbentry_t *newmbentry = mboxlist_entry_create();
newmbentry->uniqueid = xstrdupnull(makeuuid());
newmbentry->createdmodseq = modseq;
newmbentry->foldermodseq = modseq;
newmbentry->mbtype = MBTYPE_INTERMEDIATE;
newmbentry->foldermodseq = modseq;
syslog(LOG_NOTICE,
"mboxlist: creating intermediate with children: %s (%s)",
mboxname, newmbentry->uniqueid);
r = mboxlist_update_entry(mboxname, newmbentry, NULL);
mboxlist_entry_free(&newmbentry);
if (r) goto out;
sync_log_mailbox(mboxname);
}
out:
mboxlist_entry_free(&mbentry);
mbname_free(&mbname);
return r;
}
EXPORTED int mboxlist_promote_intermediary(const char *mboxname)
{
mbentry_t *mbentry = NULL, *parent = NULL;
struct mailbox *mailbox = NULL;
int r = 0;
struct txn *tid = NULL;
r = mboxlist_lookup_allow_all(mboxname, &mbentry, &tid);
if (r || !(mbentry->mbtype & MBTYPE_INTERMEDIATE)) goto done;
r = mboxlist_findparent(mboxname, &parent);
if (r) goto done;
r = mboxlist_create_partition(mboxname, parent->partition,
&mbentry->partition);
if (r) goto done;
mbentry->mbtype = 0; // intermediaries are always standard
free(mbentry->acl);
mbentry->acl = xstrdupnull(parent->acl);
r = mailbox_create(mboxname, mbentry->mbtype,
mbentry->partition, parent->acl,
mbentry->uniqueid, 0 /* options */,
mbentry->uidvalidity,
mbentry->createdmodseq,
mbentry->foldermodseq, &mailbox);
if (r) goto done;
r = mailbox_add_conversations(mailbox, /*silent*/1);
if (r) goto done;
r = mboxlist_update_entry(mboxname, mbentry, &tid);
if (r) goto done;
done:
mailbox_close(&mailbox);
if (tid) {
if (r) cyrusdb_abort(mbdb, tid);
else {
r = cyrusdb_commit(mbdb, tid);
}
}
mboxlist_entry_free(&mbentry);
mboxlist_entry_free(&parent);
return r;
}
/*
* Create a mailbox
*
* 1. verify ACL's to best of ability (CRASH: abort)
* 2. verify parent ACL's if need to
* 3. create the local mailbox locally (exclusive lock) and keep it locked
* 4. open mupdate connection if necessary
* 5. create mupdate entry (CRASH: mupdate inconsistent)
*
*/
static int mboxlist_createmailbox_full(const char *mboxname, int mbtype,
const char *partition,
int isadmin, const char *userid,
const struct auth_state *auth_state,
int options, unsigned uidvalidity,
modseq_t createdmodseq,
modseq_t highestmodseq,
modseq_t foldermodseq,
const char *copyacl, const char *uniqueid,
int localonly, int forceuser, int dbonly,
int keep_intermediaries,
struct mailbox **mboxptr)
{
int r;
char *newpartition = NULL;
char *acl = NULL;
struct mailbox *newmailbox = NULL;
int isremote = mbtype & MBTYPE_REMOTE;
mbentry_t *newmbentry = NULL;
r = mboxlist_create_namecheck(mboxname, userid, auth_state,
isadmin, forceuser);
if (r) goto done;
assert_namespacelocked(mboxname);
if (copyacl) {
acl = xstrdup(copyacl);
}
else {
r = mboxlist_create_acl(mboxname, &acl);
if (r) goto done;
}
r = mboxlist_create_partition(mboxname, partition, &newpartition);
if (r) goto done;
if (!dbonly && !isremote) {
/* Filesystem Operations */
r = mailbox_create(mboxname, mbtype, newpartition, acl, uniqueid,
options, uidvalidity, createdmodseq, highestmodseq, &newmailbox);
if (r) goto done; /* CREATE failed */
r = mailbox_add_conversations(newmailbox, /*silent*/0);
if (r) goto done;
}
/* all is well - activate the mailbox */
newmbentry = mboxlist_entry_create();
newmbentry->acl = xstrdupnull(acl);
newmbentry->mbtype = mbtype;
newmbentry->partition = xstrdupnull(newpartition);
if (newmailbox) {
newmbentry->uniqueid = xstrdupnull(newmailbox->uniqueid);
newmbentry->uidvalidity = newmailbox->i.uidvalidity;
newmbentry->createdmodseq = newmailbox->i.createdmodseq;
newmbentry->foldermodseq = foldermodseq ? foldermodseq : newmailbox->i.highestmodseq;
}
r = mboxlist_update_entry(mboxname, newmbentry, NULL);
if (!r && !keep_intermediaries) {
/* create any missing intermediaries */
r = mboxlist_update_intermediaries(mboxname, mbtype, newmbentry->foldermodseq);
}
if (r) {
xsyslog(LOG_ERR, "DBERROR: failed to insert to mailboxes list",
"mailbox=<%s> error=<%s>",
mboxname, cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
/* 9. set MUPDATE entry as commited (CRASH: commited) */
if (!r && config_mupdate_server && !localonly) {
mupdate_handle *mupdate_h = NULL;
char *loc = strconcat(config_servername, "!", newpartition, (char *)NULL);
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if (!r) r = mupdate_reserve(mupdate_h, mboxname, loc);
if (!r) r = mupdate_activate(mupdate_h, mboxname, loc, acl);
if (r) {
syslog(LOG_ERR, "MUPDATE: can't commit mailbox entry for '%s'",
mboxname);
mboxlist_update_entry(mboxname, NULL, 0);
}
if (mupdate_h) mupdate_disconnect(&mupdate_h);
free(loc);
}
done:
if (newmailbox) {
if (r) mailbox_delete(&newmailbox);
else if (mboxptr) *mboxptr = newmailbox;
else mailbox_close(&newmailbox);
}
free(acl);
free(newpartition);
mboxlist_entry_free(&newmbentry);
return r;
}
EXPORTED int mboxlist_createmailboxlock(const char *name, int mbtype,
const char *partition,
int isadmin, const char *userid,
const struct auth_state *auth_state,
int localonly, int forceuser, int dbonly,
int notify, struct mailbox **mailboxptr)
{
struct mboxlock *namespacelock = mboxname_usernamespacelock(name);
int r = mboxlist_createmailbox_unq(name, mbtype, partition, isadmin,
userid, auth_state, localonly,
forceuser, dbonly, notify, NULL,
mailboxptr);
mboxname_release(&namespacelock);
return r;
}
EXPORTED int mboxlist_createmailbox(const char *name, int mbtype,
const char *partition,
int isadmin, const char *userid,
const struct auth_state *auth_state,
int localonly, int forceuser, int dbonly,
int notify, struct mailbox **mailboxptr)
{
return mboxlist_createmailbox_unq(name, mbtype, partition, isadmin,
userid, auth_state, localonly,
forceuser, dbonly, notify, NULL,
mailboxptr);
}
EXPORTED int mboxlist_createmailbox_unq(const char *name, int mbtype,
const char *partition,
int isadmin, const char *userid,
const struct auth_state *auth_state,
int localonly, int forceuser, int dbonly,
int notify, const char *uniqueid,
struct mailbox **mailboxptr)
{
int options = config_getint(IMAPOPT_MAILBOX_DEFAULT_OPTIONS)
| OPT_POP3_NEW_UIDL;
return mboxlist_createmailbox_opts(name, mbtype, partition, isadmin, userid,
auth_state, options, localonly, forceuser,
dbonly, notify, uniqueid, mailboxptr);
}
EXPORTED int mboxlist_createmailbox_opts(const char *name, int mbtype,
const char *partition,
int isadmin, const char *userid,
const struct auth_state *auth_state,
int options, int localonly,
int forceuser, int dbonly,
int notify, const char *uniqueid,
struct mailbox **mailboxptr)
{
int r;
struct mailbox *mailbox = NULL;
uint32_t uidvalidity = 0;
modseq_t createdmodseq = 0;
mbentry_t *oldmbentry = NULL;
init_internal();
/* check if a mailbox tombstone or intermediate record exists */
r = mboxlist_lookup_allow_all(name, &oldmbentry, NULL);
if (!r) {
if (oldmbentry->mbtype & MBTYPE_DELETED) {
/* then the UIDVALIDITY must be higher than before */
if (uidvalidity <= oldmbentry->uidvalidity)
uidvalidity = oldmbentry->uidvalidity+1;
}
else if (oldmbentry->mbtype & MBTYPE_INTERMEDIATE) {
/* then use the existing mailbox ID and createdmodseq */
if (!uniqueid) uniqueid = oldmbentry->uniqueid;
createdmodseq = oldmbentry->createdmodseq;
}
}
r = mboxlist_createmailbox_full(name, mbtype, partition,
isadmin, userid, auth_state,
options, uidvalidity, createdmodseq, 0, 0, NULL,
uniqueid, localonly,
forceuser, dbonly, 0, &mailbox);
if (notify && !r) {
/* send a MailboxCreate event notification */
struct mboxevent *mboxevent = mboxevent_new(EVENT_MAILBOX_CREATE);
mboxevent_extract_mailbox(mboxevent, mailbox);
mboxevent_set_access(mboxevent, NULL, NULL, userid, mailbox->name, 1);
mboxevent_notify(&mboxevent);
mboxevent_free(&mboxevent);
}
if (mailboxptr && !r) *mailboxptr = mailbox;
else mailbox_close(&mailbox);
mboxlist_entry_free(&oldmbentry);
return r;
}
EXPORTED int mboxlist_createsync(const char *name, int mbtype,
const char *partition,
const char *userid, const struct auth_state *auth_state,
int options, unsigned uidvalidity,
modseq_t createdmodseq,
modseq_t highestmodseq,
modseq_t foldermodseq,
const char *acl, const char *uniqueid,
int local_only, int keep_intermediaries,
struct mailbox **mboxptr)
{
return mboxlist_createmailbox_full(name, mbtype, partition,
1, userid, auth_state,
options, uidvalidity,
createdmodseq, highestmodseq,
foldermodseq, acl, uniqueid,
local_only, 1, 0,
keep_intermediaries, mboxptr);
}
/* insert an entry for the proxy */
EXPORTED int mboxlist_insertremote(mbentry_t *mbentry,
struct txn **txn)
{
int r = 0;
if (mbentry->server) {
/* remote mailbox */
if (config_mupdate_config == IMAP_ENUM_MUPDATE_CONFIG_UNIFIED &&
!strcasecmp(mbentry->server, config_servername)) {
/* its on our server, make it a local mailbox */
mbentry->mbtype &= ~MBTYPE_REMOTE;
mbentry->server = NULL;
}
else {
/* make sure it's a remote mailbox */
mbentry->mbtype |= MBTYPE_REMOTE;
}
}
/* database put */
r = mboxlist_update_entry(mbentry->name, mbentry, txn);
switch (r) {
case CYRUSDB_OK:
break;
case CYRUSDB_AGAIN:
abort(); /* shouldn't happen ! */
break;
default:
xsyslog(LOG_ERR, "DBERROR: error updating database",
"mailbox=<%s> error=<%s>",
mbentry->name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
break;
}
return r;
}
/* Special function to delete a remote mailbox.
* Only affects mboxlist.
* Assumes admin powers. */
EXPORTED int mboxlist_deleteremote(const char *name, struct txn **in_tid)
{
int r;
struct txn **tid;
struct txn *lcl_tid = NULL;
mbentry_t *mbentry = NULL;
if(in_tid) {
tid = in_tid;
} else {
tid = &lcl_tid;
}
retry:
r = mboxlist_mylookup(name, &mbentry, tid, 1);
switch (r) {
case 0:
break;
case IMAP_MAILBOX_NONEXISTENT:
r = 0;
break;
case IMAP_AGAIN:
goto retry;
break;
default:
goto done;
}
if (mbentry && (mbentry->mbtype & MBTYPE_REMOTE) && !mbentry->server) {
syslog(LOG_ERR,
"mboxlist_deleteremote called on non-remote mailbox: %s",
name);
goto done;
}
r = mboxlist_update_entry(name, NULL, tid);
if (r) {
xsyslog(LOG_ERR, "DBERROR: error deleting entry",
"mailbox=<%s> error=<%s>",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
/* commit db operations, but only if we weren't passed a transaction */
if (!in_tid) {
r = cyrusdb_commit(mbdb, *tid);
if (r) {
xsyslog(LOG_ERR, "DBERROR: failed on commit",
"error=<%s>",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
tid = NULL;
}
done:
if (r && !in_tid && tid) {
/* Abort the transaction if it is still in progress */
cyrusdb_abort(mbdb, *tid);
}
mboxlist_entry_free(&mbentry);
return r;
}
/*
* Delayed Delete a mailbox: translate delete into rename
*/
EXPORTED int
mboxlist_delayed_deletemailbox(const char *name, int isadmin,
const char *userid,
const struct auth_state *auth_state,
struct mboxevent *mboxevent,
int flags)
{
mbentry_t *mbentry = NULL;
mbentry_t *newmbentry = NULL;
strarray_t existing = STRARRAY_INITIALIZER;
char newname[MAX_MAILBOX_BUFFER];
int r = 0;
long myrights;
int checkacl = flags & MBOXLIST_DELETE_CHECKACL;
int localonly = flags & MBOXLIST_DELETE_LOCALONLY;
int force = flags & MBOXLIST_DELETE_FORCE;
int keep_intermediaries = flags & MBOXLIST_DELETE_KEEP_INTERMEDIARIES;
int unprotect_specialuse = flags & MBOXLIST_DELETE_UNPROTECT_SPECIALUSE;
init_internal();
if (!isadmin && force) return IMAP_PERMISSION_DENIED;
/* delete of a user.X folder */
mbname_t *mbname = mbname_from_intname(name);
if (mbname_userid(mbname) && !strarray_size(mbname_boxes(mbname))) {
/* Can't DELETE INBOX (your own inbox) */
if (!strcmpsafe(mbname_userid(mbname), userid)) {
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
/* Only admins may delete user */
if (!isadmin) {
r = IMAP_PERMISSION_DENIED;
goto done;
}
}
if (!isadmin && mbname_userid(mbname) && !unprotect_specialuse) {
const char *protect = config_getstring(IMAPOPT_SPECIALUSE_PROTECT);
if (protect) {
struct buf attrib = BUF_INITIALIZER;
annotatemore_lookup(mbname_intname(mbname), "/specialuse", mbname_userid(mbname), &attrib);
if (attrib.len) {
strarray_t *check = strarray_split(protect, NULL, STRARRAY_TRIM);
strarray_t *uses = strarray_split(buf_cstring(&attrib), NULL, 0);
if (strarray_intersect_case(uses, check))
r = IMAP_MAILBOX_SPECIALUSE;
strarray_free(uses);
strarray_free(check);
}
buf_free(&attrib);
}
if (r) goto done;
}
r = mboxlist_lookup_allow_all(name, &mbentry, NULL);
if (r) goto done;
/* check if user has Delete right (we've already excluded non-admins
* from deleting a user mailbox) */
if (checkacl && !(mbentry->mbtype & MBTYPE_INTERMEDIATE)) {
myrights = cyrus_acl_myrights(auth_state, mbentry->acl);
if (!(myrights & ACL_DELETEMBOX)) {
/* User has admin rights over their own mailbox namespace */
if (mboxname_userownsmailbox(userid, name) &&
(config_implicitrights & ACL_ADMIN)) {
isadmin = 1;
}
/* Lie about error if privacy demands */
r = (isadmin || (myrights & ACL_LOOKUP)) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
goto done;
}
}
/* get the deleted name */
mboxname_todeleted(name, newname, 1);
/* Get mboxlist_renamemailbox to do the hard work. No ACL checks needed */
r = mboxlist_renamemailbox(mbentry, newname, mbentry->partition,
0 /* uidvalidity */,
1 /* isadmin */, userid,
auth_state,
mboxevent,
localonly /* local_only */,
force, 1,
keep_intermediaries,
0 /* move_subscription */, 0 /* silent */);
if (r) goto done;
/* Bump the deletedmodseq of the entries of mbtype. Do not
* bump the folderdeletedmodseq, yet. We'll take care of
* that in mboxlist_deletemailbox. */
r = mboxlist_lookup_allow_all(newname, &newmbentry, NULL);
if (!r) mboxname_setmodseq(newname, newmbentry->foldermodseq,
newmbentry->mbtype, MBOXMODSEQ_ISDELETE);
done:
strarray_fini(&existing);
mboxlist_entry_free(&newmbentry);
mboxlist_entry_free(&mbentry);
mbname_free(&mbname);
return r;
}
/*
* Delete a mailbox.
* Deleting the mailbox user.FOO may only be performed by an admin.
*
* 1. Begin transaction
* 2. Verify ACL's
* 3. remove from database
* 4. remove from disk
* 5. commit transaction
* 6. Open mupdate connection if necessary
* 7. delete from mupdate
*
*/
EXPORTED int mboxlist_deletemailbox(const char *name, int isadmin,
const char *userid,
const struct auth_state *auth_state,
struct mboxevent *mboxevent,
int flags)
{
mbentry_t *mbentry = NULL;
int r = 0;
long myrights;
struct mailbox *mailbox = NULL;
int isremote = 0;
mupdate_handle *mupdate_h = NULL;
int checkacl = flags & MBOXLIST_DELETE_CHECKACL;
int localonly = flags & MBOXLIST_DELETE_LOCALONLY;
int force = flags & MBOXLIST_DELETE_FORCE;
int keep_intermediaries = flags & MBOXLIST_DELETE_KEEP_INTERMEDIARIES;
int silent = flags & MBOXLIST_DELETE_SILENT;
int unprotect_specialuse = flags & MBOXLIST_DELETE_UNPROTECT_SPECIALUSE;
init_internal();
if (!isadmin && force) return IMAP_PERMISSION_DENIED;
assert_namespacelocked(name);
/* delete of a user.X folder */
mbname_t *mbname = mbname_from_intname(name);
if (mbname_userid(mbname) && !strarray_size(mbname_boxes(mbname))) {
/* Can't DELETE INBOX (your own inbox) */
if (!strcmpsafe(mbname_userid(mbname), userid)) {
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
/* Only admins may delete user */
if (!isadmin) {
r = IMAP_PERMISSION_DENIED;
goto done;
}
}
if (!isadmin && mbname_userid(mbname) && !unprotect_specialuse) {
const char *protect = config_getstring(IMAPOPT_SPECIALUSE_PROTECT);
if (protect) {
struct buf attrib = BUF_INITIALIZER;
annotatemore_lookup(mbname_intname(mbname), "/specialuse", mbname_userid(mbname), &attrib);
if (attrib.len) {
strarray_t *check = strarray_split(protect, NULL, STRARRAY_TRIM);
strarray_t *uses = strarray_split(buf_cstring(&attrib), NULL, 0);
if (strarray_intersect_case(uses, check))
r = IMAP_MAILBOX_SPECIALUSE;
strarray_free(uses);
strarray_free(check);
}
buf_free(&attrib);
}
if (r) goto done;
}
r = mboxlist_lookup_allow_all(name, &mbentry, NULL);
if (r) goto done;
if (mbentry->mbtype & MBTYPE_INTERMEDIATE) {
// make it deleted and mark it done!
if (!mboxname_isdeletedmailbox(name, NULL)) {
mbentry_t *newmbentry = mboxlist_entry_copy(mbentry);
newmbentry->mbtype = MBTYPE_DELETED;
if (!silent) {
newmbentry->foldermodseq = mboxname_nextmodseq(newmbentry->name, newmbentry->foldermodseq,
newmbentry->mbtype,
MBOXMODSEQ_ISFOLDER|MBOXMODSEQ_ISDELETE);
}
r = mboxlist_update(newmbentry, /*localonly*/1);
if (r) {
xsyslog(LOG_ERR, "DBERROR: error marking deleted",
"mailbox=<%s> error=<%s>",
name, cyrusdb_strerror(r));
}
mboxlist_entry_free(&newmbentry);
}
else {
r = mboxlist_update_entry(name, NULL, 0);
if (r) {
xsyslog(LOG_ERR, "DBERROR: error deleting",
"mailbox=<%s> error=<%s>",
name, cyrusdb_strerror(r));
}
}
goto done;
}
isremote = mbentry->mbtype & MBTYPE_REMOTE;
/* check if user has Delete right (we've already excluded non-admins
* from deleting a user mailbox) */
if (checkacl) {
myrights = cyrus_acl_myrights(auth_state, mbentry->acl);
if(!(myrights & ACL_DELETEMBOX)) {
/* User has admin rights over their own mailbox namespace */
if (mboxname_userownsmailbox(userid, name) &&
(config_implicitrights & ACL_ADMIN)) {
isadmin = 1;
}
/* Lie about error if privacy demands */
r = (isadmin || (myrights & ACL_LOOKUP)) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
goto done;
}
}
/* Lock the mailbox if it isn't a remote mailbox */
if (!isremote) {
r = mailbox_open_iwl(name, &mailbox);
if (!r) mailbox->silentchanges = silent;
}
if (r && !force) goto done;
/* remove from mupdate */
if (!isremote && !localonly && config_mupdate_server) {
/* delete the mailbox in MUPDATE */
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if (r) {
syslog(LOG_ERR,
"cannot 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);
}
if (mupdate_h) mupdate_disconnect(&mupdate_h);
}
if (r && !force) goto done;
if (!isremote && !mboxname_isdeletedmailbox(name, NULL)) {
/* store a DELETED marker */
int haschildren = mboxlist_haschildren(name);
mbentry_t *newmbentry = mboxlist_entry_create();
newmbentry->name = xstrdupnull(name);
newmbentry->mbtype = haschildren ? MBTYPE_INTERMEDIATE : MBTYPE_DELETED;
if (mailbox) {
newmbentry->uniqueid = xstrdupnull(mailbox->uniqueid);
newmbentry->uidvalidity = mailbox->i.uidvalidity;
newmbentry->createdmodseq = mailbox->i.createdmodseq;
newmbentry->foldermodseq = mailbox_modseq_dirty(mailbox);
}
r = mboxlist_update(newmbentry, /*localonly*/1);
/* any other updated intermediates get the same modseq */
if (!r && !keep_intermediaries) {
r = mboxlist_update_intermediaries(mbentry->name, mbentry->mbtype, newmbentry->foldermodseq);
}
/* Bump the modseq of entries of mbtype. There's still a tombstone
* for this mailbox, so don't bump the folderdeletedmodseq, yet. */
if (!r) {
mboxname_setmodseq(mbentry->name, newmbentry->foldermodseq,
mbentry->mbtype, MBOXMODSEQ_ISDELETE);
}
mboxlist_entry_free(&newmbentry);
}
else {
/* delete entry (including DELETED.* mailboxes, no need
* to keep that rubbish around) */
r = mboxlist_update_entry(name, NULL, 0);
if (r) {
xsyslog(LOG_ERR, "DBERROR: error deleting",
"mailbox=<%s> error=<%s>",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
if (!force) goto done;
}
/* This mailbox is gone completely, so mark its modseq */
if (!r && !isremote && mboxname_isdeletedmailbox(name, NULL)) {
mboxname_setmodseq(name, mailbox->foldermodseq, mbentry->mbtype,
MBOXMODSEQ_ISFOLDER|MBOXMODSEQ_ISDELETE);
}
if (r && !force) goto done;
}
/* delete underlying mailbox */
if (!isremote && mailbox) {
/* only on a real delete do we delete from the remote end as well */
sync_log_unmailbox(mailbox->name);
mboxevent_extract_mailbox(mboxevent, mailbox);
mboxevent_set_access(mboxevent, NULL, NULL, userid, mailbox->name, 1);
r = mailbox_delete(&mailbox);
/* abort event notification */
if (r && mboxevent)
mboxevent_free(&mboxevent);
}
done:
mailbox_close(&mailbox);
mboxlist_entry_free(&mbentry);
mbname_free(&mbname);
return r;
}
EXPORTED int mboxlist_deletemailboxlock(const char *name, int isadmin,
const char *userid,
const struct auth_state *auth_state,
struct mboxevent *mboxevent,
int flags)
{
struct mboxlock *namespacelock = mboxname_usernamespacelock(name);
int r = mboxlist_deletemailbox(name, isadmin, userid, auth_state, mboxevent, flags);
mboxname_release(&namespacelock);
return r;
}
static int _rename_check_specialuse(const char *oldname, const char *newname)
{
const char *protect = config_getstring(IMAPOPT_SPECIALUSE_PROTECT);
if (!protect) return 0;
mbname_t *old = mbname_from_intname(oldname);
mbname_t *new = mbname_from_intname(newname);
struct buf attrib = BUF_INITIALIZER;
int r = 0;
if (mbname_userid(old))
annotatemore_lookup(oldname, "/specialuse", mbname_userid(old), &attrib);
/* we have specialuse? */
if (attrib.len) {
strarray_t *check = strarray_split(protect, NULL, STRARRAY_TRIM);
strarray_t *uses = strarray_split(buf_cstring(&attrib), NULL, 0);
if (strarray_intersect_case(uses, check)) {
/* then target must be a single-depth mailbox too */
if (strarray_size(mbname_boxes(new)) != 1)
r = IMAP_MAILBOX_SPECIALUSE;
/* and have a userid as well */
if (!mbname_userid(new))
r = IMAP_MAILBOX_SPECIALUSE;
/* and not be deleted */
if (mbname_isdeleted(new))
r = IMAP_MAILBOX_SPECIALUSE;
}
strarray_free(uses);
strarray_free(check);
}
mbname_free(&new);
mbname_free(&old);
buf_free(&attrib);
return r;
}
struct renmboxdata {
size_t ol;
size_t nl;
char newname[MAX_MAILBOX_NAME+1];
const struct auth_state *authstate;
const char *partition;
const char *userid;
int local_only;
int ignorequota;
int found;
int keep_intermediaries;
int move_subscription;
};
static int renamecheck(const mbentry_t *mbentry, void *rock)
{
struct renmboxdata *text = (struct renmboxdata *)rock;
int r;
text->found++;
if((text->nl + strlen(mbentry->name + text->ol)) >= MAX_MAILBOX_NAME)
return IMAP_MAILBOX_BADNAME;
strcpy(text->newname + text->nl, mbentry->name + text->ol);
/* force create, but don't ignore policy. This is a filthy hack that
will go away when we refactor this code */
r = mboxlist_createmailboxcheck(text->newname, 0, text->partition, 1,
text->userid, text->authstate, NULL, NULL, 2);
return r;
}
static int dorename(const mbentry_t *mbentry, void *rock)
{
struct renmboxdata *text = (struct renmboxdata *)rock;
int r;
if((text->nl + strlen(mbentry->name + text->ol)) >= MAX_MAILBOX_NAME)
return IMAP_MAILBOX_BADNAME;
strcpy(text->newname + text->nl, mbentry->name + text->ol);
r = mboxlist_renamemailbox(mbentry, text->newname,
text->partition, /*uidvalidity*/0,
/*isadmin*/1, text->userid,
text->authstate,
/*mboxevent*/NULL,
text->local_only, /*forceuser*/1, text->ignorequota,
text->keep_intermediaries,
text->move_subscription, /*silent*/0);
return r;
}
EXPORTED int mboxlist_renametree(const char *oldname, const char *newname,
const char *partition, unsigned uidvalidity,
int isadmin, const char *userid,
const struct auth_state *auth_state,
struct mboxevent *mboxevent,
int local_only, int forceuser, int ignorequota,
int keep_intermediaries, int move_subscription)
{
struct renmboxdata rock;
memset(&rock, 0, sizeof(struct renmboxdata));
rock.ol = strlen(oldname);
rock.nl = strlen(newname);
memcpy(rock.newname, newname, rock.nl);
rock.partition = partition;
rock.authstate = auth_state;
rock.userid = userid;
rock.local_only = local_only;
rock.ignorequota = ignorequota;
rock.keep_intermediaries = keep_intermediaries;
rock.move_subscription = move_subscription;
mbentry_t *mbentry = NULL;
int r;
/* first check that we can rename safely */
r = mboxlist_mboxtree(oldname, renamecheck, &rock, 0);
if (r) return r;
r = mboxlist_lookup_allow_all(oldname, &mbentry, 0);
if (r) return r;
if (mbentry->mbtype & (MBTYPE_RESERVE | MBTYPE_DELETED)) {
mboxlist_entry_free(&mbentry);
return IMAP_MAILBOX_NONEXISTENT;
}
// rename the root mailbox
r = mboxlist_renamemailbox(mbentry, newname,
partition, uidvalidity,
isadmin, userid,
auth_state,
mboxevent,
local_only, forceuser, ignorequota,
keep_intermediaries, move_subscription, /*silent*/0);
mboxlist_entry_free(&mbentry);
// special-case only children exist
if (r == IMAP_MAILBOX_NONEXISTENT && rock.found) r = 0;
if (r) return r;
// now the children
r = mboxlist_mboxtree(oldname, dorename, &rock, MBOXTREE_SKIP_ROOT);
return r;
}
/*
* Rename/move a single mailbox (recursive renames are handled at a
* higher level). This only supports local mailboxes. Remote
* mailboxes are handled up in imapd.c
*/
EXPORTED int mboxlist_renamemailbox(const mbentry_t *mbentry,
const char *newname,
const char *partition, unsigned uidvalidity,
int isadmin, const char *userid,
const struct auth_state *auth_state,
struct mboxevent *mboxevent,
int local_only, int forceuser,
int ignorequota, int keep_intermediaries,
int move_subscription, int silent)
{
int r;
const char *oldname = mbentry->name;
int mupdatecommiterror = 0;
long myrights;
int isusermbox = 0; /* Are we renaming someone's inbox */
int partitionmove = 0;
struct mailbox *oldmailbox = NULL;
struct mailbox *newmailbox = NULL;
strarray_t inter = STRARRAY_INITIALIZER;
struct txn *tid = NULL;
const char *root = NULL;
char *newpartition = NULL;
mupdate_handle *mupdate_h = NULL;
mbentry_t *newmbentry = NULL;
int modseqflags = MBOXMODSEQ_ISFOLDER;
if (mboxname_isdeletedmailbox(newname, NULL))
modseqflags |= MBOXMODSEQ_ISDELETE;
init_internal();
assert_namespacelocked(mbentry->name);
assert_namespacelocked(newname);
/* special-case: intermediate mailbox */
if (mbentry->mbtype & MBTYPE_INTERMEDIATE) {
r = mboxlist_create_namecheck(newname, userid, auth_state,
isadmin, forceuser);
if (r) goto done;
newmbentry = mboxlist_entry_copy(mbentry);
free(newmbentry->name);
newmbentry->name = xstrdupnull(newname);
if (!silent) {
newmbentry->foldermodseq = mboxname_nextmodseq(newname, newmbentry->foldermodseq,
newmbentry->mbtype, modseqflags);
}
/* skip ahead to the database update */
goto dbupdate;
}
myrights = cyrus_acl_myrights(auth_state, mbentry->acl);
/* check the ACLs up-front */
if (!isadmin) {
if (!(myrights & ACL_DELETEMBOX)) {
r = (myrights & ACL_LOOKUP) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
return r;
}
}
/* 1. open mailbox */
r = mailbox_open_iwl(oldname, &oldmailbox);
if (r) return r;
oldmailbox->silentchanges = silent;
/* 2. verify valid move */
/* XXX - handle remote mailbox */
/* special case: same mailbox, must be a partition move */
if (!strcmp(oldname, newname)) {
const char *oldpath = mailbox_datapath(oldmailbox, 0);
/* Only admin can move mailboxes between partitions */
if (!isadmin) {
r = IMAP_PERMISSION_DENIED;
goto done;
}
/* No partition, we're definitely not moving anywhere */
if (!partition) {
r = IMAP_MAILBOX_EXISTS;
goto done;
}
/* let mupdate code below know it was a partition move */
partitionmove = 1;
/* this is OK because it uses a different static buffer */
root = config_partitiondir(partition);
if (!root) {
r = IMAP_PARTITION_UNKNOWN;
goto done;
}
if (!strncmp(root, oldpath, strlen(root)) &&
oldpath[strlen(root)] == '/') {
/* partitions are the same or share common prefix */
r = IMAP_MAILBOX_EXISTS;
goto done;
}
/* NOTE: this is a rename to the same mailbox name on a
* different partition. This is a pretty filthy hack,
* which should be handled by having four totally different
* codepaths: INBOX -> INBOX.foo, user rename, regular rename
* and of course this one, partition move */
newpartition = xstrdup(partition);
r = mailbox_copy_files(oldmailbox, newpartition, newname, oldmailbox->uniqueid);
if (r) goto done;
newmbentry = mboxlist_entry_create();
newmbentry->mbtype = oldmailbox->mbtype;
newmbentry->partition = xstrdupnull(newpartition);
newmbentry->acl = xstrdupnull(oldmailbox->acl);
newmbentry->uidvalidity = oldmailbox->i.uidvalidity;
newmbentry->uniqueid = xstrdupnull(oldmailbox->uniqueid);
newmbentry->createdmodseq = oldmailbox->i.createdmodseq;
newmbentry->foldermodseq = silent ? oldmailbox->foldermodseq
: mboxname_nextmodseq(newname, oldmailbox->foldermodseq,
oldmailbox->mbtype, modseqflags);
r = mboxlist_update_entry(newname, newmbentry, &tid);
if (r) goto done;
/* skip ahead to the commit */
goto dbdone;
}
if (!isadmin) {
r = _rename_check_specialuse(oldname, newname);
if (r) goto done;
}
/* RENAME of some user's INBOX */
if (mboxname_isusermailbox(oldname, 1)) {
if (mboxname_isdeletedmailbox(newname, NULL)) {
/* delete user is OK */
}
else if (mboxname_isusermailbox(newname, 1)) {
/* user rename is depends on config */
if (!config_getswitch(IMAPOPT_ALLOWUSERMOVES)) {
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
}
else if (mboxname_userownsmailbox(userid, oldname) &&
mboxname_userownsmailbox(userid, newname)) {
/* Special case of renaming inbox */
isusermbox = 1;
}
else {
/* Everything else is bogus */
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
}
r = mboxlist_create_namecheck(newname, userid, auth_state,
isadmin, forceuser);
if (r) goto done;
r = mboxlist_create_partition(newname, partition, &newpartition);
if (r) goto done;
if (!newpartition) newpartition = xstrdup(config_defpartition);
/* keep uidvalidity on rename unless specified */
if (!uidvalidity)
uidvalidity = oldmailbox->i.uidvalidity;
/* Rename the actual mailbox */
r = mailbox_rename_copy(oldmailbox, newname, newpartition, uidvalidity,
isusermbox ? userid : NULL, ignorequota,
silent, &newmailbox);
if (r) goto done;
syslog(LOG_INFO, "Rename: %s -> %s", oldname, newname);
/* create new entry */
newmbentry = mboxlist_entry_create();
newmbentry->name = xstrdupnull(newmailbox->name);
newmbentry->mbtype = newmailbox->mbtype;
newmbentry->partition = xstrdupnull(newmailbox->part);
newmbentry->acl = xstrdupnull(newmailbox->acl);
newmbentry->uidvalidity = newmailbox->i.uidvalidity;
newmbentry->uniqueid = xstrdupnull(newmailbox->uniqueid);
newmbentry->createdmodseq = newmailbox->i.createdmodseq;
newmbentry->foldermodseq = newmailbox->i.highestmodseq;
dbupdate:
do {
r = 0;
/* delete the old entry */
if (!isusermbox) {
/* store a DELETED marker */
mbentry_t *oldmbentry = mboxlist_entry_create();
oldmbentry->name = xstrdupnull(mbentry->name);
oldmbentry->mbtype = MBTYPE_DELETED;
oldmbentry->uidvalidity = mbentry->uidvalidity;
oldmbentry->uniqueid = xstrdupnull(mbentry->uniqueid);
oldmbentry->createdmodseq = mbentry->createdmodseq;
oldmbentry->foldermodseq = newmbentry->foldermodseq;
r = mboxlist_update_entry(oldname, oldmbentry, &tid);
mboxlist_entry_free(&oldmbentry);
}
/* create a new entry */
if (!r) {
r = mboxlist_update_entry(newname, newmbentry, &tid);
}
switch (r) {
case 0: /* success */
break;
case CYRUSDB_AGAIN:
tid = NULL;
break;
default:
xsyslog(LOG_ERR, "DBERROR: rename failed on store",
"oldname=<%s> newname=<%s> error=<%s>",
oldname, newname, cyrusdb_strerror(r));
r = IMAP_IOERROR;
goto done;
break;
}
} while (r == CYRUSDB_AGAIN);
dbdone:
/* 3. Commit transaction */
r = cyrusdb_commit(mbdb, tid);
tid = NULL;
if (r) {
xsyslog(LOG_ERR, "DBERROR: rename failed on commit",
"oldname=<%s> newname=<%s> error=<%s>",
oldname, newname, cyrusdb_strerror(r));
r = IMAP_IOERROR;
goto done;
}
/* Move subscription */
if (move_subscription) {
int is_subscribed = mboxlist_checksub(oldname, userid) == 0;
int r2 = mboxlist_changesub(oldname, userid, auth_state, 0, 0, 0);
if (r2) {
syslog(LOG_ERR, "CHANGESUB: can't unsubscribe %s: %s",
oldname, error_message(r2));
}
if (is_subscribed) {
r2 = mboxlist_changesub(newname, userid, auth_state, 1, 0, 0);
if (r2) {
syslog(LOG_ERR, "CHANGESUB: can't subscribe %s: %s",
newname, error_message(r2));
}
}
}
if (!local_only && config_mupdate_server) {
/* commit the mailbox in MUPDATE */
char *loc = strconcat(config_servername, "!", newpartition, (char *)NULL);
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if (!partitionmove) {
if (!r && !isusermbox)
r = mupdate_delete(mupdate_h, oldname);
if (!r) r = mupdate_reserve(mupdate_h, newname, loc);
}
if (!r) r = mupdate_activate(mupdate_h, newname, loc, newmbentry->acl);
if (r) {
syslog(LOG_ERR,
"MUPDATE: can't commit mailbox entry for '%s'",
newname);
mupdatecommiterror = r;
}
if (mupdate_h) mupdate_disconnect(&mupdate_h);
free(loc);
}
done: /* Commit or cleanup */
if (!r && newmailbox)
r = mailbox_commit(newmailbox);
if (!keep_intermediaries) {
if (!r) r = mboxlist_update_intermediaries(oldname, newmbentry->mbtype, newmbentry->foldermodseq);
if (!r) r = mboxlist_update_intermediaries(newname, newmbentry->mbtype, newmbentry->foldermodseq);
}
if (r) {
/* rollback DB changes if it was an mupdate failure */
if (mupdatecommiterror) {
r = 0;
/* delete the new entry */
if (!isusermbox)
r = mboxlist_update_entry(newname, NULL, &tid);
/* recreate an old entry */
if (!r)
r = mboxlist_update_entry(oldname, newmbentry, &tid);
/* Commit transaction */
if (!r)
r = cyrusdb_commit(mbdb, tid);
tid = NULL;
if (r) {
/* XXX HOWTO repair this mess! */
xsyslog(LOG_ERR, "DBERROR: failed DB rollback on mailboxrename",
"oldname=<%s> newname=<%s> error=<%s>",
oldname, newname, cyrusdb_strerror(r));
xsyslog(LOG_ERR, "DBERROR: mailboxdb on mupdate and backend"
" ARE NOT CONSISTENT",
"mupdate_entry=<%s> backend_entry=<%s>",
oldname, newname);
r = IMAP_IOERROR;
} else {
r = mupdatecommiterror;
}
}
if (newmailbox) mailbox_delete(&newmailbox);
if (partitionmove && newpartition)
mailbox_delete_cleanup(NULL, newpartition, newname, oldmailbox->uniqueid);
mailbox_close(&oldmailbox);
} else {
/* log the rename before we close either mailbox, so that
* we never nuke the mailbox from the replica before realising
* that it has been renamed. This can be moved later again when
* we sync mailboxes by uniqueid rather than name... */
sync_log_rename(oldname, newname);
if (newmailbox) {
/* prepare the event notification */
if (mboxevent) {
/* case of delayed delete */
if (mboxevent->type == EVENT_MAILBOX_DELETE)
mboxevent_extract_mailbox(mboxevent, oldmailbox);
else {
mboxevent_extract_mailbox(mboxevent, newmailbox);
mboxevent_extract_old_mailbox(mboxevent, oldmailbox);
}
mboxevent_set_access(mboxevent, NULL, NULL, userid, newmailbox->name, 1);
}
mailbox_rename_cleanup(&oldmailbox, isusermbox);
#ifdef WITH_DAV
mailbox_add_dav(newmailbox);
#endif
mailbox_close(&newmailbox);
/* and log an append so that squatter indexes it */
sync_log_append(newname);
}
else if (partitionmove) {
char *oldpartition = xstrdup(oldmailbox->part);
char *olduniqueid = xstrdup(oldmailbox->uniqueid);
if (config_auditlog)
syslog(LOG_NOTICE, "auditlog: partitionmove sessionid=<%s> "
"mailbox=<%s> uniqueid=<%s> oldpart=<%s> newpart=<%s>",
session_id(),
oldmailbox->name, oldmailbox->uniqueid,
oldpartition, partition);
/* this will sync-log the name anyway */
mailbox_close(&oldmailbox);
mailbox_delete_cleanup(NULL, oldpartition, oldname, olduniqueid);
free(olduniqueid);
free(oldpartition);
}
else if (mbentry->mbtype & MBTYPE_INTERMEDIATE) {
/* no event notification */
if (mboxevent) mboxevent->type = EVENT_CANCELLED;
}
else
abort(); /* impossible, in theory */
}
/* free memory */
strarray_fini(&inter);
free(newpartition);
mboxlist_entry_free(&newmbentry);
return r;
}
/*
* Check if the admin rights are present in the 'rights'
*/
static int mboxlist_have_admin_rights(const char *rights) {
int access, have_admin_access;
cyrus_acl_strtomask(rights, &access);
have_admin_access = access & ACL_ADMIN;
return have_admin_access;
}
/*
* Change the ACL for mailbox 'name' so that 'identifier' has the
* rights enumerated in the string 'rights'. If 'rights' is the null
* pointer, removes the ACL entry for 'identifier'. 'isadmin' is
* nonzero if user is a mailbox admin. 'userid' is the user's login id.
*
* 1. Start transaction
* 2. Check rights
* 3. Set db entry
* 4. Change backup copy (cyrus.header)
* 5. Commit transaction
* 6. Change mupdate entry
*
*/
EXPORTED int mboxlist_setacl(const struct namespace *namespace __attribute__((unused)),
const char *name,
const char *identifier, const char *rights,
int isadmin, const char *userid,
const struct auth_state *auth_state)
{
mbentry_t *mbentry = NULL;
int r;
int myrights;
int mode = ACL_MODE_SET;
int isusermbox = 0;
int isidentifiermbox = 0;
int anyoneuseracl = 1;
int ensure_owner_rights = 0;
int mask;
const char *mailbox_owner = NULL;
struct mailbox *mailbox = NULL;
char *newacl = NULL;
struct txn *tid = NULL;
init_internal();
/* round trip identifier to potentially strip domain */
mbname_t *mbname = mbname_from_userid(identifier);
/* XXX - enforce cross domain restrictions */
identifier = mbname_userid(mbname);
/* checks if the mailbox belongs to the user who is trying to change the
access rights */
if (mboxname_userownsmailbox(userid, name))
isusermbox = 1;
anyoneuseracl = config_getswitch(IMAPOPT_ANYONEUSERACL);
/* checks if the identifier is the mailbox owner */
if (mboxname_userownsmailbox(identifier, name))
isidentifiermbox = 1;
/* who is the mailbox owner? */
if (isusermbox) {
mailbox_owner = userid;
}
else if (isidentifiermbox) {
mailbox_owner = identifier;
}
/* ensure the access rights if the folder owner is the current user or
the identifier */
ensure_owner_rights = isusermbox || isidentifiermbox;
/* 1. Start Transaction */
/* lookup the mailbox to make sure it exists and get its acl */
do {
r = mboxlist_mylookup(name, &mbentry, &tid, 1);
} while(r == IMAP_AGAIN);
/* Can't do this to an in-transit or reserved mailbox */
if (!r && mbentry->mbtype & (MBTYPE_MOVING | MBTYPE_RESERVE | MBTYPE_DELETED)) {
r = IMAP_MAILBOX_NOTSUPPORTED;
}
/* if it is not a remote mailbox, we need to unlock the mailbox list,
* lock the mailbox, and re-lock the mailboxes list */
/* we must do this to obey our locking rules */
if (!r && !(mbentry->mbtype & MBTYPE_REMOTE)) {
cyrusdb_abort(mbdb, tid);
tid = NULL;
mboxlist_entry_free(&mbentry);
/* open & lock mailbox header */
r = mailbox_open_iwl(name, &mailbox);
if (!r) {
do {
/* lookup the mailbox to make sure it exists and get its acl */
r = mboxlist_mylookup(name, &mbentry, &tid, 1);
} while (r == IMAP_AGAIN);
}
if(r) goto done;
}
/* 2. Check Rights */
if (!r && !isadmin) {
myrights = cyrus_acl_myrights(auth_state, mbentry->acl);
if (!(myrights & ACL_ADMIN)) {
r = (myrights & ACL_LOOKUP) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
goto done;
}
}
/* 2.1 Only admin user can set 'anyone' rights if config says so */
if (!r && !isadmin && !anyoneuseracl && !strncmp(identifier, "anyone", 6)) {
r = IMAP_PERMISSION_DENIED;
goto done;
}
/* 3. Set DB Entry */
if(!r) {
/* Make change to ACL */
newacl = xstrdup(mbentry->acl);
if (rights && *rights) {
/* rights are present and non-empty */
mode = ACL_MODE_SET;
if (*rights == '+') {
rights++;
mode = ACL_MODE_ADD;
}
else if (*rights == '-') {
rights++;
mode = ACL_MODE_REMOVE;
}
/* do not allow non-admin user to remove the admin rights from mailbox owner */
if (!isadmin && isidentifiermbox && mode != ACL_MODE_ADD) {
int has_admin_rights = mboxlist_have_admin_rights(rights);
if ((has_admin_rights && mode == ACL_MODE_REMOVE) ||
(!has_admin_rights && mode != ACL_MODE_REMOVE)) {
syslog(LOG_ERR, "Denied removal of admin rights on "
"folder \"%s\" (owner: %s) by user \"%s\"", name,
mailbox_owner, userid);
r = IMAP_PERMISSION_DENIED;
goto done;
}
}
r = cyrus_acl_strtomask(rights, &mask);
if (!r && cyrus_acl_set(&newacl, identifier, mode, mask,
ensure_owner_rights ? mboxlist_ensureOwnerRights : 0,
(void *)mailbox_owner)) {
r = IMAP_INVALID_IDENTIFIER;
}
} else {
/* do not allow to remove the admin rights from mailbox owner */
if (!isadmin && isidentifiermbox) {
syslog(LOG_ERR, "Denied removal of admin rights on "
"folder \"%s\" (owner: %s) by user \"%s\"", name,
mailbox_owner, userid);
r = IMAP_PERMISSION_DENIED;
goto done;
}
if (cyrus_acl_remove(&newacl, identifier,
ensure_owner_rights ? mboxlist_ensureOwnerRights : 0,
(void *)mailbox_owner)) {
r = IMAP_INVALID_IDENTIFIER;
}
}
}
if (!r) {
/* ok, change the database */
free(mbentry->acl);
mbentry->acl = xstrdupnull(newacl);
mbentry->foldermodseq = mailbox_modseq_dirty(mailbox);
r = mboxlist_update_entry(name, mbentry, &tid);
if (r) {
xsyslog(LOG_ERR, "DBERROR: error updating acl",
"mailbox=<%s> error=<%s>",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
/* send a AclChange event notification */
struct mboxevent *mboxevent = mboxevent_new(EVENT_ACL_CHANGE);
mboxevent_extract_mailbox(mboxevent, mailbox);
mboxevent_set_acl(mboxevent, identifier, rights);
mboxevent_set_access(mboxevent, NULL, NULL, userid, mailbox->name, 0);
mboxevent_notify(&mboxevent);
mboxevent_free(&mboxevent);
}
/* 4. Change backup copy (cyrus.header) */
/* we already have it locked from above */
if (!r && !(mbentry->mbtype & MBTYPE_REMOTE)) {
mailbox_set_acl(mailbox, newacl);
/* want to commit immediately to ensure ordering */
r = mailbox_commit(mailbox);
}
/* 5. Commit transaction */
if (!r) {
if((r = cyrusdb_commit(mbdb, tid)) != 0) {
xsyslog(LOG_ERR, "DBERROR: failed on commit",
"error=<%s>",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
tid = NULL;
}
/* 6. Change mupdate entry */
if (!r && config_mupdate_server) {
mupdate_handle *mupdate_h = NULL;
/* commit the update to MUPDATE */
char buf[MAX_PARTITION_LEN + HOSTNAME_SIZE + 2];
snprintf(buf, sizeof(buf), "%s!%s", config_servername, mbentry->partition);
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if(r) {
syslog(LOG_ERR,
"cannot connect to mupdate server for setacl 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) {
/* if we are mid-transaction, abort it! */
int r2 = cyrusdb_abort(mbdb, tid);
if (r2) {
syslog(LOG_ERR,
"DBERROR: error aborting txn in mboxlist_setacl: %s",
cyrusdb_strerror(r2));
}
}
mailbox_close(&mailbox);
free(newacl);
mboxlist_entry_free(&mbentry);
mbname_free(&mbname);
return r;
}
/* change the ACL for mailbox 'name' when we have nothing but the name and the new value */
EXPORTED int mboxlist_updateacl_raw(const char *name, const char *newacl)
{
struct mailbox *mailbox = NULL;
int r = mailbox_open_iwl(name, &mailbox);
if (!r) r = mboxlist_sync_setacls(name, newacl, mailbox_modseq_dirty(mailbox));
if (!r) r = mailbox_set_acl(mailbox, newacl);
if (!r) r = mailbox_commit(mailbox);
mailbox_close(&mailbox);
return r;
}
/*
* Change the ACL for mailbox 'name'. We already have it locked
* and have written the backup copy to the header, so there's
* nothing left but to write the mailboxes.db.
*
* 1. Start transaction
* 2. Set db entry
* 3. Commit transaction
* 4. Change mupdate entry
*
*/
EXPORTED int
mboxlist_sync_setacls(const char *name, const char *newacl, modseq_t foldermodseq)
{
mbentry_t *mbentry = NULL;
int r;
struct txn *tid = NULL;
init_internal();
/* 1. Start Transaction */
/* lookup the mailbox to make sure it exists and get its acl */
do {
r = mboxlist_mylookup(name, &mbentry, &tid, 1);
} while(r == IMAP_AGAIN);
if (r) goto done;
// nothing to change, great
if (!strcmpsafe(mbentry->acl, newacl) && mbentry->foldermodseq >= foldermodseq)
goto done;
/* Can't do this to an in-transit or reserved mailbox */
if (mbentry->mbtype & (MBTYPE_MOVING | MBTYPE_RESERVE | MBTYPE_DELETED)) {
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
/* 2. Set DB Entry */
free(mbentry->acl);
mbentry->acl = xstrdupnull(newacl);
if (mbentry->foldermodseq < foldermodseq)
mbentry->foldermodseq = foldermodseq;
r = mboxlist_update_entry(name, mbentry, &tid);
if (r) {
xsyslog(LOG_ERR, "DBERROR: error updating acl",
"mailbox=<%s> error=<%s>",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
goto done;
}
/* 3. Commit transaction */
r = cyrusdb_commit(mbdb, tid);
if (r) {
xsyslog(LOG_ERR, "DBERROR: failed on commit",
"mailbox=<%s> error=<%s>",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
goto done;
}
tid = NULL;
/* 4. Change mupdate entry */
if (config_mupdate_server) {
mupdate_handle *mupdate_h = NULL;
/* commit the update to MUPDATE */
char buf[MAX_PARTITION_LEN + HOSTNAME_SIZE + 2];
sprintf(buf, "%s!%s", config_servername, mbentry->partition);
r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
if (r) {
syslog(LOG_ERR,
"cannot connect to mupdate server for syncacl 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 (tid) {
/* if we are mid-transaction, abort it! */
int r2 = cyrusdb_abort(mbdb, tid);
if (r2) {
syslog(LOG_ERR,
"DBERROR: error aborting txn in sync_setacls %s: %s",
name, cyrusdb_strerror(r2));
}
}
mboxlist_entry_free(&mbentry);
return r;
}
struct find_rock {
ptrarray_t globs;
struct namespace *namespace;
const char *userid;
const char *domain;
int mb_category;
int checkmboxlist;
int issubs;
int singlepercent;
struct db *db;
int isadmin;
const struct auth_state *auth_state;
mbname_t *mbname;
mbentry_t *mbentry;
int matchlen;
findall_p *p;
findall_cb *cb;
void *procrock;
};
/* return non-zero if we like this one */
static int find_p(void *rockp,
const char *key, size_t keylen,
const char *data, size_t datalen)
{
struct find_rock *rock = (struct find_rock *) rockp;
char intname[MAX_MAILBOX_PATH+1];
int i;
/* skip any $RACL or future $ space keys */
if (key[0] == '$') return 0;
memcpy(intname, key, keylen);
intname[keylen] = 0;
assert(!rock->mbname);
rock->mbname = mbname_from_intname(intname);
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;
}
if (rock->mb_category && mbname_category(rock->mbname, rock->namespace, rock->userid) != rock->mb_category)
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 */
const char *extname = mbname_extname(rock->mbname, rock->namespace, rock->userid);
if (!extname) goto nomatch;
int matchlen = 0;
for (i = 0; i < rock->globs.count; i++) {
glob *g = ptrarray_nth(&rock->globs, i);
int thismatch = glob_test(g, extname);
if (thismatch > matchlen) matchlen = thismatch;
}
/* If its not a match, skip it -- partial matches are ok. */
if (!matchlen) goto nomatch;
rock->matchlen = matchlen;
/* subs DB has empty keys */
if (rock->issubs)
goto good;
/* ignore entirely deleted records */
if (mboxlist_parse_entry(&rock->mbentry, key, keylen, data, datalen))
goto nomatch;
/* nobody sees tombstones */
if (rock->mbentry->mbtype & MBTYPE_DELETED)
goto nomatch;
/* only admins and mailbox owners see intermediates */
if (rock->mbentry->mbtype & MBTYPE_INTERMEDIATE) {
if (rock->isadmin ||
mboxname_userownsmailbox(rock->userid, intname)) goto good;
else goto nomatch;
}
/* check acl */
if (!rock->isadmin) {
if (!(cyrus_acl_myrights(rock->auth_state, rock->mbentry->acl) & ACL_LOOKUP)) goto nomatch;
}
good:
if (rock->p) {
struct findall_data fdata = { extname, 0, rock->mbentry, rock->mbname, 0 };
/* mbname confirms that it's an exact match */
if (rock->matchlen == (int) strlen(extname))
fdata.is_exactmatch = 1;
if (!rock->p(&fdata, rock->procrock)) goto nomatch;
return 1;
}
else {
return 1;
}
nomatch:
mboxlist_entry_free(&rock->mbentry);
mbname_free(&rock->mbname);
return 0;
}
static int find_cb(void *rockp,
/* 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 *testname = NULL;
int r = 0;
int i;
if (rock->checkmboxlist && !rock->mbentry) {
r = mboxlist_lookup(mbname_intname(rock->mbname), &rock->mbentry, NULL);
if (r) {
if (r == IMAP_MAILBOX_NONEXISTENT) r = 0;
goto done;
}
}
const char *extname = mbname_extname(rock->mbname, rock->namespace, rock->userid);
testname = xstrndup(extname, rock->matchlen);
struct findall_data fdata = { testname, rock->mb_category, rock->mbentry, rock->mbname, 0 };
if (rock->singlepercent) {
char sep = rock->namespace->hier_sep;
char *p = testname;
/* we need to try all the previous names in order */
while ((p = strchr(p, sep)) != NULL) {
*p = '\0';
/* only if this expression could fully match */
int matchlen = 0;
for (i = 0; i < rock->globs.count; i++) {
glob *g = ptrarray_nth(&rock->globs, i);
int thismatch = glob_test(g, testname);
if (thismatch > matchlen) matchlen = thismatch;
}
if (matchlen == (int)strlen(testname)) {
r = (*rock->cb)(&fdata, rock->procrock);
if (r) goto done;
}
/* replace the separator for the next longest name */
*p++ = sep;
}
}
/* mbname confirms that it's an exact match */
if (rock->matchlen == (int)strlen(extname))
fdata.is_exactmatch = 1;
r = (*rock->cb)(&fdata, rock->procrock);
done:
free(testname);
mboxlist_entry_free(&rock->mbentry);
mbname_free(&rock->mbname);
return r;
}
struct allmb_rock {
struct mboxlist_entry *mbentry;
mboxlist_cb *proc;
void *rock;
int flags;
};
static int allmbox_cb(void *rock,
const char *key,
size_t keylen,
const char *data,
size_t datalen)
{
struct allmb_rock *mbrock = (struct allmb_rock *)rock;
if (!mbrock->mbentry) {
int r = mboxlist_parse_entry(&mbrock->mbentry, key, keylen, data, datalen);
if (r) return r;
}
return mbrock->proc(mbrock->mbentry, mbrock->rock);
}
static int allmbox_p(void *rock,
const char *key,
size_t keylen,
const char *data,
size_t datalen)
{
struct allmb_rock *mbrock = (struct allmb_rock *)rock;
int r;
/* skip any dollar keys */
if (keylen && key[0] == '$') return 0;
/* free previous record */
mboxlist_entry_free(&mbrock->mbentry);
r = mboxlist_parse_entry(&mbrock->mbentry, key, keylen, data, datalen);
if (r) return 0;
if (!(mbrock->flags & MBOXTREE_TOMBSTONES) && (mbrock->mbentry->mbtype & MBTYPE_DELETED))
return 0;
if (!(mbrock->flags & MBOXTREE_INTERMEDIATES) && (mbrock->mbentry->mbtype & MBTYPE_INTERMEDIATE))
return 0;
return 1; /* process this record */
}
EXPORTED int mboxlist_allmbox(const char *prefix, mboxlist_cb *proc, void *rock, int flags)
{
struct allmb_rock mbrock = { NULL, proc, rock, flags };
int r = 0;
init_internal();
if (!prefix) prefix = "";
r = cyrusdb_foreach(mbdb, prefix, strlen(prefix),
allmbox_p, allmbox_cb, &mbrock, 0);
mboxlist_entry_free(&mbrock.mbentry);
return r;
}
EXPORTED int mboxlist_mboxtree(const char *mboxname, mboxlist_cb *proc, void *rock, int flags)
{
struct allmb_rock mbrock = { NULL, proc, rock, flags };
int r = 0;
init_internal();
if (!(flags & MBOXTREE_SKIP_ROOT)) {
r = cyrusdb_forone(mbdb, mboxname, strlen(mboxname), allmbox_p, allmbox_cb, &mbrock, 0);
if (r) goto done;
}
if (!(flags & MBOXTREE_SKIP_CHILDREN)) {
char *prefix = strconcat(mboxname, ".", (char *)NULL);
r = cyrusdb_foreach(mbdb, prefix, strlen(prefix), allmbox_p, allmbox_cb, &mbrock, 0);
free(prefix);
if (r) goto done;
}
if ((flags & MBOXTREE_DELETED)) {
struct buf buf = BUF_INITIALIZER;
const char *p = strchr(mboxname, '!');
const char *dp = config_getstring(IMAPOPT_DELETEDPREFIX);
if (p) {
buf_printf(&buf, "%.*s!%s.%s.", (int)(p-mboxname), mboxname, dp, p+1);
}
else {
buf_printf(&buf, "%s.%s.", dp, mboxname);
}
const char *prefix = buf_cstring(&buf);
r = cyrusdb_foreach(mbdb, prefix, strlen(prefix), allmbox_p, allmbox_cb, &mbrock, 0);
buf_free(&buf);
if (r) goto done;
}
done:
mboxlist_entry_free(&mbrock.mbentry);
return r;
}
-static int del_cb(void *rock,
+static int racls_del_cb(void *rock,
const char *key, size_t keylen,
const char *data __attribute__((unused)),
size_t datalen __attribute__((unused)))
{
struct txn **txn = (struct txn **)rock;
return cyrusdb_delete(mbdb, key, keylen, txn, /*force*/0);
}
static int racls_add_cb(const mbentry_t *mbentry, void *rock)
{
struct txn **txn = (struct txn **)rock;
return mboxlist_update_racl(mbentry->name, NULL, mbentry, txn);
}
EXPORTED int mboxlist_set_racls(int enabled)
{
struct txn *tid = NULL;
int r = 0;
int modified_mbdb = 0;
init_internal();
if (have_racl && !enabled) {
syslog(LOG_NOTICE, "removing reverse acl support");
/* remove */
- r = cyrusdb_foreach(mbdb, "$RACL", 5, NULL, del_cb, &tid, &tid);
+ r = cyrusdb_foreach(mbdb, "$RACL", 5, NULL, racls_del_cb, &tid, &tid);
if (!r) have_racl = 0;
modified_mbdb = 1;
}
if (enabled && !have_racl) {
/* add */
struct allmb_rock mbrock = { NULL, racls_add_cb, &tid, 0 };
/* we can't use mboxlist_allmbox because it doesn't do transactions */
syslog(LOG_NOTICE, "adding reverse acl support");
r = cyrusdb_foreach(mbdb, "", 0, allmbox_p, allmbox_cb, &mbrock, &tid);
if (r) {
syslog(LOG_ERR, "ERROR: failed to add reverse acl support %s", error_message(r));
}
modified_mbdb = 1;
mboxlist_entry_free(&mbrock.mbentry);
if (!r) r = cyrusdb_store(mbdb, "$RACL", 5, "", 0, &tid);
if (!r) have_racl = 1;
}
if (!modified_mbdb || !tid) return r;
if (r)
cyrusdb_abort(mbdb, tid);
else
cyrusdb_commit(mbdb, tid);
return r;
}
-static int runiqueid_add_cb(const mbentry_t *mbentry, void *rock)
-{
- struct txn **txn = (struct txn **)rock;
- return mboxlist_update_runiqueid(mbentry->name, NULL, mbentry, txn);
-}
-
-EXPORTED int mboxlist_set_runiqueid(int enabled)
-{
- struct txn *tid = NULL;
- int r = 0;
- int modified_mbdb = 0;
-
- init_internal();
-
- if (have_runq && !enabled) {
- syslog(LOG_NOTICE, "removing reverse unique id support");
- /* remove */
- r = cyrusdb_foreach(mbdb, "$RUNQ", 5, NULL, del_cb, &tid, &tid);
- if (!r) have_runq = 0;
- modified_mbdb = 1;
- }
- if (enabled && !have_runq) {
- /* add */
- struct allmb_rock mbrock = { NULL, runiqueid_add_cb, &tid, 0 };
- /* we can't use mboxlist_allmbox because it doesn't do transactions */
- syslog(LOG_NOTICE, "adding reverse uniqueid support");
- r = cyrusdb_foreach(mbdb, "", 0, allmbox_p, allmbox_cb, &mbrock, &tid);
- if (r) {
- syslog(LOG_ERR, "ERROR: failed to add reverse uniqueid support %s", error_message(r));
- }
- modified_mbdb = 1;
- mboxlist_entry_free(&mbrock.mbentry);
- if (!r) r = cyrusdb_store(mbdb, "$RUNQ", 5, "", 0, &tid);
- if (!r) have_runq = 1;
- }
-
- if (!modified_mbdb || !tid) return r;
-
- if (r)
- cyrusdb_abort(mbdb, tid);
- else
- cyrusdb_commit(mbdb, tid);
-
- return r;
-}
-
struct alluser_rock {
char *prev;
user_cb *proc;
void *rock;
};
static int alluser_cb(const mbentry_t *mbentry, void *rock)
{
struct alluser_rock *urock = (struct alluser_rock *)rock;
char *userid = mboxname_to_userid(mbentry->name);
int r = 0;
if (userid) {
if (strcmpsafe(urock->prev, userid)) {
r = urock->proc(userid, urock->rock);
free(urock->prev);
urock->prev = userid;
} else
free(userid);
}
return r;
}
EXPORTED int mboxlist_alluser(user_cb *proc, void *rock)
{
struct alluser_rock urock;
int r = 0;
init_internal();
urock.prev = NULL;
urock.proc = proc;
urock.rock = rock;
r = mboxlist_allmbox(NULL, alluser_cb, &urock, /*flags*/0);
free(urock.prev);
return r;
}
struct raclrock {
int prefixlen;
strarray_t *list;
};
static int racl_cb(void *rock,
const char *key, size_t keylen,
const char *data __attribute__((unused)),
size_t datalen __attribute__((unused)))
{
struct raclrock *raclrock = (struct raclrock *)rock;
strarray_appendm(raclrock->list, xstrndup(key + raclrock->prefixlen, keylen - raclrock->prefixlen));
return 0;
}
static int mboxlist_racl_matches(struct db *db,
int isuser, const char *userid,
const struct auth_state *auth_state,
const char *mboxprefix, size_t len,
strarray_t *matches)
{
struct buf raclprefix = BUF_INITIALIZER;
strarray_t *groups = NULL;
struct raclrock raclrock = { 0, matches };
int i;
/* direct access by userid */
mboxlist_racl_key(isuser, userid, NULL, &raclprefix);
/* this is the prefix */
raclrock.prefixlen = buf_len(&raclprefix);
/* we only need to look inside the prefix still, but we keep the length
* in raclrock pointing to the start of the mboxname part of the key so
* we get correct names in matches */
if (len) buf_appendmap(&raclprefix, mboxprefix, len);
cyrusdb_foreach(db,
buf_cstring(&raclprefix),
buf_len(&raclprefix),
NULL, racl_cb, &raclrock, NULL);
/* indirect access via group membership: same logic as userid, but per group */
if (auth_state)
groups = auth_groups(auth_state);
if (groups) {
for (i = 0; i < strarray_size(groups); i++) {
mboxlist_racl_key(isuser, strarray_nth(groups, i), NULL, &raclprefix);
raclrock.prefixlen = buf_len(&raclprefix);
if (len) buf_appendmap(&raclprefix, mboxprefix, len);
cyrusdb_foreach(db,
buf_cstring(&raclprefix),
buf_len(&raclprefix),
NULL, racl_cb, &raclrock, NULL);
}
strarray_free(groups);
}
// can "anyone" access this?
mboxlist_racl_key(isuser, "anyone", NULL, &raclprefix);
raclrock.prefixlen = buf_len(&raclprefix);
if (len) buf_appendmap(&raclprefix, mboxprefix, len);
cyrusdb_foreach(db,
buf_cstring(&raclprefix),
buf_len(&raclprefix),
NULL, racl_cb, &raclrock, NULL);
strarray_sort(matches, cmpstringp_mbox);
strarray_uniq(matches);
buf_free(&raclprefix);
return 0;
}
/* auth_state parameter is optional, but is needed for proper expansion
* of group RACLs if flags contains MBOXTREE_PLUS_RACL */
EXPORTED int mboxlist_usermboxtree(const char *userid,
const struct auth_state *auth_state,
mboxlist_cb *proc, void *rock, int flags)
{
char *inbox = mboxname_user_mbox(userid, 0);
int r = mboxlist_mboxtree(inbox, proc, rock, flags);
if (flags & MBOXTREE_PLUS_RACL) {
/* we're using reverse ACLs */
struct allmb_rock mbrock = { NULL, proc, rock, flags };
int i;
strarray_t matches = STRARRAY_INITIALIZER;
/* user items */
mboxlist_racl_matches(mbdb, 1, userid, auth_state, NULL, 0, &matches);
for (i = 0; !r && i < strarray_size(&matches); i++) {
const char *mboxname = strarray_nth(&matches, i);
r = cyrusdb_forone(mbdb, mboxname, strlen(mboxname),
allmbox_p, allmbox_cb, &mbrock, 0);
}
/* shared items */
strarray_fini(&matches);
strarray_init(&matches);
mboxlist_racl_matches(mbdb, 0, userid, auth_state, NULL, 0, &matches);
for (i = 0; !r && i < strarray_size(&matches); i++) {
const char *mboxname = strarray_nth(&matches, i);
r = cyrusdb_forone(mbdb, mboxname, strlen(mboxname),
allmbox_p, allmbox_cb, &mbrock, 0);
}
strarray_fini(&matches);
mboxlist_entry_free(&mbrock.mbentry);
}
free(inbox);
return r;
}
static int mboxlist_find_category(struct find_rock *rock, const char *prefix, size_t len)
{
int r = 0;
init_internal();
if (!rock->issubs && !rock->isadmin && have_racl) {
/* we're using reverse ACLs */
strarray_t matches = STRARRAY_INITIALIZER;
int i;
mboxlist_racl_matches(rock->db,
(rock->mb_category == MBNAME_OTHERUSER),
rock->userid,
rock->auth_state,
prefix, len,
&matches);
/* now call the callbacks */
for (i = 0; !r && i < strarray_size(&matches); i++) {
const char *key = strarray_nth(&matches, i);
r = cyrusdb_forone(rock->db, key, strlen(key), &find_p, &find_cb, rock, NULL);
}
strarray_fini(&matches);
}
else {
r = cyrusdb_foreach(rock->db, prefix, len, &find_p, &find_cb, rock, NULL);
}
if (r == CYRUSDB_DONE) r = 0;
return r;
}
/*
* Find all mailboxes that match 'pattern'.
* 'isadmin' is nonzero if user is a mailbox admin. 'userid'
* is the user's login id. For each matching mailbox, calls
* 'proc' with the name of the mailbox. If 'proc' ever returns
* a nonzero value, mboxlist_findall immediately stops searching
* and returns that value. 'rock' is passed along as an argument to proc in
* case it wants some persistent storage or extra data.
*/
/* Find all mailboxes that match 'pattern'. */
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);
int allowdeleted = config_getswitch(IMAPOPT_ALLOWDELETED);
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];
int r = 0;
int i;
const char *p;
if (patterns->count < 1) return 0; /* nothing to do */
for (i = 0; i < patterns->count; i++) {
glob *g = glob_init(strarray_nth(patterns, i), rock->namespace->hier_sep);
ptrarray_append(&rock->globs, g);
}
if (config_virtdomains && userid && (p = strchr(userid, '@'))) {
userlen = p - userid;
domainlen = strlen(p); /* includes separator */
snprintf(domainpat, sizeof(domainpat), "%s!", p+1);
}
else
domainpat[0] = '\0';
/* calculate the inbox (with trailing .INBOX. for later use) */
if (userid && (!(p = strchr(userid, rock->namespace->hier_sep)) ||
((p - userid) > (int)userlen)) &&
strlen(userid)+7 < MAX_MAILBOX_BUFFER) {
char *t, *tmpuser = NULL;
const char *inboxuser;
if (domainlen)
snprintf(inbox, sizeof(inbox), "%s!", userid+userlen+1);
if (rock->namespace->hier_sep == '/' && (p = strchr(userid, '.'))) {
tmpuser = xmalloc(userlen);
memcpy(tmpuser, userid, userlen);
t = tmpuser + (p - userid);
while(t < (tmpuser + userlen)) {
if (*t == '.')
*t = '^';
t++;
}
inboxuser = tmpuser;
} else
inboxuser = userid;
snprintf(inbox+domainlen, sizeof(inbox)-domainlen,
"user.%.*s.INBOX.", (int)userlen, inboxuser);
free(tmpuser);
inboxlen = strlen(inbox) - 7;
}
else {
userid = 0;
}
/* Find the common search prefix of all patterns */
const char *firstpat = strarray_nth(patterns, 0);
for (prefixlen = 0; firstpat[prefixlen]; prefixlen++) {
if (prefixlen >= MAX_MAILBOX_NAME) {
r = IMAP_MAILBOX_BADNAME;
goto done;
}
char c = firstpat[prefixlen];
for (i = 1; i < patterns->count; i++) {
const char *pat = strarray_nth(patterns, i);
if (pat[prefixlen] != c) break;
}
if (rock->namespace->hier_sep == '/') {
if (c == '/') c = '.';
else if (c == '.') c = DOTCHAR;
}
if (i < patterns->count) break;
if (c == '*' || c == '%' || c == '?') break;
commonpat[prefixlen] = c;
}
commonpat[prefixlen] = '\0';
if (patterns->count == 1) {
/* Skip pattern which matches shared namespace prefix */
if (!strcmp(firstpat+prefixlen, "%"))
rock->singlepercent = 2;
/* output prefix regardless */
if (!strcmp(firstpat+prefixlen, "*%"))
rock->singlepercent = 1;
}
/*
* Personal (INBOX) namespace (only if not admin)
*/
if (userid && !isadmin) {
/* first the INBOX */
rock->mb_category = MBNAME_INBOX;
r = cyrusdb_forone(rock->db, inbox, inboxlen, &find_p, &find_cb, rock, NULL);
if (r == CYRUSDB_DONE) r = 0;
if (r) goto done;
if (rock->namespace->isalt) {
/* do exact INBOX subs before resetting the namebuffer */
rock->mb_category = MBNAME_INBOXSUB;
r = cyrusdb_foreach(rock->db, inbox, inboxlen+7, &find_p, &find_cb, rock, NULL);
if (r == CYRUSDB_DONE) r = 0;
if (r) goto done;
/* reset the namebuffer */
if (rock->cb)
r = (*rock->cb)(NULL, rock->procrock);
if (r) goto done;
}
/* iterate through all the mailboxes under the user's inbox */
rock->mb_category = MBNAME_OWNER;
r = cyrusdb_foreach(rock->db, inbox, inboxlen+1, &find_p, &find_cb, rock, NULL);
if (r == CYRUSDB_DONE) r = 0;
if (r) goto done;
/* "Alt Prefix" folders */
if (rock->namespace->isalt) {
/* reset the namebuffer */
if (rock->cb)
r = (*rock->cb)(NULL, rock->procrock);
if (r) goto done;
rock->mb_category = MBNAME_ALTINBOX;
/* special case user.foo.INBOX. If we're singlepercent == 2, this could
return DONE, in which case we don't need to foreach the rest of the
altprefix space */
r = cyrusdb_forone(rock->db, inbox, inboxlen+6, &find_p, &find_cb, rock, NULL);
if (r == CYRUSDB_DONE) goto skipalt;
if (r) goto done;
/* special case any other altprefix stuff */
rock->mb_category = MBNAME_ALTPREFIX;
r = cyrusdb_foreach(rock->db, inbox, inboxlen+1, &find_p, &find_cb, rock, NULL);
skipalt: /* we got a done, so skip out of the foreach early */
if (r == CYRUSDB_DONE) r = 0;
if (r) goto done;
}
}
/*
* Other Users namespace
*
* If "Other Users*" can match pattern, search for those mailboxes next
*/
if (isadmin || rock->namespace->accessible[NAMESPACE_USER]) {
len = strlen(rock->namespace->prefix[NAMESPACE_USER]);
if (len) len--; // trailing separator
if (!strncmp(rock->namespace->prefix[NAMESPACE_USER], commonpat, MIN(len, prefixlen))) {
if (prefixlen <= len) {
/* we match all users */
strlcpy(domainpat+domainlen, "user.", sizeof(domainpat)-domainlen);
}
else {
/* just those in this prefix */
strlcpy(domainpat+domainlen, "user.", sizeof(domainpat)-domainlen);
strlcpy(domainpat+domainlen+5, commonpat+len+1, sizeof(domainpat)-domainlen-5);
}
rock->mb_category = MBNAME_OTHERUSER;
/* because of how domains work, with crossdomains or admin you can't prefix at all :( */
size_t thislen = (isadmin || crossdomains) ? 0 : strlen(domainpat);
/* reset the namebuffer */
if (rock->cb)
r = (*rock->cb)(NULL, rock->procrock);
if (r) goto done;
r = mboxlist_find_category(rock, domainpat, thislen);
if (r) goto done;
}
}
/*
* Shared namespace
*
* search for all remaining mailboxes.
* just bother looking at the ones that have the same pattern prefix.
*/
if (isadmin || rock->namespace->accessible[NAMESPACE_SHARED]) {
len = strlen(rock->namespace->prefix[NAMESPACE_SHARED]);
if (len) len--; // trailing separator
if (!strncmp(rock->namespace->prefix[NAMESPACE_SHARED], commonpat, MIN(len, prefixlen))) {
rock->mb_category = MBNAME_SHARED;
/* reset the namebuffer */
if (rock->cb)
r = (*rock->cb)(NULL, rock->procrock);
if (r) goto done;
/* iterate through all the non-user folders on the server */
r = mboxlist_find_category(rock, domainpat, domainlen);
if (r) goto done;
}
}
/* finally deleted namespaces - first the owner */
if (!isadmin && allowdeleted && userid) {
char prefix[MAX_MAILBOX_BUFFER];
char *inboxcopy = xstrndup(inbox, inboxlen);
mboxname_todeleted(inboxcopy, prefix, /*withtime*/0);
free(inboxcopy);
size_t prefixlen = strlen(prefix);
prefix[prefixlen] = '.';
rock->mb_category = MBNAME_OWNERDELETED;
/* reset the namebuffer */
if (rock->cb)
r = (*rock->cb)(NULL, rock->procrock);
if (r) goto done;
r = cyrusdb_foreach(rock->db, prefix, prefixlen+1, &find_p, &find_cb, rock, NULL);
if (r) goto done;
}
/* and everything else */
if (isadmin || (allowdeleted && rock->namespace->accessible[NAMESPACE_SHARED])) {
rock->mb_category = MBNAME_OTHERDELETED;
/* reset the namebuffer */
if (rock->cb)
r = (*rock->cb)(NULL, rock->procrock);
if (r) goto done;
/* iterate through all the non-user folders on the server */
r = mboxlist_find_category(rock, domainpat, domainlen);
if (r) goto done;
}
/* finish with a reset call always */
if (rock->cb)
r = (*rock->cb)(NULL, rock->procrock);
done:
for (i = 0; i < rock->globs.count; i++) {
glob *g = ptrarray_nth(&rock->globs, i);
glob_free(&g);
}
ptrarray_fini(&rock->globs);
return r;
}
EXPORTED int mboxlist_findallmulti(struct namespace *namespace,
const strarray_t *patterns, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_cb *proc, void *rock)
{
return mboxlist_findallmulti_withp(namespace, patterns, isadmin,
userid, auth_state,
NULL, proc, rock);
}
EXPORTED int mboxlist_findallmulti_withp(struct namespace *namespace,
const strarray_t *patterns, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_p *p, findall_cb *cb, void *rock)
{
int r = 0;
init_internal();
if (!namespace) namespace = mboxname_get_adminnamespace();
struct find_rock cbrock;
memset(&cbrock, 0, sizeof(struct find_rock));
cbrock.auth_state = auth_state;
cbrock.db = mbdb;
cbrock.isadmin = isadmin;
cbrock.namespace = namespace;
cbrock.p = p;
cbrock.cb = cb;
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);
return r;
}
EXPORTED int mboxlist_findall(struct namespace *namespace,
const char *pattern, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_cb *proc, void *rock)
{
return mboxlist_findall_withp(namespace, pattern, isadmin,
userid, auth_state,
NULL, proc, rock);
}
EXPORTED int mboxlist_findall_withp(struct namespace *namespace,
const char *pattern, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_p *p, findall_cb *cb, void *rock)
{
strarray_t patterns = STRARRAY_INITIALIZER;
strarray_append(&patterns, pattern);
init_internal();
int r = mboxlist_findallmulti_withp(namespace, &patterns, isadmin, userid, auth_state,
p, cb, rock);
strarray_fini(&patterns);
return r;
}
EXPORTED int mboxlist_findone(struct namespace *namespace,
const char *intname, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_cb *proc, void *rock)
{
return mboxlist_findone_withp(namespace, intname, isadmin,
userid, auth_state,
NULL, proc, rock);
}
EXPORTED int mboxlist_findone_withp(struct namespace *namespace,
const char *intname, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_p *p, findall_cb *cb, void *rock)
{
int r = 0;
if (!namespace) namespace = mboxname_get_adminnamespace();
struct find_rock cbrock;
memset(&cbrock, 0, sizeof(struct find_rock));
init_internal();
cbrock.auth_state = auth_state;
cbrock.db = mbdb;
cbrock.isadmin = isadmin;
cbrock.namespace = namespace;
cbrock.p = p;
cbrock.cb = cb;
cbrock.procrock = rock;
cbrock.userid = userid;
if (userid) {
const char *domp = strchr(userid, '@');
if (domp) cbrock.domain = domp + 1;
}
mbname_t *mbname = mbname_from_intname(intname);
glob *g = glob_init(mbname_extname(mbname, namespace, userid),
namespace->hier_sep);
ptrarray_append(&cbrock.globs, g);
mbname_free(&mbname);
r = cyrusdb_forone(cbrock.db, intname, strlen(intname), &find_p, &find_cb, &cbrock, NULL);
glob_free(&g);
ptrarray_fini(&cbrock.globs);
return r;
}
static int exists_cb(const mbentry_t *mbentry __attribute__((unused)), void *rock)
{
int *exists = (int *)rock;
*exists = 1;
return CYRUSDB_DONE; /* one is enough */
}
struct changequota_rock {
const char *root;
int silent;
};
/*
* Set all the resource quotas on, or create a quota root.
*/
EXPORTED int mboxlist_setquotas(const char *root,
quota_t newquotas[QUOTA_NUMRESOURCES],
modseq_t quotamodseq, int force)
{
struct quota q;
int r;
int res;
struct txn *tid = NULL;
struct mboxevent *mboxevents = NULL;
struct mboxevent *quotachange_event = NULL;
struct mboxevent *quotawithin_event = NULL;
int silent = quotamodseq ? 1 : 0;
init_internal();
if (!root[0] || root[0] == '.' || strchr(root, '/')
|| strchr(root, '*') || strchr(root, '%') || strchr(root, '?')) {
return IMAP_MAILBOX_BADNAME;
}
quota_init(&q, root);
r = quota_read(&q, &tid, 1);
if (!r) {
int changed = 0;
int underquota;
quota_t oldquotas[QUOTA_NUMRESOURCES];
/* has it changed? */
for (res = 0 ; res < QUOTA_NUMRESOURCES ; res++) {
oldquotas[res] = q.limits[res];
if (q.limits[res] != newquotas[res]) {
underquota = 0;
/* Prepare a QuotaChange event notification *now*.
*
* This is to ensure the QuotaChange is emitted before the
* subsequent QuotaWithin (if the latter becomes applicable).
*/
if (quotachange_event == NULL) {
quotachange_event = mboxevent_enqueue(EVENT_QUOTA_CHANGE,
&mboxevents);
}
/* prepare a QuotaWithin event notification if now under quota */
if (quota_is_overquota(&q, res, NULL) &&
(!quota_is_overquota(&q, res, newquotas) || newquotas[res] == -1)) {
if (quotawithin_event == NULL)
quotawithin_event = mboxevent_enqueue(EVENT_QUOTA_WITHIN,
&mboxevents);
underquota++;
}
q.limits[res] = newquotas[res];
changed++;
mboxevent_extract_quota(quotachange_event, &q, res);
if (underquota)
mboxevent_extract_quota(quotawithin_event, &q, res);
}
}
if (changed) {
if (quotamodseq)
q.modseq = quotamodseq;
r = quota_write(&q, silent, &tid);
if (quotachange_event == NULL) {
quotachange_event = mboxevent_enqueue(EVENT_QUOTA_CHANGE, &mboxevents);
}
for (res = 0; res < QUOTA_NUMRESOURCES; res++) {
mboxevent_extract_quota(quotachange_event, &q, res);
}
if (config_auditlog) {
struct buf item = BUF_INITIALIZER;
for (res = 0; res < QUOTA_NUMRESOURCES; res++) {
buf_printf(&item, " old%s=<%lld> new%s=<%lld>",
quota_names[res], oldquotas[res],
quota_names[res], newquotas[res]);
}
syslog(LOG_NOTICE, "auditlog: setquota root=<%s>%s", root, buf_cstring(&item));
buf_free(&item);
}
}
if (!r)
quota_commit(&tid);
goto done;
}
if (r != IMAP_QUOTAROOT_NONEXISTENT)
goto done;
if (config_virtdomains && root[strlen(root)-1] == '!') {
/* domain quota */
}
else {
mbentry_t *mbentry = NULL;
/* look for a top-level mailbox in the proposed quotaroot */
r = mboxlist_lookup(root, &mbentry, NULL);
if (r) {
if (!force && r == IMAP_MAILBOX_NONEXISTENT) {
mboxlist_mboxtree(root, exists_cb, &force, MBOXTREE_SKIP_ROOT);
}
/* are we going to force the create anyway? */
if (force) {
r = 0;
}
}
else if (mbentry->mbtype & (MBTYPE_REMOTE | MBTYPE_MOVING)) {
/* Can't set quota on a remote mailbox */
r = IMAP_MAILBOX_NOTSUPPORTED;
}
mboxlist_entry_free(&mbentry);
if (r) goto done;
}
/* safe against quota -f and other root change races */
r = quota_changelock();
if (r) goto done;
/* initialise the quota */
memcpy(q.limits, newquotas, sizeof(q.limits));
if (quotamodseq)
q.modseq = quotamodseq;
r = quota_write(&q, silent, &tid);
if (r) goto done;
/* prepare a QuotaChange event notification */
if (quotachange_event == NULL)
quotachange_event = mboxevent_enqueue(EVENT_QUOTA_CHANGE, &mboxevents);
for (res = 0; res < QUOTA_NUMRESOURCES; res++) {
mboxevent_extract_quota(quotachange_event, &q, res);
}
quota_commit(&tid);
if (config_auditlog) {
struct buf item = BUF_INITIALIZER;
for (res = 0; res < QUOTA_NUMRESOURCES; res++) {
buf_printf(&item, " new%s=<%lld>",
quota_names[res], newquotas[res]);
}
syslog(LOG_NOTICE, "auditlog: newquota root=<%s>%s", root, buf_cstring(&item));
buf_free(&item);
}
/* recurse through mailboxes, setting the quota and finding
* out the usage */
struct changequota_rock crock = { root, silent };
mboxlist_mboxtree(root, mboxlist_changequota, &crock, 0);
quota_changelockrelease();
done:
quota_free(&q);
if (r && tid) quota_abort(&tid);
if (!r) {
sync_log_quota(root);
/* send QuotaChange and QuotaWithin event notifications */
mboxevent_notify(&mboxevents);
}
mboxevent_freequeue(&mboxevents);
return r;
}
/*
* Remove a quota root
*/
EXPORTED int mboxlist_unsetquota(const char *root)
{
struct quota q;
int r=0;
init_internal();
if (!root[0] || root[0] == '.' || strchr(root, '/')
|| strchr(root, '*') || strchr(root, '%') || strchr(root, '?')) {
return IMAP_MAILBOX_BADNAME;
}
quota_init(&q, root);
r = quota_read(&q, NULL, 0);
/* already unset */
if (r == IMAP_QUOTAROOT_NONEXISTENT) {
r = 0;
goto done;
}
if (r) goto done;
r = quota_changelock();
/*
* Have to remove it from all affected mailboxes
*/
mboxlist_mboxtree(root, mboxlist_rmquota, (void *)root, /*flags*/0);
if (config_auditlog) {
struct buf item = BUF_INITIALIZER;
int res;
for (res = 0; res < QUOTA_NUMRESOURCES; res++) {
buf_printf(&item, " old%s=<%lld>", quota_names[res], q.limits[res]);
}
syslog(LOG_NOTICE, "auditlog: rmquota root=<%s>%s", root, buf_cstring(&item));
buf_free(&item);
}
r = quota_deleteroot(root, 0);
quota_changelockrelease();
if (!r) sync_log_quota(root);
done:
quota_free(&q);
return r;
}
EXPORTED int mboxlist_update_foldermodseq(const char *name, modseq_t foldermodseq)
{
mbentry_t *mbentry = NULL;
init_internal();
int r = mboxlist_mylookup(name, &mbentry, NULL, 0);
if (r) return r;
if (mbentry->foldermodseq < foldermodseq) {
mbentry->foldermodseq = foldermodseq;
r = mboxlist_update(mbentry, 0);
}
mboxlist_entry_free(&mbentry);
return r;
}
/*
* ACL access canonicalization routine which ensures that 'owner'
* retains lookup, administer, and create rights over a mailbox.
*/
EXPORTED int mboxlist_ensureOwnerRights(void *rock, const char *identifier,
int myrights)
{
char *owner = (char *)rock;
if (strcmp(identifier, owner) != 0) return myrights;
return myrights|config_implicitrights;
}
/*
* Helper function to remove the quota root for 'name'
*/
static int mboxlist_rmquota(const mbentry_t *mbentry, void *rock)
{
int r = 0;
struct mailbox *mailbox = NULL;
const char *oldroot = (const char *) rock;
assert(oldroot != NULL);
r = mailbox_open_iwl(mbentry->name, &mailbox);
if (r) goto done;
if (mailbox->quotaroot) {
if (strcmp(mailbox->quotaroot, oldroot)) {
/* Part of a different quota root */
goto done;
}
r = mailbox_set_quotaroot(mailbox, NULL);
}
done:
mailbox_close(&mailbox);
if (r) {
syslog(LOG_ERR, "LOSTQUOTA: unable to remove quota root %s for %s: %s",
oldroot, mbentry->name, error_message(r));
}
/* not a huge tragedy if we failed, so always return success */
return 0;
}
/*
* Helper function to change the quota root for 'name' to that pointed
* to by the static global struct pointer 'mboxlist_newquota'.
*/
static int mboxlist_changequota(const mbentry_t *mbentry, void *rock)
{
int r = 0;
struct mailbox *mailbox = NULL;
struct changequota_rock *crock = rock;
int res;
quota_t quota_usage[QUOTA_NUMRESOURCES];
assert(crock->root);
r = mailbox_open_iwl(mbentry->name, &mailbox);
if (r) goto done;
mailbox_get_usage(mailbox, quota_usage);
if (mailbox->quotaroot) {
quota_t quota_diff[QUOTA_NUMRESOURCES];
if (strlen(mailbox->quotaroot) >= strlen(crock->root)) {
/* Part of a child quota root - skip */
goto done;
}
/* remove usage from the old quotaroot */
for (res = 0; res < QUOTA_NUMRESOURCES ; res++) {
quota_diff[res] = -quota_usage[res];
}
r = quota_update_useds(mailbox->quotaroot, quota_diff,
mailbox->name, crock->silent);
}
/* update (or set) the quotaroot */
r = mailbox_set_quotaroot(mailbox, crock->root);
if (r) goto done;
/* update the new quota root */
r = quota_update_useds(crock->root, quota_usage, mailbox->name, crock->silent);
done:
mailbox_close(&mailbox);
if (r) {
syslog(LOG_ERR, "LOSTQUOTA: unable to change quota root for %s to %s: %s",
mbentry->name, crock->root, error_message(r));
}
/* Note, we're a callback, and it's not a huge tragedy if we
* fail, so we don't ever return a failure */
return 0;
}
EXPORTED int mboxlist_haschildren(const char *mboxname)
{
int exists = 0;
mboxlist_mboxtree(mboxname, exists_cb, &exists, MBOXTREE_SKIP_ROOT);
return exists;
}
EXPORTED void mboxlist_done(void)
{
/* DB->done() handled by cyrus_done() */
}
static void done_cb(void*rock __attribute__((unused)))
{
if (mboxlist_dbopen) {
mboxlist_close();
}
mboxlist_done();
}
static void init_internal()
{
if (!mboxlist_initialized) {
mboxlist_init(0);
}
if (!mboxlist_dbopen) {
mboxlist_open(NULL);
}
}
/* must be called after cyrus_init */
EXPORTED void mboxlist_init(int myflags)
{
if (myflags & MBOXLIST_SYNC) {
cyrusdb_sync(DB);
}
cyrus_modules_add(done_cb, NULL);
mboxlist_initialized = 1;
}
EXPORTED void mboxlist_open(const char *fname)
{
int ret, flags;
char *tofree = NULL;
if (!fname)
fname = config_getstring(IMAPOPT_MBOXLIST_DB_PATH);
/* create db file name */
if (!fname) {
tofree = strconcat(config_dir, FNAME_MBOXLIST, (char *)NULL);
fname = tofree;
}
mboxlist_init(0);
flags = CYRUSDB_CREATE;
if (config_getswitch(IMAPOPT_IMPROVED_MBOXLIST_SORT)) {
flags |= CYRUSDB_MBOXSORT;
}
ret = cyrusdb_open(DB, fname, flags, &mbdb);
if (ret != 0) {
xsyslog(LOG_ERR, "DBERROR: error opening mailboxes list",
"fname=<%s> error=<%s>",
fname, cyrusdb_strerror(ret));
/* Exiting TEMPFAIL because Sendmail thinks this
EX_OSFILE == permanent failure. */
fatal("can't read mailboxes file", EX_TEMPFAIL);
}
free(tofree);
mboxlist_dbopen = 1;
have_racl = !cyrusdb_fetch(mbdb, "$RACL", 5, NULL, NULL, NULL);
have_runq = !cyrusdb_fetch(mbdb, "$RUNQ", 5, NULL, NULL, NULL);
}
EXPORTED void mboxlist_close(void)
{
int r;
if (mboxlist_dbopen) {
r = cyrusdb_close(mbdb);
if (r) {
xsyslog(LOG_ERR, "DBERROR: error closing mailboxes",
"error=<%s>",
cyrusdb_strerror(r));
}
mboxlist_dbopen = 0;
}
}
/*
* Open the subscription list for 'userid'.
*
* On success, returns zero.
* On failure, returns an error code.
*/
static int
mboxlist_opensubs(const char *userid,
struct db **ret)
{
int r = 0, flags;
char *subsfname;
/* Build subscription list filename */
subsfname = user_hash_subs(userid);
flags = CYRUSDB_CREATE;
if (config_getswitch(IMAPOPT_IMPROVED_MBOXLIST_SORT)) {
flags |= CYRUSDB_MBOXSORT;
}
r = cyrusdb_open(SUBDB, subsfname, flags, ret);
if (r != CYRUSDB_OK) {
r = IMAP_IOERROR;
}
free(subsfname);
return r;
}
/*
* Close a subscription file
*/
static void mboxlist_closesubs(struct db *sub)
{
cyrusdb_close(sub);
}
/*
* Find subscribed mailboxes that match 'pattern'.
* 'isadmin' is nonzero if user is a mailbox admin. 'userid'
* is the user's login id. For each matching mailbox, calls
* 'proc' with the name of the mailbox.
*/
EXPORTED int mboxlist_findsubmulti(struct namespace *namespace,
const strarray_t *patterns, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_cb *proc, void *rock,
int force)
{
return mboxlist_findsubmulti_withp(namespace, patterns, isadmin,
userid, auth_state,
NULL, proc, rock,
force);
}
EXPORTED int mboxlist_findsubmulti_withp(struct namespace *namespace,
const strarray_t *patterns, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_p *p, findall_cb *cb, void *rock,
int force)
{
int r = 0;
init_internal();
if (!namespace) namespace = mboxname_get_adminnamespace();
struct find_rock cbrock;
memset(&cbrock, 0, sizeof(struct find_rock));
/* open the subscription file that contains the mailboxes the
user is subscribed to */
struct db *subs = NULL;
r = mboxlist_opensubs(userid, &subs);
if (r) return r;
cbrock.auth_state = auth_state;
cbrock.checkmboxlist = !force;
cbrock.db = subs;
cbrock.isadmin = isadmin;
cbrock.issubs = 1;
cbrock.namespace = namespace;
cbrock.p = p;
cbrock.cb = cb;
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);
mboxlist_closesubs(subs);
return r;
}
EXPORTED int mboxlist_findsub(struct namespace *namespace,
const char *pattern, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_cb *proc, void *rock,
int force)
{
return mboxlist_findsub_withp(namespace, pattern, isadmin,
userid, auth_state,
NULL, proc, rock,
force);
}
EXPORTED int mboxlist_findsub_withp(struct namespace *namespace,
const char *pattern, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_p *p, findall_cb *cb, void *rock,
int force)
{
strarray_t patterns = STRARRAY_INITIALIZER;
strarray_append(&patterns, pattern);
init_internal();
int r = mboxlist_findsubmulti_withp(namespace, &patterns, isadmin, userid, auth_state,
p, cb, rock, force);
strarray_fini(&patterns);
return r;
}
static int subsadd_cb(void *rock, const char *key, size_t keylen,
const char *val __attribute__((unused)),
size_t vallen __attribute__((unused)))
{
strarray_t *list = (strarray_t *)rock;
strarray_appendm(list, xstrndup(key, keylen));
return 0;
}
EXPORTED strarray_t *mboxlist_sublist(const char *userid)
{
struct db *subs = NULL;
strarray_t *list = strarray_new();
int r;
init_internal();
/* open subs DB */
r = mboxlist_opensubs(userid, &subs);
if (r) goto done;
r = cyrusdb_foreach(subs, "", 0, NULL, subsadd_cb, list, 0);
mboxlist_closesubs(subs);
done:
if (r) {
strarray_free(list);
return NULL;
}
return list;
}
struct submb_rock {
struct mboxlist_entry *mbentry;
const char *userid;
int flags;
mboxlist_cb *proc;
void *rock;
};
static int usersubs_cb(void *rock, const char *key, size_t keylen,
const char *data __attribute__((unused)),
size_t datalen __attribute__((unused)))
{
struct submb_rock *mbrock = (struct submb_rock *) rock;
char mboxname[MAX_MAILBOX_NAME+1];
int r;
/* free previous record */
mboxlist_entry_free(&mbrock->mbentry);
snprintf(mboxname, MAX_MAILBOX_NAME, "%.*s", (int) keylen, key);
if ((mbrock->flags & MBOXTREE_SKIP_PERSONAL) &&
mboxname_userownsmailbox(mbrock->userid, mboxname)) return 0;
r = mboxlist_lookup(mboxname, &mbrock->mbentry, NULL);
if (r == IMAP_MAILBOX_NONEXISTENT) return 0;
if (r) {
syslog(LOG_INFO, "mboxlist_lookup(%s) failed: %s",
mboxname, error_message(r));
return r;
}
return mbrock->proc(mbrock->mbentry, mbrock->rock);
}
EXPORTED int mboxlist_usersubs(const char *userid, mboxlist_cb *proc,
void *rock, int flags)
{
struct db *subs = NULL;
struct submb_rock mbrock = { NULL, userid, flags, proc, rock };
int r = 0;
init_internal();
/* open subs DB */
r = mboxlist_opensubs(userid, &subs);
if (r) return r;
/* faster to do it all in a single slurp! */
r = cyrusdb_foreach(subs, "", 0, NULL, usersubs_cb, &mbrock, 0);
mboxlist_entry_free(&mbrock.mbentry);
mboxlist_closesubs(subs);
return r;
}
/* returns CYRUSDB_NOTFOUND if the folder doesn't exist, and 0 if it does! */
EXPORTED int mboxlist_checksub(const char *name, const char *userid)
{
int r;
struct db *subs;
const char *val;
size_t vallen;
init_internal();
r = mboxlist_opensubs(userid, &subs);
if (!r) r = cyrusdb_fetch(subs, name, strlen(name), &val, &vallen, NULL);
mboxlist_closesubs(subs);
return r;
}
/*
* Change 'user's subscription status for mailbox 'name'.
* Subscribes if 'add' is nonzero, unsubscribes otherwise.
* if 'force' is set, force the subscription through even if
* we don't know about 'name'.
*/
EXPORTED int mboxlist_changesub(const char *name, const char *userid,
const struct auth_state *auth_state,
int add, int force, int notify)
{
mbentry_t *mbentry = NULL;
int r;
struct db *subs;
struct mboxevent *mboxevent;
init_internal();
if ((r = mboxlist_opensubs(userid, &subs)) != 0) {
return r;
}
if (add && !force) {
/* Ensure mailbox exists and can be seen by user */
if ((r = mboxlist_lookup(name, &mbentry, NULL))!=0) {
mboxlist_closesubs(subs);
return r;
}
if ((cyrus_acl_myrights(auth_state, mbentry->acl) & ACL_LOOKUP) == 0) {
mboxlist_closesubs(subs);
mboxlist_entry_free(&mbentry);
return IMAP_MAILBOX_NONEXISTENT;
}
}
if (add) {
r = cyrusdb_store(subs, name, strlen(name), "", 0, NULL);
} else {
r = cyrusdb_delete(subs, name, strlen(name), NULL, 0);
/* if it didn't exist, that's ok */
if (r == CYRUSDB_EXISTS) r = CYRUSDB_OK;
}
switch (r) {
case CYRUSDB_OK:
r = 0;
break;
default:
r = IMAP_IOERROR;
break;
}
sync_log_subscribe(userid, name);
mboxlist_closesubs(subs);
mboxlist_entry_free(&mbentry);
/* prepare a MailboxSubscribe or MailboxUnSubscribe event notification */
if (notify && r == 0) {
mboxevent = mboxevent_new(add ? EVENT_MAILBOX_SUBSCRIBE :
EVENT_MAILBOX_UNSUBSCRIBE);
mboxevent_set_access(mboxevent, NULL, NULL, userid, name, 1);
mboxevent_notify(&mboxevent);
mboxevent_free(&mboxevent);
}
return r;
}
/* Transaction Handlers */
EXPORTED int mboxlist_commit(struct txn *tid)
{
assert(tid);
return cyrusdb_commit(mbdb, tid);
}
int mboxlist_abort(struct txn *tid)
{
assert(tid);
return cyrusdb_abort(mbdb, tid);
}
EXPORTED int mboxlist_delayed_delete_isenabled(void)
{
enum enum_value config_delete_mode = config_getenum(IMAPOPT_DELETE_MODE);
return(config_delete_mode == IMAP_ENUM_DELETE_MODE_DELAYED);
}
diff --git a/imap/mboxlist.h b/imap/mboxlist.h
index c5bef1ad2..2f654377f 100644
--- a/imap/mboxlist.h
+++ b/imap/mboxlist.h
@@ -1,422 +1,421 @@
/* mboxlist.h -- Mailbox list manipulation routines
*
* Copyright (c) 1994-2008 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Carnegie Mellon University
* Center for Technology Transfer and Enterprise Creation
* 4615 Forbes Avenue
* Suite 302
* Pittsburgh, PA 15213
* (412) 268-7393, fax: (412) 268-7395
* innovation@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef INCLUDED_MBOXLIST_H
#define INCLUDED_MBOXLIST_H
#include "config.h"
#include "cyrusdb.h"
#include "dlist.h"
#include "mailbox.h"
#include "auth.h"
#include "mboxevent.h"
#include "mboxname.h"
/*
* Maximum length of partition name. [config.c has a limit of 70]
*/
#define MAX_PARTITION_LEN 64
/* Flags for types of mailboxes
*
* Historically, mbtype was a bitmask, which is why this set of defines looks
* like a bitmask. But, that was a mistake, which we have almost-entirely
* moved away from.
*
* Nowadays, an mbtype should properly only ever be a single one of these
* values, not a bitmask.
*
* The MBTYPES_DAV and MBTYPES_NONIMAP masks remain because they remain
* useful for checking the flavour of an mbtype, but do not take them as
* indicative of good style! Generally, if you need to set an mbtype, set
* it to one value, and if you need to compare an mbtype, compare it against
* one value.
*/
#define MBTYPE_EMAIL 0 /* default mbtype is zero */
#define MBTYPE_REMOTE (1<<0) /* Not on this server (part is remote host) */
#define MBTYPE_RESERVE (1<<1) /* Reserved [mupdate/imapd] /
Rename Target [imapd] (part is normal, but
you are not allowed to create this mailbox,
even though it doesn't actually exist) */
#define MBTYPE_NETNEWS (1<<2) /* Netnews Mailbox - NO LONGER USED */
#define MBTYPE_MOVING (1<<3) /* Mailbox in mid-transfer
(part is remotehost!localpart) */
#define MBTYPE_DELETED (1<<4) /* Mailbox has been deleted,
but not yet cleaned up */
#define MBTYPE_CALENDAR (1<<5) /* CalDAV Calendar Mailbox */
#define MBTYPE_ADDRESSBOOK (1<<6) /* CardDAV Addressbook Mailbox */
#define MBTYPE_COLLECTION (1<<7) /* WebDAV Collection Mailbox */
#define MBTYPE_INTERMEDIATE (1<<8) /* Place holder
for non-existent ancestor mailboxes */
#define MBTYPE_SUBMISSION (1<<9) /* JMAP Mail Submission Mailbox */
#define MBTYPE_PUSHSUBSCRIPTION (1<<10) /* JMAP Push Subscriptions */
#define MBTYPE_JMAPNOTIFICATION (1<<11) /* JMAP Notifications */
#define MBTYPE_UNKNOWN (1<<31) /* unknown type for error handling */
/* NOTE: don't forget to also update the mappings for these in mboxlist.c:
* mboxlist_string_to_mbtype()
* mboxlist_mbtype_to_string()
*/
#define MBTYPES_DAV (MBTYPE_CALENDAR|MBTYPE_ADDRESSBOOK|MBTYPE_COLLECTION)
#define MBTYPES_NONIMAP (MBTYPE_NETNEWS|MBTYPES_DAV|MBTYPE_SUBMISSION|MBTYPE_PUSHSUBSCRIPTION|MBTYPE_JMAPNOTIFICATION)
/* master name of the mailboxes file */
#define FNAME_MBOXLIST "/mailboxes.db"
#define HOSTNAME_SIZE 512
/* each mailbox has the following data */
struct mboxlist_entry {
char *name;
char *ext_name;
time_t mtime;
uint32_t uidvalidity;
modseq_t createdmodseq;
modseq_t foldermodseq;
int mbtype;
char *partition;
char *server; /* holds remote machine for REMOTE mailboxes */
char *acl;
/* extra fields */
char *uniqueid;
/* legacy upgrade support */
char *legacy_specialuse;
};
typedef struct mboxlist_entry mbentry_t;
mbentry_t *mboxlist_entry_create();
char *mbentry_metapath(const struct mboxlist_entry *mbentry, int metatype, int isnew);
char *mbentry_datapath(const struct mboxlist_entry *mbentry, uint32_t);
int mboxlist_parse_entry(mbentry_t **mbentryptr,
const char *name, size_t namelen,
const char *data, size_t datalen);
mbentry_t *mboxlist_entry_copy(const mbentry_t *src);
void mboxlist_entry_free(mbentry_t **mbentryptr);
const char *mboxlist_mbtype_to_string(uint32_t mbtype);
uint32_t mboxlist_string_to_mbtype(const char *string);
int mboxlist_delete(const char *name);
/* Lookup 'name' in the mailbox list. */
int mboxlist_lookup(const char *name, mbentry_t **mbentryptr,
struct txn **tid);
int mboxlist_lookup_allow_all(const char *name,
mbentry_t **mbentryptr,
struct txn **tid);
int mboxlist_lookup_by_uniqueid(const char *uniqueid, mbentry_t **entryptr);
char *mboxlist_find_specialuse(const char *use, const char *userid);
char *mboxlist_find_uniqueid(const char *uniqueid, const char *userid,
const struct auth_state *auth_state);
/* insert/delete stub entries */
int mboxlist_insertremote(mbentry_t *mbentry, struct txn **rettid);
int mboxlist_deleteremote(const char *name, struct txn **in_tid);
/* Update a mailbox's entry */
int mboxlist_update(mbentry_t *mbentry, int localonly);
/* check user's ability to create mailbox */
int mboxlist_createmailboxcheck(const char *name, int mbtype,
const char *partition,
int isadmin, const char *userid,
const struct auth_state *auth_state,
char **newacl, char **newpartition,
int forceuser);
/* create mailbox */
/* localonly creates the local mailbox without touching mupdate */
/* forceuser allows the creation of user.x.<name> without a user.x */
/* dbonly skips filesystem operations (e.g. reconstruct) */
/* notify sends a MailboxCreate event notification */
/* if given a mailbox pointer, return the still-locked mailbox
* for further manipulation */
int mboxlist_createmailbox(const char *name, int mbtype,
const char *partition,
int isadmin, const char *userid,
const struct auth_state *auth_state,
int localonly, int forceuser, int dbonly,
int notify, struct mailbox **mailboxptr);
/* create mailbox with wrapping namespacelock */
int mboxlist_createmailboxlock(const char *name, int mbtype,
const char *partition,
int isadmin, const char *userid,
const struct auth_state *auth_state,
int localonly, int forceuser, int dbonly,
int notify, struct mailbox **mailboxptr);
/* create mailbox with uniqueid */
int mboxlist_createmailbox_unq(const char *name, int mbtype,
const char *partition,
int isadmin, const char *userid,
const struct auth_state *auth_state,
int localonly, int forceuser, int dbonly,
int notify, const char *uniqueid,
struct mailbox **mailboxptr);
/* create mailbox with options and uniqueid */
int mboxlist_createmailbox_opts(const char *name, int mbtype,
const char *partition,
int isadmin, const char *userid,
const struct auth_state *auth_state,
int options, int localonly,
int forceuser, int dbonly,
int notify, const char *uniqueid,
struct mailbox **mailboxptr);
/* create mailbox from sync */
int mboxlist_createsync(const char *name, int mbtype, const char *partition,
const char *userid, const struct auth_state *auth_state,
int options, unsigned uidvalidity,
modseq_t createdmodseq,
modseq_t highestmodseq,
modseq_t foldermodseq, const char *acl,
const char *uniqueid, int local_only,
int keep_intermediaries,
struct mailbox **mboxptr);
#define MBOXLIST_DELETE_CHECKACL (1<<0)
/* setting local_only disables any communication with the mupdate server
* and deletes the mailbox from the filesystem regardless of if it is
* MBTYPE_REMOTE or not */
#define MBOXLIST_DELETE_LOCALONLY (1<<1)
/* force ignores errors and just tries to wipe the mailbox off the face of
* the planet */
#define MBOXLIST_DELETE_FORCE (1<<2)
#define MBOXLIST_DELETE_KEEP_INTERMEDIARIES (1<<3)
/* silently delete, do not bump modseq */
#define MBOXLIST_DELETE_SILENT (1<<4)
/* unprotect_specialuse ignores the specialuse_protect config */
#define MBOXLIST_DELETE_UNPROTECT_SPECIALUSE (1<<5)
/* delayed delete */
/* Translate delete into rename */
/* prepare MailboxDelete notification if mboxevent is not NULL */
int
mboxlist_delayed_deletemailbox(const char *name, int isadmin, const char *userid,
const struct auth_state *auth_state,
struct mboxevent *mboxevent, int flags);
/* Delete a mailbox. */
/* prepare MailboxDelete notification if mboxevent is not NULL */
int mboxlist_deletemailbox(const char *name, int isadmin, const char *userid,
const struct auth_state *auth_state,
struct mboxevent *mboxevent, int flags);
/* same but wrap with a namespacelock */
int mboxlist_deletemailboxlock(const char *name, int isadmin, const char *userid,
const struct auth_state *auth_state,
struct mboxevent *mboxevent, int flags);
/* rename a tree of mailboxes - renames mailbox plus any children */
int mboxlist_renametree(const char *oldname, const char *newname,
const char *partition, unsigned uidvalidity,
int isadmin, const char *userid,
const struct auth_state *auth_state,
struct mboxevent *mboxevent,
int local_only, int forceuser, int ignorequota,
int keep_intermediaries, int move_subscription);
/* Rename/move a mailbox (hierarchical) */
/* prepare MailboxRename notification if mboxevent is not NULL */
int mboxlist_renamemailbox(const mbentry_t *mbentry, const char *newname,
const char *partition, unsigned uidvalidity,
int isadmin, const char *userid,
const struct auth_state *auth_state,
struct mboxevent *mboxevent,
int local_only, int forceuser, int ignorequota,
int keep_intermediaries, int move_subscription,
int silent);
/* change ACL */
int mboxlist_setacl(const struct namespace *namespace, const char *name,
const char *identifier, const char *rights, int isadmin,
const char *userid, const struct auth_state *auth_state);
/* Change all ACLs on mailbox */
int mboxlist_updateacl_raw(const char *name, const char *acl);
int mboxlist_sync_setacls(const char *name, const char *acl, modseq_t foldermodseq);
int mboxlist_update_foldermodseq(const char *name, modseq_t foldermodseq);
int mboxlist_set_racls(int enabled);
-int mboxlist_set_runiqueid(int enabled);
int mboxlist_cleanup_deletedentries(const mbentry_t *mbentry, time_t mark);
struct findall_data {
const char *extname;
int mb_category;
const mbentry_t *mbentry;
const mbname_t *mbname;
int is_exactmatch;
};
typedef int findall_p(struct findall_data *data, void *rock);
typedef int findall_cb(struct findall_data *data, void *rock);
/* Find all mailboxes that match 'pattern'. */
int mboxlist_findall(struct namespace *namespace,
const char *pattern, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_cb *proc, void *rock);
int mboxlist_findallmulti(struct namespace *namespace,
const strarray_t *patterns, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_cb *proc, void *rock);
int mboxlist_findone(struct namespace *namespace,
const char *intname, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_cb *proc, void *rock);
int mboxlist_findall_withp(struct namespace *namespace,
const char *pattern, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_p *p, findall_cb *cb, void *rock);
int mboxlist_findallmulti_withp(struct namespace *namespace,
const strarray_t *patterns, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_p *p, findall_cb *cb, void *rock);
int mboxlist_findone_withp(struct namespace *namespace,
const char *intname, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_p *p, findall_cb *cb, void *rock);
/* Find a mailbox's parent (if any) */
int mboxlist_findparent(const char *mboxname,
mbentry_t **mbentryp);
int mboxlist_findparent_allow_all(const char *mboxname,
mbentry_t **mbentryp);
/* direct access to subs DB */
typedef int user_cb(const char *userid, void *rock);
int mboxlist_alluser(user_cb *proc, void *rock);
typedef int mboxlist_cb(const mbentry_t *mbentry, void *rock);
#define MBOXTREE_TOMBSTONES (1<<0)
#define MBOXTREE_DELETED (1<<1)
#define MBOXTREE_SKIP_ROOT (1<<2)
#define MBOXTREE_SKIP_CHILDREN (1<<3)
#define MBOXTREE_SKIP_PERSONAL (1<<4)
#define MBOXTREE_PLUS_RACL (1<<5)
#define MBOXTREE_INTERMEDIATES (1<<6)
int mboxlist_allmbox(const char *prefix, mboxlist_cb *proc, void *rock, int flags);
int mboxlist_mboxtree(const char *mboxname, mboxlist_cb *proc, void *rock, int flags);
int mboxlist_usermboxtree(const char *userid, const struct auth_state *auth_state,
mboxlist_cb *proc, void *rock, int flags);
int mboxlist_usersubs(const char *userid, mboxlist_cb *proc, void *rock, int flags);
strarray_t *mboxlist_sublist(const char *userid);
/* Find subscribed mailboxes that match 'pattern'. */
int mboxlist_findsub(struct namespace *namespace,
const char *pattern, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_cb *proc, void *rock,
int force);
int mboxlist_findsubmulti(struct namespace *namespace,
const strarray_t *patterns, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_cb *proc, void *rock,
int force);
int mboxlist_findsub_withp(struct namespace *namespace,
const char *pattern, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_p *p, findall_cb *cb, void *rock,
int force);
int mboxlist_findsubmulti_withp(struct namespace *namespace,
const strarray_t *patterns, int isadmin,
const char *userid, const struct auth_state *auth_state,
findall_p *p, findall_cb *cb, void *rock,
int force);
/* given a mailbox 'name', where should we stage messages for it?
'stagedir' should be MAX_MAILBOX_PATH. */
int mboxlist_findstage(const char *name, char *stagedir, size_t sd_len);
/* Check 'user's subscription status for mailbox 'name' */
int mboxlist_checksub(const char *name, const char *userid);
/* Change 'user's subscription status for mailbox 'name'. */
int mboxlist_changesub(const char *name, const char *userid,
const struct auth_state *auth_state,
int add, int force, int notify);
/* set or create quota root */
int mboxlist_setquotas(const char *root,
quota_t newquotas[QUOTA_NUMRESOURCES],
modseq_t modseq, int force);
int mboxlist_unsetquota(const char *root);
/* handle interemediates */
int mboxlist_update_intermediaries(const char *mboxname, int mbtype, modseq_t modseq);
int mboxlist_haschildren(const char *mboxname);
/* open the mailboxes db */
void mboxlist_open(const char *name);
/* close the database */
void mboxlist_close(void);
/* initialize database structures */
#define MBOXLIST_SYNC 0x02
void mboxlist_init(int flags);
/* done with database stuff */
void mboxlist_done(void);
/* for transactions */
int mboxlist_commit(struct txn *tid);
int mboxlist_abort(struct txn *tid);
int mboxlist_delayed_delete_isenabled(void);
/* Promote an intermediary mailbox to a real mailbox. */
int mboxlist_promote_intermediary(const char *mboxname);
#endif
diff --git a/lib/imapoptions b/lib/imapoptions
index b6be76d1d..2d8958ec2 100644
--- a/lib/imapoptions
+++ b/lib/imapoptions
@@ -1,3120 +1,3116 @@
# things inside of C comments get copied to the manpage
# things starting with # are ignored
/* .\" -*- nroff -*-
.TH IMAPD.CONF 5 "Project Cyrus" CMU
.\"
.\" Copyright (c) 1994-2008 Carnegie Mellon University. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\"
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\"
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in
.\" the documentation and/or other materials provided with the
.\" distribution.
.\"
.\" 3. The name "Carnegie Mellon University" must not be used to
.\" endorse or promote products derived from this software without
.\" prior written permission. For permission or any legal
.\" details, please contact
.\" Carnegie Mellon University
.\" Center for Technology Transfer and Enterprise Creation
.\" 4615 Forbes Avenue
.\" Suite 302
.\" Pittsburgh, PA 15213
.\" (412) 268-7393, fax: (412) 268-7395
.\" innovation@andrew.cmu.edu
.\"
.\" 4. Redistributions of any form whatsoever must retain the following
.\" acknowledgment:
.\" "This product includes software developed by Computing Services
.\" at Carnegie Mellon University (http://www.cmu.edu/computing/)."
.\"
.\" CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
.\" THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
.\" AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
.\" FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
.\" AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.SH NAME
imapd.conf \- IMAP configuration file
.SH DESCRIPTION
\fB/etc/imapd.conf\fR
is the configuration file for the Cyrus IMAP server. It defines
local parameters for IMAP.
.PP
Each line of the \fB/etc/imapd.conf\fR file has the form
.IP
\fIoption\fR: \fIvalue\fR
.PP
where \fIoption\fR is the name of the configuration option being set
and \fIvalue\fR is the value that the configuration option is being
set to.
.PP
Although there is no limit to the length of a line, a ``\\''
(backslash) character may be used as the last character on a line to
force it to continue on the next one. No additional whitespace is
inserted before or after the ``\\''. Note that a line that is split
using ``\\'' character(s) is still considered a single line.
For example
.IP
\fIoption\fR:\\
.br
.in +1
\fIvalue\fR1 \fIvalue\fR2 \\
.br
.in +1
\fIvalue\fR3
.PP
is equivalent to
.IP
\fIoption\fR: \fIvalue\fR1 \fIvalue\fR2 \fIvalue\fR3
.PP
Blank lines and lines beginning with ``#'' are ignored.
.PP
For boolean and enumerated options, the values ``yes'', ``on'', ``t'',
``true'' and ``1'' turn the option on, the values ``no'', ``off'',
``f'', ``false'' and ``0'' turn the option off.
.PP
Duration options take the form of a number followed by a unit, for example
\fB32m\fR (32 minutes). Units are \fBd\fR (days), \fBh\fR (hours), \fBm\fR
(minutes) and \fBs\fR (seconds). Multiple units can be combined and will
be summed together, for example \fB1h30m\fR is equivalent to \fB90m\fR. If
no unit is specified, an option-specific backward-compatible default unit
is assumed (documented on an option-by-option basis). These are simple time
units: 1d=24h, 1h=60m, 1m=60s (daylight savings, timezones, leap adjustments,
etc are not considered).
.SH FIELD DESCRIPTIONS
.PP
The sections below detail options that can be placed in the
\fB/etc/imapd.conf\fR file, and show each option's default value.
Some options have no default value, these are listed with
``<no default>''. Some options default to the empty string, these
are listed with ``<none>''.
*/
# OPTIONS
{ "addressbookprefix", "#addressbooks", STRING, "2.5.0" }
/* The prefix for the addressbook mailboxes hierarchies. The hierarchy
delimiter will be automatically appended. The public addressbook
hierarchy will be at the toplevel of the shared namespace. A
user's personal addressbook hierarchy will be a child of their Inbox. */
{ "admins", "", STRING, "2.3.17" }
/* The list of userids with administrative rights. Separate each userid
with a space. Sites using Kerberos authentication may use
separate "admin" instances.
.PP
Note that accounts used by users should not be administrators.
Administrative accounts should not receive mail. That is, if user
"jbRo" is a user reading mail, he should not also be in the admins line.
Some problems may occur otherwise, most notably the ability of
administrators to create top-level mailboxes visible to users,
but not writable by users. */
{ "afspts_localrealms", NULL, STRING, "2.3.17" }
/* The list of realms which are to be treated as local, and thus stripped
during identifier canonicalization (for the AFSPTS ptloader module).
This is different from loginrealms in that it occurs later in the
authorization process (as the user id is canonified for PTS lookup) */
{ "afspts_mycell", NULL, STRING, "2.3.17" }
/* Cell to use for AFS PTS lookups. Defaults to the local cell. */
{ "allowallsubscribe", 0, SWITCH, "2.3.17" }
/* Allow subscription to nonexistent mailboxes. This option is
typically used on backend servers in a Murder so that users can
subscribe to mailboxes that don't reside on their "home" server.
This option can also be used as a workaround for IMAP clients which
don't play well with nonexistent or unselectable mailboxes (e.g.,
Microsoft Outlook). */
{ "allowanonymouslogin", 0, SWITCH, "2.3.17" }
/* Permit logins by the user "anonymous" using any password. Also
allows use of the SASL ANONYMOUS mechanism. */
{ "allowapop", 1, SWITCH, "2.3.17" }
/* Allow use of the POP3 APOP authentication command.
.PP
Note that this command requires that SASL is compiled with APOP
support, that the plaintext passwords are available in a SASL auxprop
backend (e.g., sasldb), and that the system can provide enough entropy
(e.g., from /dev/urandom) to create a challenge in the banner. */
{ "allowdeleted", 0, SWITCH, "3.1.8" }
/* Allow access to deleted and expunged data via vendor.cmu-* access */
{ "allownewnews", 0, SWITCH, "2.3.17" }
/* Allow use of the NNTP NEWNEWS command.
.PP
Note that this is a very expensive command and should only be
enabled when absolutely necessary. */
{ "allowplaintext", 0, SWITCH, "3.0.0" }
/* If enabled, allows the use of cleartext passwords on the wire.
.PP
By default, the use of cleartext passwords requires a TLS/SSL
encryption layer to be negotiated prior to any cleartext
authentication mechanisms being advertised or allowed. To require a
TLS/SSL encryption layer to be negotiated prior to ANY
authentication, see the \fItls_required\fR option. */
{ "allowsetacl", 1, SWITCH, "3.1.8" }
/* Defaults to enabled. If disabled, disallows the use of the SETACL
command at all via IMAP. */
{ "allowusermoves", 0, SWITCH, "2.3.17" }
/* Allow moving user accounts (with associated meta-data) via RENAME
or XFER.
.PP
Note that measures should be taken to make sure that the user being
moved is not logged in, and cannot login during the move. Failure
to do so may result in the user's meta-data (seen state,
subscriptions, etc) being corrupted or out of date. */
{ "altnamespace", 1, SWITCH, "3.0.0" }
/* Use the alternate IMAP namespace, where personal folders reside at the
same level in the hierarchy as INBOX.
.PP
This option ONLY applies where interaction takes place with the
client/user. Currently this is limited to the IMAP protocol (imapd)
and Sieve scripts (lmtpd). This option does NOT apply to admin tools
such as cyradm (admins ONLY), reconstruct, quota, etc., NOR does it
affect LMTP delivery of messages directly to mailboxes via
plus-addressing. The default changed in 3.0 from off to on. */
{ "altprefix", "Alt Folders", STRING, "3.0.0" }
/* Alternative INBOX spellings that can't be accessed in altnamespace
otherwise go under here */
{ "annotation_db", "twoskip", STRINGLIST("skiplist", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for mailbox annotations. */
{ "annotation_db_path", NULL, STRING, "2.5.0" }
/* The absolute path to the annotations db file. If not specified,
will be configdirectory/annotations.db */
{ "anyoneuseracl", 1, SWITCH, "2.3.17" }
/* Should non-admin users be allowed to set ACLs for the 'anyone'
user on their mailboxes? In a large organization this can cause
support problems, but it's enabled by default. */
{ "annotation_allow_undefined", 0, SWITCH, "2.5.0" }
/* Allow clients to store values for entries which are not
defined either by Cyrus or in the annotations_definitions
file. */
{ "annotation_definitions", NULL, STRING, "2.5.0" }
/* File containing external (third-party) annotation definitions.
.PP
Each line of the file specifies the properties of an annotation and
has the following form:
.IP
\fIname\fR, \fIscope\fR, \fIattrib-type\fR, \fIproxy-type\fR,
\fIattrib-names\fR, \fIacl\fR
.\"
.IP \fIname\fR 5
is the hierarchical name as in RFC 5257 or RFC 5464 (in the latter case,
without the leading \fB/shared\fR or \fB/private\fR). For example,
/vendor/acme/blurdybloop.
.\"
.IP \fIscope\fR 5
specifies whether the annotation is for the \fBserver\fR, a
\fBmailbox\fR, or a \fBmessage\fR.
.\"
.IP \fIattrib-type\fR 5
.RS 5
specifies the attribute data type, which is used only to check the
string value passed by clients when setting annotations. The
\fIattrib-type\fR is one of:
.\"
.IP \fBstring\fR 5
any value is accepted.
.\"
.IP \fBcontent-type\fR 5
this obsolete data type, which was useful for early drafts of the standard,
is accepted but silently translated to \fBstring\fR.
.\"
.IP \fBboolean\fR 5
only the strings "true" or "false" are accepted. Checking is
case-insensitive but the value is forced to lowercase.
.\"
.IP \fBint\fR 5
integers are accepted.
.\"
.IP \fBuint\fR 5
non-negative integers are accepted.
.\"
.RE
.\"
.IP \fIproxy-type\fR 5
specifies whether this attribute is for the \fBbackend\fR or
\fBproxy\fR servers or both (\fBproxy_and_backend\fR)
.\"
.IP \fIattrib-names\fR 5
is the space-separated list of available attributes for the
annotation. Possible attribute names are \fBvalue.shared\fR,
\fBvalue.priv\fR, and \fBvalue\fR (which permits both \fBvalue.priv\fR
and \fBvalue.shared\fR). The attribute names \fBsize\fR,
\fBsize.shared\fR, and \fBsize.priv\fR are accepted but ignored; these
attributes are automatically provided by the server if the corresponding
\fBvalue\fR attribute is specified. Some obsolete attributes, which were
defined early drafts of the standard, are accepted and ignored with a
warning.
.\"
.IP \fIextra-permissions\fR 5
is the extra ACL permission bits required for setting this annotation, in
standard IMAP ACL permission bit string format. Note that this is
in addition to the permission bits specified in RFC 5257 and RFC 5464,
so leaving this field empty is harmless. Note also that there is no way
to specify that an annotation can only be set by an admin user; in
particular the \fBa\fP permission bit does not achieve this.
.PP
Blank lines and lines beginning with ``#'' are ignored.
*/
{ "annotation_callout", NULL, STRING, "2.5.0" }
/* The pathname of a callout to be used to automatically add annotations
or flags to a message when it is appended to a mailbox. The path can
be either an executable (including a script), or a UNIX domain
socket. */
{ "annotation_callout_disable_append", 0, SWITCH, "3.1.2" }
/* Disables annotations on append with xrunannotator */
{ "annotation_enable_legacy_commands", 0, SWITCH, "3.1.6" }
/* Whether to enable the legacy GETANNOTATION/SETANNOTATION commands.
These commands are deprecated and will be removed in the future,
but might be useful in the meantime for supporting old clients that
do not implement the RFC 5464 IMAP METADATA extension. */
{ "aps_topic", NULL, STRING, "3.0.0" }
/* Topic for Apple Push Service registration. */
{ "aps_topic_caldav", NULL, STRING, "3.0.0" }
/* Topic for Apple Push Service registration for CalDAV. */
{ "aps_topic_carddav", NULL, STRING, "3.0.0" }
/* Topic for Apple Push Service registration for CardDAV. */
{ "archive_enabled", 0, SWITCH, "3.0.0" }
/* Is archiving enabled for this server. You also need to have an
archivepartition for the mailbox. Archiving allows older email
to be stored on slower, cheaper disks - even within the same
mailbox, as distinct from partitions. */
{ "archive_days", NULL, DURATION, "3.1.8", "3.1.8", "archive_after" }
/* Deprecated in favour of \fIarchive_after\fR. */
{ "archive_after", "7d", DURATION, "3.1.8" }
/* The duration after which to move messages to the archive partition
if archiving is enabled.
.PP
For backward compatibility, if no unit is specified, days is
assumed. */
*/
{ "archive_maxsize", 1024, INT, "3.0.0" }
/* The size in kilobytes of the largest message that won't be archived
immediately. Default is 1Mb */
{ "archive_keepflagged", 0, SWITCH, "3.0.0" }
/* If set, messages with the \\Flagged system flag won't be archived,
provided they are smaller than \fBarchive_maxsize\fR. */
# Commented out - there's no such thing as "archivepartition-name",
# but we need this for the man page
# { "archivepartition-name", NULL, STRING, "3.0.0" }
/* The pathname of the archive partition \fIname\fR, corresponding to
spool partition \fBpartition-name\fR. For any mailbox residing in
a directory on \fBpartition-name\fR, the archived messages will be
stored in a corresponding directory on \fBarchivepartition-name\fR.
Note that not every \fBpartition-name\fR option is strictly required
to have a corresponding \fBarchivepartition-name\fR option, but that
without one there's no benefit to enabling archiving. */
{ "auditlog", 0, SWITCH, "2.4.0" }
/* Should cyrus output log entries for every action taken on a message
file or mailboxes list entry? It's noisy so disabled by default, but
can be very useful for tracking down what happened if things look strange */
{ "auth_mech", "unix", STRINGLIST("unix", "pts", "krb", "krb5"), "2.3.17" }
/* The authorization mechanism to use. */
{ "autocreateinboxfolders", NULL, STRING, "2.5.0", "2.5.0", "autocreate_inbox_folders" }
/* Deprecated in favor of \fIautocreate_inbox_folders\fR. */
{ "autocreatequota", 0, INT, "2.5.0", "2.5.0", "autocreate_quota" }
/* Deprecated in favor of \fIautocreate_quota\fR. */
{ "autocreatequotamsg", -1, INT, "2.5.0", "2.5.0", "autocreate_quota_messages" }
/* Deprecated in favor of \fIautocreate_quota_messages\fR. */
{ "autosievefolders", NULL, STRING, "2.5.0", "2.5.0", "autocreate_sieve_folders" }
/* Deprecated in favor of \fIautocreate_sieve_folders\fR. */
{ "generate_compiled_sieve_script", 0, SWITCH, "2.5.0", "2.5.0", "autocreate_sieve_script_compile" }
/* Deprecated in favor of \fIautocreate_sieve_script_compile\fR. */
{ "autocreate_sieve_compiled_script", NULL, STRING, "2.5.0", "2.5.0", "autocreate_sieve_script_compiled" }
/* Deprecated in favor of \fIautocreate_sieve_script_compiled\fR. */
{ "autosubscribeinboxfolders", NULL, STRING, "2.5.0", "2.5.0", "autocreate_subscribe_folders" }
/* Deprecated in favor of \fIautocreate_subscribe_folders\fR. */
{ "autosubscribesharedfolders", NULL, STRING, "2.5.0", "2.5.0", "autocreate_subscribe_sharedfolders" }
/* Deprecated in favor of \fIautocreate_subscribe_sharedfolders\fR. */
{ "autosubscribe_all_sharedfolders", 0, SWITCH, "2.5.0", "2.5.0", "autocreate_subscribe_sharedfolders_all" }
/* Deprecated in favor of \fIautocreate_subscribe_sharedfolders_all\fR. */
# Commented out - there's no single setting "autocreate_acl",
# but we need this for the man page
# { "autocreate_acl", NULL, STRING, "3.2.0" }
/* If folders are to be created by \fIautocreate_inbox_folders\fR, this
setting can be used to apply additional ACLs to the autocreated
folders. The syntax is "autocreate_acl folder identifier rights",
where \fIfolder\fR must match one of the \fIautocreate_inbox_folders\fR
folders, \fIidentifier\fR must be a valid cyrus identifier, and
\fIrights\fR must be a valid cyrus rights string. Multiple
identifier|rights pairs can be assigned to a single folder by providing
this setting multiple times.
.PP
For example, "autocreate_acl Plus anyone p" would allow lmtp delivery
to a folder named "Plus".
*/
{ "autocreate_inbox_folders", NULL, STRING, "2.5.0" }
/* If a user does not have an INBOX already, and the INBOX is to be
created, create the list of folders in this setting as well.
\fIautocreate_inbox_folders\fR is a list of INBOX's subfolders
separated by a "|", that are automatically created by the server
under the following two scenarios. Leading and trailing whitespace is
stripped, so "Junk | Trash" results in two folders: "Junk" and
"Trash". See also the \fIxlist-flag\fR option, for setting
special-use flags on autocreated folders.
.PP
INBOX folders are created under both the following conditions:
.\"
.IP 1.
The user logins via the IMAP or the POP3 protocol.
\fIautocreate_quota\fR option must have a value of zero or greater.
.\"
.IP 2.
A message arrives for the user through the \fIlmtpd(8)\fR.
\fIautocreate_post\fR option must be enabled.
.PP
*/
{ "autocreate_post", 0, SWITCH, "2.5.0" }
/* If enabled, when \fIlmtpd(8)\fR receives an incoming mail for an
INBOX that does not exist, then the INBOX is automatically created
by \fIlmtpd(8)\fR and delivery of the message continues. */
{ "autocreate_quota", -1, INT, "2.5.0" }
/* If set to a value of zero or higher, users have their INBOX folders
created upon a successful login event or upon \fIlmtpd(8)\fR
message delivery if \fIautocreate_post\fR is enabled, provided their
INBOX did not yet already exist.
.PP
The user's quota is set to the value if it is greater than zero,
otherwise the user has unlimited quota.
.PP
Note that quota is specified in kilobytes. */
{ "autocreate_quota_messages", -1, INT, "3.0.0" }
/* If set to a value of zero or higher, users who have their INBOX
folders created upon a successful login event (see
\fIautocreate_quota\fR), or upon \fIlmtpd(8)\fR message delivery if
\fIautocreate_post\fR is enabled, receive the message quota
configured in this option.
.PP
The default of -1 disables assigning message quota.
.PP
For consistency with \fIautocreate_quota\fR, a value of zero is treated
as unlimited message quota, rather than a message quota of zero. */
{ "autocreate_sieve_folders", NULL, STRING, "2.5.0" }
/* A "|" separated list of subfolders of INBOX that will be
automatically created, if requested by a sieve filter, through the
"fileinto" action. The default is to create no folders
automatically.
.PP
Leading and trailing whitespace is stripped from each folder, so a
setting of "Junk | Trash" will create two folders: "Junk" and
"Trash". */
{ "autocreate_sieve_script", NULL, STRING, "2.5.0" }
/* The full path of a file that contains a sieve script. This script
automatically becomes a user's initial default sieve filter script.
.PP
When this option is not defined, no default sieve filter is created.
The file must be readable by the Cyrus daemon. */
{ "autocreate_sieve_script_compile", 0, SWITCH, "2.5.0" }
/* If set to yes and no compiled sieve script file exists, the sieve script which is
compiled on the fly will be saved in the file name that autocreate_sieve_compiledscript
option points to. In order a compiled script to be generated, autocreate_sieve_script and
autocreate_sieve_compiledscript must have valid values */
{ "autocreate_sieve_script_compiled", NULL, STRING, "2.5.0" }
/* The full path of a file that contains a compiled in bytecode sieve script. This script
automatically becomes a user's initial default sieve filter script. If this option is
not specified, or the filename doesn't exist then the script defined by
autocreate_sieve_script is compiled on the fly and installed as the user's default
sieve script */
{ "autocreate_subscribe_folders", NULL, STRING, "2.5.0" }
/* A list of folder names, separated by "|", that the users get automatically subscribed to,
when their INBOX is created. These folder names must have been included in the
autocreateinboxfolders option of the imapd.conf. */
{ "autocreate_subscribe_sharedfolders", NULL, STRING, "2.5.0" }
/* A list of shared folders (bulletin boards), separated by "|", that the users get
automatically subscribed to, after their INBOX is created. The shared folder must
have been created and the user must have the required permissions to get subscribed
to it. Otherwise, subscribing to the shared folder fails. */
{ "autocreate_subscribe_sharedfolders_all", 0, SWITCH, "2.5.0" }
/* If set to yes, the user is automatically subscribed to all shared folders, one has permission
to subscribe to. */
{ "autocreate_users", "anyone", STRING, "2.5.0" }
/* A space separated list of users and/or groups that are allowed their INBOX to be
automatically created. */
{ "autoexpunge", 0, SWITCH, "3.1.7" }
/* If set to yes, then all \Deleted messages will be automatically expunged whenever
an index is closed, whether CLOSE, UNSELECT, SELECT or on disconnect */
# Commented out - there's no such thing as "backuppartition-name",
# but we need this for the man page
# { "backuppartition-name", NULL, STRING, "3.0.0" }
/* The pathname of the backup partition \fIname\fR. At least one backup
partition pathname MUST be specified if backups are in use. Note that
there is no relationship between spool partitions and backup partitions. */
{ "backup_compact_minsize", 0, INT, "3.0.0" }
/* The minimum size in kilobytes of chunks in each backup. The compact tool
will try to combine adjacent chunks that are smaller than this.
.PP
Setting this value to zero or negative disables combining of chunks. */
{ "backup_compact_maxsize", 0, INT, "3.0.0" }
/* The maximum size in kilobytes of chunks in each backup. The compact tool
will try to split chunks larger than this into smaller chunks.
.PP
Setting this value to zero or negative disables splitting of chunks. */
{ "backup_compact_work_threshold", 1, INT, "3.0.0" }
/* The number of chunks that must obviously need compaction before the compact
tool will go ahead with the compaction. If set to less than one, the value
is treated as being one. */
{ "backup_staging_path", NULL, STRING, "3.0.0" }
/* The absolute path of the backup staging area. If not specified,
will be temp_path/backup */
{ "backup_retention_days", NULL, DURATION, "3.1.8", "3.1.8", "backup_retention" }
/* Deprecated in favor of \fIbackup_retention\fR. */
{ "backup_retention", "7d", DURATION, "3.1.0" }
/* How long to keep content in backup after it has been deleted
from the source. If set to a negative value or zero, deleted content
will be kept indefinitely.
.PP
For backward compatibility, if no unit is specified, days is
assumed. */
{ "backup_db", "twoskip", STRINGLIST("skiplist", "sql", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for the backup locations database. */
{ "backup_db_path", NULL, STRING, "3.0.0" }
/* The absolute path to the backup db file. If not specified,
will be configdirectory/backups.db */
{ "backup_keep_previous", 0, SWITCH, "3.0.0" }
/* Whether the \fBctl_backups compact\fR and \fBctl_backups reindex\fR
commands should preserve the original file. The original file will
be named with a timestamped suffix. This is mostly useful for
debugging.
.PP
Note that with this enabled, compacting a backup will actually
increase the disk used by it (because there will now be an extra
copy: the original version, and the compacted version). */
{ "boundary_limit", 1000, INT, "2.5.0" }
/* messages are parsed recursively and a deep enough MIME structure
can cause a stack overflow. Do not parse deeper than this many
layers of MIME structure. The default of 1000 is much higher
than any sane message should have. */
{ "caldav_allowattach", 1, SWITCH, "3.0.0" }
/* Enable managed attachments support on the CalDAV server. */
{ "caldav_allowcalendaradmin", 0, SWITCH, "3.1.2" }
/* Enable per-user calendar administration web UI on the CalDAV server. */
{ "caldav_allowscheduling", "on", ENUM("off", "on", "apple"), "2.5.0" }
/* Enable calendar scheduling operations. If set to "apple", the
server will emulate Apple CalendarServer behavior as closely as
possible. */
{ "caldav_create_attach", 1, SWITCH, "3.0.0" }
/* Create the 'Attachments' collection if it doesn't already exist */
{ "caldav_create_default", 1, SWITCH, "3.0.0" }
/* Create the 'Default' calendar if it doesn't already exist */
{ "caldav_create_sched", 1, SWITCH, "3.0.0" }
/* Create the 'Inbox' and 'Outbox' calendars if they don't already exist */
{ "caldav_historical_age", "7d", DURATION, "3.1.8" }
/* How long after an occurrence of event or task has concluded
that it is considered 'historical'. Changes to historical
occurrences of events or tasks WILL NOT have invite or reply
messages sent for them. A negative value means that events
and tasks are NEVER considered historical.
.PP
For backward compatibility, if no unit is specified, days is
assumed. */
*/
{ "caldav_maxdatetime", "20380119T031407Z", STRING, "2.5.0" }
/* The latest date and time accepted by the server (ISO format). This
value is also used for expanding non-terminating recurrence rules.
.PP
Note that increasing this value will require the DAV databases for
calendars to be reconstructed with the \fBdav_reconstruct\fR
utility in order to see its effect on serer-side time-based
queries. */
{ "caldav_mindatetime", "19011213T204552Z", STRING, "2.5.0" }
/* The earliest date and time accepted by the server (ISO format). */
{ "caldav_realm", NULL, STRING, "2.5.0" }
/* The realm to present for HTTP authentication of CalDAV resources.
If not set (the default), the value of the "servername" option will
be used.*/
{ "calendarprefix", "#calendars", STRING, "2.5.0" }
/* The prefix for the calendar mailboxes hierarchies. The hierarchy
delimiter will be automatically appended. The public calendar
hierarchy will be at the toplevel of the shared namespace. A
user's personal calendar hierarchy will be a child of their Inbox. */
{ "calendar_default_displayname", "personal", STRING, "3.3.0" }
/* The displayname to be used when creating a user's 'Default' calendar. */
{ "calendar_user_address_set", NULL, STRING, "2.5.0" }
/* Space-separated list of domains corresponding to calendar user
addresses for which the server is responsible. If not set (the
default), the value of the "servername" option will be used. */
{ "calendar_component_set", "VEVENT VTODO VJOURNAL VFREEBUSY VAVAILABILITY VPOLL", BITFIELD("VEVENT", "VTODO", "VJOURNAL", "VFREEBUSY", "VAVAILABILITY", "VPOLL"), "3.1.7" }
/* Space-separated list of iCalendar component types that calendar
object resources may contain in a calendar collection.
This restriction is only set at calendar creation time and only
if the CalDAV client hasn't specified a restriction in the creation
request. */
{ "carddav_allowaddmember", 0, SWITCH, "3.1.3" }
/* Enable support for POST add-member on the CardDAV server. */
{ "carddav_allowaddressbookadmin", 0, SWITCH, "3.1.2" }
/* Enable per-user addressbook administration web UI on the CardDAV server. */
{ "carddav_realm", NULL, STRING, "2.5.0" }
/* The realm to present for HTTP authentication of CardDAV resources.
If not set (the default), the value of the "servername" option will
be used.*/
{ "carddav_repair_vcard", 0, SWITCH, "3.0.0", "3.3.1" }
/* If enabled, VCARDs with invalid content are attempted to be repaired
during creation. */
{ "chatty", 0, SWITCH, "2.5.0" }
/* If yes, syslog tags and commands for every IMAP command, mailboxes
for every lmtp connection, every POP3 command, etc */
{ "client_bind", 0, SWITCH, "3.0.0" }
/* If enabled, a specific IP will be bound when performing a client
connection. \fBclient_bind_name\fR is used if it is set, otherwise
\fBservername\fR is used. This is useful on multi-homed servers where
Cyrus should not use other services' interfaces.
.PP
If not enabled (the default), no bind will be performed. Client
connections will use an IP chosen by the operating system. */
{ "client_bind_name", NULL, STRING, "3.0.0" }
/* IPv4, IPv6 address or hostname to bind for client connections when
\fBclient_bind\fR is enabled. If not set (the default),
\fRservername\fR will be used. */
{ "client_timeout", "10s", DURATION, "3.1.8" }
/* Time to wait before returning a timeout failure when performing a
client connection (e.g. in a murder environment).
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "commandmintimer", NULL, STRING, "2.4.0" }
/* Time in seconds. Any imap command that takes longer than this
time is logged. */
{ "configdirectory", NULL, STRING, "2.3.17" }
/* The pathname of the IMAP configuration directory. This field is
required. */
{ "createonpost", 0, SWITCH, "2.5.0", "2.5.0", "autocreate_post" }
/* Deprecated in favor of \fIautocreate_post\fR. */
{ "conversations", 0, SWITCH, "3.0.0" }
/* Enable the XCONVERSATIONS extensions. Extract conversation
tracking information from incoming messages and track them
in per-user databases. */
{ "conversations_counted_flags", NULL, STRING, "3.0.0" }
/* space-separated list of flags for which per-conversation counts
will be kept. Note that you need to reconstruct the conversations
database with ctl_conversationsdb if you change this option on a
running server, or the counts will be wrong. */
{ "conversations_db", "skiplist", STRINGLIST("skiplist", "sql", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for the per-user conversations database. */
{ "conversations_expire_days", NULL, DURATION, "3.1.8", "3.1.8", "conversations_expire_after" }
/* Deprecated in favor of \fIconversations_expire_after\fR. */
{ "conversations_expire_after", "90d", DURATION, "3.1.8" }
/* How long the conversations database keeps the message tracking
information needed for receiving new messages in existing
conversations.
.PP
For backward compatibility, if no unit is specified, days is
assumed. */
{ "conversations_keep_existing", 1, SWITCH, "3.3.0" }
/* during conversations cleanup, don't clean up if there are still existing emails
with one of the mentioned CIDs */
{ "conversations_max_thread", 100, INT, "3.1.1" }
/* maximum size for a single thread. Threads will split if they have this many
messages in them and another message arrives */
{ "conversations_max_guidrecords", 5000, INT, "3.3.0" }
/* maximum records with the same guid. This is just a sanity check to stop the same
email being added and removed over and over, so the default is 5000 */
{ "conversations_max_guidexists", 100, INT, "3.3.0" }
/* maximum records with the same guid. This maps to "labels", so with the default
of 100, you can only have 100 labels on an email in JMAP */
{ "conversations_max_guidinfolder", 10, INT, "3.3.0" }
/* maximum records with the same guid in the same folder. You can't do this via JMAP,
but could via IMAP. The default of 10 should be heaps normally! */
{ "crossdomains", 0, SWITCH, "3.0.0" }
/* Enable cross domain sharing. This works best with alt namespace and
unix hierarchy separators on, so you get Other Users/foo@example.com/... */
{ "crossdomains_onlyother", 0, SWITCH, "3.0.0" }
/* only show the domain for users in other domains than your own (for
backwards compatibility if you're already sharing */
{ "cyrus_group", NULL, STRING, "3.1.7" }
/* The name of the group Cyrus services will run as. If not configured, the
primary group of cyrus_user will be used. Can be further overridden by
setting the $CYRUS_GROUP environment variable. */
{ "cyrus_user", NULL, STRING, "3.0.0" }
/* The username to use as the 'cyrus' user. If not configured, the compile
time default will be used. Can be further overridden by setting the
$CYRUS_USER environment variable. */
{ "davdriveprefix", "#drive", STRING, "3.0.0" }
/* The prefix for the DAV storage mailboxes hierarchies. The hierarchy
delimiter will be automatically appended. The public storage
hierarchy will be at the toplevel of the shared namespace. A
user's personal storage hierarchy will be a child of their Inbox. */
{ "davnotificationsprefix", "#notifications", STRING, "3.0.0" }
/* The prefix for the DAV notifications hierarchy. The hierarchy
delimiter will be automatically appended. The public notifications
hierarchy will be at the toplevel of the shared namespace. A
user's personal notifications hierarchy will be a child of their Inbox. */
{ "dav_realm", NULL, STRING, "2.5.0" }
/* The realm to present for HTTP authentication of generic DAV
resources (principals). If not set (the default), the value of the
"servername" option will be used.*/
{ "dav_lock_timeout", "20s", DURATION, "3.1.8" }
/* The maximum time to wait for a write lock on the per-user DAV database
before timeout. For HTTP requests, the HTTP status code 503 is returned
if the lock can not be obtained within this time.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "debug", 0, SWITCH, "2.5.0" }
/* If enabled, allow syslog() to pass LOG_DEBUG messages. */
{ "debug_command", NULL, STRING, "2.3.17" }
/* Debug command to be used by processes started with -D option. The string
is a C format string that gets 3 options: the first is the name of the
executable (as specified in the cmd parameter in cyrus.conf). The second
is the pid (integer) and the third is the service ID.
Example: /usr/local/bin/gdb /usr/cyrus/bin/%s %d */
{ "debug_writefail_guid", NULL, STRING, "UNRELEASED" }
/* If set, any arriving message with this guid will fail as if the underlying
disk write had failed, pretending to be a disk full condition. This is
mainly useful for regression testing certain edge case handling.
Currently only implemented for replication uploads. */
{ "defaultacl", "anyone lrs", STRING, "2.3.17" }
/* The Access Control List (ACL) placed on a newly-created (non-user)
mailbox that does not have a parent mailbox. */
{ "defaultdomain", "internal", STRING, "3.0.0" }
/* The default domain for virtual domain support */
{ "defaultpartition", NULL, STRING, "2.3.17" }
/* The partition name used by default for new mailboxes. If not
specified, the partition with the most free space will be used for
new mailboxes.
.PP
Note that the partition specified by this option must also be
specified as \fIpartition-name\fR, where you substitute 'name'
for the alphanumeric string you set \fIdefaultpartition\fR to. */
{ "defaultsearchtier", "", STRING, "3.0.0" }
/* Name of the default tier that messages will be indexed to. Search
indexes can be organized in tiers to allow index storage in different
directories and physical media. See the man page of squatter for
details. The default search tier also requires the definition
of an according \fIsearchtierpartition-name\fR entry.
.PP
This option MUST be specified for xapian search. */
{ "defaultserver", NULL, STRING, "2.3.17" }
/* The backend server name used by default for new mailboxes. If not
specified, the server with the most free space will be used for new
mailboxes. */
{ "deletedprefix", "DELETED", STRING, "2.3.17" }
/* With \fBdelete_mode\fR set to \fIdelayed\fR, the
\fBdeletedprefix\fR setting defines the prefix for the hierarchy of
deleted mailboxes.
.PP
The hierarchy delimiter will be automatically appended.
*/
{ "delete_mode", "delayed", ENUM("immediate", "delayed"), "2.5.0" }
/* The manner in which mailboxes are deleted. In the default
\fIdelayed\fR mode, mailboxes that are being deleted are renamed to
a special mailbox hierarchy under the \fBdeletedprefix\fR, to be
removed later by \fBcyr_expire(8)\fR.
.PP
In \fIimmediate\fR mode, the mailbox is removed from the filesystem
immediately.
*/
{ "delete_unsubscribe", 0, SWITCH, "3.0.0" }
/* Whether to also unsubscribe from mailboxes when they are deleted.
Note that this behaviour contravenes RFC 3501 section 6.3.9, but
may be useful for avoiding user/client software confusion.
The default is 'no'. */
{ "deleteright", "c", STRING, "2.3.17" }
/* Deprecated - only used for backwards compatibility with existing
installations. Lists the old RFC 2086 right which was used to
grant the user the ability to delete a mailbox. If a user has this
right, they will automatically be given the new 'x' right. */
{ "disable_user_namespace", 0, SWITCH, "2.5.0" }
/* Preclude list command on user namespace. If set to 'yes', the
LIST response will never include any other user's mailbox. Admin
users will always see all mailboxes. The default is 'no' */
{ "disable_shared_namespace", 0, SWITCH, "2.5.0" }
/* Preclude list command on shared namespace. If set to 'yes', the
LIST response will never include any non-user mailboxes. Admin
users will always see all mailboxes. The default is 'no' */
{ "disconnect_on_vanished_mailbox", 0, SWITCH, "2.3.17" }
/* If enabled, IMAP/POP3/NNTP clients will be disconnected by the
server if the currently selected mailbox is (re)moved by another
session. Otherwise, the missing mailbox is treated as empty while
in use by the client.*/
{ "ischedule_dkim_domain", NULL, STRING, "2.5.0" }
/* The domain to be reported as doing iSchedule DKIM signing. */
{ "ischedule_dkim_key_file", NULL, STRING, "2.5.0" }
/* File containing the private key for iSchedule DKIM signing. */
{ "ischedule_dkim_required", 1, SWITCH, "3.1.4" }
/* A DKIM signature is required on received iSchedule requests. */
{ "ischedule_dkim_selector", NULL, STRING, "2.5.0" }
/* Name of the selector subdividing the domain namespace. This
specifies the actual key used for iSchedule DKIM signing within the
domain. */
{ "duplicate_db", "twoskip", STRINGLIST("skiplist", "sql", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for the duplicate delivery suppression
and sieve. */
{ "duplicate_db_path", NULL, STRING, "2.5.0" }
/* The absolute path to the duplicate db file. If not specified,
will be configdirectory/deliver.db */
{ "duplicatesuppression", 1, SWITCH, "2.3.17" }
/* If enabled, lmtpd will suppress delivery of a message to a mailbox if
a message with the same message-id (or resent-message-id) is recorded
as having already been delivered to the mailbox. Records the mailbox
and message-id/resent-message-id of all successful deliveries. */
{ "event_content_inclusion_mode", "standard", ENUM("standard", "message", "header", "body", "headerbody"), "2.5.0" }
/* The mode in which message content may be included with MessageAppend and
MessageNew. "standard" mode is the default behavior in which message is
included up to a size with the notification. In "message" mode, the message
is included and may be truncated to a size. In "header" mode, it includes
headers truncated to a size. In "body" mode, it includes body truncated
to a size. In "headerbody" mode, it includes full headers and body truncated
to a size */
{ "event_content_size", 0, INT, "2.5.0" }
/* Truncate the message content that may be included with MessageAppend and
MessageNew. Set 0 to include the entire message itself */
{ "event_exclude_flags", NULL, STRING, "2.5.0" }
/* Don't send event notification for given IMAP flag(s) */
{ "event_exclude_specialuse", "\\Junk", STRING, "2.5.0" }
/* Don't send event notification for folder with given special-use attributes.
Set ALL for any folder */
{ "event_extra_params", "timestamp", BITFIELD("bodyStructure", "clientAddress", "diskUsed", "flagNames", "messageContent", "messageSize", "messages", "modseq", "service", "timestamp", "uidnext", "vnd.cmu.midset", "vnd.cmu.unseenMessages", "vnd.cmu.envelope", "vnd.cmu.sessionId", "vnd.cmu.mailboxACL", "vnd.cmu.mbtype", "vnd.cmu.davFilename", "vnd.cmu.davUid", "vnd.fastmail.clientId", "vnd.fastmail.sessionId", "vnd.fastmail.convExists", "vnd.fastmail.convUnseen", "vnd.fastmail.cid", "vnd.fastmail.counters", "vnd.fastmail.jmapEmail", "vnd.fastmail.jmapStates", "vnd.cmu.emailid", "vnd.cmu.threadid"), "3.1.6" }
/* Space-separated list of extra parameters to add to any appropriated event. */
{ "event_groups", "message mailbox", BITFIELD("message", "quota", "flags", "access", "mailbox", "subscription", "calendar", "applepushservice" ), "3.0.0" }
/* Space-separated list of groups of related events to turn on notification */
{ "event_notifier", NULL, STRING, "2.5.0" }
/* Notifyd(8) method to use for "EVENT" notifications which are based on
the RFC 5423. If not set, "EVENT" notifications are disabled. */
{ "expunge_mode", "delayed", ENUM("immediate", "semidelayed", "delayed"), "3.1.1" }
/* The mode in which messages (and their corresponding cache entries)
are expunged. "semidelayed" mode is the old behavior in which the
message files are purged at the time of the EXPUNGE, but index
and cache records are retained to facilitate QRESYNC.
In "delayed" mode, which is the default since Cyrus 2.5.0,
the message files are also retained, allowing unexpunge to
rescue them. In "immediate" mode, both the message files and the
index records are removed as soon as possible. In all cases,
nothing will be finally purged until all other processes have
closed the mailbox to ensure they never see data disappear under
them. In "semidelayed" or "delayed" mode, a later run of "cyr_expire"
will clean out the retained records (and possibly message files).
This reduces the amount of I/O that takes place at the time of
EXPUNGE and should result in greater responsiveness for the client,
especially when expunging a large number of messages. */
{ "failedloginpause", "3s", DURATION, "3.1.8" }
/* Time to pause after a failed login.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "flushseenstate", 1, SWITCH, "2.5.0", "2.5.0" }
/* Deprecated. No longer used */
{ "foolstupidclients", 0, SWITCH, "2.3.17" }
/* If enabled, only list the personal namespace when a LIST "*" is performed
(it changes the request to a LIST "INBOX*"). */
{ "force_sasl_client_mech", NULL, STRING, "2.3.17" }
/* Force preference of a given SASL mechanism for client side operations
(e.g., murder environments). This is separate from (and overridden by)
the ability to use the <host shortname>_mechs option to set preferred
mechanisms for a specific host */
{ "fulldirhash", 0, SWITCH, "2.3.17" }
/* If enabled, uses an improved directory hashing scheme which hashes
on the entire username instead of using just the first letter as
the hash. This changes hash algorithm used for quota and user
directories and if \fIhashimapspool\fR is enabled, the entire mail
spool.
.PP
Note that this option CANNOT be changed on a live system. The
server must be quiesced and then the directories moved with the
\fBrehash\fR utility. */
{ "hashimapspool", 0, SWITCH, "2.3.17" }
/* If enabled, the partitions will also be hashed, in addition to the
hashing done on configuration directories. This is recommended if
one partition has a very bushy mailbox tree. */
# Commented out - there's no such thing as "hostname_mechs", but we need
# this for the man page
# { "hostname_mechs", NULL, STRING, "2.3.17" }
/* Force a particular list of SASL mechanisms to be used when authenticating
to the backend server hostname (where hostname is the short hostname of
the server in question). If it is not specified it will query the server
for available mechanisms and pick one to use. - Cyrus Murder */
# Commented out - there's no such thing as "hostname_password", but we need
# this for the man page
# { "hostname_password", NULL, STRING, "2.3.17" }
/* The password to use for authentication to the backend server hostname
(where hostname is the short hostname of the server) - Cyrus Murder */
{ "httpallowcompress", 1, SWITCH, "2.5.0" }
/* If enabled, the server will compress response payloads if the client
indicates that it can accept them. Note that the compressed data
will appear in telemetry logs, leaving only the response headers as
human-readable.*/
{ "httpallowcors", NULL, STRING, "2.5.0" }
/* A wildmat pattern specifying a list of origin URIs ( scheme "://"
host [ ":" port ] ) that are allowed to make Cross-Origin Resource
Sharing (CORS) requests on the server. By default, CORS requests
are disabled.
.PP
Note that the scheme and host should both be lowercase, the port
should be omitted if using the default for the scheme (80 for http,
443 for https), and there should be no trailing '/' (e.g.:
"http://www.example.com:8080", "https://example.org"). */
{ "httpallowtrace", 0, SWITCH, "2.5.0" }
/* Allow use of the TRACE method.
.PP
Note that sensitive data might be disclosed by the response. */
{ "httpallowedurls", NULL, STRING, "2.5.0" }
/* Space-separated list of relative URLs (paths) rooted at
"httpdocroot" (see below) to be served by httpd. If set, this
option will limit served static content to only those paths specified
(returning "404 Not Found" to any other client requested URLs).
Otherwise, httpd will serve any content found in "httpdocroot".
.PP
Note that any path specified by "rss_feedlist_template" is an
exception to this rule.*/
{ "httpcontentmd5", 0, SWITCH, "2.5.0" }
/* If enabled, HTTP responses will include a Content-MD5 header for
the purpose of providing an end-to-end message integrity check
(MIC) of the payload body. Note that enabling this option will
use additional CPU to generate the MD5 digest, which may be ignored
by clients anyways. */
{ "httpdocroot", NULL, STRING, "2.5.0" }
/* If set, http will serve the static content (html/text/jpeg/gif
files, etc) rooted at this directory. Otherwise, httpd will not
serve any static content. */
{ "httpkeepalive", "20s", DURATION, "3.1.8" }
/* Set the length of the HTTP server's keepalive heartbeat. The
default is 20 seconds. The minimum value is 0, which will disable
the keepalive heartbeat. When enabled, if a request takes longer
than \fIhttpkeepalive\fR to process, the server will send the client
provisional responses every \fIhttpkeepalive\fR until the final
response can be sent.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "httplogheaders", NULL, STRING, "3.3.1" }
/* Space-separated list of HTTP header fields that will be included
in the requests logged by httpd(8). */
{ "httpmodules", "", BITFIELD("admin", "caldav", "carddav", "cgi", "domainkey", "freebusy", "ischedule", "jmap", "prometheus", "rss", "tzdist", "webdav"), "3.1.7" }
/* Space-separated list of HTTP modules that will be enabled in
httpd(8). This option has no effect on modules that are disabled
at compile time due to missing dependencies (e.g. libical).
.PP
Note that "domainkey" depends on "ischedule" being enabled, and
that both "freebusy" and "ischedule" depend on "caldav" being
enabled. */
{ "httpprettytelemetry", 0, SWITCH, "2.5.0" }
/* If enabled, HTTP response payloads including server-generated
markup languages (HTML, XML) will utilize line breaks and
indentation to promote better human-readability in telemetry logs.
Note that enabling this option will increase the amount of data
sent across the wire. */
{ "httptimeout", "5m", DURATION, "3.1.8" }
/* Set the length of the HTTP server's inactivity autologout timer.
The default is 5 minutes. The minimum value is 0, which will
disable persistent connections.
.PP
For backwards compatibility, if no unit is specified, minutes
is assumed. */
{ "icalendar_max_size", 0, INT, "UNRELEASED" }
/* Maximum allowed iCalendar size.
If non-zero, CalDAV and JMAP will reject storage of resources whose
iCalendar representation is larger than \fIicalendar_max_size\fR bytes.
If set to 0, this will allow iCalendar resources of any size (the default). */
{ "idlesocket", "{configdirectory}/socket/idle", STRING, "2.3.17" }
/* Unix domain socket that idled listens on. */
{ "ignorereference", 0, SWITCH, "2.3.17" }
/* For backwards compatibility with Cyrus 1.5.10 and earlier -- ignore
the reference argument in LIST or LSUB commands. */
{ "imapidlepoll", "60s", DURATION, "3.1.8" }
/* The interval for polling for mailbox changes and ALERTs while running
the IDLE command. This option is used when idled is not enabled or
cannot be contacted. The minimum value is 1 second. A value of 0
will disable IDLE.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "imapidresponse", 1, SWITCH, "2.3.17" }
/* If enabled, the server responds to an ID command with a parameter
list containing: version, vendor, support-url, os, os-version,
command, arguments, environment. Otherwise the server returns NIL. */
{ "imapmagicplus", 0, SWITCH, "2.3.17" }
/* Only list a restricted set of mailboxes via IMAP by using
userid+namespace syntax as the authentication/authorization id.
Using userid+ (with an empty namespace) will list only subscribed
mailboxes. */
{ "imipnotifier", NULL, STRING, "3.0.0" }
/* Notifyd(8) method to use for "IMIP" notifications which are based on
the RFC 6047. If not set, "IMIP" notifications are disabled. */
{ "implicit_owner_rights", "lkxan", STRING, "3.1.2" }
/* The implicit Access Control List (ACL) for the owner of a mailbox. */
# Commented out - there's no such thing as "@include", but we need
# this for the man page
# { "@include", NULL, STRING, "2.3.17" }
/* Directive which includes the specified file as part of the
configuration. If the path to the file is not absolute, CYRUS_PATH
is prepended. */
{ "improved_mboxlist_sort", 0, SWITCH, "2.3.17" }
/* If enabled, a special comparator will be used which will correctly
sort mailbox names that contain characters such as ' ' and '-'.
.PP
Note that this option SHOULD NOT be changed on a live system. The
mailboxes database should be dumped (ctl_mboxlist) before the
option is changed, removed, and then undumped after changing the
option. When not using flat files for the subscriptions databases
the same has to be done (cyr_dbtool) for each subscription database
See improved_mboxlist_sort.html.*/
{ "jmap_emailsearch_db_path", NULL, STRING, "3.1.6", "UNRELEASED" }
/* The absolute path to the JMAP email search cache file. If not
specified, JMAP Email/query and Email/queryChanges will not
cache email search results. */
{ "jmap_querycache_max_age", "0m", DURATION, "UNRELEASED" }
/* The duration after which unused cached JMAP query results
must be evicted from process memory. If non-zero, then the
full result of the last query (before windowing) is stored
in-memory. Subsequent queries with the same expression and
query state can then page through the cached result.
A zero value disables query result caching.
This feature currently only is enabled for Email/query. */
{ "jmap_preview_annot", NULL, STRING, "3.1.1" }
/* The name of the per-message annotation, if any, to store message
previews. */
{ "jmap_imagesize_annot", NULL, STRING, "3.1.1" }
/* The name of the per-message annotation, if any, that stores a
JSON object, mapping message part numbers of MIME image types
to an array of their image dimensions. The array must have at
least two entries, where the first entry denotes the width
and the second entry the height of the image. Any additional
values are ignored.
For example, if message part 1.2 contains an image of width 300
and height 200, then the value of this annotation would be:
{ "1.2" : [ 300, 200 ] }
*/
{ "jmap_inlinedcids_annot", NULL, STRING, "3.1.1" }
/* The name of the per-message annotation, if any, that stores a
JSON object, mapping RFC 2392 Content-IDs referenced in HTML bodies
to the respective HTML body part number.
For example, if message part 1.2 contains HTML and references an
inlined image at "cid:foo", then the value of this annotation
would be:
{ "<foo>" : "1.2" }
Note that the Content-ID key must be URL-unescaped and enclosed in
angular brackets, as defined in RFC 2392. */
{ "jmap_preview_length", 64, INT, "3.1.1" }
/* The maximum byte length of dynamically generated message previews. Previews
stored in jmap_preview_annot take precedence. */
{ "jmap_max_size_upload", 1048576, INT, "3.1.6" }
/* The maximum size (in kilobytes) that the JMAP API accepts
for blob uploads. Returned as the maxSizeUpload property
value of the JMAP \"urn:ietf:params:jmap:core\" capabilities object.
Default is 1Gb. */
{ "jmap_max_size_blob_set", 4096, INT, "3.3.0" }
/* The maximum size (in kilobytes) that the JMAP API accepts
for Blob/set. Returned as the maxSizeBlobSet property
value of the JMAP \"https://cyrusimap.org/ns/jmap/blob\" capabilities object.
Default is 4Mb. */
{ "jmap_max_concurrent_upload", 5, INT, "3.1.6" }
/* The value to return for the maxConcurrentUpload property of
the JMAP \"urn:ietf:params:jmap:core\" capabilities object. The Cyrus JMAP
implementation does not enforce this rate-limit. */
{ "jmap_max_size_request", 10240, INT, "3.1.6" }
/* The maximum size (in kilobytes) that the JMAP API accepts
for requests at the API endpoint. Returned as the
maxSizeRequest property value of the JMAP \"urn:ietf:params:jmap:core\"
capabilities object. Default is 10Mb. */
{ "jmap_max_concurrent_requests", 5, INT, "3.1.6" }
/* The value to return for the maxConcurrentRequests property of
the JMAP \"urn:ietf:params:jmap:core\" capabilities object. The Cyrus JMAP
implementation does not enforce this rate-limit. */
{ "jmap_max_calls_in_request", 50, INT, "3.1.6" }
/* The maximum number of calls per JMAP request object.
Returned as the maxCallsInRequest property value of the
JMAP \"urn:ietf:params:jmap:core\" capabilities object. */
{ "jmap_max_delayed_send", "512d", DURATION, "3.1.8" }
/* The value to return for the maxDelayedSend property of
the JMAP \"urn:ietf:params:jmap:emailsubmission\" capabilities object.
The Cyrus JMAP implementation does not enforce this limit.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "jmap_max_objects_in_get", 4096, INT, "3.1.6" }
/* The maximum number of ids that a JMAP client may request in
a single \"/get\" type method call. The actual number
of returned objects in the response may exceed this number
if the JMAP object type supports unbounded \"/get\" calls.
Returned as the maxObjectsInGet property value of the
JMAP \"urn:ietf:params:jmap:core\" capabilities object. */
{ "jmap_max_objects_in_set", 4096, INT, "3.1.6" }
/* The maximum number of objects a JMAP client may send to create,
update or destroy in a single /set type method call.
Returned as the maxObjectsInSet property value of the
JMAP \"urn:ietf:params:jmap:core\" capabilities object. */
{ "jmap_mail_max_size_attachments_per_email", 10240, INT, "3.1.6" }
/* The value (in kilobytes) to return for the maxSizeAttachmentsPerEmail
property of the JMAP \"urn:ietf:params:jmap:mail\" capabilities object. The Cyrus
JMAP implementation does not enforce this size limit. Default is 10 Mb.*/
{ "jmap_nonstandard_extensions", 0, SWITCH, "3.1.9" }
/* If enabled, support non-standard JMAP extensions. If not enabled,
only IETF standard JMAP functionality is supported. */
{ "jmap_pushpoll", "60s", DURATION, "UNRELEASED" }
/* The interval for polling for changes on an EventSource connection or
when push has been ennabled on a WebSocket channel.
The minimum value is 1 second. A value of 0 will disable push.
.PP
If no unit is specified, seconds is assumed. */
{ "jmap_set_has_attachment", 1, SWITCH, "3.1.5" }
/* If enabled, the $hasAttachment flag is determined and set for new messages
created with the JMAP Email/set or Email/import methods. This option should
typically be enabled, but installations using Cyrus-external message
annatotors to determine the $hasAttachment flag might want to disable it. */
{ "jmap_vacation", 1, SWITCH, "3.1.8" }
/* If enabled, support the JMAP vacation extension */
{ "jmapuploadfolder", "#jmap", STRING, "3.1.1" }
/* the name of the folder for JMAP uploads (#jmap) */
{ "jmapsubmission_deleteonsend", 1, SWITCH, "3.1.8" }
/* If enabled (the default) then delete the EmailSubmission as soon as the email
* has been sent */
{ "jmapsubmissionfolder", "#jmapsubmission", STRING, "3.1.8" }
/* the name of the folder for JMAP Submissions (#jmapsubmission) */
{ "jmappushsubscriptionfolder", "#jmappushsubscription", STRING, "3.1.8" }
/* the name of the folder for JMAP Push Subscriptions (#jmappushsubscription) */
{ "jmapnotificationfolder", "#jmapnotification", STRING, "3.3.0" }
/* the name of the folder for JMAP notifications (#jmapnotification) */
{ "iolog", 0, SWITCH, "2.5.0" }
/* Should cyrus output I/O log entries */
{ "ldap_authz", NULL, STRING, "2.3.17" }
/* SASL authorization ID for the LDAP server */
{ "ldap_base", "", STRING, "2.3.17" }
/* Contains the LDAP base dn for the LDAP ptloader module */
{ "ldap_bind_dn", NULL, STRING, "2.3.17" }
/* Bind DN for the connection to the LDAP server (simple bind).
Do not use for anonymous simple binds */
{ "ldap_deref", "never", STRINGLIST("search", "find", "always", "never"), "2.3.17" }
/* Specify how aliases dereferencing is handled during search. */
{ "ldap_domain_base_dn", "", STRING, "2.5.0" }
/* Base DN to search for domain name spaces. */
{ "ldap_domain_filter", "(&(objectclass=domainrelatedobject)(associateddomain=%s))", STRING, "2.5.0" }
/* Filter to use searching for domains */
{ "ldap_domain_name_attribute", "associateddomain", STRING, "2.5.0" }
/* The attribute name for domains. */
{ "ldap_domain_scope", "sub", STRINGLIST("sub", "one", "base"), "2.5.0" }
/* Search scope */
{ "ldap_domain_result_attribute", "inetdomainbasedn", STRING, "2.5.0" }
/* Result attribute */
{ "ldap_filter", "(uid=%u)", STRING, "2.3.17" }
/* Specify a filter that searches user identifiers. The following tokens can be
used in the filter string:
%% = %
%u = user
%U = user portion of %u (%U = test when %u = test@domain.tld)
%d = domain portion of %u if available (%d = domain.tld when %u =
test@domain.tld), otherwise same as %R
%R = domain portion of %u starting with @ (%R = @domain.tld
when %u = test@domain.tld)
%D = user dn. (use when ldap_member_method: filter)
%1-9 = domain tokens (%1 = tld, %2 = domain when %d = domain.tld)
ldap_filter is not used when ldap_sasl is enabled. */
{ "ldap_group_base", "", STRING, "2.3.17" }
/* LDAP base dn for ldap_group_filter. */
{ "ldap_group_filter", "(cn=%u)", STRING, "2.3.17" }
/* Specify a filter that searches for group identifiers.
See ldap_filter for more options. */
{ "ldap_group_scope", "sub", STRINGLIST("sub", "one", "base"), "2.3.17" }
/* Specify search scope for ldap_group_filter. */
{ "ldap_id", NULL, STRING, "2.3.17" }
/* SASL authentication ID for the LDAP server */
{ "ldap_mech", NULL, STRING, "2.3.17" }
/* SASL mechanism for LDAP authentication */
{ "ldap_user_attribute", NULL, STRING, "2.5.0" }
/* Specify LDAP attribute to use as canonical user id */
{ "ldap_member_attribute", NULL, STRING, "2.3.17" }
/* See ldap_member_method. */
{ "ldap_member_base", "", STRING, "2.3.17" }
/* LDAP base dn for ldap_member_filter. */
{ "ldap_member_filter", "(member=%D)", STRING, "2.3.17" }
/* Specify a filter for "ldap_member_method: filter".
See ldap_filter for more options. */
{ "ldap_member_method", "attribute", STRINGLIST("attribute", "filter"), "2.3.17" }
/* Specify a group method. The "attribute" method retrieves groups from
a multi-valued attribute specified in ldap_member_attribute.
The "filter" method uses a filter, specified by ldap_member_filter, to find
groups; ldap_member_attribute is a single-value attribute group name. */
{ "ldap_member_scope", "sub", STRINGLIST("sub", "one", "base"), "2.3.17" }
/* Specify search scope for ldap_member_filter. */
{ "ldap_password", NULL, STRING, "2.3.17" }
/* Password for the connection to the LDAP server (SASL and simple bind).
Do not use for anonymous simple binds */
{ "ldap_realm", NULL, STRING, "2.3.17" }
/* SASL realm for LDAP authentication */
{ "ldap_referrals", 0, SWITCH, "2.3.17" }
/* Specify whether or not the client should follow referrals. */
{ "ldap_restart", 1, SWITCH, "2.3.17" }
/* Specify whether or not LDAP I/O operations are automatically restarted
if they abort prematurely. */
{ "ldap_sasl", 1, SWITCH, "2.3.17" }
/* Use SASL for LDAP binds in the LDAP PTS module. */
{ "ldap_sasl_authc", NULL, STRING, "2.5.0", "2.5.0", "ldap_id" }
/* Deprecated. Use ldap_id */
{ "ldap_sasl_authz", NULL, STRING, "2.5.0", "2.5.0", "ldap_authz" }
/* Deprecated. Use ldap_authz */
{ "ldap_sasl_mech", NULL, STRING, "2.5.0", "2.5.0", "ldap_mech" }
/* Deprecated. Use ldap_mech */
{ "ldap_sasl_password", NULL, STRING, "2.5.0", "2.5.0", "ldap_password" }
/* Deprecated. User ldap_password */
{ "ldap_sasl_realm", NULL, STRING, "2.5.0", "2.5.0", "ldap_realm" }
/* Deprecated. Use ldap_realm */
{ "ldap_scope", "sub", STRINGLIST("sub", "one", "base"), "2.3.17" }
/* Specify search scope. */
{ "ldap_servers", "ldap://localhost/", STRING, "2.5.0", "2.5.0", "ldap_uri" }
/* Deprecated. Use ldap_uri */
{ "ldap_size_limit", 1, INT, "2.3.17" }
/* Specify a number of entries for a search request to return. */
{ "ldap_start_tls", 0, SWITCH, "2.3.17" }
/* Use transport layer security for ldap:// using STARTTLS. Do not use
ldaps:// in 'ldap_uri' with this option enabled. */
{ "ldap_time_limit", "5s", DURATION, "3.1.8" }
/* How long to wait for a search request to complete.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "ldap_timeout", "5s", DURATION, "3.1.8" }
/* How long a search can take before timing out.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "ldap_ca_dir", NULL, STRING, "2.5.0" }
/* Path to a directory with CA (Certificate Authority) certificates. */
{ "ldap_ca_file", NULL, STRING, "2.5.0" }
/* Path to a file containing CA (Certificate Authority) certificate(s). */
{ "ldap_ciphers", NULL, STRING, "2.5.0" }
/* List of SSL/TLS ciphers to allow. The format of the string is
described in ciphers(1). */
{ "ldap_client_cert", NULL, STRING, "2.5.0" }
/* File containing the client certificate. */
{ "ldap_client_key", NULL, STRING, "2.5.0" }
/* File containing the private client key. */
{ "ldap_verify_peer", 0, SWITCH, "2.5.0" }
/* Require and verify server certificate. If this option is yes,
you must specify ldap_ca_file or ldap_ca_dir. */
{ "ldap_tls_cacert_dir", NULL, STRING, "2.5.0", "2.5.0", "ldap_ca_dir" }
/* Deprecated in favor of \fIldap_ca_dir\fR. */
{ "ldap_tls_cacert_file", NULL, STRING, "2.5.0", "2.5.0", "ldap_ca_file" }
/* Deprecated in favor of \fIldap_ca_file\fR. */
{ "ldap_tls_cert", NULL, STRING, "2.5.0", "2.5.0", "ldap_client_cert" }
/* Deprecated in favor of \fIldap_client_cert\fR. */
{ "ldap_tls_key", NULL, STRING, "2.5.0", "2.5.0", "ldap_client_key" }
/* Deprecated in favor of \fIldap_client_key\fR. */
{ "ldap_tls_check_peer", 0, SWITCH, "2.5.0", "2.5.0", "ldap_verify_peer" }
/* Deprecated in favor of \fIldap_verify_peer\fR. */
{ "ldap_tls_ciphers", NULL, STRING, "2.5.0", "2.5.0", "ldap_ciphers" }
/* Deprecated in favor of \fIldap_ciphers\fR. */
{ "ldap_uri", NULL, STRING, "2.3.17" }
/* Contains a list of the URLs of all the LDAP servers when using the
LDAP PTS module. */
{ "ldap_version", 3, INT, "2.3.17" }
/* Specify the LDAP protocol version. If ldap_start_tls and/or
ldap_use_sasl are enabled, ldap_version will be automatically
set to 3. */
{ "literalminus", 0, SWITCH, "3.0.0" }
/* if enabled, CAPABILITIES will reply with LITERAL- rather than
LITERAL+ (RFC 7888). Doesn't actually size-restrict uploads though */
{ "lmtp_downcase_rcpt", 1, SWITCH, "2.5.0" }
/* If enabled, lmtpd will convert the recipient addresses to lowercase
(up to a '+' character, if present). */
{ "lmtp_exclude_specialuse", "\\Snoozed", STRING, "3.1.8" }
/* Don't allow delivery to folders with given special-use attributes.
.PP
Note that "snoozing" of emails can currently only be done via the
JMAP protocol, so delivery directly to the \Snoozed mailbox is
prohibited by default as it will not be moved back into INBOX
automatically. */
{ "lmtp_fuzzy_mailbox_match", 0, SWITCH, "2.3.17" }
/* If enabled, and the mailbox specified in the detail part of the
recipient (everything after the '+') does not exist, lmtpd will try
to find the closest match (ignoring case, ignoring whitespace,
falling back to parent) to the specified mailbox name. */
{ "lmtp_over_quota_perm_failure", 0, SWITCH, "2.3.17" }
/* If enabled, lmtpd returns a permanent failure code when a user's
mailbox is over quota. By default, the failure is temporary,
causing the MTA to queue the message and retry later. */
{ "lmtp_preparse", 0, SWITCH, "3.3.1" }
/* If enabled, lmtpd will map in the email and parse the xapian data
for jmapsearch. The advantage is that the parsing is done without
holding any locks. The disadvantage is that the parsing is done
even if it winds up not being needed. */
{ "lmtp_strict_quota", 0, SWITCH, "2.3.17" }
/* If enabled, lmtpd returns a failure code when the incoming message
will cause the user's mailbox to exceed its quota. By default, the
failure won't occur until the mailbox is already over quota. */
{ "lmtp_strict_rfc2821", 1, SWITCH, "2.5.0" }
/* By default, lmtpd will be strict (per RFC 2821) with regards to which
envelope addresses are allowed. If this option is set to false, 8bit
characters in the local-part of envelope addresses are changed to 'X'
instead. This is useful to avoid generating backscatter with
certain MTAs like Postfix or Exim which accept such messages. */
{ "lmtpsocket", "{configdirectory}/socket/lmtp", STRING, "2.3.17" }
/* Unix domain socket that lmtpd listens on, used by deliver(8). This should
match the path specified in cyrus.conf(5). */
{ "lmtptxn_timeout", "5m", DURATION, "3.1.8" }
/* Timeout used during a lmtp transaction to a remote backend (e.g. in a
murder environment). Can be used to prevent hung lmtpds on proxy hosts
when a backend server becomes unresponsive during a lmtp transaction.
The default is 5 minutes - change to zero for infinite.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "lock_debugtime", NULL, STRING, "3.1.4" }
/* A floating point number of seconds. If set, time how long we wait for
any lock, and syslog the filename and time if it's longer than this
value. The default of NULL means not to time locks. */
# xxx how does this tie into virtual domains?
{ "loginrealms", "", STRING, "2.3.17" }
/* The list of remote realms whose users may authenticate using cross-realm
authentication identifiers. Separate each realm name by a space. (A
cross-realm identity is considered any identity returned by SASL
with an "@" in it.). */
{ "loginuseacl", 0, SWITCH, "2.3.17" }
/* If enabled, any authentication identity which has \fBa\fR rights on a
user's INBOX may log in as that user. */
{ "logtimestamps", 0, SWITCH, "2.3.17" }
/* Include notations in the protocol telemetry logs indicating the number of
seconds since the last command or response. */
{ "mailbox_default_options", 0, INT, "2.3.17" }
/* Default "options" field for the mailbox on create. You'll want to know
what you're doing before setting this, but it can apply some default
annotations like duplicate suppression */
{ "mailbox_initial_flags", NULL, STRING, "2.5.0" }
/* space-separated list of permanent flags which will be pre-set in every
newly created mailbox. If you know you will require particular
flag names then this avoids a possible race condition against a client
that fills the entire 128 available slots. Default is NULL, which is
no flags. Example: $Label1 $Label2 $Label3 NotSpam Spam */
{ "mailbox_maxmessages_addressbook", 0, INT, "3.3.0" }
/* Limit the number of messages that may exist in a single mailbox of
"addressbook" type. Default (0) means no limit. This limit applies
after quotas are checked, so if you have both quota limits and this
set, then you will be denied if you are either over quota or over
this per-mailbox count. */
{ "mailbox_maxmessages_calendar", 0, INT, "3.3.0" }
/* Limit the number of messages that may exist in a single mailbox of
"calendar" type. Default (0) means no limit. This limit applies
after quotas are checked, so if you have both quota limits and this
set, then you will be denied if you are either over quota or over
this per-mailbox count. */
{ "mailbox_maxmessages_email", 0, INT, "3.3.0" }
/* Limit the number of messages that may exist in a single mailbox of
"email" (normal) type. Default (0) means no limit. This limit applies
after quotas are checked, so if you have both quota limits and this
set, then you will be denied if you are either over quota or over
this per-mailbox count. */
{ "mailnotifier", NULL, STRING, "2.3.17" }
/* Notifyd(8) method to use for "MAIL" notifications. If not set, "MAIL"
notifications are disabled. */
{ "master_bind_errors_fatal", 0, SWITCH, "3.3.0" }
/* If enabled, failure to bind a port during startup is treated as a fatal
error, causing master to shut down immediately. The default is to keep
running, with the affected service disabled until the next SIGHUP causes
it to retry.
.PP
Note that this only applies during startup. New services that fail to
come up in response to a reconfig+SIGHUP will just be logged and disabled
like the default behaviour, without causing master to exit. */
{ "maxheaderlines", 1000, INT, "2.3.17" }
/* Maximum number of lines of header that will be processed into cache
records. Default 1000. If set to zero, it is unlimited.
If a message hits the limit, an error will be logged and the rest of
the lines in the header will be skipped. This is to avoid malformed
messages causing giant cache records */
{ "maxlogins_per_host", 0, INT, "2.5.0" }
/* Maximum number of logged in sessions allowed per host,
zero means no limit */
{ "maxlogins_per_user", 0, INT, "2.5.0" }
/* Maximum number of logged in sessions allowed per user,
zero means no limit */
{ "maxmessagesize", 0, INT, "2.3.17" }
/* Maximum incoming LMTP message size. If non-zero, lmtpd will reject
messages larger than \fImaxmessagesize\fR bytes. If set to 0, this
will allow messages of any size (the default). */
{ "maxquoted", 131072, INT, "2.3.17" }
/* Maximum size of a single quoted string for the parser. Default 128k */
{ "maxword", 131072, INT, "2.3.17" }
/* Maximum size of a single word for the parser. Default 128k */
{ "mboxkey_db", "twoskip", STRINGLIST("skiplist", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for mailbox keys. */
{ "mboxlist_db", "twoskip", STRINGLIST("flat", "skiplist", "sql", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for the mailbox list. */
{ "mboxlist_db_path", NULL, STRING, "2.5.0" }
/* The absolute path to the mailboxes db file. If not specified
will be configdirectory/mailboxes.db */
{ "mboxname_lockpath", NULL, STRING, "2.4.0" }
/* Path to mailbox name lock files (default $conf/lock) */
{ "metapartition_files", "", BITFIELD("header", "index", "cache", "expunge", "squat", "annotations", "lock", "dav", "archivecache"), "3.0.0" }
/* Space-separated list of metadata files to be stored on a
\fImetapartition\fR rather than in the mailbox directory on a spool
partition. */
# Commented out - there's no such thing as "metapartition-name",
# but we need this for the man page
# { "metapartition-name", NULL, STRING, "2.3.17" }
/* The pathname of the metadata partition \fIname\fR, corresponding to
spool partition \fBpartition-name\fR. For any mailbox residing in
a directory on \fBpartition-name\fR, the metadata files listed in
\fImetapartition_files\fR will be stored in a corresponding directory on
\fBmetapartition-name\fR. Note that not every
\fBpartition-name\fR option is required to have a corresponding
\fBmetapartition-name\fR option, so that you can selectively choose
which spool partitions will have separate metadata partitions. */
{ "mupdate_authname", NULL, STRING, "2.3.17" }
/* The SASL username (Authentication Name) to use when authenticating to the
mupdate server (if needed). */
{ "mupdate_config", "standard", ENUM("standard", "unified", "replicated"), "2.3.17" }
/* The configuration of the mupdate servers in the Cyrus Murder.
The "standard" config is one in which there are discreet frontend
(proxy) and backend servers. The "unified" config is one in which
a server can be both a frontend and backend. The "replicated"
config is one in which multiple backend servers all share the same
mailspool, but each have their own "replicated" copy of
mailboxes.db. */
{ "munge8bit", 1, SWITCH, "2.3.17" }
/* If enabled, lmtpd munges messages with 8-bit characters in the
headers. The 8-bit characters are changed to `X'. If
\fBreject8bit\fR is enabled, setting \fBmunge8bit\fR has no effect.
(A proper solution to non-ASCII characters in headers is offered by
RFC 2047 and its predecessors.) */
# xxx badly worded
{ "mupdate_connections_max", 128, INT, "2.3.17" }
/* The max number of connections that a mupdate process will allow, this
is related to the number of file descriptors in the mupdate process.
Beyond this number connections will be immediately issued a BYE response. */
{ "mupdate_password", NULL, STRING, "2.3.17" }
/* The SASL password (if needed) to use when authenticating to the
mupdate server. */
{ "mupdate_port", 3905, INT, "2.3.17" }
/* The port of the mupdate server for the Cyrus Murder */
{ "mupdate_realm", NULL, STRING, "2.3.17" }
/* The SASL realm (if needed) to use when authenticating to the mupdate
server. */
{ "mupdate_retry_delay", 20, INT, "2.3.17" }
/* The base time to wait between connection retries to the mupdate server. */
{ "mupdate_server", NULL, STRING, "2.3.17" }
/* The mupdate server for the Cyrus Murder */
{ "mupdate_username", "", STRING, "2.3.17" }
/* The SASL username (Authorization Name) to use when authenticating to
the mupdate server */
{ "mupdate_workers_max", 50, INT, "2.3.17" }
/* The maximum number of mupdate worker threads (overall) */
{ "mupdate_workers_maxspare", 10, INT, "2.3.17" }
/* The maximum number of idle mupdate worker threads */
{ "mupdate_workers_minspare", 2, INT, "2.3.17" }
/* The minimum number of idle mupdate worker threads */
{ "mupdate_workers_start", 5, INT, "2.3.17" }
/* The number of mupdate worker threads to start */
{ "netscapeurl", NULL, STRING, "2.3.17" }
/* If enabled at compile time, this specifies a URL to reply when
Netscape asks the server where the mail administration HTTP server
is. Administrators should set this to a local resource. */
{ "newsaddheaders", "to", BITFIELD("to", "replyto"), "2.5.0" }
/* Space-separated list of headers to be added to incoming usenet
articles. Added \fITo:\fR headers will contain email
delivery addresses corresponding to each newsgroup in the
\fINewsgroups:\fR header. Added \fIReply-To:\fR headers will
contain email delivery addresses corresponding to each newsgroup in
the \fIFollowup-To:\fR or \fINewsgroups:\fR header. If the
specified header(s) already exist in an article, the email
delivery addresses will be appended to the original header body(s).
.br
.sp
This option applies if and only if the \fBnewspostuser\fR option is
set. */
{ "newsgroups", "*", STRING, "2.4.0" }
/* A wildmat pattern specifying which mailbox hierarchies should be
treated as newsgroups. Only mailboxes matching the wildmat will
accept and/or serve articles via NNTP. If not set, a default
wildmat of "*" (ALL shared mailboxes) will be used. If the
\fInewsprefix\fR option is also set, the default wildmat will be
translated to "<newsprefix>.*" */
{ "newsmaster", "news", STRING, "2.3.17" }
/* Userid that is used for checking access controls when executing
Usenet control messages. For instance, to allow articles to be
automatically deleted by cancel messages, give the "news" user
the 'd' right on the desired mailboxes. To allow newsgroups to be
automatically created, deleted and renamed by the corresponding
control messages, give the "news" user the 'c' right on the desired
mailbox hierarchies. */
{ "newspeer", NULL, STRING, "2.3.17" }
/* A list of whitespace-separated news server specifications to which
articles should be fed. Each server specification is a string of
the form [user[:pass]@]host[:port][/wildmat] where 'host' is the fully
qualified hostname of the server, 'port' is the port on which the
server is listening, 'user' and 'pass' are the authentication
credentials and 'wildmat' is a pattern that specifies which groups
should be fed. If no 'port' is specified, port 119 is used. If
no 'wildmat' is specified, all groups are fed. If 'user' is specified
(even if empty), then the NNTP POST command will be used to feed
the article to the server, otherwise the IHAVE command will be
used.
.br
.sp
A '@' may be used in place of '!' in the wildmat to prevent feeding
articles cross-posted to the given group, otherwise cross-posted
articles are fed if any part of the wildmat matches. For example,
the string "peer.example.com:*,!control.*,@local.*" would feed all
groups except control messages and local groups to
peer.example.com. In the case of cross-posting to local groups,
these articles would not be fed. */
{ "newspostuser", NULL, STRING, "2.5.0" }
/* Userid used to deliver usenet articles to newsgroup folders
(usually via lmtp2nntp). For example, if set to "post", email sent
to "post+comp.mail.imap" would be delivered to the "comp.mail.imap"
folder.
.br
.sp
When set, the Cyrus NNTP server will add the header(s) specified in
the \fBnewsaddheaders\fR option to each incoming usenet article.
The added header(s) will contain email delivery addresses
corresponding to each relevant newsgroup. If not set, no headers
are added to usenet articles. */
{ "newsprefix", NULL, STRING, "2.3.17" }
/* Prefix to be prepended to newsgroup names to make the corresponding
IMAP mailbox names. */
{ "newsrc_db_path", NULL, STRING, "2.5.0" }
/* The absolute path to the newsrc db file. If not specified,
will be configdirectory/fetchnews.db */
{ "nntptimeout", "3m", DURATION, "3.1.8" }
/* Set the length of the NNTP server's inactivity autologout timer.
The minimum value is 3 minutes, also the default.
.PP
For backward compatibility, if no unit is specified, minutes is
assumed. */
{ "notesmailbox", NULL, STRING, "3.0.0" }
/* The top level mailbox in each user's account which is used to store
* Apple-style Notes. Default is blank (disabled) */
{ "notifysocket", "{configdirectory}/socket/notify", STRING, "2.3.17" }
/* Unix domain socket that the mail notification daemon listens on. */
{ "notify_external", NULL, STRING, "2.4.0" }
/* Path to the external program that notifyd(8) will call to send mail
notifications.
.PP
The external program will be called with the following
command line options:
.TP
.BI \-c " class"
.\"
.TP
.BI \-p " priority"
.\"
.TP
.BI \-u " user"
.\"
.TP
.BI \-m " mailbox"
.PP
And the notification message will be available on \fIstdin\fR.
*/
# Commented out - there's no such thing as "partition-name", but we need
# this for the man page
# { "partition-name", NULL, STRING, "2.3.17" }
/* The pathname of the partition \fIname\fR. At least one partition
pathname MUST be specified. If the \fBdefaultpartition\fR option is
used, then its pathname MUST be specified. For example, if the
value of the \fBdefaultpartion\fR option is \fBpart1\fR, then the
\fBpartition-part1\fR field is required. */
{ "partition_select_mode", "freespace-most", STRINGLIST("random", "freespace-most", "freespace-percent-most", "freespace-percent-weighted", "freespace-percent-weighted-delta"), "2.5.0" }
/* Partition selection mode.
.PP
.IP \fIrandom\fR 5
(pseudo-)random selection
.\"
.IP \fIfreespace-most\fR 5
partition with the most free space (KiB)
.\"
.IP \fIfreespace-percent-most\fR 5
partition with the most free space (%)
.\"
.IP \fIfreespace-percent-weighted\fR 5
each partition is weighted according to its free space (%); the more free space
the partition has, the more chances it has to be selected
.\"
.IP \fIfreespace-percent-weighted-delta\fR 5
each partition is weighted according to its difference of free space (%)
compared to the most used partition; the more the partition is lagging behind
the most used partition, the more chances it has to be selected
.PP
Note that actually even the most used partition has a few chances to be
selected, and those chances increase when other partitions get closer
*/
{ "partition_select_exclude", NULL, STRING, "2.5.0" }
/* List of partitions to exclude from selection mode. */
{ "partition_select_usage_reinit", 0, INT, "2.5.0" }
/* For a given session, number of \fBoperations\fR (e.g. partition selection)
for which partitions usage data are cached. */
{ "partition_select_soft_usage_limit", 0, INT, "2.5.0" }
/* Limit of partition usage (%): if a partition is over that limit, it is
automatically excluded from selection mode.
.PP
If all partitions are over that limit, this feature is not used anymore.
*/
{ "plaintextloginpause", NULL, DURATION, "3.1.8" }
/* Time to pause after a successful plaintext login. For systems that
support strong authentication, this permits users to perceive a cost
of using plaintext passwords. (This does not affect the use of PLAIN
in SASL authentications.)
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "plaintextloginalert", NULL, STRING, "2.3.17" }
/* Message to send to client after a successful plaintext login. */
{ "popexpiretime", "-1", DURATION, "3.1.8" }
/* The duration advertised as being the minimum a message may be
left on the POP server before it is deleted (via the CAPA command,
defined in the POP3 Extension Mechanism, which some clients may
support). This duration has a granularity of whole days, with partial
days truncated (so e.g. "45m" is effectively "0d"). "NEVER", the
default, may be specified with a negative number.
.PP
The Cyrus POP3 server never deletes mail, no matter what the value of
this parameter is. However, if a site implements a less liberal policy,
it needs to change this parameter accordingly.
.PP
For backward compatibility, if no unit is specified, days is
assumed. */
{ "popminpoll", NULL, DURATION, "3.1.8" }
/* Set the minimum amount of time the server forces users to wait
between successive POP logins.
.PP
For backward compatibility, if no unit is specified, minutes is
assumed. */
{ "popsubfolders", 0, SWITCH, "2.3.17" }
/* Allow access to subfolders of INBOX via POP3 by using
userid+subfolder syntax as the authentication/authorization id. */
{ "poppollpadding", 1, INT, "2.3.17" }
/* Create a softer minimum poll restriction. Allows \fIpoppollpadding\fR
connections before the minpoll restriction is triggered. Additionally,
one padding entry is recovered every \fIpopminpoll\fR minutes.
This allows for the occasional polling rate faster than popminpoll,
(i.e., for clients that require a send/receive to send mail) but still
enforces the rate long-term. Default is 1 (disabled).
.br
.sp
The easiest way to think of it is a queue of past connections, with one
slot being filled for every connection, and one slot being cleared
every \fIpopminpoll\fR minutes. When the queue is full, the user
will not be able to check mail again until a slot is cleared. If the
user waits a sufficient amount of time, they will get back many or all
of the slots. */
{ "poptimeout", "10m", DURATION, "3.1.8" }
/* Set the length of the POP server's inactivity autologout timer.
The minimum value is 10 minutes, the default.
.PP
For backward compatibility, if no unit is specified, minutes is
assumed. */
{ "popuseacl", 0, SWITCH, "2.3.17" }
/* Enforce IMAP ACLs in the pop server. Due to the nature of the POP3
protocol, the only rights which are used by the pop server are 'r',
't', and 's' for the owner of the mailbox. The 'r' right allows the
user to open the mailbox and list/retrieve messages. The 't' right
allows the user to delete messages. The 's' right allows messages
retrieved by the user to have the \\Seen flag set (only if
\fIpopuseimapflags\fR is also enabled). */
{ "popuseimapflags", 0, SWITCH, "2.3.17" }
/* If enabled, the pop server will set and obey IMAP flags. Messages
having the \\Deleted flag are ignored as if they do not exist.
Messages that are retrieved by the client will have the \\Seen flag
set. All messages will have the \\Recent flag unset. */
{ "postmaster", "postmaster", STRING, "2.3.17" }
/* Username that is used as the 'From' address in rejection MDNs produced
by sieve. */
{ "postspec", NULL, STRING, "2.3.17" }
{ "postuser", "", STRING, "2.3.17" }
/* Userid used to deliver messages to shared folders. For example, if
set to "bb", email sent to "bb+shared.blah" would be delivered to
the "shared.blah" folder. By default, an email address of
"+shared.blah" would be used. */
{ "proc_path", NULL, STRING, "2.5.0" }
/* Path to proc directory. Default is NULL - must be an absolute path
if specified. If not specified, the path $configdirectory/proc/ will be
used. */
{ "prometheus_enabled", 0, SWITCH, "3.1.2" }
/* Whether tracking of service metrics for Prometheus is enabled. */
{ "prometheus_need_auth", "admin", STRINGLIST("none", "user", "admin"), "3.1.2" }
/* Authentication level required to fetch Prometheus metrics. */
{ "prometheus_update_freq", "10s", DURATION, "3.1.8" }
/* Frequency in at which promstatsd should re-collate its statistics
report. The minimum value is 1 second, the default is 10 seconds.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
*/
{ "prometheus_stats_dir", NULL, STRING, "3.1.2" }
/* Directory to use for gathering prometheus statistics. If specified,
must be an absolute path. If not specified, the default path
$configdirectory/stats/ will be used. It may be advantageous to locate this
directory on ephemeral storage. */
{ "proxy_authname", "proxy", STRING, "2.3.17" }
/* The authentication name to use when authenticating to a backend server
in the Cyrus Murder. */
{ "proxy_compress", 0, SWITCH, "2.3.17" }
/* Try to enable protocol-specific compression when performing a client
connection to a backend server in the Cyrus Murder.
.PP
Note that this should only be necessary over slow network
connections. Also note that currently only IMAP and MUPDATE support
compression. */
{ "proxy_password", NULL, STRING, "2.3.17" }
/* The default password to use when authenticating to a backend server
in the Cyrus Murder. May be overridden on a host-specific basis using
the hostname_password option. */
{ "proxy_realm", NULL, STRING, "2.3.17" }
/* The authentication realm to use when authenticating to a backend server
in the Cyrus Murder */
{ "proxyd_allow_status_referral", 0, SWITCH, "2.3.17" }
/* Set to true to allow proxyd to issue referrals to clients that support it
when answering the STATUS command. This is disabled by default since
some clients issue many STATUS commands in a row, and do not cache the
connections that these referrals would cause, thus resulting in a higher
authentication load on the respective backend server. */
{ "proxyd_disable_mailbox_referrals", 0, SWITCH, "2.3.17" }
/* Set to true to disable the use of mailbox-referrals on the
proxy servers. */
{ "proxyservers", NULL, STRING, "2.3.17" }
/* A list of users and groups that are allowed to proxy for other
users, separated by spaces. Any user listed in this will be
allowed to login for any other user: use with caution.
In a standard murder this option should ONLY be set on backends.
DO NOT SET on frontends or things won't work properly. */
{ "pts_module", "afskrb", STRINGLIST("afskrb", "ldap"), "2.3.17" }
/* The PTS module to use. */
{ "ptloader_sock", NULL, STRING, "2.3.17" }
/* Unix domain socket that ptloader listens on.
(defaults to configdirectory/ptclient/ptsock) */
{ "ptscache_db", "twoskip", STRINGLIST("skiplist", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for the pts cache. */
{ "ptscache_db_path", NULL, STRING, "2.5.0" }
/* The absolute path to the ptscache db file. If not specified,
will be configdirectory/ptscache.db */
{ "ptscache_timeout", "3h", DURATION, "3.1.8" }
/* The timeout for the PTS cache database when using the auth_krb_pts
authorization method (default: 3 hours).
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "ptskrb5_convert524", 1, SWITCH, "2.3.16" }
/* When using the AFSKRB ptloader module with Kerberos 5 canonicalization,
do the final 524 conversion to get a n AFS style name (using '.' instead
of '/', and using short names */
{ "ptskrb5_strip_default_realm", 1, SWITCH, "2.3.17" }
/* When using the AFSKRB ptloader module with Kerberos 5 canonicalization,
strip the default realm from the userid (this does not affect the stripping
of realms specified by the afspts_localrealms option) */
{ "qosmarking", "cs0", ENUM("cs0", "cs1", "cs2", "cs3", "cs4", "cs5", "cs6", "cs7", "af11", "af12", "af13", "af21", "af22", "af23", "af31", "af32", "af33", "af41", "af42", "af43", "ef"), "2.5.0" }
/* This specifies the Class Selector or Differentiated Services Code Point
designation on IP headers (in the ToS field). */
{ "quota_db", "quotalegacy", STRINGLIST("flat", "skiplist", "sql", "quotalegacy", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for quotas. */
{ "quota_db_path", NULL, STRING, "2.5.0" }
/* The absolute path for the quota database (if you choose a single-file
quota DB type - or the base path if you choose quotalegacy). If
not specified will be configdirectory/quotas.db or configdirectory/quota/ */
{ "quota_use_conversations", 0, SWITCH, "3.3.0" }
/* If conversations it enabled and quotaroot is a user folder, use the
conversations quota counts, which count multiple copies of exactly the
same message (by GUID) as only one */
{ "quotawarn", 90, INT, "2.3.17" }
/* The percent of quota utilization over which the server generates
warnings. */
{ "quotawarnkb", 0, INT, "2.3.17" }
/* The maximum amount of free space (in kB) at which to give a quota
warning (if this value is 0, or if the quota is smaller than this
amount, then warnings are always given). */
{ "quotawarnmsg", 0, INT, "2.5.0" }
/* The maximum amount of messages at which to give a quota warning
(if this value is 0, or if the quota is smaller than this
amount, then warnings are always given). */
{ "readonly", 0, SWITCH, "3.3.0" }
/* If enabled, all IMAP, POP and JMAP connections are read-only,
* no writes allowed */
{ "reject8bit", 0, SWITCH, "2.3.17" }
/* If enabled, lmtpd rejects messages with 8-bit characters in the
headers. */
{ "restore_authname", NULL, STRING, "3.0.0" }
/* The authentication used by the restore tool when authenticating
to an IMAP/sync server. */
{ "restore_password", NULL, STRING, "3.0.0" }
/* The password used by the restore tool when authenticating to an
IMAP/sync server. */
{ "restore_realm", NULL, STRING, "3.0.0" }
/* The authentication realm used by the restore tool when
authenticating to an IMAP/sync server. */
{ "reverseacls", 0, SWITCH, "3.0.0" }
/* At startup time, ctl_cyrusdb -r will check this value and it
will either add or remove reverse ACL pointers from mailboxes.db */
-{ "reverseuniqueids", 1, SWITCH, "3.3.0" }
-/* At startup time, ctl_cyrusdb -r will check this value and it
- will either add or remove reverse UNIQUEID pointers from mailboxes.db */
-
{ "rfc2046_strict", 0, SWITCH, "2.3.17" }
/* If enabled, imapd will be strict (per RFC 2046) when matching MIME
boundary strings. This means that boundaries containing other
boundaries as substrings will be treated as identical. Since
enabling this option will break some messages created by Eudora 5.1
(and earlier), it is recommended that it be left disabled unless
there is good reason to do otherwise. */
{ "rfc2047_utf8", 0, SWITCH, "3.0.0" }
/* If enabled, imapd will parse any non-encoded character sequence in
MIME header values as UTF8. This is useful for installations that
either advertise the UTF8SMTP (RFC 5335) extension or receive mails
with improperly escaped UTF-8 byte sequences. It is recommended that
this option is left disabled unless there is good reason to do
otherwise. */
{ "rfc3028_strict", 1, SWITCH, "2.3.17" }
/* If enabled, Sieve will be strict (per RFC 3028) with regards to
which headers are allowed to be used in address and envelope tests.
This means that only those headers which are defined to contain addresses
will be allowed in address tests and only "to" and "from" will be
allowed in envelope tests. When disabled, ANY grammatically correct header
will be allowed. */
{ "rss_feedlist_template", NULL, STRING, "2.5.0" }
/* File containing HTML that will be used as a template for displaying
the list of available RSS feeds. A single instance of the variable
%RSS_FEEDLIST% should appear in the file, which will be replaced by
a nested unordered list of feeds. The toplevel unordered list will
be tagged with an id of "feed" (<ul id='feed'>) which can be used
by stylesheet(s) in your template. The dynamically created list of
feeds based on the HTML template will be accessible at the "/rss"
URL on the server. */
{ "rss_feeds", "*", STRING, "2.5.0" }
/* A wildmat pattern specifying which mailbox hierarchies should be
treated as RSS feeds. Only mailboxes matching the wildmat will
have their messages available via RSS. If not set, a default
wildmat of "*" (ALL mailboxes) will be used. */
{ "rss_maxage", NULL, DURATION, "3.1.8" }
/* Maximum age of items to display in an RSS channel. If non-zero,
httpd will only display items received within this time period.
If set to 0, all available items will be displayed (the default).
.PP
For backward compatibility, if no unit is specified, days is
assumed. */
{ "rss_maxitems", 0, INT, "2.5.0" }
/* Maximum number of items to display in an RSS channel. If non-zero,
httpd will display no more than the \fIrss_maxitems\fR most recent
items. If set to 0, all available items will be displayed (the
default). */
{ "rss_maxsynopsis", 0, INT, "2.5.0" }
/* Maximum RSS item synopsis length. If non-zero, httpd will display
no more than the first \fIrss_maxsynopsis\fR characters of an
item's synopsis. If set to 0, the entire synopsis will be
displayed (the default). */
{ "rss_realm", NULL, STRING, "2.5.0" }
/* The realm to present for HTTP authentication of RSS feeds. If not
set (the default), the value of the "servername" option will be
used.*/
# Commented out - used by libsasl
# { "sasl_auto_transition", 0, SWITCH, "2.3.17" }
/* If enabled, the SASL library will automatically create authentication
secrets when given a plaintext password. See the SASL documentation. */
{ "sasl_maximum_layer", 256, INT, "2.3.17" }
/* Maximum SSF (security strength factor) that the server will allow a
client to negotiate. */
{ "sasl_minimum_layer", 0, INT, "2.3.17" }
/* The minimum SSF that the server will allow a client to negotiate.
A value of 1 requires integrity protection; any higher value
requires some amount of encryption. */
# Commented out - used by libsasl
# { "sasl_option", 0, STRING, "2.3.17" }
/* Any SASL option can be set by preceding it with \fBsasl_\fR. This
file overrides the SASL configuration file. */
# Commented out - used by libsasl
# { "sasl_pwcheck_method", NULL, STRING, "2.3.17" }
/* The mechanism used by the server to verify plaintext passwords.
Possible values include "auxprop", "saslauthd", and "pwcheck". */
{ "search_batchsize", 20, INT, "3.0.0" }
/* The number of messages to be indexed in one batch (default 20).
Note that long batches may delay user commands or mail delivery. */
{ "search_attachment_extractor_url", NULL, STRING, "3.3.1" }
/* A HTTP or HTTPS URL to extract search text from rich text attachments
and other media during search indexing. The server at this URL must
implement the following protocol:
1. For each attachment of an email, Cyrus sends a GET request to the
URL <extractor-url>/<cyrus-id>, where <extractor-url> is the
configured URL and <cyrus-id> is a Cyrus-chosen path segment that
uniquely identifies this attachment.
2. If the extractor already has a cached plain text extract of the
attachment identified by <cyrus-id> then it may return HTTP status
code 200 (OK) and the plain text extract with a Content-Type
"text/plain" header. Otherwise it must return HTTP status 404 (Not Found).
3. If Cyrus receives the HTTP status code 404 (Not Found), then it sends
a PUT request to the same URL as previously. The PUT request body
contains the decoded, binary body of the attachment. The Content-Type
request header has the same value as declared in the MIME part
headers, including any type parameters.
4. The extractor must return the plain text extract with either HTTP status
200 (OK) or 201 (Created) and a Content-Type "text/plain" header.
If no text can be extracted, then the extractor may return any return code
in the range 4xx, or 200 and an empty response body.
Any other HTTP status code is treated as an error. For performance
reasons, the Cyrus indexer attempts to keep-alive the TCP connection
to the extractor.
Xapian only.
*/
{ "search_index_language", 0, SWITCH, "3.3.1" }
/*
If enabled, then messages bodies are stemmed by detected language
in addition to the default English stemmer.
Xapian only.
*/
{ "search_index_parts", 0, SWITCH, "3.2.1", "3.2.1" }
/*
Deprecated. No longer used.
*/
{ "search_index_skip_domains", NULL, STRING, "3.3.1" }
/*
A space separated list of domains - if set, any users in the listed domains
will be skipped when indexing.
*/
{ "search_index_skip_users", NULL, STRING, "3.3.1" }
/*
A space separated list of usernames - if set, any users in the list
will be skipped when indexing.
*/
{ "search_query_language", 0, SWITCH, "3.3.0", "3.3.0" }
/*
Deprecated. No longer used.
*/
{ "search_normalisation_max", 1000, INT, "3.0.0" }
/* A resource bound for the combinatorial explosion of search expression
tree complexity caused by normalising expressions with many OR nodes.
These can use more CPU time to optimise than they save IO time in scanning
folders. */
{ "search_engine", "none", ENUM("none", "squat", "xapian"), "3.1.2" }
/* The indexing engine used to speed up searching. */
{ "search_fuzzy_always", 0, SWITCH, "3.1.5" }
/* Whether to enable RFC 6203 FUZZY search for all IMAP SEARCH. If turned
on, search attributes will be searched using FUZZY search by default.
If turned off, clients have to explicitly use the FUZZY search key to
enable fuzzy search for regular SEARCH commands. */
{ "search_index_headers", 1, SWITCH, "3.0.0" }
/* Whether to index headers other than From, To, Cc, Bcc, and Subject.
Experiment shows that some headers such as Received and DKIM-Signature
can contribute up to 2/3rds of the index size but almost nothing to
the utility of searching. Note that if header indexing is disabled,
headers can still be searched, the searches will just be slower.
*/
{ "search_indexed_db", "twoskip", STRINGLIST("flat", "skiplist", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for the search latest indexed uid state. Xapian only. */
{ "search_maxtime", NULL, STRING, "3.0.0" }
/* The maximum number of seconds to run a search for before aborting. Default
of no value means search "forever" until other timeouts. */
{ "search_maxsize", 4096, INT, "3.5.0" }
/* The maximum size in kilobytes to index for each message part. Message
contents that occur after this byte offset will not be indexed or
search snippets generated from.
Default is 4Mb. Xapian-only. */
{ "search_queryscan", 5000, INT, "3.1.7" }
/* The minimum number of records require to do a direct scan of all G keys
* rather than indexed lookups. A value of 0 means always do indexed lookups.
*/
{ "search_skipdiacrit", 1, SWITCH, "2.5.0" }
/* When searching, should diacriticals be stripped from the search
terms. The default is "true", a search for "hav" will match
"Håvard". This is not RFC 5051 compliant, but it backwards
compatible, and may be preferred by some sites. */
{ "search_skiphtml", 0, SWITCH, "3.0.0" }
/* If enabled, HTML parts of messages are skipped, i.e. not indexed and
not searchable. Otherwise, they're indexed. */
{ "search_whitespace", "merge", ENUM("skip", "merge", "keep"), "2.5.0" }
/* When searching, how whitespace should be handled. Options are:
"skip" (default in 2.3 and earlier series) - where a search for
"equi" would match "the quick brown fox". "merge" - the default,
where "he qu" would match "the quick brownfox", and "keep",
where whitespace must match exactly. The default of "merge" is
recommended for most cases - it's a good compromise which
keeps words separate. */
{ "search_snippet_length", 255, INT, "3.0.0" }
/* The maximum byte length of a snippet generated by the XSNIPPETS
command. Only supported by the Xapian search backend, which
attempts to always fill search_snippet_length bytes in the
generated snippet. */
{ "search_stopword_path", NULL, STRING, "3.1.7" }
/* The absolute base path to the search stopword lists. If not specified,
no stopwords will be taken into account during search indexing. Currently,
the only supported and default stop word file is english.txt. */
# Commented out - there's no such thing as "searchpartition-name",
# but we need this for the man page
# { "searchpartition-name", NULL, STRING, "3.1.4" }
/* The pathname where to store the xapian search indexes of \fIsearchtier\fR
for mailboxes of partition \fIname\fR. This must be configured for the
\fIdefaultsearchtier\fR and any additional search tier (see squatter for
details).
.PP
For example: if \fIdefaultpartition\fR is defined as part1 and
\fIdefaultsearchtier\fR as tier1 then the configuration must contain
an entry \fItier1searchpartition-part1\fR that defines the path where to
store this tier1's search index for the part1 partition.
.PP
This option MUST be specified for xapian search. */
{ "seenstate_db", "twoskip", STRINGLIST("flat", "skiplist", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for the seen state. */
{ "sendmail", "/usr/lib/sendmail", STRING, "2.3.17" }
/* The pathname of the sendmail executable. Sieve invokes sendmail
for sending rejections, redirects and vacation responses. */
{ "sendmail_auth_id", "CYRUS_SENDMAIL_AUTH_ID", STRING, "3.1.3" }
/* The name of an environment variable to set when invoking sendmail.
The value of this environment variable will contain the user id
of the currently authenticated user. If no user is authenticated
the environment variable is not set. */
{ "serverlist", NULL, STRING, "2.3.17" }
/* Whitespace separated list of backend server names. Used for
finding server with the most available free space for proxying
CREATE. */
{ "serverlist_select_mode", "freespace-most", STRINGLIST("random", "freespace-most", "freespace-percent-most", "freespace-percent-weighted", "freespace-percent-weighted-delta"), "2.5.0" }
/* Server selection mode.
.PP
.IP \fIrandom\fR 5
(pseudo-)random selection
.\"
.IP \fIfreespace-most\fR 5
backend with the most (total) free space (KiB)
.\"
.IP \fIfreespace-percent-most\fR 5
backend whose partition has the most free space (%)
.\"
.IP \fIfreespace-percent-weighted\fR 5
same as for partition selection, comparing the free space (%) of the least used
partition of each backend
.\"
.IP \fIfreespace-percent-weighted-delta\fR 5
same as for partition selection, comparing the free space (%) of the least used
partition of each backend.
.PP
*/
{ "serverlist_select_usage_reinit", 0, INT, "2.5.0" }
/* For a given session, number of \fBoperations\fR (e.g. backend selection)
for which backend usage data are cached. */
{ "serverlist_select_soft_usage_limit", 0, INT, "2.5.0" }
/* Limit of backend usage (%): if a backend is over that limit, it is
automatically excluded from selection mode.
.PP
If all backends are over that limit, this feature is not used anymore.
*/
{ "servername", NULL, STRING, "2.3.17" }
/* This is the hostname visible in the greeting messages of the POP,
IMAP and LMTP daemons. If it is unset, then the result returned
from gethostname(2) is used. This is also the value used by murder
clusters to identify the host name. It should be resolvable by
DNS to the correct host, and unique within an active cluster. If
you are using low level replication (e.g. drbd) then it should be
the same on each copy and the DNS name should also be moved to
the new master on failover. */
{ "serverinfo", "on", ENUM("off", "min", "on"), "2.3.17" }
/* The server information to display in the greeting and capability
responses. Information is displayed as follows:
.IP
"off" = no server information in the greeting or capabilities
.br
"min" = \fIservername\fR in the greeting; no server information in the capabilities
.br
"on" = \fIservername\fR and product version in the greeting;
product version in the capabilities
.PP
*/
{ "sharedprefix", "Shared Folders", STRING, "2.3.17" }
/* If using the alternate IMAP namespace, the prefix for the shared
namespace. The hierarchy delimiter will be automatically appended.
*/
{ "sieve_allowreferrals", 1, SWITCH, "2.3.17" }
/* If enabled, timsieved will issue referrals to clients when the
user's scripts reside on a remote server (in a Murder).
Otherwise, timsieved will proxy traffic to the remote server. */
{ "sieve_duplicate_max_expiration", "90d", DURATION, "3.1.8" }
/* Maximum expiration time for duplicate message tracking records.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
*/
{ "sieve_extensions", "fileinto reject vacation vacation-seconds notify include envelope environment body relational regex subaddress copy date index imap4flags mailbox mboxmetadata servermetadata variables editheader extlists duplicate ihave fcc special-use redirect-dsn redirect-deliverby mailboxid vnd.cyrus.log vnd.cyrus.jmapquery snooze", BITFIELD("fileinto", "reject", "vacation", "vacation-seconds", "notify", "include", "envelope", "environment", "body", "relational", "regex", "subaddress", "copy", "date", "index", "imap4flags=imapflags", "mailbox", "mboxmetadata", "servermetadata", "variables", "editheader", "extlists", "duplicate", "ihave", "fcc", "special-use", "redirect-dsn", "redirect-deliverby", "mailboxid", "vnd.cyrus.log=x-cyrus-log", "vnd.cyrus.jmapquery=x-cyrus-jmapquery", "snooze=vnd.cyrus.snooze=x-cyrus-snooze"), "3.3.1" }
/* Space-separated list of Sieve extensions allowed to be used in
sieve scripts, enforced at submission by timsieved(8). Any
previously installed script will be unaffected by this option and
will continue to execute regardless of the extensions used. This
option has no effect on options that are disabled at compile time
(e.g., "regex"). */
{ "sieve_maxscriptsize", 32, INT, "2.3.17" }
/* Maximum size (in kilobytes) any sieve script can be, enforced at
submission by timsieved(8). */
{ "sieve_maxscripts", 5, INT, "2.3.17" }
/* Maximum number of sieve scripts any user may have, enforced at
submission by timsieved(8). */
{ "sieve_utf8fileinto", 0, SWITCH, "2.3.17" }
/* If enabled, the sieve engine expects folder names for the
\fIfileinto\fR action in scripts to use UTF8 encoding. Otherwise,
modified UTF7 encoding should be used. */
{ "sieve_sasl_send_unsolicited_capability", 0, SWITCH, "2.3.17" }
/* If enabled, timsieved will emit a capability response after a successful
SASL authentication, per draft-martin-managesieve-12.txt . */
{ "sieve_use_lmtp_reject", 1, SWITCH, "3.1.1" }
/* Enabled by default. If reject can be done via LMTP, then return a 550
rather than generating the bounce message in Cyrus. */
{ "sieve_vacation_min_response", "3d", DURATION, "3.1.8" }
/* Minimum time interval between consecutive vacation responses, per
draft-ietf-vacation-seconds.txt. The default is 3 days.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "sieve_vacation_max_response", "90d", DURATION, "3.1.8" }
/* Maximum time interval between consecutive vacation responses, per
draft-ietf-vacation-seconds.txt. The default is 90 days. The
minimum is 7 days.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "sievedir", "/usr/sieve", STRING, "2.3.17" }
/* If sieveusehomedir is false, this directory is searched for Sieve
scripts. */
{ "sievenotifier", NULL, STRING, "2.3.17" }
/* Notifyd(8) method to use for "SIEVE" notifications. If not set, "SIEVE"
notifications are disabled.
.PP
This method is only used when no method is specified in the script. */
{ "sieveusehomedir", 0, SWITCH, "2.3.17" }
/* If enabled, lmtpd will look for Sieve scripts in user's home
directories: ~user/.sieve. */
{ "anysievefolder", 0, SWITCH, "2.5.0" }
/* It must be "yes" in order to permit the autocreation of any INBOX subfolder
requested by a sieve filter, through the "fileinto" action. (default = no) */
{ "singleinstancestore", 1, SWITCH, "2.3.17" }
/* If enabled, imapd, lmtpd and nntpd attempt to only write one copy
of a message per partition and create hard links, resulting in a
potentially large disk savings. */
{ "skiplist_always_checkpoint", 1, SWITCH, "2.3.17" }
/* If enabled, this option forces the skiplist cyrusdb backend to
always checkpoint when doing a recovery. This causes slightly
more IO, but on the other hand leads to more efficient databases,
and the entire file is already "hot". */
{ "skiplist_unsafe", 0, SWITCH, "2.3.17" }
/* If enabled, this option forces the skiplist cyrusdb backend to
not sync writes to the disk. Enabling this option is NOT RECOMMENDED. */
{ "smtp_backend", "sendmail", STRINGLIST("host", "sendmail"), "3.1.4" }
/* The SMTP backend to use for sending email.
The \"host\" backend sends message submissions via
a TCP socket to the SMTP host defined in the config
option smtp_host.
The \"sendmail\" backend forks the Cyrus process into
the executable defined in the config option sendmail.
The executable must accept \"-bs\" as command line
argument, read from stdin and must implement the minimum
SMTP protocol as defined in section 4.5.1 of RFC 5321.
If the SMTP EHLO command reports AUTH (RFC 4954) as a
supported extension, then the MAIL FROM command includes
the AUTH parameter, with its value set to the name of any
authenticated user which triggered the email. The AUTH
parameter is omitted if the user is unknown to the calling
process.
If the directory
\fIconfigdirectory\fR/log/smtpclient.\ \fIsmtp_backend\fR
exists, then telemetry logs for outgoing SMTP sessions will
be created in this directory.
*/
{ "smtp_host", "localhost:587", STRING, "3.1.4" }
/* The SMTP host to use for sending mail (also see the
smtp_backend option). The value of this option must
the name or IP address of a TCP host, followed optionally
by a colon and the port or service to use. The default
port is 587. TLS may be activated by appending \"/tls\"
to the value. Authentication is enabled if smtp_auth_authname
is set. Authentication can be explicitly disabled by appending
\"/noauth\" to the host address. */
{ "smtp_auth_authname", NULL, STRING, "3.1.4" }
/* The authentication name to use when authenticating to the SMTP
server defined in smtp_host. */
{ "smtp_auth_password", NULL, STRING, "3.1.4" }
/* The password to use when authenticating to the SMTP server defined
in smtp_host. */
{ "smtp_auth_realm", NULL, STRING, "3.1.4" }
/* The authentication SASL realm to use when authenticating to a SMTP
server. */
{ "soft_noauth", 1, SWITCH, "2.3.17" }
/* If enabled, lmtpd returns temporary failures if the client does not
successfully authenticate. Otherwise lmtpd returns permanent failures
(causing the mail to bounce immediately). */
{ "sortcache_db", "twoskip", STRINGLIST("skiplist", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for caching sort results (currently only
used for xconvmultisort) */
{ "specialuse_extra", NULL, STRING, "2.5.0" }
/* Whitespace separated list of extra special-use attributes
that can be set on a mailbox. RFC 6154 currently lists
what special-use attributes can be set. This allows
extending that list in the future or adding your own
if needed. */
{ "specialuse_nochildren", NULL, STRING, "3.3.2" }
/* Whitespace separated list of special-use attributes that may not contain
child folders. If set, mailboxes with any of these attributes may not
have child folders created, and these attributes cannot be added to
mailboxes that already have children.. */
{ "specialuse_protect", "\\Archive \\Drafts \\Important \\Junk \\Sent \\Trash", STRING, "3.1.7" }
/* Whitespace separated list of special-use attributes
to protect the mailboxes for. If set, don't allow
mailboxes with these special use attributes to be deleted
or renamed to have a different parent. Default is the built-in list*/
{ "specialusealways", 1, SWITCH, "3.1.1" }
/* If enabled, this option causes LIST and LSUB output to always include
the XLIST "special-use" flags */
{ "sql_database", NULL, STRING, "2.3.17" }
/* Name of the database which contains the cyrusdb table(s). */
{ "sql_engine", NULL, STRINGLIST("mysql", "pgsql", "sqlite"), "2.3.17" }
/* Name of the SQL engine to use. */
{ "sql_hostnames", "", STRING, "2.3.17" }
/* Comma separated list of SQL servers (in host[:port] format). */
{ "sql_passwd", NULL, STRING, "2.3.17" }
/* Password to use for authentication to the SQL server. */
{ "sql_user", NULL, STRING, "2.3.17" }
/* Username to use for authentication to the SQL server. */
{ "sql_usessl", 0, SWITCH, "2.3.17" }
/* If enabled, a secure connection will be made to the SQL server. */
{ "srs_alwaysrewrite", 0, SWITCH, "2.5.0" }
/* If true, perform SRS rewriting for ALL forwarding, even when not required. */
{ "srs_domain", NULL, STRING, "3.1.2" }
/* The domain to use in rewritten addresses. This must point only to machines
which know the encoding secret used by this system. When present, SRS is
enabled. */
{ "srs_hashlength", 0, INT, "3.1.2" }
/* The hash length to generate in a rewritten address. */
{ "srs_secrets", NULL, STRING, "3.1.2" }
/* A list of secrets with which to generate addresses. */
{ "srs_separator", NULL, STRING, "3.1.2" }
/* The separator to appear immediately after SRS[01] in rewritten addresses. */
{ "srvtab", "", STRING, "2.3.17" }
/* The pathname of \fIsrvtab\fR file containing the server's private
key. This option is passed to the SASL library and overrides its
default setting. */
{ "submitservers", NULL, STRING, "2.3.17" }
/* A list of users and groups that are allowed to resolve "urlauth=submit+"
IMAP URLs, separated by spaces. Any user listed in this will be
allowed to fetch the contents of any valid "urlauth=submit+" IMAP URL:
use with caution. */
{ "subscription_db", "flat", STRINGLIST("flat", "skiplist", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for the subscriptions list. */
{ "suppress_capabilities", NULL, STRING, "3.0.0" }
/* Suppress the named capabilities from any capability response. Use the
exact case as it appears in the response, e.g.
"suppress_capabilities: ESEARCH QRESYNC WITHIN XLIST LIST-EXTENDED"
if you have a murder with 2.3.x backends and don't want clients being
confused by new capabilities that some backends don't support. */
{ "statuscache", 0, SWITCH, "2.3.17" }
/* Enable/disable the imap status cache. */
{ "statuscache_db", "twoskip", STRINGLIST("skiplist", "sql", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for the imap status cache. */
{ "statuscache_db_path", NULL, STRING, "2.5.0" }
/* The absolute path to the statuscache db file. If not specified,
will be configdirectory/statuscache.db */
{ "sync_authname", NULL, STRING, "2.5.0" }
/* The authentication name to use when authenticating to a sync server.
Prefix with a channel name to only apply for that channel */
{ "sync_batchsize", 8192, INT, "3.0.0" }
/* the number of messages to upload in a single mailbox replication.
Default is 8192. If there are more than this many messages appended
to the mailbox, generate a synthetic partial state and send that. */
{ "sync_cache_db", "twoskip", STRINGLIST("skiplist", "sql", "twoskip", "zeroskip"), "3.3.1" }
/* The cyrusdb backend to use for the replication cache. */
{ "sync_cache_db_path", NULL, STRING, "3.3.1" }
/* The path for the replication cache. Prefix with a
channel name to apply for that channel. NOTE, it's
quite important to have a different one per backend! */
{ "sync_host", NULL, STRING, "2.5.0" }
/* Name of the host (replica running sync_server(8)) to which
replication actions will be sent by sync_client(8).
Prefix with a channel name to only apply for that channel */
{ "sync_log", 0, SWITCH, "2.3.17" }
/* Enable replication action logging by lmtpd(8), imapd(8), pop3d(8),
and nntpd(8). The log {configdirectory}/sync/log is used by
sync_client(8) for "rolling" replication. */
{ "sync_log_chain", 0, SWITCH, "2.4.0" }
/* Enable replication action logging by sync_server as well, allowing
chaining of replicas. Use this on 'B' for A => B => C replication layout */
{ "sync_log_channels", NULL, STRING, "2.5.0" }
/* If specified, log all events to multiple log files in directories
specified by each "channel". Each channel can then be processed
separately, such as by multiple sync_client(8)s in a mesh replication
scheme, or by squatter(8) for rolling search index updates.
.PP
You can use "" (the two-character string U+22 U+22) to mean the
default sync channel. */
{ "sync_log_unsuppressable_channels", "squatter", STRING, "2.5.0" }
/* If specified, the named channels are exempt from the effect of setting
sync_log_chain:off, i.e. they are always logged to by the sync_server
process. This is only really useful to allow rolling search indexing
on a replica. */
{ "sync_password", NULL, STRING, "2.5.0" }
/* The default password to use when authenticating to a sync server.
Prefix with a channel name to only apply for that channel */
{ "sync_port", NULL, STRING, "3.0.0" }
/* Name of the service (or port number) of the replication service on
replica host. Prefix with a channel name to only apply for that
channel. If not specified, and if sync_try_imap is set to "yes"
(the default), then the replication client will first try "imap"
(port 143) to check if imapd supports replication. otherwise it
will default to "csync" (usually port 2005). */
{ "sync_realm", NULL, STRING, "2.5.0" }
/* The authentication realm to use when authenticating to a sync server.
Prefix with a channel name to only apply for that channel */
{ "sync_reconnect_maxwait", "20m", DURATION, "UNRELEASED" }
/* When a rolling sync_client cannot connect to the replica, it enters
a retry loop with an exponential backoff between attempts. This
option sets the upper limit on that exponential backoff: no matter
how long the replica has been down so far, sync_client will never
wait longer than sync_reconnect_maxwait between retries.
.PP
If this is zero or negative, the backoff duration will be allowed
to increase indefinitely (not recommended).
.PP
If no unit is specified, seconds is assumed. */
{ "sync_repeat_interval", "1s", DURATION, "3.1.8" }
/* Minimum interval between replication runs in rolling replication
mode. If a replication run takes longer than this time, we repeat
immediately. Prefix with a channel name to only apply for that
channel.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "sync_rightnow_channel", NULL, STRING, "3.3.1" }
/* if set, run sync_client to this channel immediately. As with channels,
set this value to '""' to sync the default channel! */
{ "sync_shutdown_file", NULL, STRING, "2.5.0" }
/* Simple latch used to tell sync_client(8) that it should shut down at the
next opportunity. Safer than sending signals to running processes.
Prefix with a channel name to only apply for that channel */
{ "sync_timeout", "30m", DURATION, "3.1.8" }
/* How long to wait for a response before returning a timeout failure
when talking to a replication peer (client or server). The minimum
duration is 3 seconds, the default is 30 minutes.
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "sync_try_imap", 1, SWITCH, "3.0.0" }
/* Whether sync_client should try to perform an IMAP connection
before falling back to csync. If this is set to "no",
sync_client will only use csync. Prefix with a channel name to
apply only for that channel */
{ "syslog_prefix", NULL, STRING, "3.1.8" }
/* String to be prepended to the process name in syslog entries. Can
be further overridden by setting the $CYRUS_SYSLOG_PREFIX environment
variable.
.PP
Using the $CYRUS_SYSLOG_PREFIX environment variable has the additional
advantage that it can be set before the \fBimapd.conf\fR is read, so
errors while reading the config file can be syslogged with the correct
prefix. */
{ "syslog_facility", NULL, STRING, "2.5.0" }
/* Configure a syslog facility. The default is whatever is compiled
in. Allowed values are: DAEMON, MAIL, NEWS, USER, and LOCAL0
through to LOCAL7 */
{ "tcp_keepalive", 0, SWITCH, "2.4.0" }
/* Enable keepalive on TCP connections */
{ "tcp_keepalive_cnt", 0, INT, "2.4.0" }
/* Number of TCP keepalive probes to send before declaring the
connection dead (0 == system default) */
{ "tcp_keepalive_idle", "0", DURATION, "3.1.8" }
/* How long a connection must be idle before keepalive probes are sent
(0 == system default).
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "tcp_keepalive_intvl", "0", DURATION, "3.1.8" }
/* Time between keepalive probes (0 == system default).
.PP
For backward compatibility, if no unit is specified, seconds is
assumed. */
{ "temp_path", "/tmp", STRING, "3.3.0" }
/* The pathname to store temporary files in. It is recommended to
use an in-memory filesystem such as tmpfs for this path. */
{ "telemetry_bysessionid", 0, SWITCH, "3.0.0" }
/* If true, log by sessionid instead of PID for telemetry */
{ "timeout", "32m", DURATION, "3.1.8" }
/* The length of the IMAP server's inactivity autologout timer.
The minimum value is 30 minutes. The default is 32 minutes,
to allow a bit of leeway for clients that try to NOOP every
30 minutes.
.PP
For backward compatibility, if no unit is specified, minutes
is assumed. */
{ "imapidletimeout", NULL, DURATION, "3.1.8" }
/* Timeout for idling clients (RFC 2177). If not set (the default),
the value of "timeout" will be used instead.
.PP
For backward compatibility, if no unit is specified, minutes
is assumed. */
{ "tls_ca_file", NULL, STRING, "2.5.0", "2.5.0", "tls_client_ca_file" }
/* Deprecated in favor of \fItls_client_ca_file\fR. */
{ "tls_ca_path", NULL, STRING, "2.5.0", "2.5.0", "tls_client_ca_dir" }
/* Deprecated in favor of \fItls_client_ca_dir\fR. */
{ "tlscache_db", "twoskip", STRINGLIST("skiplist", "sql", "twoskip", "zeroskip"), "2.5.0", "2.5.0", "tls_sessions_db" }
/* Deprecated in favor of \fItls_sessions_db\fR. */
{ "tlscache_db_path", NULL, STRING, "2.5.0", "2.5.0", "tls_sessions_db_path" }
/* Deprecated in favor of \fItls_sessions_db_path\fR. */
{ "tls_cert_file", NULL, STRING, "2.5.0", "2.5.0", "tls_server_cert" }
/* Deprecated in favor of \fItls_server_cert\fR. */
{ "tls_cipher_list", "DEFAULT", STRING, "2.5.0", "2.5.0", "tls_ciphers" }
/* Deprecated in favor of \fItls_ciphers\fR. */
{ "tls_ciphers", "DEFAULT", STRING, "2.5.0" }
/* The list of SSL/TLS ciphers to allow. The format of the string
(and definition of "DEFAULT") is described in \fBciphers(1)\fR.
.PP
See also Mozilla's server-side TLS recommendations:
.PP
https://wiki.mozilla.org/Security/Server_Side_TLS */
{ "tls_crl_file", NULL, STRING, "3.1.2" }
/* Path to a file containing the Certificate Revocation List */
{ "tls_client_ca_dir", NULL, STRING, "2.5.0" }
/* Path to a directory containing the CA certificates used to verify
client SSL certificates used for authentication. */
{ "tls_client_ca_file", NULL, STRING, "2.5.0" }
/* Path to a file containing the CA certificate(s) used to verify
client SSL certificates used for authentication. */
{ "tls_client_cert", NULL, STRING, "2.5.0" }
/* File containing the certificate presented to a server for authentication
during STARTTLS. A value of "disabled" will disable this server's use
of certificate-based authentication. */
{ "tls_client_certs", "optional", ENUM("off", "optional", "require"), "2.5.0" }
/* Disable ("off"), allow ("optional", default) or require ("require") the
use of SSL certificates by clients to authenticate themselves. */
{ "tls_client_key", NULL, STRING, "2.5.0" }
/* File containing the private key belonging to the tls_client_cert
certificate. A value of "disabled" will disable this server's use
of certificate-based authentication. */
{ "tls_eccurve", "prime256v1", STRING, "2.5.0" }
/* The elliptic curve used for ECDHE. Default is NIST Suite B prime256.
See 'openssl ecparam -list_curves' for possible values. */
{ "tls_key_file", NULL, STRING, "2.5.0", "2.5.0", "tls_server_key" }
/* Deprecated in favor of \fItls_server_key\fR. */
{ "tls_required", 0, SWITCH, "3.0.0" }
/* If enabled, require a TLS/SSL encryption layer to be negotiated
prior to ANY authentication mechanisms being advertised or allowed. */
{ "tls_prefer_server_ciphers", 0, SWITCH, "2.5.0" }
/* Prefer the ciphers on the server side instead of client side. */
{ "tls_server_ca_dir", NULL, STRING, "2.5.0" }
/* Path to a directory with CA certificates used to verify certificates
offered by the server, when cyrus acts as client. This directory must
have filenames with the hashed value of the certificates (see
openssl(1)). */
{ "tls_server_ca_file", NULL, STRING, "2.5.0" }
/* Path to a file containing CA certificates used to verify certificates
offered by the server, when cyrus acts as client. */
{ "tls_server_cert", NULL, STRING, "3.1.8" }
/* File containing the certificate, including the full chain, presented to clients.
Two certificates can be set, e.g RSA and EC, if the filenames are separated with
comma without spaces. */
{ "tls_server_dhparam", NULL, STRING, "3.1.4" }
/* File containing the DH parameters belonging to the certificate in
tls_server_cert. */
{ "tls_server_key", NULL, STRING, "3.1.8" }
/* File containing the private key belonging to the certificate in
tls_server_cert. If not set, tls_server_cert must contain both private and
public key. Two files with keys can be set, if two certificates are used, in
which case the files must be separated with comma without spaces */
{ "tls_sessions_db", "twoskip", STRINGLIST("skiplist", "sql", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for the TLS cache. */
{ "tls_sessions_db_path", NULL, STRING, "2.5.0" }
/* The absolute path to the TLS sessions db file. If not specified,
will be configdirectory/tls_sessions.db */
{ "tls_session_timeout", "24h", DURATION, "3.1.8" }
/* The length of time that a TLS session will be cached for later
reuse. The maximum value is 24 hours, also the default. A
value of 0 will disable session caching.
.PP
For backward compatibility, if no unit is specified, minutes is
assumed. */
{ "tls_versions", "tls1_0 tls1_1 tls1_2 tls1_3", STRING, "3.1.8" }
/* A list of SSL/TLS versions to not disable. Cyrus IMAP SSL/TLS starts
with all protocols, and subtracts protocols not in this list. Newer
versions of SSL/TLS will need to be added here to allow them to get
disabled. */
{ "uidl_format", "cyrus", ENUM("uidonly", "cyrus", "dovecot", "courier"), "3.0.0" }
/* Choose the format for UIDLs in pop3. Possible values are "uidonly",
"cyrus", "dovecot" and "courier". "uidonly" forces the old default
of UID, "cyrus" is UIDVALIDITY.UID. Dovecot is 8 digits of leading
hex (lower case) each UID UIDVALIDITY. Courier is UIDVALIDITY-UID. */
{ "umask", "077", STRING, "2.3.17" }
/* The umask value used by various Cyrus IMAP programs. */
{ "userdeny_db", "flat", STRINGLIST("flat", "skiplist", "sql", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for the user access list. */
{ "userdeny_db_path", NULL, STRING, "2.5.0" }
/* The absolute path to the userdeny db file. If not specified,
will be configdirectory/user_deny.db */
{ "username_tolower", 1, SWITCH, "2.3.17" }
/* Convert usernames to all lowercase before login/authentication. This
is useful with authentication backends which ignore case during
username lookups (such as LDAP). */
{ "userprefix", "Other Users", STRING, "2.3.17" }
/* If using the alternate IMAP namespace, the prefix for the other users
namespace. The hierarchy delimiter will be automatically appended. */
# xxx badly worded
{ "unix_group_enable", 1, SWITCH, "2.3.17" }
/* Should we look up groups when using auth_unix (disable this if you are
not using groups in ACLs for your IMAP server, and you are using auth_unix
with a backend (such as LDAP) that can make getgrent() calls very
slow) */
{ "unixhierarchysep", 1, SWITCH, "3.0.0" }
/* Use the UNIX separator character '/' for delimiting levels of
mailbox hierarchy. Turn off to use the netnews separator
character '.'. Note that with the newnews separator, no dots may
occur in mailbox names. The default switched in 3.0 from off to on. */
{ "vcard_max_size", 0, INT, "UNRELEASED" }
/* Maximum allowed vCard size.
If non-zero, CardDAV and JMAP will reject storage of contacts whose
vCard representation is larger than \fIvcard_max_size\fR bytes.
If set to 0, this will allow vCards of any size (the default). */
{ "virtdomains", "off", ENUM("off", "userid", "on"), "3.1.8" }
/* Configure virtual domain support.
.PP
.IP off
Cyrus does not know or care about domains. Only the local part of email
addresses is ever considered. This is not recommended for any deployment,
but is currently the default.
.\"
.IP userid
The user's domain is determined by splitting a fully qualified userid at the
last '@' or '%' symbol. If the userid is unqualified, the \fIdefaultdomain\fR
will be used. This is the recommended configuration for all deployments.
If you wish to provide calendaring services you must use this configuration.
.\"
.IP on
Fully qualified userids are respected, as per "userid". Unqualified userids
will have their domain determined by doing a reverse lookup on the IP address
of the incoming network interface, or if no record is found, the
\fIdefaultdomain\fR will be used.
.PP
*/
{ "virusscan_notification_subject", "Automatically deleted mail", STRING, "3.1.8" }
/* The text used in the subject of email notifications created by
\fBcyr_virusscan(8)\fR when deleting infected mail. */
{ "virusscan_notification_template", NULL, STRING, "3.1.8" }
/* The absolute path to a file containing a template to use to describe
infected messages that have been deleted by \fBcyr_virusscan(8)\fR.
See \fBcyr_virusscan(8)\fR for specification of the format of this file.
If not specified, the builtin default template will be used. */
{ "websocket_timeout", "30m", DURATION, "UNRELEASED" }
/* Set the length of the HTTP server's inactivity autologout timer
when a WebSocket channel has been established.
The default is 30 minutes. The minimum value is 0, which will
disable WebSockets.
.PP
If no unit is specified, minutes is assumed. */
{ "xbackup_enabled", 0, SWITCH, "3.0.0" }
/* Enable support for the XBACKUP command in imapd. If enabled, admin
users can use this command to provoke a replication of specified users
to the named backup channel. */
# Commented out - there's no such thing as "xlist-flag", but we need
# this for the man page
# { "xlist-flag", NULL, STRING, "3.0.0" }
/* Set the special-use flag \fIflag\fR on the specified folder when it
is autocreated (see the \fIautocreate_inbox_folders\fR option). For
example, if \fBxlist-junk: Spam\fR is set, and the folder \fBSpam\fR
is autocreated, the special-use flag \fB\\Junk\fR will be set on it.
.PP
(This option is so named for backward compatibility with old config
files.)
*/
{ "lmtp_catchall_mailbox", NULL, STRING, "2.5.0" }
/* Mail sent to mailboxes which do not exist, will be delivered to
this user. NOTE: This must be an existing local user name with an
INBOX, NOT an email address! */
{ "zoneinfo_db", "twoskip", STRINGLIST("flat", "skiplist", "twoskip", "zeroskip"), "3.1.6" }
/* The cyrusdb backend to use for zoneinfo. This database is used by the
"tzdist" \fIhttpmodules\fR, and is managed by \fBctl_zoneinfo(8)\fR.*/
{ "zoneinfo_db_path", NULL, STRING, "2.5.0" }
/* The absolute path to the zoneinfo db file. If not specified,
will be configdirectory/zoneinfo.db */
{ "zoneinfo_dir", NULL, STRING, "3.2.0" }
/* The absolute path to the zoneinfo directory, containing timezone
definitions as generated by the vzic tool. If not specified, whatever
definitions libical finds will be used.
.PP
If you are providing a Time Zone Data Distribution Service (i.e. you have
"tzdist" listed in \fIhttpmodules\fR), then this configuration option MUST
be specified. */
{ "object_storage_enabled", 0, SWITCH, "3.0.0" }
/* Is Object storage enabled for this server. You also need to have
archiving enabled and archivepartition for the mailbox.
Only email files will be stored on object Storage archive partition will be
used to store any other files */
{ "object_storage_dummy_spool", NULL, STRING, "3.0.0" }
/* Dummy object storage spool; this is for test only.
Spool where user directory (container) will be created to store all emails
in a flat structure */
{ "openio_namespace", NULL, STRING, "3.0.0" }
/* The OpenIO namespace used to store archived email messages. A namespace
identifies the physical platform cyrus must contact. This directive is used
by the OpenIO's SDK to locate its platform entry point. */
{ "openio_account", NULL, STRING, "3.0.0" }
/* The OpenIO account used to account for stored emails. Accounts are unique
in their namespace. They provides virtual partitions, with quotas and QoS
features. */
{ "openio_rawx_timeout", "30s", DURATION, "3.1.8" }
/* The OpenIO timeout to query to the RAWX services (default 30 sec). */
{ "openio_proxy_timeout", "5s", DURATION, "3.1.8" }
/* The OpenIO timeout to query to the PROXY services (default 5 sec). */
{ "openio_autocreate", 0, SWITCH, "3.0.0" }
/* Allow the OpenIO SDK to autocreate containers. Mainly destined to be turned
on development environments. In production, the container should have been
provisioned with the mailboxes. */
{ "openio_verbosity", NULL, STRING, "3.0.0" }
/* Sets the logging verbosity of the OpenIO's internal behavior. Admissible
values are: "warning", "notice", "info", "debug", "trace", "quiet".
The default verbosity is "warning". Set to "notice" for a few lines on a
per-client basis. Set to "info" for a few lines on a per-request basis. Set
to "debug" Set to "trace" to activate the underlying libcurl debug
output. Enabling a verbosity higher to equal than "debug" requires
the cyrus to be set in debug mode. The special "quiet" value disables all
kinds of logging at the GLib level. */
{ "caringo_hostname", NULL, STRING, "3.0.0" }
/* The Caringo hostname used to store archived email messages. A hostname
identifies the physical platform cyrus must contact. This directive is used
by the Caringo's SDK (CastorSDK: Caringo Simple Content Storage Protocol (SCSP)
on HTTP 1.1 using a RESTful architecture */
{ "caringo_port", 80, INT, "3.0.0" }
/* The port of the caringo server (caringo_hostname); default is 80. */
{ "fastmailsharing", 0, SWITCH, "3.0.0" }
/* If enabled, use FastMail style sharing (oldschool full server paths) */
/*
.SH SEE ALSO
.PP
\fBimapd(8)\fR, \fBpop3d(8)\fR, \fBnntpd(8)\fR, \fBlmtpd(8)\fR,
\fBhttpd(8)\fR, \fBtimsieved(8)\fR, \fBidled(8)\fR, \fBnotifyd(8)\fR,
\fBdeliver(8)\fR, \fBmaster(8)\fR, \fBciphers(1)\fR
*/

File Metadata

Mime Type
text/x-diff
Expires
Sat, Apr 4, 3:32 AM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18822428
Default Alt Text
(322 KB)

Event Timeline