From 6bba6801f4c3e4ba73737079094b9199d9963492 Mon Sep 17 00:00:00 2001 From: Tomas Janousek Date: Apr 04 2007 15:18:08 +0000 Subject: merge from fc5: Wed Apr 04 2007 Tomas Janousek - 2.3.8-2 - fixed mboxlist backup rotation (#197054) little cleanup --- diff --git a/.cvsignore b/.cvsignore index 606923d..f343dfe 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1,2 +1,3 @@ cyrus_sharedbackup-0.1.tar.gz cyrus-imapd-2.3.8.tar.gz +cyrus-imapd-2.3.8.tar.gz.sig diff --git a/cyrus-imapd-2.3.3-autosieve-0.6.0.diff b/cyrus-imapd-2.3.3-autosieve-0.6.0.diff deleted file mode 100644 index 63eb9fa..0000000 --- a/cyrus-imapd-2.3.3-autosieve-0.6.0.diff +++ /dev/null @@ -1,181 +0,0 @@ -diff -Naur cyrus-imapd-2.3.3/README.autosievefolder cyrus-imapd-2.3.3-autosieve.uncompiled/README.autosievefolder ---- cyrus-imapd-2.3.3/README.autosievefolder 1970-01-01 02:00:00.000000000 +0200 -+++ cyrus-imapd-2.3.3-autosieve.uncompiled/README.autosievefolder 2006-03-01 16:57:26.000000000 +0200 -@@ -0,0 +1,42 @@ -+Cyrus IMAP autosievefolder patch -+---------------------------------- -+ -+NOTE : This patch has been created at the University of Athens. For more info, as well -+as more patches on Cyrus IMAPD server, please visit http://email.uoa.gr -+ -+ -+ When the lmtpd daemon receives an email message prior to delivering it to the -+INBOX folder of the user, checks if the user has specified sieve filters. If the -+user has specified sieve filters the filters are evaluated. If the message matches -+any of the filters the action that is specified in the filter is executed. If the action -+is FileInto it is stored in the subfolder specified in the filter. If the -+subfolder doesn't exist then the message is sent to the INBOX folder of the user. -+ -+ With this patch if the folder doesn't exist AND the name of the subfolder is -+specified in the autosievefolders option, OR the anysievefolder is set to -+yes in the cyrus-imap configuration file then the subfolder is created and the mail -+is stored there. -+ -+ -+Check the following options of the imapd.conf file -+================================================== -+ -+* anysievefolder : 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) -+* autosievefolders : It is a "|" separated list of subfolders of INBOX that will be -+automatically created, if requested by a sieve filter, through the "fileinto" -+action. (default = null) -+ i.e. autosievefolders: Junk | Spam -+ -+WARNING: anysievefolder, takes precedence over autosievefolders . Which means that if -+anysievefolder is set to "yes", cyrus will create any INBOX subfolder requested, no-matter what the value of autosievefolders is. -+ -+ -+Things to be done -+================= -+ -+1. Support cyrus wildcards in the autosievefolders option. -+ -+ -+For more information and updates please visit http://email.uoa.gr/projects/cyrus/autosievefolder -+ -diff -Naur cyrus-imapd-2.3.3/imap/lmtp_sieve.c cyrus-imapd-2.3.3-autosieve.uncompiled/imap/lmtp_sieve.c ---- cyrus-imapd-2.3.3/imap/lmtp_sieve.c 2005-11-21 18:26:54.000000000 +0200 -+++ cyrus-imapd-2.3.3-autosieve.uncompiled/imap/lmtp_sieve.c 2006-03-01 16:57:26.000000000 +0200 -@@ -86,6 +86,9 @@ - struct auth_state *authstate; - } script_data_t; - -+static int autosieve_subfolder(char *userid, struct auth_state *auth_state, -+ char *subfolder, struct namespace *namespace); -+ - static char *make_sieve_db(const char *user) - { - static char buf[MAX_MAILBOX_PATH+1]; -@@ -487,7 +490,20 @@ - sd->username, mdata->notifyheader, - namebuf, quotaoverride, 0); - } -- -+ -+ if (ret == IMAP_MAILBOX_NONEXISTENT) { -+ /* if "plus" folder under INBOX, then try to create it */ -+ ret = autosieve_subfolder((char *) sd->username, sd->authstate, namebuf, mdata->namespace); -+ -+ /* Try to deliver the mail again. */ -+ if (!ret) -+ ret = deliver_mailbox(md->f, mdata->content, mdata->stage, md->size, -+ fc->imapflags->flag, fc->imapflags->nflags, -+ (char *) sd->username, sd->authstate, md->id, -+ sd->username, mdata->notifyheader, -+ namebuf, quotaoverride, 0); -+ } -+ - if (!ret) { - snmp_increment(SIEVE_FILEINTO, 1); - return SIEVE_OK; -@@ -939,3 +955,80 @@ - we'll do normal delivery */ - return r; - } -+ -+ -+#define SEP '|' -+ -+static int autosieve_subfolder(char *userid, struct auth_state *auth_state, -+ char *subfolder, struct namespace *namespace) -+{ -+ char option_name_external[MAX_MAILBOX_NAME + 1]; -+ char option_name_internal[MAX_MAILBOX_NAME + 1]; -+ const char *subf ; -+ char *p, *q, *next_subf; -+ int len, r = 0; -+ int createsievefolder = 0; -+ -+ /* Check if subfolder or userid are NULL */ -+ if(userid == NULL || subfolder == NULL) -+ return IMAP_MAILBOX_NONEXISTENT; -+ -+ syslog(LOG_DEBUG, "autosievefolder: autosieve_subfolder() was called for user %s, folder %s", -+ userid, subfolder); -+ -+ if (config_getswitch(IMAPOPT_ANYSIEVEFOLDER)) { -+ createsievefolder = 1; -+ } else if ((subf = config_getstring(IMAPOPT_AUTOSIEVEFOLDERS)) != NULL) { -+ /* Roll through subf */ -+ next_subf = (char *) subf; -+ while (*next_subf) { -+ for (p = next_subf ; isspace((int) *p) || *p == SEP ; p++); -+ for (next_subf = p ; *next_subf && *next_subf != SEP ; next_subf++); -+ for (q = next_subf ; q > p && (isspace((int) *q) || *q == SEP || !*q); q--); -+ -+ if (!*p) continue; -+ -+ len = q - p + 1; -+ /* -+ * This is a preliminary length check based on the assumption -+ * that the *final* internal format will be something -+ * like user.userid.subfolder(s). -+ */ -+ if (len > sizeof(option_name_external) - strlen(userid) - 5) -+ return IMAP_MAILBOX_BADNAME; -+ -+ strlcpy(option_name_external, namespace->prefix[NAMESPACE_INBOX], sizeof(option_name_external)); -+ strncat(option_name_external, p, len); -+ -+ /* -+ * Transform the option folder name to internal namespace and compare it -+ * with what must be created. -+ */ -+ r = namespace->mboxname_tointernal(namespace, option_name_external, userid, option_name_internal); -+ if (r) continue; -+ -+ if (!strcmp(option_name_internal, subfolder)) { -+ createsievefolder = 1; -+ break; -+ } -+ } -+ } -+ -+ if (createsievefolder) { -+ /* Folder is already in internal namespace format */ -+ r = mboxlist_createmailbox(subfolder, MAILBOX_FORMAT_NORMAL, NULL, -+ 1, userid, auth_state, 0, 0, 0); -+ if (!r) { -+ mboxlist_changesub(subfolder, userid, auth_state, 1, 1); -+ syslog(LOG_DEBUG, "autosievefolder: User %s, folder %s creation succeeded", -+ userid, subfolder); -+ return 0; -+ } else { -+ syslog(LOG_ERR, "autosievefolder: User %s, folder %s creation failed. %s", -+ userid, subfolder,error_message(r)); -+ return r; -+ } -+ } else -+ return IMAP_MAILBOX_NONEXISTENT; -+} -+ -diff -Naur cyrus-imapd-2.3.3/lib/imapoptions cyrus-imapd-2.3.3-autosieve.uncompiled/lib/imapoptions ---- cyrus-imapd-2.3.3/lib/imapoptions 2006-02-01 21:44:06.000000000 +0200 -+++ cyrus-imapd-2.3.3-autosieve.uncompiled/lib/imapoptions 2006-03-01 16:57:26.000000000 +0200 -@@ -863,6 +863,15 @@ - /* If enabled, lmtpd will look for Sieve scripts in user's home - directories: ~user/.sieve. */ - -+{ "anysievefolder", 0, SWITCH } -+/* 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) */ -+ -+{ "autosievefolders", NULL, STRING } -+/* It is a "|" separated list of subfolders of INBOX that will be automatically created, -+ if requested by a sieve filter, through the "fileinto" action. (default = null) -+ i.e. autosievefolders: Junk | Spam */ -+ - { "singleinstancestore", 1, SWITCH } - /* If enabled, imapd, lmtpd and nntpd attempt to only write one copy - of a message per partition and create hard links, resulting in a diff --git a/cyrus-imapd-2.3.7-autocreate-0.10-0.diff b/cyrus-imapd-2.3.7-autocreate-0.10-0.diff deleted file mode 100644 index ab88e4e..0000000 --- a/cyrus-imapd-2.3.7-autocreate-0.10-0.diff +++ /dev/null @@ -1,12929 +0,0 @@ ---- cyrus-imapd-2.3.7/imap/Makefile.in.autocreate0 2006-03-15 19:56:29.000000000 +0100 -+++ cyrus-imapd-2.3.7/imap/Makefile.in 2006-07-23 12:37:31.000000000 +0200 -@@ -101,7 +101,7 @@ - convert_code.o duplicate.o saslclient.o saslserver.o signals.o \ - annotate.o search_engines.o squat.o squat_internal.o mbdump.o \ - imapparse.o telemetry.o user.o notify.o protocol.o idle.o quota_db.o \ -- sync_log.o $(SEEN) mboxkey.o backend.o tls.o -+ sync_log.o $(SEEN) autosieve.o mboxkey.o backend.o tls.o - - IMAPDOBJS=pushstats.o imapd.o proxy.o imap_proxy.o index.o version.o - -@@ -117,7 +117,7 @@ - fud smmapd reconstruct quota mbpath ipurge \ - cyrdump chk_cyrus cvt_cyrusdb deliver ctl_mboxlist \ - ctl_deliver ctl_cyrusdb squatter mbexamine cyr_expire arbitron \ -- unexpunge @IMAP_PROGS@ -+ unexpunge compile_sieve @IMAP_PROGS@ - - BUILTSOURCES = imap_err.c imap_err.h pushstats.c pushstats.h \ - lmtpstats.c lmtpstats.h xversion.h mupdate_err.c mupdate_err.h \ -@@ -185,7 +185,7 @@ - ### Services - idled: idled.o mutex_fake.o libimap.a $(DEPLIBS) - $(CC) $(LDFLAGS) -o idled \ -- idled.o mutex_fake.o libimap.a $(DEPLIBS) $(LIBS) -+ idled.o mutex_fake.o libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - - lmtpd: lmtpd.o proxy.o $(LMTPOBJS) $(SIEVE_OBJS) mutex_fake.o \ - libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(SERVICE) -@@ -199,10 +199,10 @@ - $(SERVICE) lmtpd.o proxy.o $(LMTPOBJS) $(SIEVE_OBJS) \ - mutex_fake.o libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) $(LIB_WRAP) - --imapd: xversion $(IMAPDOBJS) mutex_fake.o libimap.a $(DEPLIBS) $(SERVICE) -+imapd: xversion $(IMAPDOBJS) mutex_fake.o libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(SERVICE) - $(CC) $(LDFLAGS) -o imapd \ - $(SERVICE) $(IMAPDOBJS) mutex_fake.o \ -- libimap.a $(DEPLIBS) $(LIBS) $(LIB_WRAP) -+ libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) $(LIB_WRAP) - - imapd.pure: $(IMAPDOBJS) mutex_fake.o libimap.a $(DEPLIBS) $(SERVICE) - $(PURIFY) $(PUREOPT) $(CC) $(LDFLAGS) -o imapd.pure \ -@@ -219,7 +219,7 @@ - $(CC) $(LDFLAGS) -o mupdate \ - $(SERVICETHREAD) mupdate.o mupdate-slave.o mupdate-client.o \ - mutex_pthread.o tls.o libimap.a \ -- $(DEPLIBS) $(LIBS) $(LIB_WRAP) -lpthread -+ $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) $(LIB_WRAP) -lpthread - - mupdate.pure: mupdate.o mupdate-slave.o mupdate-client.o mutex_pthread.o \ - libimap.a $(DEPLIBS) -@@ -228,118 +228,122 @@ - mutex_pthread.o libimap.a $(DEPLIBS) $(LIBS) $(LIB_WRAP) -lpthread - - pop3d: pop3d.o proxy.o backend.o tls.o mutex_fake.o libimap.a \ -- $(DEPLIBS) $(SERVICE) -+ $(SIEVE_LIBS) $(DEPLIBS) $(SERVICE) - $(CC) $(LDFLAGS) -o pop3d pop3d.o proxy.o backend.o tls.o $(SERVICE) \ -- mutex_fake.o libimap.a $(DEPLIBS) $(LIBS) $(LIB_WRAP) -+ mutex_fake.o libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) $(LIB_WRAP) - - nntpd: nntpd.o proxy.o backend.o index.o smtpclient.o spool.o tls.o \ -- mutex_fake.o nntp_err.o libimap.a $(DEPLIBS) $(SERVICE) -+ mutex_fake.o nntp_err.o libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(SERVICE) - $(CC) $(LDFLAGS) -o nntpd nntpd.o proxy.o backend.o index.o spool.o \ - smtpclient.o tls.o $(SERVICE) mutex_fake.o nntp_err.o \ -- libimap.a $(DEPLIBS) $(LIBS) $(LIB_WRAP) -+ libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) $(LIB_WRAP) - --fud: fud.o libimap.a mutex_fake.o $(DEPLIBS) $(SERVICE) -+fud: fud.o libimap.a mutex_fake.o $(DEPLIBS) $(SERVICE) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o fud $(SERVICE) fud.o mutex_fake.o libimap.a \ -- $(DEPLIBS) $(LIBS) $(LIB_WRAP) -+ $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) $(LIB_WRAP) - --smmapd: smmapd.o libimap.a mutex_fake.o $(DEPLIBS) $(SERVICE) -+smmapd: smmapd.o libimap.a mutex_fake.o $(DEPLIBS) $(SERVICE) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o smmapd $(SERVICE) smmapd.o mutex_fake.o libimap.a \ -- $(DEPLIBS) $(LIBS) $(LIB_WRAP) -+ $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) $(LIB_WRAP) - - sync_server: sync_server.o sync_support.o sync_commit.o \ -- imapparse.o tls.o libimap.a mutex_fake.o $(DEPLIBS) $(SERVICE) -+ imapparse.o tls.o libimap.a mutex_fake.o $(DEPLIBS) $(SERVICE) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o \ - sync_server sync_server.o sync_support.o sync_commit.o \ - imapparse.o tls.o $(SERVICE) libimap.a mutex_fake.o \ -- $(DEPLIBS) $(LIBS) $(LIB_WRAP) -+ $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) $(LIB_WRAP) - - ### Command Line Utilities --arbitron: arbitron.o $(CLIOBJS) libimap.a $(DEPLIBS) -+arbitron: arbitron.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o arbitron arbitron.o $(CLIOBJS) \ -- libimap.a $(DEPLIBS) $(LIBS) -+ libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --cvt_cyrusdb: cvt_cyrusdb.o mutex_fake.o libimap.a $(DEPLIBS) -+cvt_cyrusdb: cvt_cyrusdb.o mutex_fake.o libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o cvt_cyrusdb cvt_cyrusdb.o $(CLIOBJS) \ -- libimap.a $(DEPLIBS) $(LIBS) -+ libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --chk_cyrus: chk_cyrus.o mutex_fake.o libimap.a $(DEPLIBS) -+chk_cyrus: chk_cyrus.o mutex_fake.o libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o chk_cyrus chk_cyrus.o $(CLIOBJS) \ -- libimap.a $(DEPLIBS) $(LIBS) -+ libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --deliver: deliver.o $(LMTPOBJS) proxy.o mutex_fake.o libimap.a $(DEPLIBS) -+deliver: deliver.o $(LMTPOBJS) proxy.o mutex_fake.o libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o deliver deliver.o $(LMTPOBJS) proxy.o \ -- mutex_fake.o libimap.a $(DEPLIBS) $(LIBS) -+ mutex_fake.o libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --ctl_deliver: ctl_deliver.o $(CLIOBJS) libimap.a $(DEPLIBS) -+ctl_deliver: ctl_deliver.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o \ -- $@ ctl_deliver.o $(CLIOBJS) libimap.a $(DEPLIBS) $(LIBS) -+ $@ ctl_deliver.o $(CLIOBJS) libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --ctl_mboxlist: ctl_mboxlist.o mupdate-client.o $(CLIOBJS) libimap.a $(DEPLIBS) -+ctl_mboxlist: ctl_mboxlist.o mupdate-client.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o $@ ctl_mboxlist.o mupdate-client.o $(CLIOBJS) \ -- libimap.a $(DEPLIBS) $(LIBS) -+ libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --ctl_cyrusdb: ctl_cyrusdb.o $(CLIOBJS) libimap.a $(DEPLIBS) -+ctl_cyrusdb: ctl_cyrusdb.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o \ -- $@ ctl_cyrusdb.o $(CLIOBJS) libimap.a $(DEPLIBS) $(LIBS) -+ $@ ctl_cyrusdb.o $(CLIOBJS) libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --cyr_expire: cyr_expire.o $(CLIOBJS) libimap.a $(DEPLIBS) -+cyr_expire: cyr_expire.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o $@ cyr_expire.o $(CLIOBJS) \ -- libimap.a $(DEPLIBS) $(LIBS) -+ libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --fetchnews: fetchnews.o $(CLIOBJS) libimap.a $(DEPLIBS) -+fetchnews: fetchnews.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o \ -- $@ fetchnews.o $(CLIOBJS) libimap.a $(DEPLIBS) $(LIBS) -+ $@ fetchnews.o $(CLIOBJS) libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --squatter: squatter.o index.o squat_build.o $(CLIOBJS) libimap.a $(DEPLIBS) -+squatter: squatter.o index.o squat_build.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o squatter squatter.o index.o squat_build.o \ -- $(CLIOBJS) libimap.a $(DEPLIBS) $(LIBS) -+ $(CLIOBJS) libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --mbpath: mbpath.o $(CLIOBJS) libimap.a $(DEPLIBS) -+mbpath: mbpath.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o mbpath mbpath.o $(CLIOBJS) libimap.a \ -- $(DEPLIBS) $(LIBS) -- --ipurge: ipurge.o $(CLIOBJS) libimap.a $(DEPLIBS) -+ $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) -+ -+ipurge: ipurge.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o ipurge ipurge.o $(CLIOBJS) \ -- libimap.a $(DEPLIBS) $(LIBS) -+ libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --cyrdump: cyrdump.o index.o $(CLIOBJS) libimap.a $(DEPLIBS) -+cyrdump: cyrdump.o index.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o cyrdump cyrdump.o index.o $(CLIOBJS) \ -- libimap.a $(DEPLIBS) $(LIBS) -+ libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --mbexamine: mbexamine.o $(CLIOBJS) libimap.a $(DEPLIBS) -+mbexamine: mbexamine.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o \ -- mbexamine mbexamine.o $(CLIOBJS) libimap.a $(DEPLIBS) $(LIBS) -+ mbexamine mbexamine.o $(CLIOBJS) libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --reconstruct: reconstruct.o $(CLIOBJS) libimap.a $(DEPLIBS) -+reconstruct: reconstruct.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o \ -- reconstruct reconstruct.o $(CLIOBJS) libimap.a $(DEPLIBS) $(LIBS) -+ reconstruct reconstruct.o $(CLIOBJS) libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --quota: quota.o $(CLIOBJS) libimap.a $(DEPLIBS) -+quota: quota.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o quota quota.o $(CLIOBJS) \ -- libimap.a $(DEPLIBS) $(LIBS) -+ libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --tls_prune: tls_prune.o tls.o $(CLIOBJS) libimap.a $(DEPLIBS) -+tls_prune: tls_prune.o tls.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o \ -- $@ tls_prune.o tls.o $(CLIOBJS) libimap.a $(DEPLIBS) $(LIBS) -+ $@ tls_prune.o tls.o $(CLIOBJS) libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --unexpunge: unexpunge.o $(CLIOBJS) libimap.a $(DEPLIBS) -+unexpunge: unexpunge.o $(CLIOBJS) libimap.a $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o $@ unexpunge.o $(CLIOBJS) \ -- libimap.a $(DEPLIBS) $(LIBS) -+ libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - --make_md5: make_md5.o libimap.a mutex_fake.o $(DEPLIBS) -- $(CC) $(LDFLAGS) -o make_md5 make_md5.o libimap.a mutex_fake.o $(DEPLIBS) $(LIBS) -+make_md5: make_md5.o libimap.a mutex_fake.o $(DEPLIBS) $(SIEVE_LIBS) -+ $(CC) $(LDFLAGS) -o make_md5 make_md5.o libimap.a mutex_fake.o $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - - sync_client: sync_client.o sync_support.o \ -- backend.o tls.o imapparse.o libimap.a mutex_fake.o $(DEPLIBS) -+ backend.o tls.o imapparse.o libimap.a mutex_fake.o $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o \ - sync_client sync_client.o sync_support.o \ -- backend.o tls.o imapparse.o libimap.a mutex_fake.o $(DEPLIBS) $(LIBS) -+ backend.o tls.o imapparse.o libimap.a mutex_fake.o $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - - sync_reset: sync_reset.o sync_support.o sync_commit.o \ -- libimap.a mutex_fake.o $(DEPLIBS) -+ libimap.a mutex_fake.o $(DEPLIBS) $(SIEVE_LIBS) - $(CC) $(LDFLAGS) -o \ - sync_reset sync_reset.o sync_support.o sync_commit.o \ -- libimap.a mutex_fake.o $(DEPLIBS) $(LIBS) -+ libimap.a mutex_fake.o $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) -+ -+compile_sieve: compile_sieve.o libimap.a $(DEPLIBS) $(SIEVE_LIBS) -+ $(CC) $(LDFLAGS) -o compile_sieve compile_sieve.o $(CLIOBJS) \ -+ libimap.a $(SIEVE_LIBS) $(DEPLIBS) $(LIBS) - - ### Other Misc Targets - ---- /dev/null 2006-07-21 18:50:55.248316500 +0200 -+++ cyrus-imapd-2.3.7/imap/autosieve.c 2006-07-23 12:35:41.000000000 +0200 -@@ -0,0 +1,587 @@ -+#include -+#include -+#include -+ -+#ifdef HAVE_UNISTD_H -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "global.h" -+#include "util.h" -+#include "mailbox.h" -+#include "imap_err.h" -+#include "sieve_interface.h" -+#include "script.h" -+ -+#define TIMSIEVE_FAIL -1 -+#define TIMSIEVE_OK 0 -+#define MAX_FILENAME 1024 -+ -+static int get_script_name(char *sievename, size_t buflen, const char *filename); -+static int get_script_dir(char *sieve_script_dir, size_t buflen, char *userid, const char *sieve_dir); -+int autoadd_sieve(char *userid, const char *source_script); -+ -+static void fatal(const char *s, int code); -+static void foo(void); -+static int sieve_notify(void *ac __attribute__((unused)), -+ void *interp_context __attribute__((unused)), -+ void *script_context __attribute__((unused)), -+ void *message_context __attribute__((unused)), -+ const char **errmsg __attribute__((unused))); -+static int mysieve_error(int lineno, const char *msg, -+ void *i __attribute__((unused)), void *s); -+static int is_script_parsable(FILE *stream, char **errstr, sieve_script_t **ret); -+ -+ -+sieve_vacation_t vacation2 = { -+ 0, /* min response */ -+ 0, /* max response */ -+ (sieve_callback *) &foo, /* autorespond() */ -+ (sieve_callback *) &foo /* send_response() */ -+}; -+ -+ -+/* -+ * Find the name of the sieve script -+ * given the source script and compiled script names -+ */ -+static int get_script_name(char *sievename, size_t buflen, const char *filename) -+{ -+ char *p; -+ int r; -+ -+ p = strrchr(filename, '/'); -+ if (p == NULL) -+ p = (char *) filename; -+ else -+ p++; -+ -+ r = strlcpy(sievename, p, buflen) - buflen; -+ return (r >= 0 || r == -buflen ? 1 : 0); -+} -+ -+ -+/* -+ * Find the directory where the sieve scripts of the user -+ * reside -+ */ -+static int get_script_dir(char *sieve_script_dir, size_t buflen, char *userid, const char *sieve_dir) -+{ -+ char *user = NULL, *domain = NULL; -+ -+ /* Setup the user and the domain */ -+ if(config_virtdomains && (domain = strchr(userid, '@'))) { -+ user = (char *) xmalloc((domain - userid +1) * sizeof(char)); -+ strlcpy(user, userid, domain - userid + 1); -+ domain++; -+ } else -+ user = userid; -+ -+ /* Find the dir path where the sieve scripts of the user will reside */ -+ if (config_virtdomains && domain) { -+ if(snprintf(sieve_script_dir, buflen, "%s%s%c/%s/%c/%s/", -+ sieve_dir, FNAME_DOMAINDIR, dir_hash_c(domain), domain, dir_hash_c(user), user) >= buflen) { -+ free(user); -+ return 1; -+ } -+ } else { -+ if(snprintf(sieve_script_dir, buflen, "%s/%c/%s/", -+ sieve_dir, dir_hash_c(user), user) >= buflen) -+ return 1; -+ } -+ -+ /* Free the xmalloced user memory, reserved above */ -+ if(user != userid) -+ free(user); -+ -+ return 0; -+} -+ -+int autoadd_sieve(char *userid, const char *source_script) -+{ -+ sieve_script_t *s = NULL; -+ bytecode_info_t *bc = NULL; -+ char *err = NULL; -+ FILE *in_stream, *out_fp; -+ int out_fd, in_fd, r, k; -+ int do_compile = 0; -+ const char *sieve_dir = NULL; -+ const char *compiled_source_script = NULL; -+ char sievename[MAX_FILENAME]; -+ char sieve_script_name[MAX_FILENAME]; -+ char sieve_script_dir[MAX_FILENAME]; -+ char sieve_bcscript_name[MAX_FILENAME]; -+ char sieve_default[MAX_FILENAME]; -+ char sieve_tmpname[MAX_FILENAME]; -+ char sieve_bctmpname[MAX_FILENAME]; -+ char sieve_bclink_name[MAX_FILENAME]; -+ char buf[4096]; -+ mode_t oldmask; -+ struct stat statbuf; -+ -+ /* We don't support using the homedirectory, like timsieved */ -+ if (config_getswitch(IMAPOPT_SIEVEUSEHOMEDIR)) { -+ syslog(LOG_WARNING,"autocreate_sieve: autocreate_sieve does not work with sieveusehomedir option in imapd.conf"); -+ return 1; -+ } -+ -+ /* Check if sievedir is defined in imapd.conf */ -+ if(!(sieve_dir = config_getstring(IMAPOPT_SIEVEDIR))) { -+ syslog(LOG_WARNING, "autocreate_sieve: sievedir option is not defined. Check imapd.conf"); -+ return 1; -+ } -+ -+ /* Check if autocreate_sieve_compiledscript is defined in imapd.conf */ -+ if(!(compiled_source_script = config_getstring(IMAPOPT_AUTOCREATE_SIEVE_COMPILEDSCRIPT))) { -+ syslog(LOG_WARNING, "autocreate_sieve: autocreate_sieve_compiledscript option is not defined. Compiling it"); -+ do_compile = 1; -+ } -+ -+ if(get_script_dir(sieve_script_dir, sizeof(sieve_script_dir), userid, sieve_dir)) { -+ syslog(LOG_WARNING, "autocreate_sieve: Cannot find sieve scripts directory"); -+ return 1; -+ } -+ -+ if (get_script_name(sievename, sizeof(sievename), source_script)) { -+ syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve script %s", source_script); -+ return 1; -+ } -+ -+ if(snprintf(sieve_tmpname, sizeof(sieve_tmpname), "%s%s.script.NEW",sieve_script_dir, sievename) >= sizeof(sieve_tmpname)) { -+ syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_dir, sievename, userid); -+ return 1; -+ } -+ if(snprintf(sieve_bctmpname, sizeof(sieve_bctmpname), "%s%s.bc.NEW",sieve_script_dir, sievename) >= sizeof(sieve_bctmpname)) { -+ syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_dir, sievename, userid); -+ return 1; -+ } -+ if(snprintf(sieve_script_name, sizeof(sieve_script_name), "%s%s.script",sieve_script_dir, sievename) >= sizeof(sieve_script_name)) { -+ syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_dir, sievename, userid); -+ return 1; -+ } -+ if(snprintf(sieve_bcscript_name, sizeof(sieve_bcscript_name), "%s%s.bc",sieve_script_dir, sievename) >= sizeof(sieve_bcscript_name)) { -+ syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_dir, sievename, userid); -+ return 1; -+ } -+ if(snprintf(sieve_default, sizeof(sieve_default), "%s%s",sieve_script_dir,"defaultbc") >= sizeof(sieve_default)) { -+ syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_dir, sievename, userid); -+ return 1; -+ } -+ if(snprintf(sieve_bclink_name, sizeof(sieve_bclink_name), "%s.bc", sievename) >= sizeof(sieve_bclink_name)) { -+ syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_dir, sievename, userid); -+ return 1; -+ } -+ -+ /* Check if a default sieve filter alrady exists */ -+ if(!stat(sieve_default,&statbuf)) { -+ syslog(LOG_WARNING,"autocreate_sieve: Default sieve script already exists"); -+ fclose(in_stream); -+ return 1; -+ } -+ -+ /* Open the source script. if there is a problem with that exit */ -+ in_stream = fopen(source_script, "r"); -+ if(!in_stream) { -+ syslog(LOG_WARNING,"autocreate_sieve: Unable to open sieve script %s. Check permissions",source_script); -+ return 1; -+ } -+ -+ -+ /* -+ * At this point we start the modifications of the filesystem -+ */ -+ -+ /* Create the directory where the sieve scripts will reside */ -+ r = cyrus_mkdir(sieve_script_dir, 0755); -+ if(r == -1) { -+ /* If this fails we just leave */ -+ syslog(LOG_WARNING,"autocreate_sieve: Unable to create directory %s. Check permissions",sieve_script_name); -+ return 1; -+ } -+ -+ /* -+ * We open the file that will be used as the bc file. If this file exists, overwrite it -+ * since something bad has happened. We open the file here so that this error checking is -+ * done before we try to open the rest of the files to start copying etc. -+ */ -+ out_fd = open(sieve_bctmpname, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); -+ if(out_fd < 0) { -+ if(errno == EEXIST) { -+ syslog(LOG_WARNING,"autocreate_sieve: File %s already exists. Probaly left over. Ignoring",sieve_bctmpname); -+ } else if (errno == EACCES) { -+ syslog(LOG_WARNING,"autocreate_sieve: No access to create file %s. Check permissions",sieve_bctmpname); -+ fclose(in_stream); -+ return 1; -+ } else { -+ syslog(LOG_WARNING,"autocreate_sieve: Unable to create %s. Unknown error",sieve_bctmpname); -+ fclose(in_stream); -+ return 1; -+ } -+ } -+ -+ if(!do_compile && compiled_source_script && (in_fd = open(compiled_source_script, O_RDONLY)) != -1) { -+ while((r = read(in_fd, buf, sizeof(buf))) > 0) { -+ if((k=write(out_fd, buf,r)) < 0) { -+ syslog(LOG_WARNING, "autocreate_sieve: Error writing to file: %s, error: %d", sieve_bctmpname, errno); -+ close(out_fd); -+ close(in_fd); -+ fclose(in_stream); -+ unlink(sieve_bctmpname); -+ return 1; -+ } -+ } -+ -+ if(r == 0) { /* EOF */ -+ close(out_fd); -+ close(in_fd); -+ } else if (r < 0) { -+ syslog(LOG_WARNING, "autocreate_sieve: Error reading compiled script file: %s. Will try to compile it", -+ compiled_source_script); -+ close(in_fd); -+ do_compile = 1; -+ if(lseek(out_fd, 0, SEEK_SET)) { -+ syslog(LOG_WARNING, "autocreate_sieve: Major IO problem. Aborting"); -+ return 1; -+ } -+ } -+ close(in_fd); -+ } else { -+ if(compiled_source_script) -+ syslog(LOG_WARNING,"autocreate_sieve: Problem opening compiled script file: %s. Compiling it", compiled_source_script); -+ do_compile = 1; -+ } -+ -+ -+ /* Because we failed to open a precompiled bc sieve script, we compile one */ -+ if(do_compile) { -+ if(is_script_parsable(in_stream,&err, &s) == TIMSIEVE_FAIL) { -+ if(err && *err) { -+ syslog(LOG_WARNING,"autocreate_sieve: Error while parsing script %s.",err); -+ free(err); -+ } else -+ syslog(LOG_WARNING,"autocreate_sieve: Error while parsing script"); -+ -+ unlink(sieve_bctmpname); -+ fclose(in_stream); -+ close(out_fd); -+ return 1; -+ } -+ -+ /* generate the bytecode */ -+ if(sieve_generate_bytecode(&bc, s) == TIMSIEVE_FAIL) { -+ syslog(LOG_WARNING,"autocreate_sieve: problem compiling sieve script"); -+ /* removing the copied script and cleaning up memory */ -+ unlink(sieve_bctmpname); -+ sieve_script_free(&s); -+ fclose(in_stream); -+ close(out_fd); -+ return 1; -+ } -+ -+ if(sieve_emit_bytecode(out_fd, bc) == TIMSIEVE_FAIL) { -+ syslog(LOG_WARNING,"autocreate_sieve: problem emiting sieve script"); -+ /* removing the copied script and cleaning up memory */ -+ unlink(sieve_bctmpname); -+ sieve_free_bytecode(&bc); -+ sieve_script_free(&s); -+ fclose(in_stream); -+ close(out_fd); -+ return 1; -+ } -+ -+ /* clean up the memory */ -+ sieve_free_bytecode(&bc); -+ sieve_script_free(&s); -+ } -+ -+ close(out_fd); -+ rewind(in_stream); -+ -+ /* Copy the initial script */ -+ oldmask = umask(077); -+ if((out_fp = fopen(sieve_tmpname, "w")) == NULL) { -+ syslog(LOG_WARNING,"autocreate_sieve: Unable to open %s destination sieve script", sieve_tmpname); -+ unlink(sieve_bctmpname); -+ umask(oldmask); -+ fclose(in_stream); -+ return 1; -+ } -+ umask(oldmask); -+ -+ while((r = fread(buf,sizeof(char), sizeof(buf), in_stream))) { -+ if( fwrite(buf,sizeof(char), r, out_fp) != r) { -+ syslog(LOG_WARNING,"autocreate_sieve: Problem writing to sieve script file: %s",sieve_tmpname); -+ fclose(out_fp); -+ unlink(sieve_tmpname); -+ unlink(sieve_bctmpname); -+ fclose(in_stream); -+ return 1; -+ } -+ } -+ -+ if(feof(in_stream)) { -+ fclose(out_fp); -+ } else { /* ferror */ -+ fclose(out_fp); -+ unlink(sieve_tmpname); -+ unlink(sieve_bctmpname); -+ fclose(in_stream); -+ return 1; -+ } -+ -+ /* Renaming the necessary stuff */ -+ if(rename(sieve_tmpname, sieve_script_name)) { -+ unlink(sieve_tmpname); -+ unlink(sieve_bctmpname); -+ return 1; -+ } -+ -+ if(rename(sieve_bctmpname, sieve_bcscript_name)) { -+ unlink(sieve_bctmpname); -+ unlink(sieve_bcscript_name); -+ return 1; -+ } -+ -+ /* end now with the symlink */ -+ if(symlink(sieve_bclink_name, sieve_default)) { -+ if(errno != EEXIST) { -+ syslog(LOG_WARNING, "autocreate_sieve: problem making the default link."); -+ /* Lets delete the files */ -+ unlink(sieve_script_name); -+ unlink(sieve_bcscript_name); -+ } -+ } -+ -+ /* -+ * If everything has succeeded AND we have compiled the script AND we have requested -+ * to generate the global script so that it is not compiled each time then we create it. -+ */ -+ if(do_compile && -+ config_getswitch(IMAPOPT_GENERATE_COMPILED_SIEVE_SCRIPT)) { -+ -+ if(!compiled_source_script) { -+ syslog(LOG_WARNING, "autocreate_sieve: To save a compiled sieve script, autocreate_sieve_compiledscript must have been defined in imapd.conf"); -+ return 0; -+ } -+ -+ if(snprintf(sieve_tmpname, sizeof(sieve_tmpname), "%s.NEW", compiled_source_script) >= sizeof(sieve_tmpname)) -+ return 0; -+ -+ /* -+ * Copy everything from the newly created bc sieve sieve script. -+ */ -+ if((in_fd = open(sieve_bcscript_name, O_RDONLY))<0) { -+ return 0; -+ } -+ -+ if((out_fd = open(sieve_tmpname, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) { -+ if(errno == EEXIST) { -+ /* Someone is already doing this so just bail out. */ -+ syslog(LOG_WARNING, "autocreate_sieve: %s already exists. Some other instance processing it, or it is left over", sieve_tmpname); -+ close(in_fd); -+ return 0; -+ } else if (errno == EACCES) { -+ syslog(LOG_WARNING,"autocreate_sieve: No access to create file %s. Check permissions",sieve_tmpname); -+ close(in_fd); -+ return 0; -+ } else { -+ syslog(LOG_WARNING,"autocreate_sieve: Unable to create %s",sieve_tmpname); -+ close(in_fd); -+ return 0; -+ } -+ } -+ -+ while((r = read(in_fd, buf, sizeof(buf))) > 0) { -+ if((k = write(out_fd,buf,r)) < 0) { -+ syslog(LOG_WARNING, "autocreate_sieve: Error writing to file: %s, error: %d", sieve_tmpname, errno); -+ close(out_fd); -+ close(in_fd); -+ unlink(sieve_tmpname); -+ return 0; -+ } -+ } -+ -+ if(r == 0 ) { /*EOF */ -+ close(out_fd); -+ close(in_fd); -+ } else if (r < 0) { -+ syslog(LOG_WARNING, "autocreate_sieve: Error writing to file: %s, error: %d", sieve_tmpname, errno); -+ close(out_fd); -+ close(in_fd); -+ unlink(sieve_tmpname); -+ return 0; -+ } -+ -+ /* Rename the temporary created sieve script to its final name. */ -+ if(rename(sieve_tmpname, compiled_source_script)) { -+ if(errno != EEXIST) { -+ unlink(sieve_tmpname); -+ unlink(compiled_source_script); -+ } -+ return 0; -+ } -+ -+ syslog(LOG_NOTICE, "autocreate_sieve: Compiled sieve script was successfully saved in %s", compiled_source_script); -+ } -+ -+ return 0; -+} -+ -+static void fatal(const char *s, int code) -+{ -+ printf("Fatal error: %s (%d)\r\n", s, code); -+ exit(1); -+} -+ -+/* to make larry's stupid functions happy :) */ -+static void foo(void) -+{ -+ fatal("stub function called", 0); -+} -+ -+static int sieve_notify(void *ac __attribute__((unused)), -+ void *interp_context __attribute__((unused)), -+ void *script_context __attribute__((unused)), -+ void *message_context __attribute__((unused)), -+ const char **errmsg __attribute__((unused))) -+{ -+ fatal("stub function called", 0); -+ return SIEVE_FAIL; -+} -+ -+static int mysieve_error(int lineno, const char *msg, -+ void *i __attribute__((unused)), void *s) -+{ -+ char buf[1024]; -+ char **errstr = (char **) s; -+ -+ snprintf(buf, 80, "line %d: %s\r\n", lineno, msg); -+ *errstr = (char *) xrealloc(*errstr, strlen(*errstr) + strlen(buf) + 30); -+ syslog(LOG_DEBUG, "%s", buf); -+ strcat(*errstr, buf); -+ -+ return SIEVE_OK; -+} -+ -+/* end the boilerplate */ -+ -+/* returns TRUE or FALSE */ -+int is_script_parsable(FILE *stream, char **errstr, sieve_script_t **ret) -+{ -+ sieve_interp_t *i; -+ sieve_script_t *s; -+ int res; -+ -+ res = sieve_interp_alloc(&i, NULL); -+ if (res != SIEVE_OK) { -+ syslog(LOG_WARNING, "sieve_interp_alloc() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_redirect(i, (sieve_callback *) &foo); -+ if (res != SIEVE_OK) { -+ syslog(LOG_WARNING, "sieve_register_redirect() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ res = sieve_register_discard(i, (sieve_callback *) &foo); -+ if (res != SIEVE_OK) { -+ syslog(LOG_WARNING, "sieve_register_discard() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ res = sieve_register_reject(i, (sieve_callback *) &foo); -+ if (res != SIEVE_OK) { -+ syslog(LOG_WARNING, "sieve_register_reject() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ res = sieve_register_fileinto(i, (sieve_callback *) &foo); -+ if (res != SIEVE_OK) { -+ syslog(LOG_WARNING, "sieve_register_fileinto() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ res = sieve_register_keep(i, (sieve_callback *) &foo); -+ if (res != SIEVE_OK) { -+ syslog(LOG_WARNING, "sieve_register_keep() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_imapflags(i, NULL); -+ if (res != SIEVE_OK) { -+ syslog(LOG_WARNING, "sieve_register_imapflags() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_size(i, (sieve_get_size *) &foo); -+ if (res != SIEVE_OK) { -+ syslog(LOG_WARNING, "sieve_register_size() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_header(i, (sieve_get_header *) &foo); -+ if (res != SIEVE_OK) { -+ syslog(LOG_WARNING, "sieve_register_header() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_envelope(i, (sieve_get_envelope *) &foo); -+ if (res != SIEVE_OK) { -+ syslog(LOG_WARNING, "sieve_register_envelope() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_vacation(i, &vacation2); -+ if (res != SIEVE_OK) { -+ syslog(LOG_WARNING, "sieve_register_vacation() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_notify(i, &sieve_notify); -+ if (res != SIEVE_OK) { -+ syslog(LOG_WARNING, "sieve_register_notify() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_parse_error(i, &mysieve_error); -+ if (res != SIEVE_OK) { -+ syslog(LOG_WARNING, "sieve_register_parse_error() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ rewind(stream); -+ -+ *errstr = (char *) xmalloc(20 * sizeof(char)); -+ strcpy(*errstr, "script errors:\r\n"); -+ -+ res = sieve_script_parse(i, stream, errstr, &s); -+ -+ if (res == SIEVE_OK) { -+ if(ret) { -+ *ret = s; -+ } else { -+ sieve_script_free(&s); -+ } -+ free(*errstr); -+ *errstr = NULL; -+ } -+ -+ /* free interpreter */ -+ sieve_interp_free(&i); -+ -+ return (res == SIEVE_OK) ? TIMSIEVE_OK : TIMSIEVE_FAIL; -+} -+ -+/* -+ * Btw the initial date of this patch is Sep, 02 2004 which is the birthday of -+ * Pavlos. Author of cyrusmaster. So consider this patch as his birthday present -+ */ -+ ---- /dev/null 2006-07-21 18:50:55.248316500 +0200 -+++ cyrus-imapd-2.3.7/imap/compile_sieve.c 2006-07-23 12:35:41.000000000 +0200 -@@ -0,0 +1,364 @@ -+/* This tool compiles the sieve script from a command -+line so that it can be used wby the autoadd patch */ -+#include -+#include -+ -+#include -+#include -+#ifdef HAVE_UNISTD_H -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "global.h" -+ -+#include "util.h" -+#include "mailbox.h" -+#include "imap_err.h" -+#include "sieve_interface.h" -+#include "script.h" -+ -+#include -+ -+#define TIMSIEVE_FAIL -1 -+#define TIMSIEVE_OK 0 -+#define MAX_FILENAME_SIZE 100 -+ -+/* Needed by libconfig */ -+const int config_need_data = 0; -+ -+static int is_script_parsable(FILE *stream, char **errstr, sieve_script_t **ret); -+ -+static void fatal(const char *s, int code) -+{ -+ printf("Fatal error: %s (%d)\r\n", s, code); -+ -+ exit(1); -+} -+ -+void usage(void) -+{ -+ fprintf(stderr, -+ "Usage:\n\tcompile_sieve [-C ] [-i -o ]\n"); -+ exit(-1); -+} -+ -+ -+int main (int argc, char **argv) -+{ -+ -+ sieve_script_t *s = NULL; -+ bytecode_info_t *bc = NULL; -+ char *err = NULL; -+ FILE *in_stream; -+ int out_fd,r, k, opt; -+ char *source_script = NULL; -+ char *compiled_source_script = NULL; -+ mode_t oldmask; -+ struct stat statbuf; -+ char *alt_config = NULL; -+ extern char *optarg; -+ char sieve_tmpname[MAX_MAILBOX_NAME+1]; -+ -+ if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE); -+ -+ while((opt = getopt(argc, argv, "C:i:o:")) != EOF) { -+ switch (opt) { -+ case 'C': /* alt config file */ -+ alt_config = optarg; -+ break; -+ case 'i': /* input script file */ -+ source_script = optarg; -+ break; -+ case 'o': /* output script file */ -+ compiled_source_script = optarg; -+ break; -+ default: -+ usage(); -+ break; -+ } -+ } -+ -+ if(source_script && !compiled_source_script) { -+ fprintf(stderr, "No output file was defined\n"); -+ usage(); -+ } else if (!source_script && compiled_source_script) { -+ fprintf(stderr, "No input file was defined\n"); -+ usage(); -+ } -+ -+ /* -+ * If no has been defined, then read them from -+ * the configuration file. -+ */ -+ if (!source_script && !compiled_source_script) { -+ cyrus_init(alt_config, "compile_sieve", 0); -+ -+ /* Initially check if we want to have the sieve script created */ -+ if(!(source_script = (char *) config_getstring(IMAPOPT_AUTOCREATE_SIEVE_SCRIPT))) { -+ fprintf(stderr,"autocreate_sieve_script option not defined. Check imapd.conf\n"); -+ return 1; -+ } -+ -+ /* Check if we have an already compiled sieve script*/ -+ if(!(compiled_source_script = (char *) config_getstring(IMAPOPT_AUTOCREATE_SIEVE_COMPILEDSCRIPT))) { -+ fprintf(stderr, "autocreate_sieve_compiledscript option not defined. Check imapd.conf\n"); -+ return 1; -+ } -+ -+ if(!strrchr(source_script,'/') || !strrchr(compiled_source_script,'/')) { -+ /* -+ * At this point the only think that is inconsistent is the directory -+ * that was created. But if the user will have any sieve scripts then -+ * they will eventually go there, so no big deal -+ */ -+ fprintf(stderr, -+ "In imapd.conf the full path of the filenames must be defined\n"); -+ return 1; -+ } -+ } -+ -+ printf("input file : %s, output file : %s\n", source_script, compiled_source_script); -+ -+ -+ if(strlen(compiled_source_script) + sizeof(".NEW") + 1 > sizeof(sieve_tmpname)) { -+ fprintf(stderr, "Filename %s is too big\n", compiled_source_script); -+ return 1; -+ } -+ -+ snprintf(sieve_tmpname, sizeof(sieve_tmpname), "%s.NEW", compiled_source_script); -+ -+ in_stream = fopen(source_script,"r"); -+ -+ if(!in_stream) { -+ fprintf(stderr,"Unable to open %s source sieve script\n",source_script); -+ return; -+ } -+ -+ /* -+ * We open the file that will be used as the bc file. If this file exists, overwrite it -+ * since something bad has happened. We open the file here so that this error checking is -+ * done before we try to open the rest of the files to start copying etc. -+ */ -+ out_fd = open(sieve_tmpname, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); -+ if(out_fd < 0) { -+ if(errno == EEXIST) { -+ fprintf(stderr, "File %s already exists\n", sieve_tmpname); -+ } else if (errno == EACCES) { -+ fprintf(stderr,"No access to create file %s. Please check that you have the correct permissions\n", -+ sieve_tmpname); -+ } else { -+ fprintf(stderr,"Unable to create %s. Please check that you have the correct permissions\n", -+ sieve_tmpname); -+ } -+ -+ fclose(in_stream); -+ return 1; -+ } -+ -+ if(is_script_parsable(in_stream,&err, &s) == TIMSIEVE_FAIL) { -+ if(err && *err) { -+ fprintf(stderr, "Error while parsing script %s\n",err); -+ free(err); -+ } -+ else -+ fprintf(stderr,"Error while parsing script\n"); -+ unlink(sieve_tmpname); -+ fclose(in_stream); -+ close(out_fd); -+ return; -+ } -+ -+ -+ /* generate the bytecode */ -+ if(sieve_generate_bytecode(&bc,s) == TIMSIEVE_FAIL) { -+ fprintf(stderr,"Error occured while compiling sieve script\n"); -+ /* removing the copied script and cleaning up memory */ -+ unlink(sieve_tmpname); -+ sieve_script_free(&s); -+ fclose(in_stream); -+ close(out_fd); -+ return; -+ } -+ if(sieve_emit_bytecode(out_fd,bc) == TIMSIEVE_FAIL) { -+ fprintf(stderr, "Error occured while emitting sieve script\n"); -+ unlink(sieve_tmpname); -+ sieve_free_bytecode(&bc); -+ sieve_script_free(&s); -+ fclose(in_stream); -+ close(out_fd); -+ return; -+ } -+ -+ /* clean up the memory */ -+ sieve_free_bytecode(&bc); -+ sieve_script_free(&s); -+ -+ close(out_fd); -+ -+ if(rename(sieve_tmpname, compiled_source_script)) { -+ if(errno != EEXIST) { -+ unlink(sieve_tmpname); -+ unlink(compiled_source_script); -+ return 1; -+ } -+ } -+ return 0; -+} -+ -+ -+/* to make larry's stupid functions happy :) */ -+static void foo(void) -+{ -+ fatal("stub function called", 0); -+} -+ -+extern sieve_vacation_t vacation2;/* = { -+ 0, / min response / -+ 0, / max response / -+ (sieve_callback *) &foo, / autorespond() / -+ (sieve_callback *) &foo / send_response() / -+}; */ -+ -+static int sieve_notify(void *ac __attribute__((unused)), -+ void *interp_context __attribute__((unused)), -+ void *script_context __attribute__((unused)), -+ void *message_context __attribute__((unused)), -+ const char **errmsg __attribute__((unused))) -+{ -+ fatal("stub function called", 0); -+ return SIEVE_FAIL; -+} -+ -+static int mysieve_error(int lineno, const char *msg, -+ void *i __attribute__((unused)), void *s) -+{ -+ char buf[1024]; -+ char **errstr = (char **) s; -+ -+ snprintf(buf, 80, "line %d: %s\r\n", lineno, msg); -+ *errstr = (char *) xrealloc(*errstr, strlen(*errstr) + strlen(buf) + 30); -+ fprintf(stderr, "%s\n", buf); -+ strcat(*errstr, buf); -+ -+ return SIEVE_OK; -+} -+ -+/* end the boilerplate */ -+ -+/* returns TRUE or FALSE */ -+int is_script_parsable(FILE *stream, char **errstr, sieve_script_t **ret) -+{ -+ sieve_interp_t *i; -+ sieve_script_t *s; -+ int res; -+ -+ res = sieve_interp_alloc(&i, NULL); -+ if (res != SIEVE_OK) { -+ fprintf(stderr, "sieve_interp_alloc() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_redirect(i, (sieve_callback *) &foo); -+ if (res != SIEVE_OK) { -+ fprintf(stderr, "sieve_register_redirect() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ res = sieve_register_discard(i, (sieve_callback *) &foo); -+ if (res != SIEVE_OK) { -+ fprintf(stderr, "sieve_register_discard() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ res = sieve_register_reject(i, (sieve_callback *) &foo); -+ if (res != SIEVE_OK) { -+ fprintf(stderr, "sieve_register_reject() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ res = sieve_register_fileinto(i, (sieve_callback *) &foo); -+ if (res != SIEVE_OK) { -+ fprintf(stderr, "sieve_register_fileinto() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ res = sieve_register_keep(i, (sieve_callback *) &foo); -+ if (res != SIEVE_OK) { -+ fprintf(stderr, "sieve_register_keep() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_imapflags(i, NULL); -+ if (res != SIEVE_OK) { -+ fprintf(stderr, "sieve_register_imapflags() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_size(i, (sieve_get_size *) &foo); -+ if (res != SIEVE_OK) { -+ fprintf(stderr, "sieve_register_size() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_header(i, (sieve_get_header *) &foo); -+ if (res != SIEVE_OK) { -+ fprintf(stderr, "sieve_register_header() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_envelope(i, (sieve_get_envelope *) &foo); -+ if (res != SIEVE_OK) { -+ fprintf(stderr, "sieve_register_envelope() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_vacation(i, &vacation2); -+ if (res != SIEVE_OK) { -+ fprintf(stderr, "sieve_register_vacation() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_notify(i, &sieve_notify); -+ if (res != SIEVE_OK) { -+ fprintf(stderr, "sieve_register_notify() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ res = sieve_register_parse_error(i, &mysieve_error); -+ if (res != SIEVE_OK) { -+ fprintf(stderr, "sieve_register_parse_error() returns %d\n", res); -+ return TIMSIEVE_FAIL; -+ } -+ -+ rewind(stream); -+ -+ *errstr = (char *) xmalloc(20 * sizeof(char)); -+ strcpy(*errstr, "script errors:\r\n"); -+ -+ res = sieve_script_parse(i, stream, errstr, &s); -+ -+ if (res == SIEVE_OK) { -+ if(ret) { -+ *ret = s; -+ } else { -+ sieve_script_free(&s); -+ } -+ free(*errstr); -+ *errstr = NULL; -+ } -+ -+ /* free interpreter */ -+ sieve_interp_free(&i); -+ -+ return (res == SIEVE_OK) ? TIMSIEVE_OK : TIMSIEVE_FAIL; -+} -+ -+ -+ -+ -+ -+ ---- cyrus-imapd-2.3.7/imap/imapd.c.autocreate0 2006-07-03 15:22:41.000000000 +0200 -+++ cyrus-imapd-2.3.7/imap/imapd.c 2006-07-23 12:35:41.000000000 +0200 -@@ -197,6 +197,7 @@ - void motd_file(int fd); - void shut_down(int code); - void fatal(const char *s, int code); -+void autocreate_inbox(void); - - void cmdloop(void); - void cmd_login(char *tag, char *user); -@@ -1904,6 +1905,43 @@ - } - - /* -+ * Autocreate Inbox and subfolders upon login -+ */ -+void autocreate_inbox() -+{ -+ char inboxname[MAX_MAILBOX_NAME+1]; -+ int autocreatequota; -+ int r; -+ -+ /* -+ * Exlude admin's accounts -+ */ -+ if (imapd_userisadmin || imapd_userisproxyadmin) -+ return; -+ -+ /* -+ * Exclude anonymous -+ */ -+ if (!strcmp(imapd_userid, "anonymous")) -+ return; -+ -+ if ((autocreatequota = config_getint(IMAPOPT_AUTOCREATEQUOTA))) { -+ /* This is actyally not required -+ as long as the lenght of userid is ok */ -+ r = (*imapd_namespace.mboxname_tointernal) (&imapd_namespace, -+ "INBOX", imapd_userid, inboxname); -+ if (!r) -+ r = mboxlist_lookup(inboxname, NULL, NULL); -+ -+ if (r == IMAP_MAILBOX_NONEXISTENT) { -+ mboxlist_autocreateinbox(&imapd_namespace, imapd_userid, -+ imapd_authstate, inboxname, autocreatequota); -+ } -+ } -+} -+ -+ -+/* - * Perform a LOGIN command - */ - void cmd_login(char *tag, char *user) -@@ -2071,6 +2109,9 @@ - strcspn(imapd_userid, "@") : 0); - - freebuf(&passwdbuf); -+ -+ autocreate_inbox(); -+ - return; - } - -@@ -2227,6 +2268,8 @@ - config_virtdomains ? - strcspn(imapd_userid, "@") : 0); - -+ autocreate_inbox(); -+ - return; - } - ---- /dev/null 2006-07-21 18:50:55.248316500 +0200 -+++ cyrus-imapd-2.3.7/imap/imapd.c.orig 2006-07-23 12:35:41.000000000 +0200 -@@ -0,0 +1,9588 @@ -+/* -+ * Copyright (c) 1998-2003 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 other legal -+ * details, please contact -+ * Office of Technology Transfer -+ * Carnegie Mellon University -+ * 5000 Forbes Avenue -+ * Pittsburgh, PA 15213-3890 -+ * (412) 268-4387, fax: (412) 268-7395 -+ * tech-transfer@andrew.cmu.edu -+ * -+ * 4. Redistributions of any form whatsoever must retain the following -+ * "This product includes software developed by Computing Services -+ * acknowledgment: -+ * 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. -+ */ -+ -+/* $Id: cyrus-imapd-2.3.7-autocreate-0.10-0.diff,v 1.1 2006/07/23 12:03:32 prockai Exp $ */ -+ -+#include -+ -+#ifdef HAVE_UNISTD_H -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "acl.h" -+#include "annotate.h" -+#include "append.h" -+#include "auth.h" -+#include "backend.h" -+#include "charset.h" -+#include "exitcodes.h" -+#include "idle.h" -+#include "global.h" -+#include "imap_err.h" -+#include "proxy.h" -+#include "imap_proxy.h" -+#include "imapd.h" -+#include "imapurl.h" -+#include "imparse.h" -+#include "index.h" -+#include "iptostring.h" -+#include "mailbox.h" -+#include "message.h" -+#include "mboxkey.h" -+#include "mboxlist.h" -+#include "mboxname.h" -+#include "mbdump.h" -+#include "mkgmtime.h" -+#include "mupdate-client.h" -+#include "quota.h" -+#include "sync_log.h" -+#include "telemetry.h" -+#include "tls.h" -+#include "user.h" -+#include "util.h" -+#include "version.h" -+#include "xmalloc.h" -+ -+#include "pushstats.h" /* SNMP interface */ -+ -+extern void seen_done(void); -+ -+extern int optind; -+extern char *optarg; -+ -+/* global state */ -+const int config_need_data = CONFIG_NEED_PARTITION_DATA; -+ -+static char shutdownfilename[1024]; -+static int imaps = 0; -+static sasl_ssf_t extprops_ssf = 0; -+ -+/* PROXY STUFF */ -+/* we want a list of our outgoing connections here and which one we're -+ currently piping */ -+ -+static const int ultraparanoid = 1; /* should we kick after every operation? */ -+unsigned int proxy_cmdcnt; -+ -+static int referral_kick = 0; /* kick after next command recieved, for -+ referrals that are likely to change the -+ mailbox list */ -+ -+/* all subscription commands go to the backend server containing the -+ user's inbox */ -+struct backend *backend_inbox = NULL; -+ -+/* the current server most commands go to */ -+struct backend *backend_current = NULL; -+ -+/* our cached connections */ -+struct backend **backend_cached = NULL; -+ -+/* are we doing virtdomains with multiple IPs? */ -+static int disable_referrals; -+ -+/* has the client issued an RLIST or RLSUB? */ -+static int supports_referrals; -+ -+/* end PROXY STUFF */ -+ -+/* per-user/session state */ -+struct protstream *imapd_out = NULL; -+struct protstream *imapd_in = NULL; -+struct protgroup *protin = NULL; -+static char imapd_clienthost[NI_MAXHOST*2+1] = "[local]"; -+static int imapd_logfd = -1; -+char *imapd_userid = NULL, *proxy_userid = NULL; -+static char *imapd_magicplus = NULL; -+struct auth_state *imapd_authstate = 0; -+static int imapd_userisadmin = 0; -+static int imapd_userisproxyadmin = 0; -+static sasl_conn_t *imapd_saslconn; /* the sasl connection context */ -+static int imapd_starttls_done = 0; /* have we done a successful starttls? */ -+const char *plaintextloginalert = NULL; -+#ifdef HAVE_SSL -+/* our tls connection, if any */ -+static SSL *tls_conn = NULL; -+#endif /* HAVE_SSL */ -+ -+/* stage(s) for APPEND */ -+struct appendstage { -+ struct stagemsg *stage; -+ char **flag; -+ int nflags, flagalloc; -+ time_t internaldate; -+} **stage = NULL; -+unsigned long numstage = 0; -+ -+/* the sasl proxy policy context */ -+static struct proxy_context imapd_proxyctx = { -+ 1, 1, &imapd_authstate, &imapd_userisadmin, &imapd_userisproxyadmin -+}; -+ -+/* current sub-user state */ -+static struct mailbox mboxstruct; -+static struct mailbox *imapd_mailbox; -+int imapd_exists = -1; -+ -+/* current namespace */ -+struct namespace imapd_namespace; -+ -+static const char *monthname[] = { -+ "jan", "feb", "mar", "apr", "may", "jun", -+ "jul", "aug", "sep", "oct", "nov", "dec" -+}; -+ -+static const int max_monthdays[] = { -+ 31, 29, 31, 30, 31, 30, -+ 31, 31, 30, 31, 30, 31 -+}; -+ -+void motd_file(int fd); -+void shut_down(int code); -+void fatal(const char *s, int code); -+ -+void cmdloop(void); -+void cmd_login(char *tag, char *user); -+void cmd_authenticate(char *tag, char *authtype, char *resp); -+void cmd_noop(char *tag, char *cmd); -+void cmd_capability(char *tag); -+void cmd_append(char *tag, char *name, const char *cur_name); -+void cmd_select(char *tag, char *cmd, char *name); -+void cmd_close(char *tag); -+void cmd_fetch(char *tag, char *sequence, int usinguid); -+void cmd_partial(const char *tag, const char *msgno, char *data, -+ const char *start, const char *count); -+void cmd_store(char *tag, char *sequence, char *operation, int usinguid); -+void cmd_search(char *tag, int usinguid); -+void cmd_sort(char *tag, int usinguid); -+void cmd_thread(char *tag, int usinguid); -+void cmd_copy(char *tag, char *sequence, char *name, int usinguid); -+void cmd_expunge(char *tag, char *sequence); -+void cmd_create(char *tag, char *name, char *partition, int localonly); -+void cmd_delete(char *tag, char *name, int localonly, int force); -+void cmd_dump(char *tag, char *name, int uid_start); -+void cmd_undump(char *tag, char *name); -+void cmd_xfer(char *tag, char *name, char *toserver, char *topart); -+void cmd_rename(char *tag, char *oldname, char *newname, char *partition); -+void cmd_reconstruct(const char *tag, const char *name, int recursive); -+void cmd_find(char *tag, char *namespace, char *pattern); -+void cmd_list(char *tag, int listopts, char *reference, char *pattern); -+void cmd_changesub(char *tag, char *namespace, char *name, int add); -+void cmd_getacl(const char *tag, const char *name); -+void cmd_listrights(char *tag, char *name, char *identifier); -+void cmd_myrights(const char *tag, const char *name); -+void cmd_setacl(char *tag, const char *name, -+ const char *identifier, const char *rights); -+void cmd_getquota(const char *tag, const char *name); -+void cmd_getquotaroot(const char *tag, const char *name); -+void cmd_setquota(const char *tag, const char *quotaroot); -+void cmd_status(char *tag, char *name); -+void cmd_unselect(char* tag); -+void cmd_namespace(char* tag); -+void cmd_mupdatepush(char *tag, char *name); -+void cmd_id(char* tag); -+extern void id_getcmdline(int argc, char **argv); -+extern void id_response(struct protstream *pout); -+ -+void cmd_idle(char* tag); -+void idle_update(idle_flags_t flags); -+ -+void cmd_starttls(char *tag, int imaps); -+ -+#ifdef HAVE_SSL -+void cmd_urlfetch(char *tag); -+void cmd_genurlauth(char *tag); -+void cmd_resetkey(char *tag, char *mailbox, char *mechanism); -+#endif -+ -+#ifdef ENABLE_X_NETSCAPE_HACK -+void cmd_netscrape(char* tag); -+#endif -+ -+void cmd_getannotation(char* tag, char *mboxpat); -+void cmd_setannotation(char* tag, char *mboxpat); -+ -+int getannotatefetchdata(char *tag, -+ struct strlist **entries, struct strlist **attribs); -+int getannotatestoredata(char *tag, struct entryattlist **entryatts); -+ -+void annotate_response(struct entryattlist *l); -+ -+#ifdef ENABLE_LISTEXT -+int getlistopts(char *tag, int *listopts); -+#endif -+ -+int getsearchprogram(char *tag, struct searchargs *searchargs, -+ int *charset, int parsecharset); -+int getsearchcriteria(char *tag, struct searchargs *searchargs, -+ int *charset, int parsecharset); -+int getsearchdate(time_t *start, time_t *end); -+int getsortcriteria(char *tag, struct sortcrit **sortcrit); -+int getdatetime(time_t *date); -+ -+void printstring(const char *s); -+void printastring(const char *s); -+ -+void appendfieldlist(struct fieldlist **l, char *section, -+ struct strlist *fields, char *trail, -+ void *d, size_t size); -+void freefieldlist(struct fieldlist *l); -+void freestrlist(struct strlist *l); -+void appendsearchargs(struct searchargs *s, struct searchargs *s1, -+ struct searchargs *s2); -+void freesearchargs(struct searchargs *s); -+static void freesortcrit(struct sortcrit *s); -+ -+static int mailboxdata(char *name, int matchlen, int maycreate, void *rock); -+static int listdata(char *name, int matchlen, int maycreate, void *rock); -+static void mstringdata(char *cmd, char *name, int matchlen, int maycreate, -+ int listopts); -+ -+extern void setproctitle_init(int argc, char **argv, char **envp); -+extern int proc_register(const char *progname, const char *clienthost, -+ const char *userid, const char *mailbox); -+extern void proc_cleanup(void); -+ -+extern int saslserver(sasl_conn_t *conn, const char *mech, -+ const char *init_resp, const char *resp_prefix, -+ const char *continuation, const char *empty_resp, -+ struct protstream *pin, struct protstream *pout, -+ int *sasl_result, char **success_data); -+ -+/* Enable the resetting of a sasl_conn_t */ -+static int reset_saslconn(sasl_conn_t **conn); -+ -+static struct -+{ -+ char *ipremoteport; -+ char *iplocalport; -+ sasl_ssf_t ssf; -+ char *authid; -+} saslprops = {NULL,NULL,0,NULL}; -+ -+static int imapd_canon_user(sasl_conn_t *conn, void *context, -+ const char *user, unsigned ulen, -+ unsigned flags, const char *user_realm, -+ char *out, unsigned out_max, unsigned *out_ulen) -+{ -+ char userbuf[MAX_MAILBOX_NAME+1], *p; -+ size_t n; -+ int r; -+ -+ if (!ulen) ulen = strlen(user); -+ -+ if (config_getswitch(IMAPOPT_IMAPMAGICPLUS)) { -+ /* make a working copy of the auth[z]id */ -+ if (ulen > MAX_MAILBOX_NAME) { -+ sasl_seterror(conn, 0, "buffer overflow while canonicalizing"); -+ return SASL_BUFOVER; -+ } -+ memcpy(userbuf, user, ulen); -+ userbuf[ulen] = '\0'; -+ user = userbuf; -+ -+ /* See if we're using the magic plus -+ (currently we don't support anything after '+') */ -+ if ((p = strchr(userbuf, '+')) && -+ (n = config_virtdomains ? strcspn(p, "@") : strlen(p)) == 1) { -+ -+ if (flags & SASL_CU_AUTHZID) { -+ /* make a copy of the magic plus */ -+ if (imapd_magicplus) free(imapd_magicplus); -+ imapd_magicplus = xstrndup(p, n); -+ } -+ -+ /* strip the magic plus from the auth[z]id */ -+ memmove(p, p+n, strlen(p+n)+1); -+ ulen -= n; -+ } -+ } -+ -+ r = mysasl_canon_user(conn, context, user, ulen, flags, user_realm, -+ out, out_max, out_ulen); -+ -+ if (!r && imapd_magicplus && flags == SASL_CU_AUTHZID) { -+ /* If we're only doing the authzid, put back the magic plus -+ in case its used in the challenge/response calculation */ -+ n = strlen(imapd_magicplus); -+ if (*out_ulen + n > out_max) { -+ sasl_seterror(conn, 0, "buffer overflow while canonicalizing"); -+ r = SASL_BUFOVER; -+ } -+ else { -+ p = (config_virtdomains && (p = strchr(out, '@'))) ? -+ p : out + *out_ulen; -+ memmove(p+n, p, strlen(p)+1); -+ memcpy(p, imapd_magicplus, n); -+ *out_ulen += n; -+ } -+ } -+ -+ return r; -+} -+ -+static int imapd_proxy_policy(sasl_conn_t *conn, -+ void *context, -+ const char *requested_user, unsigned rlen, -+ const char *auth_identity, unsigned alen, -+ const char *def_realm, -+ unsigned urlen, -+ struct propctx *propctx) -+{ -+ if (config_getswitch(IMAPOPT_IMAPMAGICPLUS)) { -+ char userbuf[MAX_MAILBOX_NAME+1], *p; -+ size_t n; -+ -+ /* make a working copy of the authzid */ -+ if (!rlen) rlen = strlen(requested_user); -+ if (rlen > MAX_MAILBOX_NAME) { -+ sasl_seterror(conn, 0, "buffer overflow while proxying"); -+ return SASL_BUFOVER; -+ } -+ memcpy(userbuf, requested_user, rlen); -+ userbuf[rlen] = '\0'; -+ requested_user = userbuf; -+ -+ /* See if we're using the magic plus */ -+ if ((p = strchr(userbuf, '+'))) { -+ n = config_virtdomains ? strcspn(p, "@") : strlen(p); -+ -+ /* strip the magic plus from the authzid */ -+ memmove(p, p+n, strlen(p+n)+1); -+ rlen -= n; -+ } -+ } -+ -+ return mysasl_proxy_policy(conn, context, requested_user, rlen, -+ auth_identity, alen, def_realm, urlen, propctx); -+} -+ -+static const struct sasl_callback mysasl_cb[] = { -+ { SASL_CB_GETOPT, &mysasl_config, NULL }, -+ { SASL_CB_PROXY_POLICY, &imapd_proxy_policy, (void*) &imapd_proxyctx }, -+ { SASL_CB_CANON_USER, &imapd_canon_user, (void*) &disable_referrals }, -+ { SASL_CB_LIST_END, NULL, NULL } -+}; -+ -+/* imapd_refer() issues a referral to the client. */ -+static void imapd_refer(const char *tag, -+ const char *server, -+ const char *mailbox) -+{ -+ struct imapurl imapurl; -+ char url[MAX_MAILBOX_PATH+1]; -+ -+ memset(&imapurl, 0, sizeof(struct imapurl)); -+ imapurl.server = server; -+ imapurl.mailbox = mailbox; -+ imapurl.auth = !strcmp(imapd_userid, "anonymous") ? "anonymous" : "*"; -+ -+ imapurl_toURL(url, &imapurl); -+ -+ prot_printf(imapd_out, "%s NO [REFERRAL %s] Remote mailbox.\r\n", -+ tag, url); -+} -+ -+/* wrapper for mboxlist_lookup that will force a referral if we are remote -+ * returns IMAP_SERVER_UNAVAILABLE if we don't have a place to send the client -+ * (that'd be a bug). -+ * returns IMAP_MAILBOX_MOVED if we referred the client */ -+/* ext_name is the external name of the mailbox */ -+/* you can avoid referring the client by setting tag or ext_name to NULL. */ -+int mlookup(const char *tag, const char *ext_name, -+ const char *name, int *flags, char **pathp, char **mpathp, -+ char **partp, char **aclp, struct txn **tid) -+{ -+ int r, mbtype; -+ char *remote, *acl; -+ -+ r = mboxlist_detail(name, &mbtype, pathp, mpathp, &remote, &acl, tid); -+ if ((r == IMAP_MAILBOX_NONEXISTENT || (mbtype & MBTYPE_RESERVE)) && -+ config_mupdate_server) { -+ /* It is not currently active, make sure we have the most recent -+ * copy of the database */ -+ kick_mupdate(); -+ r = mboxlist_detail(name, &mbtype, pathp, mpathp, &remote, &acl, tid); -+ } -+ -+ if(partp) *partp = remote; -+ if(aclp) *aclp = acl; -+ if(flags) *flags = mbtype; -+ if(r) return r; -+ -+ if(mbtype & MBTYPE_RESERVE) return IMAP_MAILBOX_RESERVED; -+ -+ if(mbtype & MBTYPE_MOVING) { -+ /* do we have rights on the mailbox? */ -+ if(!imapd_userisadmin && -+ (!acl || !(cyrus_acl_myrights(imapd_authstate,acl) & ACL_LOOKUP))) { -+ r = IMAP_MAILBOX_NONEXISTENT; -+ } else if(tag && ext_name && remote && *remote) { -+ char *c = NULL; -+ -+ c = strchr(remote, '!'); -+ if(c) *c = '\0'; -+ imapd_refer(tag, remote, ext_name); -+ r = IMAP_MAILBOX_MOVED; -+ } else if(config_mupdate_server) { -+ r = IMAP_SERVER_UNAVAILABLE; -+ } else { -+ r = IMAP_MAILBOX_NOTSUPPORTED; -+ } -+ } -+ else if (mbtype & MBTYPE_REMOTE) { -+ /* xxx hide the fact that we are storing partitions */ -+ if(remote && *remote) { -+ char *c; -+ c = strchr(remote, '!'); -+ if(c) *c = '\0'; -+ } -+ } -+ -+ return r; -+} -+ -+static void imapd_reset(void) -+{ -+ int i; -+ -+ proc_cleanup(); -+ -+ /* close backend connections */ -+ i = 0; -+ while (backend_cached && backend_cached[i]) { -+ proxy_downserver(backend_cached[i]); -+ if (backend_cached[i]->last_result.s) { -+ free(backend_cached[i]->last_result.s); -+ } -+ free(backend_cached[i]); -+ i++; -+ } -+ if (backend_cached) free(backend_cached); -+ backend_cached = NULL; -+ backend_inbox = backend_current = NULL; -+ proxy_cmdcnt = 0; -+ disable_referrals = 0; -+ supports_referrals = 0; -+ -+ if (imapd_mailbox) { -+ index_closemailbox(imapd_mailbox); -+ mailbox_close(imapd_mailbox); -+ imapd_mailbox = 0; -+ } -+ -+ if (imapd_in) { -+ /* Flush the incoming buffer */ -+ prot_NONBLOCK(imapd_in); -+ prot_fill(imapd_in); -+ -+ prot_free(imapd_in); -+ } -+ -+ if (imapd_out) { -+ /* Flush the outgoing buffer */ -+ prot_flush(imapd_out); -+ -+ prot_free(imapd_out); -+ } -+ -+ imapd_in = imapd_out = NULL; -+ -+ if (protin) protgroup_reset(protin); -+ -+#ifdef HAVE_SSL -+ if (tls_conn) { -+ if (tls_reset_servertls(&tls_conn) == -1) { -+ fatal("tls_reset() failed", EC_TEMPFAIL); -+ } -+ tls_conn = NULL; -+ } -+#endif -+ -+ cyrus_reset_stdio(); -+ -+ strcpy(imapd_clienthost, "[local]"); -+ if (imapd_logfd != -1) { -+ close(imapd_logfd); -+ imapd_logfd = -1; -+ } -+ if (imapd_userid != NULL) { -+ free(imapd_userid); -+ imapd_userid = NULL; -+ } -+ if (proxy_userid != NULL) { -+ free(proxy_userid); -+ proxy_userid = NULL; -+ } -+ if (imapd_magicplus != NULL) { -+ free(imapd_magicplus); -+ imapd_magicplus = NULL; -+ } -+ if (imapd_authstate) { -+ auth_freestate(imapd_authstate); -+ imapd_authstate = NULL; -+ } -+ imapd_userisadmin = 0; -+ imapd_userisproxyadmin = 0; -+ if (imapd_saslconn) { -+ sasl_dispose(&imapd_saslconn); -+ imapd_saslconn = NULL; -+ } -+ imapd_starttls_done = 0; -+ plaintextloginalert = NULL; -+ -+ if(saslprops.iplocalport) { -+ free(saslprops.iplocalport); -+ saslprops.iplocalport = NULL; -+ } -+ if(saslprops.ipremoteport) { -+ free(saslprops.ipremoteport); -+ saslprops.ipremoteport = NULL; -+ } -+ if(saslprops.authid) { -+ free(saslprops.authid); -+ saslprops.authid = NULL; -+ } -+ saslprops.ssf = 0; -+ -+ imapd_exists = -1; -+} -+ -+/* -+ * run once when process is forked; -+ * MUST NOT exit directly; must return with non-zero error code -+ */ -+int service_init(int argc, char **argv, char **envp) -+{ -+ int ret; -+ int opt; -+ -+ if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE); -+ setproctitle_init(argc, argv, envp); -+ -+ /* set signal handlers */ -+ signals_set_shutdown(&shut_down); -+ signal(SIGPIPE, SIG_IGN); -+ -+ /* load the SASL plugins */ -+ global_sasl_init(1, 1, mysasl_cb); -+ -+ ret = snprintf(shutdownfilename, sizeof(shutdownfilename), -+ "%s/msg/shutdown", config_dir); -+ -+ if(ret < 0 || ret >= sizeof(shutdownfilename)) { -+ fatal("shutdownfilename buffer too small (configdirectory too long)", -+ EC_CONFIG); -+ } -+ -+ /* open the mboxlist, we'll need it for real work */ -+ mboxlist_init(0); -+ mboxlist_open(NULL); -+ mailbox_initialize(); -+ -+ /* open the quota db, we'll need it for real work */ -+ quotadb_init(0); -+ quotadb_open(NULL); -+ -+ /* setup for sending IMAP IDLE notifications */ -+ idle_enabled(); -+ -+ /* create connection to the SNMP listener, if available. */ -+ snmp_connect(); /* ignore return code */ -+ snmp_set_str(SERVER_NAME_VERSION,CYRUS_VERSION); -+ -+ while ((opt = getopt(argc, argv, "sp:")) != EOF) { -+ switch (opt) { -+ case 's': /* imaps (do starttls right away) */ -+ imaps = 1; -+ if (!tls_enabled()) { -+ syslog(LOG_ERR, "imaps: required OpenSSL options not present"); -+ fatal("imaps: required OpenSSL options not present", -+ EC_CONFIG); -+ } -+ break; -+ case 'p': /* external protection */ -+ extprops_ssf = atoi(optarg); -+ break; -+ default: -+ break; -+ } -+ } -+ -+ /* Initialize the annotatemore extention */ -+ if (config_mupdate_server) -+ annotatemore_init(0, annotate_fetch_proxy, annotate_store_proxy); -+ else -+ annotatemore_init(0, NULL, NULL); -+ annotatemore_open(NULL); -+ -+ /* Create a protgroup for input from the client and selected backend */ -+ protin = protgroup_new(2); -+ -+ /* YYY Sanity checks possible here? */ -+ message_uuid_client_init(getenv("CYRUS_UUID_PREFIX")); -+ -+ return 0; -+} -+ -+/* -+ * run for each accepted connection -+ */ -+#ifdef ID_SAVE_CMDLINE -+int service_main(int argc, char **argv, char **envp __attribute__((unused))) -+#else -+int service_main(int argc __attribute__((unused)), -+ char **argv __attribute__((unused)), -+ char **envp __attribute__((unused))) -+#endif -+{ -+ socklen_t salen; -+ int timeout; -+ sasl_security_properties_t *secprops = NULL; -+ struct sockaddr_storage imapd_localaddr, imapd_remoteaddr; -+ char localip[60], remoteip[60]; -+ char hbuf[NI_MAXHOST]; -+ int niflags; -+ int imapd_haveaddr = 0; -+ -+ signals_poll(); -+ -+#ifdef ID_SAVE_CMDLINE -+ /* get command line args for use in ID before getopt mangles them */ -+ id_getcmdline(argc, argv); -+#endif -+ -+ sync_log_init(); -+ -+ imapd_in = prot_new(0, 0); -+ imapd_out = prot_new(1, 1); -+ protgroup_insert(protin, imapd_in); -+ -+ /* Find out name of client host */ -+ salen = sizeof(imapd_remoteaddr); -+ if (getpeername(0, (struct sockaddr *)&imapd_remoteaddr, &salen) == 0 && -+ (imapd_remoteaddr.ss_family == AF_INET || -+ imapd_remoteaddr.ss_family == AF_INET6)) { -+ if (getnameinfo((struct sockaddr *)&imapd_remoteaddr, salen, -+ hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD) == 0) { -+ strncpy(imapd_clienthost, hbuf, sizeof(hbuf)); -+ strlcat(imapd_clienthost, " ", sizeof(imapd_clienthost)); -+ imapd_clienthost[sizeof(imapd_clienthost)-30] = '\0'; -+ } else { -+ imapd_clienthost[0] = '\0'; -+ } -+ niflags = NI_NUMERICHOST; -+#ifdef NI_WITHSCOPEID -+ if (((struct sockaddr *)&imapd_remoteaddr)->sa_family == AF_INET6) -+ niflags |= NI_WITHSCOPEID; -+#endif -+ if (getnameinfo((struct sockaddr *)&imapd_remoteaddr, salen, hbuf, -+ sizeof(hbuf), NULL, 0, niflags) != 0) -+ strlcpy(hbuf, "unknown", sizeof(hbuf)); -+ strlcat(imapd_clienthost, "[", sizeof(imapd_clienthost)); -+ strlcat(imapd_clienthost, hbuf, sizeof(imapd_clienthost)); -+ strlcat(imapd_clienthost, "]", sizeof(imapd_clienthost)); -+ salen = sizeof(imapd_localaddr); -+ if (getsockname(0, (struct sockaddr *)&imapd_localaddr, &salen) == 0) { -+ if(iptostring((struct sockaddr *)&imapd_remoteaddr, salen, -+ remoteip, sizeof(remoteip)) == 0 -+ && iptostring((struct sockaddr *)&imapd_localaddr, salen, -+ localip, sizeof(localip)) == 0) { -+ imapd_haveaddr = 1; -+ } -+ } -+ } -+ -+ /* create the SASL connection */ -+ if (sasl_server_new("imap", config_servername, -+ NULL, NULL, NULL, NULL, 0, -+ &imapd_saslconn) != SASL_OK) { -+ fatal("SASL failed initializing: sasl_server_new()", EC_TEMPFAIL); -+ } -+ -+ /* never allow plaintext, since IMAP has the LOGIN command */ -+ secprops = mysasl_secprops(SASL_SEC_NOPLAINTEXT); -+ sasl_setprop(imapd_saslconn, SASL_SEC_PROPS, secprops); -+ sasl_setprop(imapd_saslconn, SASL_SSF_EXTERNAL, &extprops_ssf); -+ -+ if (imapd_haveaddr) { -+ sasl_setprop(imapd_saslconn, SASL_IPREMOTEPORT, remoteip); -+ saslprops.ipremoteport = xstrdup(remoteip); -+ sasl_setprop(imapd_saslconn, SASL_IPLOCALPORT, localip); -+ saslprops.iplocalport = xstrdup(localip); -+ } -+ -+ proc_register("imapd", imapd_clienthost, NULL, NULL); -+ -+ /* Set inactivity timer */ -+ timeout = config_getint(IMAPOPT_TIMEOUT); -+ if (timeout < 30) timeout = 30; -+ prot_settimeout(imapd_in, timeout*60); -+ prot_setflushonread(imapd_in, imapd_out); -+ -+ /* we were connected on imaps port so we should do -+ TLS negotiation immediately */ -+ if (imaps == 1) cmd_starttls(NULL, 1); -+ -+ snmp_increment(TOTAL_CONNECTIONS, 1); -+ snmp_increment(ACTIVE_CONNECTIONS, 1); -+ -+ cmdloop(); -+ -+ /* LOGOUT executed */ -+ prot_flush(imapd_out); -+ snmp_increment(ACTIVE_CONNECTIONS, -1); -+ -+ /* cleanup */ -+ imapd_reset(); -+ -+ return 0; -+} -+ -+/* Called by service API to shut down the service */ -+void service_abort(int error) -+{ -+ shut_down(error); -+} -+ -+/* -+ * found a motd file; spit out message and return -+ */ -+void motd_file(fd) -+int fd; -+{ -+ struct protstream *motd_in; -+ char buf[1024]; -+ char *p; -+ -+ motd_in = prot_new(fd, 0); -+ -+ prot_fgets(buf, sizeof(buf), motd_in); -+ if ((p = strchr(buf, '\r'))!=NULL) *p = 0; -+ if ((p = strchr(buf, '\n'))!=NULL) *p = 0; -+ -+ for(p = buf; *p == '['; p++); /* can't have [ be first char, sigh */ -+ prot_printf(imapd_out, "* OK [ALERT] %s\r\n", p); -+} -+ -+/* -+ * Cleanly shut down and exit -+ */ -+void shut_down(int code) __attribute__((noreturn)); -+void shut_down(int code) -+{ -+ int i; -+ -+ proc_cleanup(); -+ -+ i = 0; -+ while (backend_cached && backend_cached[i]) { -+ proxy_downserver(backend_cached[i]); -+ if (backend_cached[i]->last_result.s) { -+ free(backend_cached[i]->last_result.s); -+ } -+ free(backend_cached[i]); -+ i++; -+ } -+ if (backend_cached) free(backend_cached); -+ -+ if (imapd_mailbox) { -+ index_closemailbox(imapd_mailbox); -+ mailbox_close(imapd_mailbox); -+ } -+ seen_done(); -+ mboxkey_done(); -+ mboxlist_close(); -+ mboxlist_done(); -+ -+ quotadb_close(); -+ quotadb_done(); -+ -+ annotatemore_close(); -+ annotatemore_done(); -+ -+ if (imapd_in) { -+ /* Flush the incoming buffer */ -+ prot_NONBLOCK(imapd_in); -+ prot_fill(imapd_in); -+ -+ prot_free(imapd_in); -+ } -+ -+ if (imapd_out) { -+ /* Flush the outgoing buffer */ -+ prot_flush(imapd_out); -+ prot_free(imapd_out); -+ -+ /* one less active connection */ -+ snmp_increment(ACTIVE_CONNECTIONS, -1); -+ } -+ -+ if (protin) protgroup_free(protin); -+ -+#ifdef HAVE_SSL -+ tls_shutdown_serverengine(); -+#endif -+ -+ cyrus_done(); -+ -+ exit(code); -+} -+ -+void fatal(const char *s, int code) -+{ -+ static int recurse_code = 0; -+ -+ if (recurse_code) { -+ /* We were called recursively. Just give up */ -+ proc_cleanup(); -+ snmp_increment(ACTIVE_CONNECTIONS, -1); -+ exit(recurse_code); -+ } -+ recurse_code = code; -+ if (imapd_out) { -+ prot_printf(imapd_out, "* BYE Fatal error: %s\r\n", s); -+ prot_flush(imapd_out); -+ } -+ if (stage) { -+ /* Cleanup the stage(s) */ -+ while (numstage) { -+ struct appendstage *curstage = stage[--numstage]; -+ -+ append_removestage(curstage->stage); -+ while (curstage->nflags--) { -+ free(curstage->flag[curstage->nflags]); -+ } -+ if (curstage->flag) free((char *) curstage->flag); -+ free(curstage); -+ } -+ free(stage); -+ } -+ -+ syslog(LOG_ERR, "Fatal error: %s", s); -+ shut_down(code); -+} -+ -+/* -+ * Check the currently selected mailbox for updates. -+ * -+ * 'be' is the backend (if any) that we just proxied a command to. -+ */ -+static void imapd_check(struct backend *be, int usinguid, int checkseen) -+{ -+ if (backend_current && backend_current != be) { -+ /* remote mailbox */ -+ char mytag[128]; -+ -+ proxy_gentag(mytag, sizeof(mytag)); -+ -+ prot_printf(backend_current->out, "%s Noop\r\n", mytag); -+ pipe_until_tag(backend_current, mytag, 0); -+ } -+ else if (imapd_mailbox) { -+ /* local mailbox */ -+ index_check(imapd_mailbox, usinguid, checkseen); -+ } -+} -+ -+/* -+ * Top-level command loop parsing -+ */ -+void cmdloop() -+{ -+ int fd; -+ char motdfilename[1024]; -+ int c; -+ int ret; -+ int usinguid, havepartition, havenamespace, recursive; -+ static struct buf tag, cmd, arg1, arg2, arg3, arg4; -+ char *p, shut[1024]; -+ const char *err; -+ -+ prot_printf(imapd_out, -+ "* OK %s Cyrus IMAP4 %s%s server ready\r\n", config_servername, -+ config_mupdate_server ? "(Murder) " : "", CYRUS_VERSION); -+ -+ ret = snprintf(motdfilename, sizeof(motdfilename), "%s/msg/motd", -+ config_dir); -+ -+ if(ret < 0 || ret >= sizeof(motdfilename)) { -+ fatal("motdfilename buffer too small (configdirectory too long)", -+ EC_CONFIG); -+ } -+ -+ if ((fd = open(motdfilename, O_RDONLY, 0)) != -1) { -+ motd_file(fd); -+ close(fd); -+ } -+ -+ for (;;) { -+ /* Flush any buffered output */ -+ prot_flush(imapd_out); -+ if (backend_current) prot_flush(backend_current->out); -+ -+ /* Check for shutdown file */ -+ if ( !imapd_userisadmin && imapd_userid -+ && shutdown_file(shut, sizeof(shut))) { -+ for (p = shut; *p == '['; p++); /* can't have [ be first char */ -+ prot_printf(imapd_out, "* BYE [ALERT] %s\r\n", p); -+ shut_down(0); -+ } -+ -+ signals_poll(); -+ -+ if (!proxy_check_input(protin, imapd_in, imapd_out, -+ backend_current ? backend_current->in : NULL, -+ NULL, 0)) { -+ /* No input from client */ -+ continue; -+ } -+ -+ /* Parse tag */ -+ c = getword(imapd_in, &tag); -+ if (c == EOF) { -+ if ((err = prot_error(imapd_in))!=NULL -+ && strcmp(err, PROT_EOF_STRING)) { -+ syslog(LOG_WARNING, "%s, closing connection", err); -+ prot_printf(imapd_out, "* BYE %s\r\n", err); -+ } -+ return; -+ } -+ if (c != ' ' || !imparse_isatom(tag.s) || (tag.s[0] == '*' && !tag.s[1])) { -+ prot_printf(imapd_out, "* BAD Invalid tag\r\n"); -+ eatline(imapd_in, c); -+ continue; -+ } -+ -+ /* Parse command name */ -+ c = getword(imapd_in, &cmd); -+ if (!cmd.s[0]) { -+ prot_printf(imapd_out, "%s BAD Null command\r\n", tag.s); -+ eatline(imapd_in, c); -+ continue; -+ } -+ if (islower((unsigned char) cmd.s[0])) -+ cmd.s[0] = toupper((unsigned char) cmd.s[0]); -+ for (p = &cmd.s[1]; *p; p++) { -+ if (isupper((unsigned char) *p)) *p = tolower((unsigned char) *p); -+ } -+ -+ /* if we need to force a kick, do so */ -+ if (referral_kick) { -+ kick_mupdate(); -+ referral_kick = 0; -+ } -+ -+ if (plaintextloginalert) { -+ prot_printf(imapd_out, "* OK [ALERT] %s\r\n", -+ plaintextloginalert); -+ plaintextloginalert = NULL; -+ } -+ -+ /* Only Authenticate/Login/Logout/Noop/Capability/Id/Starttls -+ allowed when not logged in */ -+ if (!imapd_userid && !strchr("ALNCIS", cmd.s[0])) goto nologin; -+ -+ /* note that about half the commands (the common ones that don't -+ hit the mailboxes file) now close the mailboxes file just in -+ case it was open. */ -+ switch (cmd.s[0]) { -+ case 'A': -+ if (!strcmp(cmd.s, "Authenticate")) { -+ int haveinitresp = 0; -+ -+ if (c != ' ') goto missingargs; -+ c = getword(imapd_in, &arg1); -+ if (!imparse_isatom(arg1.s)) { -+ prot_printf(imapd_out, "%s BAD Invalid authenticate mechanism\r\n", tag.s); -+ eatline(imapd_in, c); -+ continue; -+ } -+ if (c == ' ') { -+ haveinitresp = 1; -+ c = getword(imapd_in, &arg2); -+ if (c == EOF) goto missingargs; -+ } -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ -+ if (imapd_userid) { -+ prot_printf(imapd_out, "%s BAD Already authenticated\r\n", tag.s); -+ continue; -+ } -+ cmd_authenticate(tag.s, arg1.s, haveinitresp ? arg2.s : NULL); -+ -+ snmp_increment(AUTHENTICATE_COUNT, 1); -+ } -+ else if (!imapd_userid) goto nologin; -+ else if (!strcmp(cmd.s, "Append")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ -+ cmd_append(tag.s, arg1.s, NULL); -+ -+ snmp_increment(APPEND_COUNT, 1); -+ } -+ else goto badcmd; -+ break; -+ -+ case 'B': -+ if (!strcmp(cmd.s, "Bboard")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ -+ cmd_select(tag.s, cmd.s, arg1.s); -+ -+ snmp_increment(BBOARD_COUNT, 1); -+ } -+ else goto badcmd; -+ break; -+ -+ case 'C': -+ if (!strcmp(cmd.s, "Capability")) { -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_capability(tag.s); -+ -+ snmp_increment(CAPABILITY_COUNT, 1); -+ } -+ else if (!imapd_userid) goto nologin; -+ else if (!strcmp(cmd.s, "Check")) { -+ if (!imapd_mailbox && !backend_current) goto nomailbox; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ -+ cmd_noop(tag.s, cmd.s); -+ -+ snmp_increment(CHECK_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Copy")) { -+ if (!imapd_mailbox && !backend_current) goto nomailbox; -+ usinguid = 0; -+ if (c != ' ') goto missingargs; -+ copy: -+ c = getword(imapd_in, &arg1); -+ if (c == '\r') goto missingargs; -+ if (c != ' ' || !imparse_issequence(arg1.s)) goto badsequence; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ -+ cmd_copy(tag.s, arg1.s, arg2.s, usinguid); -+ -+ snmp_increment(COPY_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Create")) { -+ havepartition = 0; -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == EOF) goto missingargs; -+ if (c == ' ') { -+ havepartition = 1; -+ c = getword(imapd_in, &arg2); -+ if (!imparse_isatom(arg2.s)) goto badpartition; -+ } -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_create(tag.s, arg1.s, havepartition ? arg2.s : 0, 0); -+ -+ snmp_increment(CREATE_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Close")) { -+ if (!imapd_mailbox && !backend_current) goto nomailbox; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ -+ cmd_close(tag.s); -+ -+ snmp_increment(CLOSE_COUNT, 1); -+ } -+ else goto badcmd; -+ break; -+ -+ case 'D': -+ if (!strcmp(cmd.s, "Delete")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_delete(tag.s, arg1.s, 0, 0); -+ -+ snmp_increment(DELETE_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Deleteacl")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_setacl(tag.s, arg1.s, arg2.s, NULL); -+ -+ snmp_increment(DELETEACL_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Dump")) { -+ int uid_start = 0; -+ -+ if(c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if(c == ' ') { -+ c = getastring(imapd_in, imapd_out, &arg2); -+ if(!imparse_isnumber(arg2.s)) goto extraargs; -+ uid_start = atoi(arg2.s); -+ } -+ -+ if(c == '\r') c = prot_getc(imapd_in); -+ if(c != '\n') goto extraargs; -+ -+ cmd_dump(tag.s, arg1.s, uid_start); -+ /* snmp_increment(DUMP_COUNT, 1);*/ -+ } -+ else goto badcmd; -+ break; -+ -+ case 'E': -+ if (!strcmp(cmd.s, "Expunge")) { -+ if (!imapd_mailbox && !backend_current) goto nomailbox; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ -+ cmd_expunge(tag.s, 0); -+ -+ snmp_increment(EXPUNGE_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Examine")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ -+ cmd_select(tag.s, cmd.s, arg1.s); -+ -+ snmp_increment(EXAMINE_COUNT, 1); -+ } -+ else goto badcmd; -+ break; -+ -+ case 'F': -+ if (!strcmp(cmd.s, "Fetch")) { -+ if (!imapd_mailbox && !backend_current) goto nomailbox; -+ usinguid = 0; -+ if (c != ' ') goto missingargs; -+ fetch: -+ c = getword(imapd_in, &arg1); -+ if (c == '\r') goto missingargs; -+ if (c != ' ' || !imparse_issequence(arg1.s)) goto badsequence; -+ -+ cmd_fetch(tag.s, arg1.s, usinguid); -+ -+ snmp_increment(FETCH_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Find")) { -+ c = getword(imapd_in, &arg1); -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_find(tag.s, arg1.s, arg2.s); -+ -+ snmp_increment(FIND_COUNT, 1); -+ } -+ else goto badcmd; -+ break; -+ -+ case 'G': -+ if (!strcmp(cmd.s, "Getacl")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_getacl(tag.s, arg1.s); -+ -+ snmp_increment(GETACL_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Getannotation")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ -+ cmd_getannotation(tag.s, arg1.s); -+ -+ snmp_increment(GETANNOTATION_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Getquota")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_getquota(tag.s, arg1.s); -+ -+ snmp_increment(GETQUOTA_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Getquotaroot")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_getquotaroot(tag.s, arg1.s); -+ -+ snmp_increment(GETQUOTAROOT_COUNT, 1); -+ } -+#ifdef HAVE_SSL -+ else if (!strcmp(cmd.s, "Genurlauth")) { -+ if (c != ' ') goto missingargs; -+ -+ cmd_genurlauth(tag.s); -+ /* snmp_increment(GENURLAUTH_COUNT, 1);*/ -+ } -+#endif -+ else goto badcmd; -+ break; -+ -+ case 'I': -+ if (!strcmp(cmd.s, "Id")) { -+ if (c != ' ') goto missingargs; -+ cmd_id(tag.s); -+ -+ snmp_increment(ID_COUNT, 1); -+ } -+ else if (!imapd_userid) goto nologin; -+ else if (!strcmp(cmd.s, "Idle") && idle_enabled()) { -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_idle(tag.s); -+ -+ snmp_increment(IDLE_COUNT, 1); -+ } -+ else goto badcmd; -+ break; -+ -+ case 'L': -+ if (!strcmp(cmd.s, "Login")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if(c != ' ') goto missingargs; -+ -+ cmd_login(tag.s, arg1.s); -+ -+ snmp_increment(LOGIN_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Logout")) { -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ -+ snmp_increment(LOGOUT_COUNT, 1); -+ -+ /* force any responses from our selected backend */ -+ if (backend_current) imapd_check(NULL, 0, 0); -+ -+ prot_printf(imapd_out, "* BYE %s\r\n", -+ error_message(IMAP_BYE_LOGOUT)); -+ prot_printf(imapd_out, "%s OK %s\r\n", tag.s, -+ error_message(IMAP_OK_COMPLETED)); -+ return; -+ } -+ else if (!imapd_userid) goto nologin; -+ else if (!strcmp(cmd.s, "List")) { -+ int listopts = LIST_CHILDREN; -+#ifdef ENABLE_LISTEXT -+ /* Check for and parse LISTEXT options */ -+ c = prot_getc(imapd_in); -+ if (c == '(') { -+ c = getlistopts(tag.s, &listopts); -+ if (c == EOF) { -+ eatline(imapd_in, c); -+ continue; -+ } -+ } -+ else -+ prot_ungetc(c, imapd_in); -+#endif /* ENABLE_LISTEXT */ -+ if (imapd_magicplus) listopts += LIST_SUBSCRIBED; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_list(tag.s, listopts, arg1.s, arg2.s); -+ -+ snmp_increment(LIST_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Lsub")) { -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_list(tag.s, LIST_LSUB | LIST_CHILDREN, arg1.s, arg2.s); -+ -+ snmp_increment(LSUB_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Listrights")) { -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_listrights(tag.s, arg1.s, arg2.s); -+ -+ snmp_increment(LISTRIGHTS_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Localappend")) { -+ /* create a local-only mailbox */ -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ if (c != ' ') goto missingargs; -+ -+ cmd_append(tag.s, arg1.s, *arg2.s ? arg2.s : NULL); -+ -+ snmp_increment(APPEND_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Localcreate")) { -+ /* create a local-only mailbox */ -+ havepartition = 0; -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == EOF) goto missingargs; -+ if (c == ' ') { -+ havepartition = 1; -+ c = getword(imapd_in, &arg2); -+ if (!imparse_isatom(arg2.s)) goto badpartition; -+ } -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_create(tag.s, arg1.s, havepartition ? arg2.s : NULL, 1); -+ -+ /* xxxx snmp_increment(CREATE_COUNT, 1); */ -+ } -+ else if (!strcmp(cmd.s, "Localdelete")) { -+ /* delete a mailbox locally only */ -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_delete(tag.s, arg1.s, 1, 1); -+ -+ /* xxxx snmp_increment(DELETE_COUNT, 1); */ -+ } -+ else goto badcmd; -+ break; -+ -+ case 'M': -+ if (!strcmp(cmd.s, "Myrights")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_myrights(tag.s, arg1.s); -+ -+ /* xxxx snmp_increment(MYRIGHTS_COUNT, 1); */ -+ } -+ else if (!strcmp(cmd.s, "Mupdatepush")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if(c == EOF) goto missingargs; -+ if(c == '\r') c = prot_getc(imapd_in); -+ if(c != '\n') goto extraargs; -+ cmd_mupdatepush(tag.s, arg1.s); -+ -+ /* xxxx snmp_increment(MUPDATEPUSH_COUNT, 1); */ -+ } else goto badcmd; -+ break; -+ -+ case 'N': -+ if (!strcmp(cmd.s, "Noop")) { -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ -+ cmd_noop(tag.s, cmd.s); -+ -+ /* xxxx snmp_increment(NOOP_COUNT, 1); */ -+ } -+#ifdef ENABLE_X_NETSCAPE_HACK -+ else if (!strcmp(cmd.s, "Netscape")) { -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_netscrape(tag.s); -+ } -+#endif -+ else if (!imapd_userid) goto nologin; -+ else if (!strcmp(cmd.s, "Namespace")) { -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_namespace(tag.s); -+ -+ /* xxxx snmp_increment(NAMESPACE_COUNT, 1); */ -+ } -+ else goto badcmd; -+ break; -+ -+ case 'P': -+ if (!strcmp(cmd.s, "Partial")) { -+ if (!imapd_mailbox && !backend_current) goto nomailbox; -+ if (c != ' ') goto missingargs; -+ c = getword(imapd_in, &arg1); -+ if (c != ' ') goto missingargs; -+ c = getword(imapd_in, &arg2); -+ if (c != ' ') goto missingargs; -+ c = getword(imapd_in, &arg3); -+ if (c != ' ') goto missingargs; -+ c = getword(imapd_in, &arg4); -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ -+ cmd_partial(tag.s, arg1.s, arg2.s, arg3.s, arg4.s); -+ -+ /* xxxx snmp_increment(PARTIAL_COUNT, 1); */ -+ } -+ else goto badcmd; -+ break; -+ -+ case 'R': -+ if (!strcmp(cmd.s, "Rename")) { -+ havepartition = 0; -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ if (c == EOF) goto missingargs; -+ if (c == ' ') { -+ havepartition = 1; -+ c = getword(imapd_in, &arg3); -+ if (!imparse_isatom(arg3.s)) goto badpartition; -+ } -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_rename(tag.s, arg1.s, arg2.s, havepartition ? arg3.s : 0); -+ -+ /* xxxx snmp_increment(RENAME_COUNT, 1); */ -+ } else if(!strcmp(cmd.s, "Reconstruct")) { -+ recursive = 0; -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if(c == ' ') { -+ /* Optional RECURSEIVE argument */ -+ c = getword(imapd_in, &arg2); -+ if(!imparse_isatom(arg2.s)) -+ goto extraargs; -+ else if(!strcasecmp(arg2.s, "RECURSIVE")) -+ recursive = 1; -+ else -+ goto extraargs; -+ } -+ if(c == '\r') c = prot_getc(imapd_in); -+ if(c != '\n') goto extraargs; -+ cmd_reconstruct(tag.s, arg1.s, recursive); -+ -+ /* snmp_increment(RECONSTRUCT_COUNT, 1); */ -+ } -+ else if (!strcmp(cmd.s, "Rlist")) { -+ supports_referrals = !disable_referrals; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_list(tag.s, LIST_CHILDREN | LIST_REMOTE, arg1.s, arg2.s); -+ -+/* snmp_increment(LIST_COUNT, 1); */ -+ } -+ else if (!strcmp(cmd.s, "Rlsub")) { -+ supports_referrals = !disable_referrals; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_list(tag.s, LIST_LSUB | LIST_CHILDREN | LIST_REMOTE, -+ arg1.s, arg2.s); -+/* snmp_increment(LSUB_COUNT, 1); */ -+ } -+#ifdef HAVE_SSL -+ else if (!strcmp(cmd.s, "Resetkey")) { -+ int have_mbox = 0, have_mech = 0; -+ -+ if (c == ' ') { -+ have_mbox = 1; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == EOF) goto missingargs; -+ if (c == ' ') { -+ have_mech = 1; -+ c = getword(imapd_in, &arg2); -+ } -+ } -+ -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_resetkey(tag.s, have_mbox ? arg1.s : 0, -+ have_mech ? arg2.s : 0); -+ /* snmp_increment(RESETKEY_COUNT, 1);*/ -+ } -+#endif -+ else goto badcmd; -+ break; -+ -+ case 'S': -+ if (!strcmp(cmd.s, "Starttls")) { -+ if (!tls_enabled()) { -+ /* we don't support starttls */ -+ goto badcmd; -+ } -+ -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ -+ /* if we've already done SASL fail */ -+ if (imapd_userid != NULL) { -+ prot_printf(imapd_out, -+ "%s BAD Can't Starttls after authentication\r\n", tag.s); -+ continue; -+ } -+ -+ /* check if already did a successful tls */ -+ if (imapd_starttls_done == 1) { -+ prot_printf(imapd_out, -+ "%s BAD Already did a successful Starttls\r\n", -+ tag.s); -+ continue; -+ } -+ cmd_starttls(tag.s, 0); -+ -+ snmp_increment(STARTTLS_COUNT, 1); -+ continue; -+ } -+ if (!imapd_userid) { -+ goto nologin; -+ } else if (!strcmp(cmd.s, "Store")) { -+ if (!imapd_mailbox && !backend_current) goto nomailbox; -+ usinguid = 0; -+ if (c != ' ') goto missingargs; -+ store: -+ c = getword(imapd_in, &arg1); -+ if (c != ' ' || !imparse_issequence(arg1.s)) goto badsequence; -+ c = getword(imapd_in, &arg2); -+ if (c != ' ') goto missingargs; -+ -+ cmd_store(tag.s, arg1.s, arg2.s, usinguid); -+ -+ snmp_increment(STORE_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Select")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ -+ cmd_select(tag.s, cmd.s, arg1.s); -+ -+ snmp_increment(SELECT_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Search")) { -+ if (!imapd_mailbox && !backend_current) goto nomailbox; -+ usinguid = 0; -+ if (c != ' ') goto missingargs; -+ search: -+ -+ cmd_search(tag.s, usinguid); -+ -+ snmp_increment(SEARCH_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Subscribe")) { -+ if (c != ' ') goto missingargs; -+ havenamespace = 0; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == ' ') { -+ havenamespace = 1; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ } -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ if (havenamespace) { -+ cmd_changesub(tag.s, arg1.s, arg2.s, 1); -+ } -+ else { -+ cmd_changesub(tag.s, (char *)0, arg1.s, 1); -+ } -+ snmp_increment(SUBSCRIBE_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Setacl")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg3); -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_setacl(tag.s, arg1.s, arg2.s, arg3.s); -+ -+ snmp_increment(SETACL_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Setannotation")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ -+ cmd_setannotation(tag.s, arg1.s); -+ -+ snmp_increment(SETANNOTATION_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Setquota")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ cmd_setquota(tag.s, arg1.s); -+ -+ snmp_increment(SETQUOTA_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Sort")) { -+ if (!imapd_mailbox && !backend_current) goto nomailbox; -+ usinguid = 0; -+ if (c != ' ') goto missingargs; -+ sort: -+ cmd_sort(tag.s, usinguid); -+ -+ snmp_increment(SORT_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Status")) { -+ if (c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') goto missingargs; -+ cmd_status(tag.s, arg1.s); -+ -+ snmp_increment(STATUS_COUNT, 1); -+ } -+ else goto badcmd; -+ break; -+ -+ case 'T': -+ if (!strcmp(cmd.s, "Thread")) { -+ if (!imapd_mailbox && !backend_current) goto nomailbox; -+ usinguid = 0; -+ if (c != ' ') goto missingargs; -+ thread: -+ cmd_thread(tag.s, usinguid); -+ -+ snmp_increment(THREAD_COUNT, 1); -+ } -+ else goto badcmd; -+ break; -+ -+ case 'U': -+ if (!strcmp(cmd.s, "Uid")) { -+ if (!imapd_mailbox && !backend_current) goto nomailbox; -+ usinguid = 1; -+ if (c != ' ') goto missingargs; -+ c = getword(imapd_in, &arg1); -+ if (c != ' ') goto missingargs; -+ lcase(arg1.s); -+ if (!strcmp(arg1.s, "fetch")) { -+ goto fetch; -+ } -+ else if (!strcmp(arg1.s, "store")) { -+ goto store; -+ } -+ else if (!strcmp(arg1.s, "search")) { -+ goto search; -+ } -+ else if (!strcmp(arg1.s, "sort")) { -+ goto sort; -+ } -+ else if (!strcmp(arg1.s, "thread")) { -+ goto thread; -+ } -+ else if (!strcmp(arg1.s, "copy")) { -+ goto copy; -+ } -+ else if (!strcmp(arg1.s, "expunge")) { -+ c = getword(imapd_in, &arg1); -+ if (!imparse_issequence(arg1.s)) goto badsequence; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_expunge(tag.s, arg1.s); -+ -+ snmp_increment(EXPUNGE_COUNT, 1); -+ } -+ else { -+ prot_printf(imapd_out, "%s BAD Unrecognized UID subcommand\r\n", tag.s); -+ eatline(imapd_in, c); -+ } -+ } -+ else if (!strcmp(cmd.s, "Unsubscribe")) { -+ if (c != ' ') goto missingargs; -+ havenamespace = 0; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c == ' ') { -+ havenamespace = 1; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ } -+ if (c == EOF) goto missingargs; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ if (havenamespace) { -+ cmd_changesub(tag.s, arg1.s, arg2.s, 0); -+ } -+ else { -+ cmd_changesub(tag.s, (char *)0, arg1.s, 0); -+ } -+ -+ snmp_increment(UNSUBSCRIBE_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Unselect")) { -+ if (!imapd_mailbox && !backend_current) goto nomailbox; -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ cmd_unselect(tag.s); -+ -+ snmp_increment(UNSELECT_COUNT, 1); -+ } -+ else if (!strcmp(cmd.s, "Undump")) { -+ if(c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ -+ /* we want to get a list at this point */ -+ if(c != ' ') goto missingargs; -+ -+ cmd_undump(tag.s, arg1.s); -+ /* snmp_increment(UNDUMP_COUNT, 1);*/ -+ } -+#ifdef HAVE_SSL -+ else if (!strcmp(cmd.s, "Urlfetch")) { -+ if (c != ' ') goto missingargs; -+ -+ cmd_urlfetch(tag.s); -+ /* snmp_increment(URLFETCH_COUNT, 1);*/ -+ } -+#endif -+ else goto badcmd; -+ break; -+ -+ case 'X': -+ if (!strcmp(cmd.s, "Xfer")) { -+ int havepartition = 0; -+ -+ /* Mailbox */ -+ if(c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg1); -+ -+ /* Dest Server */ -+ if(c != ' ') goto missingargs; -+ c = getastring(imapd_in, imapd_out, &arg2); -+ -+ if(c == ' ') { -+ /* Dest Partition */ -+ c = getastring(imapd_in, imapd_out, &arg3); -+ if (!imparse_isatom(arg3.s)) goto badpartition; -+ havepartition = 1; -+ } -+ -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') goto extraargs; -+ -+ cmd_xfer(tag.s, arg1.s, arg2.s, -+ (havepartition ? arg3.s : NULL)); -+ /* snmp_increment(XFER_COUNT, 1);*/ -+ } -+ else goto badcmd; -+ break; -+ -+ default: -+ badcmd: -+ prot_printf(imapd_out, "%s BAD Unrecognized command\r\n", tag.s); -+ eatline(imapd_in, c); -+ } -+ -+ continue; -+ -+ nologin: -+ prot_printf(imapd_out, "%s BAD Please login first\r\n", tag.s); -+ eatline(imapd_in, c); -+ continue; -+ -+ nomailbox: -+ prot_printf(imapd_out, "%s BAD Please select a mailbox first\r\n", tag.s); -+ eatline(imapd_in, c); -+ continue; -+ -+ missingargs: -+ prot_printf(imapd_out, "%s BAD Missing required argument to %s\r\n", tag.s, cmd.s); -+ eatline(imapd_in, c); -+ continue; -+ -+ extraargs: -+ prot_printf(imapd_out, "%s BAD Unexpected extra arguments to %s\r\n", tag.s, cmd.s); -+ eatline(imapd_in, c); -+ continue; -+ -+ badsequence: -+ prot_printf(imapd_out, "%s BAD Invalid sequence in %s\r\n", tag.s, cmd.s); -+ eatline(imapd_in, c); -+ continue; -+ -+ badpartition: -+ prot_printf(imapd_out, "%s BAD Invalid partition name in %s\r\n", -+ tag.s, cmd.s); -+ eatline(imapd_in, c); -+ continue; -+ } -+} -+ -+/* -+ * Perform a LOGIN command -+ */ -+void cmd_login(char *tag, char *user) -+{ -+ char userbuf[MAX_MAILBOX_NAME+1]; -+ unsigned userlen; -+ const char *canon_user = userbuf; -+ char c; -+ struct buf passwdbuf; -+ char *passwd; -+ const char *reply = NULL; -+ int r; -+ -+ if (imapd_userid) { -+ eatline(imapd_in, ' '); -+ prot_printf(imapd_out, "%s BAD Already logged in\r\n", tag); -+ return; -+ } -+ -+ r = imapd_canon_user(imapd_saslconn, NULL, user, 0, -+ SASL_CU_AUTHID | SASL_CU_AUTHZID, NULL, -+ userbuf, sizeof(userbuf), &userlen); -+ -+ if (r) { -+ syslog(LOG_NOTICE, "badlogin: %s plaintext %s invalid user", -+ imapd_clienthost, beautify_string(user)); -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, -+ error_message(IMAP_INVALID_USER)); -+ return; -+ } -+ -+ /* possibly disallow login */ -+ if ((imapd_starttls_done == 0) && -+ (config_getswitch(IMAPOPT_ALLOWPLAINTEXT) == 0) && -+ !is_userid_anonymous(canon_user)) { -+ eatline(imapd_in, ' '); -+ prot_printf(imapd_out, "%s NO Login only available under a layer\r\n", -+ tag); -+ return; -+ } -+ -+ memset(&passwdbuf,0,sizeof(struct buf)); -+ c = getastring(imapd_in, imapd_out, &passwdbuf); -+ -+ if(c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ freebuf(&passwdbuf); -+ prot_printf(imapd_out, -+ "%s BAD Unexpected extra arguments to LOGIN\r\n", -+ tag); -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ passwd = passwdbuf.s; -+ -+ if (is_userid_anonymous(canon_user)) { -+ if (config_getswitch(IMAPOPT_ALLOWANONYMOUSLOGIN)) { -+ passwd = beautify_string(passwd); -+ if (strlen(passwd) > 500) passwd[500] = '\0'; -+ syslog(LOG_NOTICE, "login: %s anonymous %s", -+ imapd_clienthost, passwd); -+ reply = "Anonymous access granted"; -+ imapd_userid = xstrdup("anonymous"); -+ } -+ else { -+ syslog(LOG_NOTICE, "badlogin: %s anonymous login refused", -+ imapd_clienthost); -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, -+ error_message(IMAP_ANONYMOUS_NOT_PERMITTED)); -+ freebuf(&passwdbuf); -+ return; -+ } -+ } -+ else if ((r = sasl_checkpass(imapd_saslconn, -+ canon_user, -+ strlen(canon_user), -+ passwd, -+ strlen(passwd))) != SASL_OK) { -+ syslog(LOG_NOTICE, "badlogin: %s plaintext %s %s", -+ imapd_clienthost, canon_user, sasl_errdetail(imapd_saslconn)); -+ -+ sleep(3); -+ -+ if ((reply = sasl_errstring(r, NULL, NULL)) != NULL) { -+ prot_printf(imapd_out, "%s NO Login failed: %s\r\n", tag, reply); -+ } else { -+ prot_printf(imapd_out, "%s NO Login failed: %d\r\n", tag, r); -+ } -+ -+ snmp_increment_args(AUTHENTICATION_NO, 1, -+ VARIABLE_AUTH, 0 /* hash_simple("LOGIN") */, -+ VARIABLE_LISTEND); -+ freebuf(&passwdbuf); -+ return; -+ } -+ else { -+ r = sasl_getprop(imapd_saslconn, SASL_USERNAME, -+ (const void **) &canon_user); -+ -+ if(r != SASL_OK) { -+ if ((reply = sasl_errstring(r, NULL, NULL)) != NULL) { -+ prot_printf(imapd_out, "%s NO Login failed: %s\r\n", -+ tag, reply); -+ } else { -+ prot_printf(imapd_out, "%s NO Login failed: %d\r\n", tag, r); -+ } -+ -+ snmp_increment_args(AUTHENTICATION_NO, 1, -+ VARIABLE_AUTH, 0 /* hash_simple("LOGIN") */, -+ VARIABLE_LISTEND); -+ freebuf(&passwdbuf); -+ return; -+ } -+ -+ reply = "User logged in"; -+ imapd_userid = xstrdup(canon_user); -+ snmp_increment_args(AUTHENTICATION_YES, 1, -+ VARIABLE_AUTH, 0 /*hash_simple("LOGIN") */, -+ VARIABLE_LISTEND); -+ syslog(LOG_NOTICE, "login: %s %s%s plaintext%s %s", imapd_clienthost, -+ imapd_userid, imapd_magicplus ? imapd_magicplus : "", -+ imapd_starttls_done ? "+TLS" : "", -+ reply ? reply : ""); -+ -+ /* Apply penalty only if not under layer */ -+ if (!imapd_starttls_done) { -+ int plaintextloginpause = config_getint(IMAPOPT_PLAINTEXTLOGINPAUSE); -+ if (plaintextloginpause) { -+ sleep(plaintextloginpause); -+ } -+ -+ /* Fetch plaintext login nag message */ -+ plaintextloginalert = config_getstring(IMAPOPT_PLAINTEXTLOGINALERT); -+ } -+ } -+ -+ imapd_authstate = auth_newstate(imapd_userid); -+ -+ imapd_userisadmin = global_authisa(imapd_authstate, IMAPOPT_ADMINS); -+ -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, reply); -+ -+ /* Create telemetry log */ -+ imapd_logfd = telemetry_log(imapd_userid, imapd_in, imapd_out, 0); -+ -+ /* Set namespace */ -+ if ((r = mboxname_init_namespace(&imapd_namespace, -+ imapd_userisadmin || imapd_userisproxyadmin)) != 0) { -+ syslog(LOG_ERR, error_message(r)); -+ fatal(error_message(r), EC_CONFIG); -+ } -+ -+ /* Make a copy of the external userid for use in proxying */ -+ proxy_userid = xstrdup(imapd_userid); -+ -+ /* Translate any separators in userid */ -+ mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid, -+ config_virtdomains ? -+ strcspn(imapd_userid, "@") : 0); -+ -+ freebuf(&passwdbuf); -+ return; -+} -+ -+/* -+ * Perform an AUTHENTICATE command -+ */ -+void -+cmd_authenticate(char *tag, char *authtype, char *resp) -+{ -+ int sasl_result; -+ -+ const int *ssfp; -+ char *ssfmsg=NULL; -+ -+ const char *canon_user; -+ -+ int r; -+ -+ r = saslserver(imapd_saslconn, authtype, resp, "", "+ ", "", -+ imapd_in, imapd_out, &sasl_result, NULL); -+ -+ if (r) { -+ const char *errorstring = NULL; -+ -+ switch (r) { -+ case IMAP_SASL_CANCEL: -+ prot_printf(imapd_out, -+ "%s BAD Client canceled authentication\r\n", tag); -+ break; -+ case IMAP_SASL_PROTERR: -+ errorstring = prot_error(imapd_in); -+ -+ prot_printf(imapd_out, -+ "%s NO Error reading client response: %s\r\n", -+ tag, errorstring ? errorstring : ""); -+ break; -+ default: -+ /* failed authentication */ -+ errorstring = sasl_errstring(sasl_result, NULL, NULL); -+ -+ syslog(LOG_NOTICE, "badlogin: %s %s [%s]", -+ imapd_clienthost, authtype, sasl_errdetail(imapd_saslconn)); -+ -+ snmp_increment_args(AUTHENTICATION_NO, 1, -+ VARIABLE_AUTH, 0, /* hash_simple(authtype) */ -+ VARIABLE_LISTEND); -+ sleep(3); -+ -+ if (errorstring) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, errorstring); -+ } else { -+ prot_printf(imapd_out, "%s NO Error authenticating\r\n", tag); -+ } -+ } -+ -+ reset_saslconn(&imapd_saslconn); -+ return; -+ } -+ -+ /* successful authentication */ -+ -+ /* get the userid from SASL --- already canonicalized from -+ * mysasl_proxy_policy() -+ */ -+ sasl_result = sasl_getprop(imapd_saslconn, SASL_USERNAME, -+ (const void **) &canon_user); -+ if (sasl_result != SASL_OK) { -+ prot_printf(imapd_out, "%s NO weird SASL error %d SASL_USERNAME\r\n", -+ tag, sasl_result); -+ syslog(LOG_ERR, "weird SASL error %d getting SASL_USERNAME", -+ sasl_result); -+ reset_saslconn(&imapd_saslconn); -+ return; -+ } -+ -+ /* If we're proxying, the authzid may contain a magic plus, -+ so re-canonify it */ -+ if (config_getswitch(IMAPOPT_IMAPMAGICPLUS) && strchr(canon_user, '+')) { -+ char userbuf[MAX_MAILBOX_NAME+1]; -+ unsigned userlen; -+ -+ sasl_result = imapd_canon_user(imapd_saslconn, NULL, canon_user, 0, -+ SASL_CU_AUTHID | SASL_CU_AUTHZID, -+ NULL, userbuf, sizeof(userbuf), &userlen); -+ if (sasl_result != SASL_OK) { -+ prot_printf(imapd_out, -+ "%s NO SASL canonification error %d\r\n", -+ tag, sasl_result); -+ reset_saslconn(&imapd_saslconn); -+ return; -+ } -+ -+ imapd_userid = xstrdup(userbuf); -+ } else { -+ imapd_userid = xstrdup(canon_user); -+ } -+ -+ proc_register("imapd", imapd_clienthost, imapd_userid, (char *)0); -+ -+ syslog(LOG_NOTICE, "login: %s %s%s %s%s %s", imapd_clienthost, -+ imapd_userid, imapd_magicplus ? imapd_magicplus : "", -+ authtype, imapd_starttls_done ? "+TLS" : "", "User logged in"); -+ -+ sasl_getprop(imapd_saslconn, SASL_SSF, (const void **) &ssfp); -+ -+ /* really, we should be doing a sasl_getprop on SASL_SSF_EXTERNAL, -+ but the current libsasl doesn't allow that. */ -+ if (imapd_starttls_done) { -+ switch(*ssfp) { -+ case 0: ssfmsg = "tls protection"; break; -+ case 1: ssfmsg = "tls plus integrity protection"; break; -+ default: ssfmsg = "tls plus privacy protection"; break; -+ } -+ } else { -+ switch(*ssfp) { -+ case 0: ssfmsg = "no protection"; break; -+ case 1: ssfmsg = "integrity protection"; break; -+ default: ssfmsg = "privacy protection"; break; -+ } -+ } -+ -+ snmp_increment_args(AUTHENTICATION_YES, 1, -+ VARIABLE_AUTH, 0, /* hash_simple(authtype) */ -+ VARIABLE_LISTEND); -+ -+ prot_printf(imapd_out, "%s OK Success (%s)\r\n", tag, ssfmsg); -+ -+ prot_setsasl(imapd_in, imapd_saslconn); -+ prot_setsasl(imapd_out, imapd_saslconn); -+ -+ /* Create telemetry log */ -+ imapd_logfd = telemetry_log(imapd_userid, imapd_in, imapd_out, 0); -+ -+ /* Set namespace */ -+ if ((r = mboxname_init_namespace(&imapd_namespace, -+ imapd_userisadmin || imapd_userisproxyadmin)) != 0) { -+ syslog(LOG_ERR, error_message(r)); -+ fatal(error_message(r), EC_CONFIG); -+ } -+ -+ /* Make a copy of the external userid for use in proxying */ -+ proxy_userid = xstrdup(imapd_userid); -+ -+ /* Translate any separators in userid */ -+ mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid, -+ config_virtdomains ? -+ strcspn(imapd_userid, "@") : 0); -+ -+ return; -+} -+ -+/* -+ * Perform a NOOP command -+ */ -+void cmd_noop(char *tag, char *cmd) -+{ -+ if (backend_current) { -+ /* remote mailbox */ -+ prot_printf(backend_current->out, "%s %s\r\n", tag, cmd); -+ -+ return; -+ } -+ -+ if (imapd_mailbox) { -+ index_check(imapd_mailbox, 0, 1); -+ } -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+} -+ -+/* -+ * Parse and perform an ID command. -+ * -+ * the command has been parsed up to the parameter list. -+ * -+ * we only allow one ID in non-authenticated state from a given client. -+ * we only allow MAXIDFAILED consecutive failed IDs from a given client. -+ * we only record MAXIDLOG ID responses from a given client. -+ */ -+void cmd_id(char *tag) -+{ -+ static int did_id = 0; -+ static int failed_id = 0; -+ static int logged_id = 0; -+ int error = 0; -+ int c = EOF, npair = 0; -+ static struct buf arg, field; -+ struct attvaluelist *params = 0; -+ -+ /* check if we've already had an ID in non-authenticated state */ -+ if (!imapd_userid && did_id) { -+ prot_printf(imapd_out, -+ "%s NO Only one Id allowed in non-authenticated state\r\n", -+ tag); -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ /* check if we've had too many failed IDs in a row */ -+ if (failed_id >= MAXIDFAILED) { -+ prot_printf(imapd_out, "%s NO Too many (%u) invalid Id commands\r\n", -+ tag, failed_id); -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ /* ok, accept parameter list */ -+ c = getword(imapd_in, &arg); -+ /* check for "NIL" or start of parameter list */ -+ if (strcasecmp(arg.s, "NIL") && c != '(') { -+ prot_printf(imapd_out, "%s BAD Invalid parameter list in Id\r\n", tag); -+ eatline(imapd_in, c); -+ failed_id++; -+ return; -+ } -+ -+ /* parse parameter list */ -+ if (c == '(') { -+ for (;;) { -+ if (c == ')') { -+ /* end of string/value pairs */ -+ break; -+ } -+ -+ /* get field name */ -+ c = getstring(imapd_in, imapd_out, &field); -+ if (c != ' ') { -+ prot_printf(imapd_out, -+ "%s BAD Invalid/missing field name in Id\r\n", -+ tag); -+ error = 1; -+ break; -+ } -+ -+ /* get field value */ -+ c = getnstring(imapd_in, imapd_out, &arg); -+ if (c != ' ' && c != ')') { -+ prot_printf(imapd_out, -+ "%s BAD Invalid/missing value in Id\r\n", -+ tag); -+ error = 1; -+ break; -+ } -+ -+ /* ok, we're anal, but we'll still process the ID command */ -+ if (strlen(field.s) > MAXIDFIELDLEN) { -+ prot_printf(imapd_out, -+ "%s BAD field longer than %u octets in Id\r\n", -+ tag, MAXIDFIELDLEN); -+ error = 1; -+ break; -+ } -+ if (strlen(arg.s) > MAXIDVALUELEN) { -+ prot_printf(imapd_out, -+ "%s BAD value longer than %u octets in Id\r\n", -+ tag, MAXIDVALUELEN); -+ error = 1; -+ break; -+ } -+ if (++npair > MAXIDPAIRS) { -+ prot_printf(imapd_out, -+ "%s BAD too many (%u) field-value pairs in ID\r\n", -+ tag, MAXIDPAIRS); -+ error = 1; -+ break; -+ } -+ -+ /* ok, we're happy enough */ -+ appendattvalue(¶ms, field.s, arg.s); -+ } -+ -+ if (error || c != ')') { -+ /* erp! */ -+ eatline(imapd_in, c); -+ freeattvalues(params); -+ failed_id++; -+ return; -+ } -+ c = prot_getc(imapd_in); -+ } -+ -+ /* check for CRLF */ -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ prot_printf(imapd_out, -+ "%s BAD Unexpected extra arguments to Id\r\n", tag); -+ eatline(imapd_in, c); -+ freeattvalues(params); -+ failed_id++; -+ return; -+ } -+ -+ /* log the client's ID string. -+ eventually this should be a callback or something. */ -+ if (npair && logged_id < MAXIDLOG) { -+ char logbuf[MAXIDLOGLEN + 1] = ""; -+ struct attvaluelist *pptr; -+ -+ for (pptr = params; pptr; pptr = pptr->next) { -+ /* should we check for and format literals here ??? */ -+ snprintf(logbuf + strlen(logbuf), MAXIDLOGLEN - strlen(logbuf), -+ " \"%s\" ", pptr->attrib); -+ if (!strcmp(pptr->value, "NIL")) -+ snprintf(logbuf + strlen(logbuf), MAXIDLOGLEN - strlen(logbuf), -+ "NIL"); -+ else -+ snprintf(logbuf + strlen(logbuf), MAXIDLOGLEN - strlen(logbuf), -+ "\"%s\"", pptr->value); -+ } -+ -+ syslog(LOG_INFO, "client id:%s", logbuf); -+ -+ logged_id++; -+ } -+ -+ freeattvalues(params); -+ -+ /* spit out our ID string. -+ eventually this might be configurable. */ -+ if (config_getswitch(IMAPOPT_IMAPIDRESPONSE)) { -+ id_response(imapd_out); -+ prot_printf(imapd_out, ")\r\n"); -+ } -+ else -+ prot_printf(imapd_out, "* ID NIL\r\n"); -+ -+ imapd_check(NULL, 0, 0); -+ -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ -+ failed_id = 0; -+ did_id = 1; -+} -+ -+/* -+ * Perform an IDLE command -+ */ -+void cmd_idle(char *tag) -+{ -+ int c = EOF; -+ static struct buf arg; -+ static int idle_period = -1; -+ -+ if (!backend_current) { /* Local mailbox */ -+ /* Setup for doing mailbox updates */ -+ if (!idle_init(idle_update)) { -+ prot_printf(imapd_out, -+ "%s NO cannot start idling\r\n", tag); -+ return; -+ } -+ -+ /* Tell client we are idling and waiting for end of command */ -+ prot_printf(imapd_out, "+ idling\r\n"); -+ prot_flush(imapd_out); -+ -+ /* Start doing mailbox updates */ -+ if (imapd_mailbox) index_check(imapd_mailbox, 0, 1); -+ idle_start(imapd_mailbox); -+ -+ /* Get continuation data */ -+ c = getword(imapd_in, &arg); -+ -+ /* Stop updates and do any necessary cleanup */ -+ idle_done(imapd_mailbox); -+ } -+ else { /* Remote mailbox */ -+ int done = 0, shutdown = 0; -+ char buf[2048]; -+ -+ /* get polling period */ -+ if (idle_period == -1) { -+ idle_period = config_getint(IMAPOPT_IMAPIDLEPOLL); -+ } -+ -+ if (CAPA(backend_current, CAPA_IDLE)) { -+ /* Start IDLE on backend */ -+ prot_printf(backend_current->out, "%s IDLE\r\n", tag); -+ if (!prot_fgets(buf, sizeof(buf), backend_current->in)) { -+ -+ /* If we received nothing from the backend, fail */ -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, -+ error_message(IMAP_SERVER_UNAVAILABLE)); -+ return; -+ } -+ if (buf[0] != '+') { -+ /* If we received anything but a continuation response, -+ spit out what we received and quit */ -+ prot_write(imapd_out, buf, strlen(buf)); -+ return; -+ } -+ } -+ -+ /* Tell client we are idling and waiting for end of command */ -+ prot_printf(imapd_out, "+ idling\r\n"); -+ prot_flush(imapd_out); -+ -+ /* Pipe updates to client while waiting for end of command */ -+ while (!done) { -+ /* Flush any buffered output */ -+ prot_flush(imapd_out); -+ -+ /* Check for shutdown file */ -+ if (!imapd_userisadmin && shutdown_file(buf, sizeof(buf))) { -+ shutdown = done = 1; -+ goto done; -+ } -+ -+ done = proxy_check_input(protin, imapd_in, imapd_out, -+ backend_current->in, NULL, idle_period); -+ -+ /* If not running IDLE on backend, poll the mailbox for updates */ -+ if (!CAPA(backend_current, CAPA_IDLE)) { -+ imapd_check(NULL, 0, 1); -+ } -+ } -+ -+ /* Get continuation data */ -+ c = getword(imapd_in, &arg); -+ -+ done: -+ if (CAPA(backend_current, CAPA_IDLE)) { -+ /* Either the client timed out, or ended the command. -+ In either case we're done, so terminate IDLE on backend */ -+ prot_printf(backend_current->out, "Done\r\n"); -+ pipe_until_tag(backend_current, tag, 0); -+ } -+ -+ if (shutdown) { -+ char *p; -+ -+ for (p = buf; *p == '['; p++); /* can't have [ be first char */ -+ prot_printf(imapd_out, "* BYE [ALERT] %s\r\n", p); -+ shut_down(0); -+ } -+ } -+ -+ imapd_check(NULL, 0, 1); -+ -+ if (c != EOF) { -+ if (!strcasecmp(arg.s, "Done") && -+ (c = (c == '\r') ? prot_getc(imapd_in) : c) == '\n') { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ } -+ else { -+ prot_printf(imapd_out, -+ "%s BAD Invalid Idle continuation\r\n", tag); -+ eatline(imapd_in, c); -+ } -+ } -+} -+ -+/* Send unsolicited untagged responses to the client */ -+void idle_update(idle_flags_t flags) -+{ -+ if ((flags & IDLE_MAILBOX) && imapd_mailbox) -+ index_check(imapd_mailbox, 0, 1); -+ -+ if (flags & IDLE_ALERT) { -+ char shut[1024]; -+ if (! imapd_userisadmin && shutdown_file(shut, sizeof(shut))) { -+ char *p; -+ for (p = shut; *p == '['; p++); /* can't have [ be first char */ -+ prot_printf(imapd_out, "* BYE [ALERT] %s\r\n", p); -+ shut_down(0); -+ } -+ } -+ -+ prot_flush(imapd_out); -+} -+ -+/* -+ * Perform a CAPABILITY command -+ */ -+void cmd_capability(char *tag) -+{ -+ const char *sasllist; /* the list of SASL mechanisms */ -+ int mechcount; -+ -+ imapd_check(NULL, 0, 0); -+ -+ prot_printf(imapd_out, "* CAPABILITY " CAPABILITY_STRING); -+ -+ if (idle_enabled()) { -+ prot_printf(imapd_out, " IDLE"); -+ } -+ -+ if (tls_enabled() && !imapd_starttls_done && !imapd_authstate) { -+ prot_printf(imapd_out, " STARTTLS"); -+ } -+ if (imapd_authstate || -+ (!imapd_starttls_done && !config_getswitch(IMAPOPT_ALLOWPLAINTEXT))) { -+ prot_printf(imapd_out, " LOGINDISABLED"); -+ } -+ -+ if(config_mupdate_server) { -+ prot_printf(imapd_out, " MUPDATE=mupdate://%s/", config_mupdate_server); -+ } -+ -+ /* add the SASL mechs */ -+ if (!imapd_authstate && -+ sasl_listmech(imapd_saslconn, NULL, -+ "AUTH=", " AUTH=", " SASL-IR", -+ &sasllist, -+ NULL, &mechcount) == SASL_OK && mechcount > 0) { -+ prot_printf(imapd_out, " %s", sasllist); -+ } else { -+ /* else don't show anything */ -+ } -+ -+#ifdef ENABLE_LISTEXT -+ prot_printf(imapd_out, " LISTEXT LIST-SUBSCRIBED"); -+#endif /* ENABLE_LISTEXT */ -+ -+#ifdef ENABLE_X_NETSCAPE_HACK -+ prot_printf(imapd_out, " X-NETSCAPE"); -+#endif -+ -+#ifdef HAVE_SSL -+ prot_printf(imapd_out, " URLAUTH"); -+#endif -+ prot_printf(imapd_out, "\r\n%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+} -+ -+/* -+ * Parse and perform an APPEND command. -+ * The command has been parsed up to and including -+ * the mailbox name. -+ */ -+static int isokflag(char *s, int *isseen) -+{ -+ if (s[0] == '\\') { -+ lcase(s); -+ if (!strcmp(s, "\\seen")) { -+ *isseen = 1; -+ return 1; -+ } -+ if (!strcmp(s, "\\answered")) return 1; -+ if (!strcmp(s, "\\flagged")) return 1; -+ if (!strcmp(s, "\\draft")) return 1; -+ if (!strcmp(s, "\\deleted")) return 1; -+ -+ /* uh oh, system flag i don't recognize */ -+ return 0; -+ } else { -+ /* valid user flag? */ -+ return imparse_isatom(s); -+ } -+} -+ -+static int getliteralsize(char *p, int c, -+ unsigned *size, const char **parseerr) -+ -+{ -+ int sawdigit = 0; -+ int isnowait = 0; -+ -+ /* Check for literal8 */ -+ if (*p == '~') { -+ p++; -+ /* We don't support binary append yet */ -+ return IMAP_NO_UNKNOWN_CTE; -+ } -+ if (*p != '{') { -+ *parseerr = "Missing required argument to Append command"; -+ return IMAP_PROTOCOL_ERROR; -+ } -+ -+ /* Read size from literal */ -+ isnowait = 0; -+ *size = 0; -+ for (++p; *p && isdigit((int) *p); p++) { -+ sawdigit++; -+ if (*size > (UINT_MAX - (*p - '0')) / 10) -+ return IMAP_MESSAGE_TOO_LARGE; -+ *size = (*size)*10 + *p - '0'; -+#if 0 -+ if (*size < 0) { -+ lose(); -+ } -+#endif -+ } -+ if (*p == '+') { -+ isnowait++; -+ p++; -+ } -+ -+ if (c == '\r') { -+ c = prot_getc(imapd_in); -+ } -+ else { -+ prot_ungetc(c, imapd_in); -+ c = ' '; /* Force a syntax error */ -+ } -+ -+ if (*p != '}' || p[1] || c != '\n' || !sawdigit) { -+ *parseerr = "Invalid literal in Append command"; -+ return IMAP_PROTOCOL_ERROR; -+ } -+ -+ if (!isnowait) { -+ /* Tell client to send the message */ -+ prot_printf(imapd_out, "+ go ahead\r\n"); -+ prot_flush(imapd_out); -+ } -+ -+ return 0; -+} -+ -+static int catenate_text(FILE *f, unsigned *totalsize, const char **parseerr) -+{ -+ int c; -+ static struct buf arg; -+ unsigned size = 0; -+ char buf[4096+1]; -+ int n; -+ int r; -+ -+ c = getword(imapd_in, &arg); -+ -+ /* Read size from literal */ -+ r = getliteralsize(arg.s, c, &size, parseerr); -+ if (r) return r; -+ -+ if (*totalsize > UINT_MAX - size) r = IMAP_MESSAGE_TOO_LARGE; -+ -+ /* Catenate message part to stage */ -+ while (size) { -+ n = prot_read(imapd_in, buf, size > 4096 ? 4096 : size); -+ if (!n) { -+ syslog(LOG_ERR, -+ "IOERROR: reading message: unexpected end of file"); -+ return IMAP_IOERROR; -+ } -+ -+ buf[n] = '\0'; -+ if (n != strlen(buf)) r = IMAP_MESSAGE_CONTAINSNULL; -+ -+ size -= n; -+ if (r) continue; -+ -+ /* XXX do we want to try and validate the message like -+ we do in message_copy_strict()? */ -+ -+ if (f) fwrite(buf, n, 1, f); -+ } -+ -+ *totalsize += size; -+ -+ return r; -+} -+ -+static int catenate_url(const char *s, const char *cur_name, FILE *f, -+ unsigned *totalsize, const char **parseerr) -+{ -+ struct imapurl url; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ struct mailbox mboxstruct, *mailbox; -+ unsigned msgno; -+ int r = 0, doclose = 0; -+ unsigned long size = 0; -+ -+ imapurl_fromURL(&url, s); -+ -+ if (url.server) { -+ *parseerr = "Only relative URLs are supported"; -+ r = IMAP_BADURL; -+#if 0 -+ } else if (url.server && strcmp(url.server, config_servername)) { -+ *parseerr = "Can not catenate messages from another server"; -+ r = IMAP_BADURL; -+#endif -+ } else if (!url.mailbox && !imapd_mailbox && !cur_name) { -+ *parseerr = "No mailbox is selected or specified"; -+ r = IMAP_BADURL; -+ } else if (url.mailbox || (url.mailbox = cur_name)) { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, -+ url.mailbox, -+ imapd_userid, mailboxname); -+ if (!r) { -+ if (!imapd_mailbox || strcmp(imapd_mailbox->name, mailboxname)) { -+ /* not the currently selected mailbox, so try to open it */ -+ int mbtype; -+ char *newserver; -+ -+ /* lookup the location of the mailbox */ -+ r = mlookup(NULL, NULL, mailboxname, &mbtype, NULL, NULL, -+ &newserver, NULL, NULL); -+ -+ if (!r && (mbtype & MBTYPE_REMOTE)) { -+ /* remote mailbox */ -+ struct backend *be; -+ -+ be = proxy_findserver(newserver, &protocol[PROTOCOL_IMAP], -+ proxy_userid, &backend_cached, -+ &backend_current, &backend_inbox, imapd_in); -+ if (!s) { -+ r = IMAP_SERVER_UNAVAILABLE; -+ } else { -+ r = proxy_catenate_url(be, &url, f, &size, parseerr); -+ if (*totalsize > UINT_MAX - size) -+ r = IMAP_MESSAGE_TOO_LARGE; -+ else -+ *totalsize += size; -+ } -+ -+ free(url.freeme); -+ -+ return r; -+ } -+ -+ /* local mailbox */ -+ if (!r) { -+ r = mailbox_open_header(mailboxname, imapd_authstate, -+ &mboxstruct); -+ } -+ -+ if (!r) { -+ doclose = 1; -+ r = mailbox_open_index(&mboxstruct); -+ } -+ -+ if (!r && !(mboxstruct.myrights & ACL_READ)) { -+ r = (imapd_userisadmin || (mboxstruct.myrights & ACL_LOOKUP)) ? -+ IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT; -+ } -+ -+ if (!r) { -+ mailbox = &mboxstruct; -+ index_operatemailbox(mailbox); -+ } -+ } else { -+ mailbox = imapd_mailbox; -+ } -+ } -+ -+ if (r) { -+ *parseerr = error_message(r); -+ r = IMAP_BADURL; -+ } -+ } else { -+ mailbox = imapd_mailbox; -+ } -+ -+ if (r) { -+ /* nothing to do, handled up top */ -+ } else if (url.uidvalidity && -+ (mailbox->uidvalidity != url.uidvalidity)) { -+ *parseerr = "Uidvalidity of mailbox has changed"; -+ r = IMAP_BADURL; -+ } else if (!url.uid || !(msgno = index_finduid(url.uid)) || -+ (index_getuid(msgno) != url.uid)) { -+ *parseerr = "No such message in mailbox"; -+ r = IMAP_BADURL; -+ } else { -+ /* Catenate message part to stage */ -+ struct protstream *s = prot_new(fileno(f), 1); -+ -+ r = index_urlfetch(mailbox, msgno, url.section, -+ url.start_octet, url.octet_count, s, &size); -+ if (r == IMAP_BADURL) -+ *parseerr = "No such message part"; -+ else if (!r) { -+ if (*totalsize > UINT_MAX - size) -+ r = IMAP_MESSAGE_TOO_LARGE; -+ else -+ *totalsize += size; -+ } -+ -+ prot_flush(s); -+ prot_free(s); -+ -+ /* XXX do we want to try and validate the message like -+ we do in message_copy_strict()? */ -+ } -+ -+ free(url.freeme); -+ -+ if (doclose) { -+ mailbox_close(&mboxstruct); -+ if (imapd_mailbox) index_operatemailbox(imapd_mailbox); -+ } -+ -+ return r; -+} -+ -+static int append_catenate(FILE *f, const char *cur_name, unsigned *totalsize, -+ const char **parseerr, const char **url) -+{ -+ int c, r = 0; -+ static struct buf arg; -+ -+ do { -+ c = getword(imapd_in, &arg); -+ if (c != ' ') { -+ *parseerr = "Missing message part data in Append command"; -+ return IMAP_PROTOCOL_ERROR; -+ } -+ -+ if (!strcasecmp(arg.s, "TEXT")) { -+ int r1 = catenate_text(!r ? f : NULL, totalsize, parseerr); -+ if (r1) return r1; -+ -+ /* if we see a SP, we're trying to catenate more than one part */ -+ -+ /* Parse newline terminating command */ -+ c = prot_getc(imapd_in); -+ } -+ else if (!strcasecmp(arg.s, "URL")) { -+ c = getastring(imapd_in, imapd_out, &arg); -+ if (c != ' ' && c != ')') { -+ *parseerr = "Missing URL in Append command"; -+ return IMAP_PROTOCOL_ERROR; -+ } -+ -+ if (!r) { -+ r = catenate_url(arg.s, cur_name, f, totalsize, parseerr); -+ if (r) *url = arg.s; -+ } -+ } -+ else { -+ *parseerr = "Invalid message part type in Append command"; -+ return IMAP_PROTOCOL_ERROR; -+ } -+ -+ fflush(f); -+ } while (c == ' '); -+ -+ if (c != ')') { -+ *parseerr = "Missing space or ) after catenate list in Append command"; -+ return IMAP_PROTOCOL_ERROR; -+ } -+ -+ if (ferror(f) || fsync(fileno(f))) { -+ syslog(LOG_ERR, "IOERROR: writing message: %m"); -+ return IMAP_IOERROR; -+ } -+ -+ return r; -+} -+ -+/* If an APPEND is proxied from another server, -+ * 'cur_name' is the name of the currently selected mailbox (if any) -+ * in case we have to resolve relative URLs -+ */ -+#define FLAGGROW 10 -+void cmd_append(char *tag, char *name, const char *cur_name) -+{ -+ int c; -+ static struct buf arg; -+ char *p; -+ time_t now = time(NULL); -+ unsigned size, totalsize = 0; -+ int sync_seen = 0; -+ int r, i; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ struct appendstate mailbox; -+ unsigned long uidvalidity; -+ unsigned long firstuid, num; -+ long doappenduid = 0; -+ const char *parseerr = NULL, *url = NULL; -+ int mbtype; -+ char *newserver; -+ FILE *f; -+ int numalloc = 5; -+ struct appendstage *curstage; -+ -+ /* See if we can append */ -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ if (!r) { -+ r = mlookup(tag, name, mailboxname, &mbtype, NULL, NULL, -+ &newserver, NULL, NULL); -+ } -+ -+ if (!r && (mbtype & MBTYPE_REMOTE)) { -+ /* remote mailbox */ -+ struct backend *s = NULL; -+ -+ if (supports_referrals) { -+ imapd_refer(tag, newserver, name); -+ /* Eat the argument */ -+ eatline(imapd_in, prot_getc(imapd_in)); -+ return; -+ } -+ -+ s = proxy_findserver(newserver, &protocol[PROTOCOL_IMAP], -+ proxy_userid, &backend_cached, -+ &backend_current, &backend_inbox, imapd_in); -+ if (!s) r = IMAP_SERVER_UNAVAILABLE; -+ -+ imapd_check(s, 0, 0); -+ -+ if (!r) { -+ int is_active = 1; -+ s->context = (void*) &is_active; -+ if (imapd_mailbox) { -+ prot_printf(s->out, "%s Localappend {%d+}\r\n%s {%d+}\r\n%s ", -+ tag, strlen(name), name, -+ strlen(imapd_mailbox->name), imapd_mailbox->name); -+ } else { -+ prot_printf(s->out, "%s Localappend {%d+}\r\n%s {%d+}\r\n%s ", -+ tag, strlen(name), name, 0, ""); -+ } -+ if (!(r = pipe_command(s, 16384))) { -+ if (s != backend_current) pipe_including_tag(s, tag, 0); -+ } -+ s->context = NULL; -+ } else { -+ eatline(imapd_in, prot_getc(imapd_in)); -+ } -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, -+ prot_error(imapd_in) ? prot_error(imapd_in) : -+ error_message(r)); -+ } -+ -+ return; -+ } -+ -+ /* local mailbox */ -+ if (!r) { -+ r = append_check(mailboxname, MAILBOX_FORMAT_NORMAL, -+ imapd_authstate, ACL_INSERT, totalsize); -+ } -+ if (r) { -+ eatline(imapd_in, ' '); -+ prot_printf(imapd_out, "%s NO %s%s\r\n", -+ tag, -+ (r == IMAP_MAILBOX_NONEXISTENT && -+ mboxlist_createmailboxcheck(mailboxname, 0, 0, -+ imapd_userisadmin, -+ imapd_userid, imapd_authstate, -+ (char **)0, (char **)0) == 0) -+ ? "[TRYCREATE] " : "", error_message(r)); -+ return; -+ } -+ -+ stage = xmalloc(numalloc * sizeof(struct appendstage *)); -+ -+ c = ' '; /* just parsed a space */ -+ /* we loop, to support MULTIAPPEND */ -+ while (!r && c == ' ') { -+ /* Grow the stage array, if necessary */ -+ if (numstage == numalloc) { -+ /* Avoid integer wrap as arg to xrealloc */ -+ if (numalloc > INT_MAX/(2*sizeof(struct appendstage *))) -+ goto done; -+ numalloc *= 2; -+ stage = xrealloc(stage, numalloc * sizeof(struct appendstage *)); -+ } -+ curstage = stage[numstage] = xzmalloc(sizeof(struct appendstage)); -+ numstage++; -+ /* Parse flags */ -+ c = getword(imapd_in, &arg); -+ if (c == '(' && !arg.s[0]) { -+ curstage->nflags = 0; -+ do { -+ c = getword(imapd_in, &arg); -+ if (!curstage->nflags && !arg.s[0] && c == ')') break; /* empty list */ -+ if (!isokflag(arg.s, &sync_seen)) { -+ parseerr = "Invalid flag in Append command"; -+ r = IMAP_PROTOCOL_ERROR; -+ goto done; -+ } -+ if (curstage->nflags == curstage->flagalloc) { -+ curstage->flagalloc += FLAGGROW; -+ curstage->flag = -+ (char **) xrealloc((char *) curstage->flag, -+ curstage->flagalloc * sizeof(char *)); -+ } -+ curstage->flag[curstage->nflags] = xstrdup(arg.s); -+ curstage->nflags++; -+ } while (c == ' '); -+ if (c != ')') { -+ parseerr = -+ "Missing space or ) after flag name in Append command"; -+ r = IMAP_PROTOCOL_ERROR; -+ goto done; -+ } -+ c = prot_getc(imapd_in); -+ if (c != ' ') { -+ parseerr = "Missing space after flag list in Append command"; -+ r = IMAP_PROTOCOL_ERROR; -+ goto done; -+ } -+ c = getword(imapd_in, &arg); -+ } -+ -+ /* Parse internaldate */ -+ if (c == '\"' && !arg.s[0]) { -+ prot_ungetc(c, imapd_in); -+ c = getdatetime(&(curstage->internaldate)); -+ if (c != ' ') { -+ parseerr = "Invalid date-time in Append command"; -+ r = IMAP_PROTOCOL_ERROR; -+ goto done; -+ } -+ c = getword(imapd_in, &arg); -+ } else { -+ curstage->internaldate = now; -+ } -+ -+ /* Stage the message */ -+ f = append_newstage(mailboxname, now, numstage, &(curstage->stage)); -+ if (!f) { -+ r = IMAP_IOERROR; -+ goto done; -+ } -+ -+ if (!strcasecmp(arg.s, "CATENATE")) { -+ if (c != ' ' || (c = prot_getc(imapd_in) != '(')) { -+ parseerr = "Missing message part(s) in Append command"; -+ r = IMAP_PROTOCOL_ERROR; -+ goto done; -+ } -+ -+ /* Catenate the message part(s) to stage */ -+ size = 0; -+ r = append_catenate(f, cur_name, &size, &parseerr, &url); -+ if (r) goto done; -+ } -+ else { -+ /* Read size from literal */ -+ r = getliteralsize(arg.s, c, &size, &parseerr); -+ if (r) goto done; -+ -+ /* Copy message to stage */ -+ r = message_copy_strict(imapd_in, f, size); -+ } -+ totalsize += size; -+ fclose(f); -+ -+ /* if we see a SP, we're trying to append more than one message */ -+ -+ /* Parse newline terminating command */ -+ c = prot_getc(imapd_in); -+ } -+ -+ done: -+ if (r) { -+ eatline(imapd_in, c); -+ } else { -+ /* we should be looking at the end of the line */ -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ parseerr = "junk after literal"; -+ r = IMAP_PROTOCOL_ERROR; -+ eatline(imapd_in, c); -+ } -+ } -+ -+ /* Append from the stage(s) */ -+ if (!r) { -+ r = append_setup(&mailbox, mailboxname, MAILBOX_FORMAT_NORMAL, -+ imapd_userid, imapd_authstate, ACL_INSERT, totalsize); -+ } -+ if (!r) { -+ struct body *body = NULL; -+ -+ doappenduid = (mailbox.m.myrights & ACL_READ); -+ -+ for (i = 0; !r && i < numstage; i++) { -+ r = append_fromstage(&mailbox, &body, stage[i]->stage, stage[i]->internaldate, -+ (const char **) stage[i]->flag, stage[i]->nflags, 0); -+ if (body) message_free_body(body); -+ } -+ if (body) free(body); -+ -+ if (!r) { -+ r = append_commit(&mailbox, totalsize, &uidvalidity, &firstuid, &num); -+ if (!r) { -+ sync_log_append(mailboxname); -+ if (sync_seen) sync_log_seen(imapd_userid, mailboxname); -+ } -+ } else { -+ append_abort(&mailbox); -+ } -+ } -+ -+ /* Cleanup the stage(s) */ -+ while (numstage) { -+ curstage = stage[--numstage]; -+ -+ append_removestage(curstage->stage); -+ while (curstage->nflags--) { -+ free(curstage->flag[curstage->nflags]); -+ } -+ if (curstage->flag) free((char *) curstage->flag); -+ free(curstage); -+ } -+ if (stage) free(stage); -+ stage = NULL; -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (r == IMAP_PROTOCOL_ERROR && parseerr) { -+ prot_printf(imapd_out, "%s BAD %s\r\n", tag, parseerr); -+ } else if (r == IMAP_BADURL) { -+ prot_printf(imapd_out, "%s NO [BADURL \"%s\"] %s\r\n", -+ tag, url, parseerr); -+ } else if (r) { -+ prot_printf(imapd_out, "%s NO %s%s\r\n", -+ tag, -+ (r == IMAP_MAILBOX_NONEXISTENT && -+ mboxlist_createmailboxcheck(mailboxname, 0, 0, -+ imapd_userisadmin, -+ imapd_userid, imapd_authstate, -+ (char **)0, (char **)0) == 0) -+ ? "[TRYCREATE] " : r == IMAP_MESSAGE_TOO_LARGE -+ ? "[TOOBIG]" : "", error_message(r)); -+ } else if (doappenduid) { -+ /* is this a space seperated list or sequence list? */ -+ prot_printf(imapd_out, "%s OK [APPENDUID %lu", tag, uidvalidity); -+ if (num == 1) { -+ prot_printf(imapd_out, " %lu", firstuid); -+ } else { -+ prot_printf(imapd_out, " %lu:%lu", firstuid, firstuid + num - 1); -+ } -+ prot_printf(imapd_out, "] %s\r\n", error_message(IMAP_OK_COMPLETED)); -+ } else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ } -+} -+ -+/* -+ * Perform a SELECT/EXAMINE/BBOARD command -+ */ -+void cmd_select(char *tag, char *cmd, char *name) -+{ -+ struct mailbox mailbox; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ int r = 0; -+ double usage; -+ int doclose = 0; -+ int mbtype; -+ char *newserver; -+ struct backend *backend_next = NULL; -+ static char lastqr[MAX_MAILBOX_PATH+1] = ""; -+ static time_t nextalert = 0; -+ -+ if (imapd_mailbox) { -+ index_closemailbox(imapd_mailbox); -+ mailbox_close(imapd_mailbox); -+ imapd_mailbox = 0; -+ } -+ -+ if (cmd[0] == 'B') { -+ /* BBoard namespace is empty */ -+ r = IMAP_MAILBOX_NONEXISTENT; -+ } -+ else { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ } -+ -+ if (!r) { -+ r = mlookup(tag, name, mailboxname, &mbtype, NULL, NULL, -+ &newserver, NULL, NULL); -+ } -+ if (r == IMAP_MAILBOX_MOVED) return; -+ -+ if (!r && (mbtype & MBTYPE_REMOTE)) { -+ if (supports_referrals) { -+ imapd_refer(tag, newserver, name); -+ return; -+ } -+ -+ backend_next = proxy_findserver(newserver, &protocol[PROTOCOL_IMAP], -+ proxy_userid, &backend_cached, -+ &backend_current, &backend_inbox, -+ imapd_in); -+ if (!backend_next) r = IMAP_SERVER_UNAVAILABLE; -+ -+ if (backend_current && backend_current != backend_next) { -+ char mytag[128]; -+ -+ /* remove backend_current from the protgroup */ -+ protgroup_delete(protin, backend_current->in); -+ -+ /* switching servers; flush old server output */ -+ proxy_gentag(mytag, sizeof(mytag)); -+ prot_printf(backend_current->out, "%s Unselect\r\n", mytag); -+ /* do not fatal() here, because we don't really care about this -+ * server anymore anyway */ -+ pipe_until_tag(backend_current, mytag, 1); -+ } -+ backend_current = backend_next; -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ return; -+ } -+ -+ prot_printf(backend_current->out, "%s %s {%d+}\r\n%s\r\n", tag, cmd, -+ strlen(name), name); -+ switch (pipe_including_tag(backend_current, tag, 0)) { -+ case PROXY_OK: -+ proc_register("imapd", imapd_clienthost, imapd_userid, mailboxname); -+ syslog(LOG_DEBUG, "open: user %s opened %s on %s", -+ imapd_userid, name, newserver); -+ -+ /* add backend_current to the protgroup */ -+ protgroup_insert(protin, backend_current->in); -+ break; -+ default: -+ syslog(LOG_DEBUG, "open: user %s failed to open %s", imapd_userid, -+ name); -+ /* not successfully selected */ -+ backend_current = NULL; -+ break; -+ } -+ -+ return; -+ } -+ -+ /* local mailbox */ -+ if (backend_current) { -+ char mytag[128]; -+ -+ /* remove backend_current from the protgroup */ -+ protgroup_delete(protin, backend_current->in); -+ -+ /* switching servers; flush old server output */ -+ proxy_gentag(mytag, sizeof(mytag)); -+ prot_printf(backend_current->out, "%s Unselect\r\n", mytag); -+ /* do not fatal() here, because we don't really care about this -+ * server anymore anyway */ -+ pipe_until_tag(backend_current, mytag, 1); -+ } -+ backend_current = NULL; -+ -+ if (!r) { -+ r = mailbox_open_header(mailboxname, imapd_authstate, &mailbox); -+ } -+ -+ if (!r) { -+ doclose = 1; -+ r = mailbox_open_index(&mailbox); -+ } -+ if (!r && !(mailbox.myrights & ACL_READ)) { -+ r = (imapd_userisadmin || (mailbox.myrights & ACL_LOOKUP)) ? -+ IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT; -+ } -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ if (doclose) mailbox_close(&mailbox); -+ return; -+ } -+ -+ mboxstruct = mailbox; -+ imapd_mailbox = &mboxstruct; -+ -+ index_newmailbox(imapd_mailbox, cmd[0] == 'E'); -+ -+ /* Examine command puts mailbox in read-only mode */ -+ if (cmd[0] == 'E') { -+ imapd_mailbox->myrights &= ~(ACL_SEEN|ACL_WRITE|ACL_DELETEMSG|ACL_EXPUNGE); -+ } -+ -+ if (imapd_mailbox->myrights & ACL_EXPUNGE) { -+ time_t now = time(NULL); -+ -+ /* Warn if mailbox is close to or over quota */ -+ r = quota_read(&imapd_mailbox->quota, NULL, 0); -+ if (!r && imapd_mailbox->quota.limit > 0 && -+ (strcmp(imapd_mailbox->quota.root, lastqr) || now > nextalert)) { -+ /* Warn if the following possibilities occur: -+ * - quotawarnkb not set + quotawarn hit -+ * - quotawarnkb set larger than mailbox + quotawarn hit -+ * - quotawarnkb set + hit + quotawarn hit -+ */ -+ int warnsize = config_getint(IMAPOPT_QUOTAWARNKB); -+ if (warnsize <= 0 || warnsize >= imapd_mailbox->quota.limit || -+ ((uquota_t) (imapd_mailbox->quota.limit - warnsize)) * QUOTA_UNITS < -+ imapd_mailbox->quota.used) { -+ usage = ((double) imapd_mailbox->quota.used * 100.0) / (double) -+ ((uquota_t) imapd_mailbox->quota.limit * QUOTA_UNITS); -+ if (usage >= 100.0) { -+ prot_printf(imapd_out, "* NO [ALERT] %s\r\n", -+ error_message(IMAP_NO_OVERQUOTA)); -+ } -+ else if (usage > config_getint(IMAPOPT_QUOTAWARN)) { -+ int usageint = (int) usage; -+ prot_printf(imapd_out, "* NO [ALERT] "); -+ prot_printf(imapd_out, error_message(IMAP_NO_CLOSEQUOTA), -+ usageint); -+ prot_printf(imapd_out, "\r\n"); -+ } -+ } -+ strlcpy(lastqr, imapd_mailbox->quota.root, sizeof(lastqr)); -+ nextalert = now + 600; /* ALERT every 10 min regardless */ -+ } -+ } -+ -+ prot_printf(imapd_out, "%s OK [READ-%s] %s\r\n", tag, -+ (imapd_mailbox->myrights & (ACL_INSERT|ACL_EXPUNGE|ACL_WRITE|ACL_DELETEMSG)) ? -+ "WRITE" : "ONLY", error_message(IMAP_OK_COMPLETED)); -+ -+ proc_register("imapd", imapd_clienthost, imapd_userid, mailboxname); -+ syslog(LOG_DEBUG, "open: user %s opened %s", imapd_userid, name); -+} -+ -+/* -+ * Perform a CLOSE command -+ */ -+void cmd_close(char *tag) -+{ -+ int r; -+ -+ if (backend_current) { -+ /* remote mailbox */ -+ prot_printf(backend_current->out, "%s Close\r\n", tag); -+ /* xxx do we want this to say OK if the connection is gone? -+ * saying NO is clearly wrong, hense the fatal request. */ -+ pipe_including_tag(backend_current, tag, 0); -+ -+ /* remove backend_current from the protgroup */ -+ protgroup_delete(protin, backend_current->in); -+ -+ backend_current = NULL; -+ return; -+ } -+ -+ /* local mailbox */ -+ if (!(imapd_mailbox->myrights & ACL_EXPUNGE)) r = 0; -+ else { -+ r = mailbox_expunge(imapd_mailbox, (int (*)())0, (char *)0, 0); -+ if (!r) sync_log_mailbox(imapd_mailbox->name); -+ } -+ -+ index_closemailbox(imapd_mailbox); -+ mailbox_close(imapd_mailbox); -+ imapd_mailbox = 0; -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } -+ else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ } -+} -+ -+/* -+ * Perform an UNSELECT command -- for some support of IMAP proxy. -+ * Just like close except no expunge. -+ */ -+void cmd_unselect(char *tag) -+{ -+ if (backend_current) { -+ /* remote mailbox */ -+ prot_printf(backend_current->out, "%s Unselect\r\n", tag); -+ /* xxx do we want this to say OK if the connection is gone? -+ * saying NO is clearly wrong, hense the fatal request. */ -+ pipe_including_tag(backend_current, tag, 0); -+ backend_current = NULL; -+ -+ /* remove backend_current from the protgroup */ -+ protgroup_delete(protin, backend_current->in); -+ return; -+ } -+ -+ /* local mailbox */ -+ index_closemailbox(imapd_mailbox); -+ mailbox_close(imapd_mailbox); -+ imapd_mailbox = 0; -+ -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+} -+ -+/* -+ * Parse the syntax for a partial fetch: -+ * "<" number "." nz-number ">" -+ */ -+#define PARSE_PARTIAL(start_octet, octet_count) \ -+ (start_octet) = (octet_count) = 0; \ -+ if (*p == '<' && isdigit((int) p[1])) { \ -+ (start_octet) = p[1] - '0'; \ -+ p += 2; \ -+ while (isdigit((int) *p)) { \ -+ (start_octet) = \ -+ (start_octet) * 10 + *p++ - '0'; \ -+ } \ -+ \ -+ if (*p == '.' && p[1] >= '1' && p[1] <= '9') { \ -+ (octet_count) = p[1] - '0'; \ -+ p[0] = '>'; p[1] = '\0'; /* clip off the octet count \ -+ (its not used in the reply) */ \ -+ p += 2; \ -+ while (isdigit((int) *p)) { \ -+ (octet_count) = \ -+ (octet_count) * 10 + *p++ - '0'; \ -+ } \ -+ } \ -+ else p--; \ -+ \ -+ if (*p != '>') { \ -+ prot_printf(imapd_out, \ -+ "%s BAD Invalid body partial\r\n", tag); \ -+ eatline(imapd_in, c); \ -+ goto freeargs; \ -+ } \ -+ p++; \ -+ } -+ -+/* -+ * Parse and perform a FETCH/UID FETCH command -+ * The command has been parsed up to and including -+ * the sequence -+ */ -+void cmd_fetch(char *tag, char *sequence, int usinguid) -+{ -+ const char *cmd = usinguid ? "UID Fetch" : "Fetch"; -+ static struct buf fetchatt, fieldname; -+ int c; -+ int inlist = 0; -+ int fetchitems = 0; -+ struct fetchargs fetchargs; -+ struct octetinfo oi; -+ struct strlist *newfields = 0; -+ char *p, *section; -+ int fetchedsomething, r; -+ clock_t start = clock(); -+ char mytime[100]; -+ -+ if (backend_current) { -+ /* remote mailbox */ -+ prot_printf(backend_current->out, "%s %s %s ", tag, cmd, sequence); -+ pipe_command(backend_current, 65536); -+ return; -+ } -+ -+ /* local mailbox */ -+ memset(&fetchargs, 0, sizeof(struct fetchargs)); -+ -+ c = getword(imapd_in, &fetchatt); -+ if (c == '(' && !fetchatt.s[0]) { -+ inlist = 1; -+ c = getword(imapd_in, &fetchatt); -+ } -+ for (;;) { -+ ucase(fetchatt.s); -+ switch (fetchatt.s[0]) { -+ case 'A': -+ if (!inlist && !strcmp(fetchatt.s, "ALL")) { -+ fetchitems |= FETCH_ALL; -+ } -+ else goto badatt; -+ break; -+ -+ case 'B': -+ if (!strncmp(fetchatt.s, "BINARY[", 7) || -+ !strncmp(fetchatt.s, "BINARY.PEEK[", 12) || -+ !strncmp(fetchatt.s, "BINARY.SIZE[", 12)) { -+ int binsize = 0; -+ -+ p = section = fetchatt.s + 7; -+ if (!strncmp(p, "PEEK[", 5)) { -+ p = section += 5; -+ } -+ else if (!strncmp(p, "SIZE[", 5)) { -+ p = section += 5; -+ binsize = 1; -+ } -+ else { -+ fetchitems |= FETCH_SETSEEN; -+ } -+ while (isdigit((int) *p) || *p == '.') { -+ if (*p == '.' && !isdigit((int) p[-1])) break; -+ /* Part number can not begin with '0' */ -+ if (*p == '0' && !isdigit((int) p[-1])) break; -+ p++; -+ } -+ -+ if (*p != ']') { -+ prot_printf(imapd_out, "%s BAD Invalid binary section\r\n", tag); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ p++; -+ -+ if (!binsize) PARSE_PARTIAL(oi.start_octet, oi.octet_count); -+ -+ if (*p) { -+ prot_printf(imapd_out, "%s BAD Junk after binary section\r\n", tag); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ if (binsize) -+ appendstrlist_withdata(&fetchargs.sizesections, section, &oi, sizeof(oi)); -+ else -+ appendstrlist_withdata(&fetchargs.binsections, section, &oi, sizeof(oi)); -+ } -+ else if (!strcmp(fetchatt.s, "BODY")) { -+ fetchitems |= FETCH_BODY; -+ } -+ else if (!strcmp(fetchatt.s, "BODYSTRUCTURE")) { -+ fetchitems |= FETCH_BODYSTRUCTURE; -+ } -+ else if (!strncmp(fetchatt.s, "BODY[", 5) || -+ !strncmp(fetchatt.s, "BODY.PEEK[", 10)) { -+ p = section = fetchatt.s + 5; -+ if (!strncmp(p, "PEEK[", 5)) { -+ p = section += 5; -+ } -+ else { -+ fetchitems |= FETCH_SETSEEN; -+ } -+ while (isdigit((int) *p) || *p == '.') { -+ if (*p == '.' && !isdigit((int) p[-1])) break; -+ /* Obsolete section 0 can only occur before close brace */ -+ if (*p == '0' && !isdigit((int) p[-1]) && p[1] != ']') break; -+ p++; -+ } -+ -+ if (*p == 'H' && !strncmp(p, "HEADER.FIELDS", 13) && -+ (p == section || p[-1] == '.') && -+ (p[13] == '\0' || !strcmp(p+13, ".NOT"))) { -+ -+ /* -+ * If not top-level or a HEADER.FIELDS.NOT, can't pull -+ * the headers out of the cache. -+ */ -+ if (p != section || p[13] != '\0') { -+ fetchargs.cache_atleast = BIT32_MAX; -+ } -+ -+ if (c != ' ') { -+ prot_printf(imapd_out, -+ "%s BAD Missing required argument to %s %s\r\n", -+ tag, cmd, fetchatt.s); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ c = prot_getc(imapd_in); -+ if (c != '(') { -+ prot_printf(imapd_out, "%s BAD Missing required open parenthesis in %s %s\r\n", -+ tag, cmd, fetchatt.s); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ do { -+ c = getastring(imapd_in, imapd_out, &fieldname); -+ for (p = fieldname.s; *p; p++) { -+ if (*p <= ' ' || *p & 0x80 || *p == ':') break; -+ } -+ if (*p || !*fieldname.s) { -+ prot_printf(imapd_out, "%s BAD Invalid field-name in %s %s\r\n", -+ tag, cmd, fetchatt.s); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ appendstrlist(&newfields, fieldname.s); -+ if (fetchargs.cache_atleast < BIT32_MAX) { -+ bit32 this_ver = -+ mailbox_cached_header(fieldname.s); -+ if(this_ver > fetchargs.cache_atleast) -+ fetchargs.cache_atleast = this_ver; -+ } -+ } while (c == ' '); -+ if (c != ')') { -+ prot_printf(imapd_out, "%s BAD Missing required close parenthesis in %s %s\r\n", -+ tag, cmd, fetchatt.s); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ -+ /* Grab/parse the ] part */ -+ c = getword(imapd_in, &fieldname); -+ p = fieldname.s; -+ if (*p++ != ']') { -+ prot_printf(imapd_out, "%s BAD Missing required close bracket after %s %s\r\n", -+ tag, cmd, fetchatt.s); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ -+ PARSE_PARTIAL(oi.start_octet, oi.octet_count); -+ -+ if (*p) { -+ prot_printf(imapd_out, "%s BAD Junk after body section\r\n", tag); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ appendfieldlist(&fetchargs.fsections, -+ section, newfields, fieldname.s, -+ &oi, sizeof(oi)); -+ newfields = 0; -+ break; -+ } -+ -+ switch (*p) { -+ case 'H': -+ if (p != section && p[-1] != '.') break; -+ if (!strncmp(p, "HEADER]", 7)) p += 6; -+ break; -+ -+ case 'M': -+ if (!strncmp(p-1, ".MIME]", 6)) p += 4; -+ break; -+ -+ case 'T': -+ if (p != section && p[-1] != '.') break; -+ if (!strncmp(p, "TEXT]", 5)) p += 4; -+ break; -+ } -+ -+ if (*p != ']') { -+ prot_printf(imapd_out, "%s BAD Invalid body section\r\n", tag); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ p++; -+ -+ PARSE_PARTIAL(oi.start_octet, oi.octet_count); -+ -+ if (*p) { -+ prot_printf(imapd_out, "%s BAD Junk after body section\r\n", tag); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ appendstrlist_withdata(&fetchargs.bodysections, section, -+ &oi, sizeof(oi)); -+ } -+ else goto badatt; -+ break; -+ -+ case 'E': -+ if (!strcmp(fetchatt.s, "ENVELOPE")) { -+ fetchitems |= FETCH_ENVELOPE; -+ } -+ else goto badatt; -+ break; -+ -+ case 'F': -+ if (!inlist && !strcmp(fetchatt.s, "FAST")) { -+ fetchitems |= FETCH_FAST; -+ } -+ else if (!inlist && !strcmp(fetchatt.s, "FULL")) { -+ fetchitems |= FETCH_FULL; -+ } -+ else if (!strcmp(fetchatt.s, "FLAGS")) { -+ fetchitems |= FETCH_FLAGS; -+ } -+ else goto badatt; -+ break; -+ -+ case 'I': -+ if (!strcmp(fetchatt.s, "INTERNALDATE")) { -+ fetchitems |= FETCH_INTERNALDATE; -+ } -+ else goto badatt; -+ break; -+ -+ case 'R': -+ if (!strcmp(fetchatt.s, "RFC822")) { -+ fetchitems |= FETCH_RFC822|FETCH_SETSEEN; -+ } -+ else if (!strcmp(fetchatt.s, "RFC822.HEADER")) { -+ fetchitems |= FETCH_HEADER; -+ } -+ else if (!strcmp(fetchatt.s, "RFC822.PEEK")) { -+ fetchitems |= FETCH_RFC822; -+ } -+ else if (!strcmp(fetchatt.s, "RFC822.SIZE")) { -+ fetchitems |= FETCH_SIZE; -+ } -+ else if (!strcmp(fetchatt.s, "RFC822.TEXT")) { -+ fetchitems |= FETCH_TEXT|FETCH_SETSEEN; -+ } -+ else if (!strcmp(fetchatt.s, "RFC822.TEXT.PEEK")) { -+ fetchitems |= FETCH_TEXT; -+ } -+ else if (!strcmp(fetchatt.s, "RFC822.HEADER.LINES") || -+ !strcmp(fetchatt.s, "RFC822.HEADER.LINES.NOT")) { -+ if (c != ' ') { -+ prot_printf(imapd_out, "%s BAD Missing required argument to %s %s\r\n", -+ tag, cmd, fetchatt.s); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ c = prot_getc(imapd_in); -+ if (c != '(') { -+ prot_printf(imapd_out, "%s BAD Missing required open parenthesis in %s %s\r\n", -+ tag, cmd, fetchatt.s); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ do { -+ c = getastring(imapd_in, imapd_out, &fieldname); -+ for (p = fieldname.s; *p; p++) { -+ if (*p <= ' ' || *p & 0x80 || *p == ':') break; -+ } -+ if (*p || !*fieldname.s) { -+ prot_printf(imapd_out, "%s BAD Invalid field-name in %s %s\r\n", -+ tag, cmd, fetchatt.s); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ lcase(fieldname.s);; -+ /* 19 is magic number -- length of -+ * "RFC822.HEADERS.NOT" */ -+ appendstrlist(strlen(fetchatt.s) == 19 ? -+ &fetchargs.headers : &fetchargs.headers_not, -+ fieldname.s); -+ if (strlen(fetchatt.s) != 19) { -+ fetchargs.cache_atleast = BIT32_MAX; -+ } -+ if (fetchargs.cache_atleast < BIT32_MAX) { -+ bit32 this_ver = -+ mailbox_cached_header(fieldname.s); -+ if(this_ver > fetchargs.cache_atleast) -+ fetchargs.cache_atleast = this_ver; -+ } -+ } while (c == ' '); -+ if (c != ')') { -+ prot_printf(imapd_out, "%s BAD Missing required close parenthesis in %s %s\r\n", -+ tag, cmd, fetchatt.s); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ c = prot_getc(imapd_in); -+ } -+ else goto badatt; -+ break; -+ -+ case 'U': -+ if (!strcmp(fetchatt.s, "UID")) { -+ fetchitems |= FETCH_UID; -+ } -+ else goto badatt; -+ break; -+ -+ default: -+ badatt: -+ prot_printf(imapd_out, "%s BAD Invalid %s attribute %s\r\n", tag, cmd, fetchatt.s); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ -+ if (inlist && c == ' ') c = getword(imapd_in, &fetchatt); -+ else break; -+ } -+ -+ if (inlist && c == ')') { -+ inlist = 0; -+ c = prot_getc(imapd_in); -+ } -+ if (inlist) { -+ prot_printf(imapd_out, "%s BAD Missing close parenthesis in %s\r\n", tag, cmd); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ prot_printf(imapd_out, "%s BAD Unexpected extra arguments to %s\r\n", tag, cmd); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ -+ if (!fetchitems && !fetchargs.bodysections && !fetchargs.fsections && -+ !fetchargs.binsections && !fetchargs.sizesections && -+ !fetchargs.headers && !fetchargs.headers_not) { -+ prot_printf(imapd_out, "%s BAD Missing required argument to %s\r\n", tag, cmd); -+ goto freeargs; -+ } -+ -+ if (usinguid || config_getswitch(IMAPOPT_FLUSHSEENSTATE)) { -+ if (usinguid) fetchitems |= FETCH_UID; -+ index_check(imapd_mailbox, usinguid, /* update \Seen state from disk? */ -+ config_getswitch(IMAPOPT_FLUSHSEENSTATE) << 1 /* quiet */); -+ } -+ -+ fetchargs.fetchitems = fetchitems; -+ r = index_fetch(imapd_mailbox, sequence, usinguid, &fetchargs, -+ &fetchedsomething); -+ -+ snprintf(mytime, sizeof(mytime), "%2.3f", -+ (clock() - start) / (double) CLOCKS_PER_SEC); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s (%s sec)\r\n", tag, -+ error_message(r), mytime); -+ } else if (fetchedsomething || usinguid) { -+ prot_printf(imapd_out, "%s OK %s (%s sec)\r\n", tag, -+ error_message(IMAP_OK_COMPLETED), mytime); -+ if (config_getswitch(IMAPOPT_FLUSHSEENSTATE) && -+ (fetchargs.fetchitems & FETCH_SETSEEN)) { -+ /* flush \Seen state to disk */ -+ index_check(imapd_mailbox, usinguid, 2 /* quiet */); -+ } -+ } else { -+ /* normal FETCH, nothing came back */ -+ prot_printf(imapd_out, "%s NO %s (%s sec)\r\n", tag, -+ error_message(IMAP_NO_NOSUCHMSG), mytime); -+ } -+ -+ freeargs: -+ freestrlist(newfields); -+ freestrlist(fetchargs.bodysections); -+ freefieldlist(fetchargs.fsections); -+ freestrlist(fetchargs.headers); -+ freestrlist(fetchargs.headers_not); -+} -+ -+#undef PARSE_PARTIAL /* cleanup */ -+ -+/* -+ * Perform a PARTIAL command -+ */ -+void cmd_partial(const char *tag, const char *msgno, char *data, -+ const char *start, const char *count) -+{ -+ const char *pc; -+ char *p; -+ struct fetchargs fetchargs; -+ char *section; -+ int prev; -+ int fetchedsomething; -+ -+ if (backend_current) { -+ /* remote mailbox */ -+ prot_printf(backend_current->out, "%s Partial %s %s %s %s\r\n", -+ tag, msgno, data, start, count); -+ return; -+ } -+ -+ /* local mailbox */ -+ memset(&fetchargs, 0, sizeof(struct fetchargs)); -+ -+ for (pc = msgno; *pc; pc++) { -+ if (!isdigit((int) *pc)) break; -+ } -+ if (*pc || !*msgno) { -+ prot_printf(imapd_out, "%s BAD Invalid message number\r\n", tag); -+ return; -+ } -+ -+ lcase(data); -+ if (!strcmp(data, "rfc822")) { -+ fetchargs.fetchitems = FETCH_RFC822|FETCH_SETSEEN; -+ } -+ else if (!strcmp(data, "rfc822.header")) { -+ fetchargs.fetchitems = FETCH_HEADER; -+ } -+ else if (!strcmp(data, "rfc822.peek")) { -+ fetchargs.fetchitems = FETCH_RFC822; -+ } -+ else if (!strcmp(data, "rfc822.text")) { -+ fetchargs.fetchitems = FETCH_TEXT|FETCH_SETSEEN; -+ } -+ else if (!strcmp(data, "rfc822.text.peek")) { -+ fetchargs.fetchitems = FETCH_TEXT; -+ } -+ else if (!strncmp(data, "body[", 5) || -+ !strncmp(data, "body.peek[", 10)) { -+ p = section = data + 5; -+ if (!strncmp(p, "peek[", 5)) { -+ p = section += 5; -+ } -+ else { -+ fetchargs.fetchitems = FETCH_SETSEEN; -+ } -+ while (isdigit((int) *p) || *p == '.') { -+ if (*p == '.' && (p == section || !isdigit((int) p[1]))) break; -+ p++; -+ } -+ if (p == section || *p != ']' || p[1]) { -+ prot_printf(imapd_out, "%s BAD Invalid body section\r\n", tag); -+ freestrlist(fetchargs.bodysections); -+ return; -+ } -+ *(p+1) = '\0'; /* Keep the closing bracket in place */ -+ appendstrlist(&fetchargs.bodysections, section); -+ } -+ else { -+ prot_printf(imapd_out, "%s BAD Invalid Partial item\r\n", tag); -+ freestrlist(fetchargs.bodysections); -+ return; -+ } -+ -+ for (pc = start; *pc; pc++) { -+ if (!isdigit((int) *pc)) break; -+ prev = fetchargs.start_octet; -+ fetchargs.start_octet = fetchargs.start_octet*10 + *pc - '0'; -+ if(fetchargs.start_octet < prev) { -+ fetchargs.start_octet = 0; -+ break; -+ } -+ } -+ if (*pc || !fetchargs.start_octet) { -+ prot_printf(imapd_out, "%s BAD Invalid starting octet\r\n", tag); -+ freestrlist(fetchargs.bodysections); -+ return; -+ } -+ fetchargs.start_octet--; /* Normalize to be 0-based */ -+ -+ prev = fetchargs.octet_count; -+ for (pc = count; *pc; pc++) { -+ if (!isdigit((int) *pc)) break; -+ prev = fetchargs.octet_count; -+ fetchargs.octet_count = fetchargs.octet_count*10 + *pc - '0'; -+ if(fetchargs.octet_count < prev) { -+ prev = -1; -+ break; -+ } -+ } -+ if (*pc || !*count || prev == -1) { -+ prot_printf(imapd_out, "%s BAD Invalid octet count\r\n", tag); -+ freestrlist(fetchargs.bodysections); -+ return; -+ } -+ -+ fetchargs.fetchitems |= FETCH_IS_PARTIAL; -+ -+ index_fetch(imapd_mailbox, msgno, 0, &fetchargs, &fetchedsomething); -+ -+ index_check(imapd_mailbox, 0, /* flush \Seen state to disk? */ -+ config_getswitch(IMAPOPT_FLUSHSEENSTATE) && -+ fetchedsomething && (fetchargs.fetchitems & FETCH_SETSEEN)); -+ -+ if (fetchedsomething) { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ } else { -+ prot_printf(imapd_out, -+ "%s BAD Invalid sequence in PARTIAL command\r\n", -+ tag); -+ } -+ -+ freestrlist(fetchargs.bodysections); -+} -+ -+/* -+ * Parse and perform a STORE/UID STORE command -+ * The command has been parsed up to and including -+ * the FLAGS/+FLAGS/-FLAGS -+ */ -+void cmd_store(char *tag, char *sequence, char *operation, int usinguid) -+{ -+ const char *cmd = usinguid ? "UID Store" : "Store"; -+ struct storeargs storeargs; -+ static struct buf flagname; -+ int len, c; -+ char **flag = 0; -+ int nflags = 0, flagalloc = 0; -+ int flagsparsed = 0, inlist = 0; -+ int r; -+ -+ if (backend_current) { -+ /* remote mailbox */ -+ prot_printf(backend_current->out, "%s %s %s %s ", -+ tag, cmd, sequence, operation); -+ pipe_command(backend_current, 65536); -+ return; -+ } -+ -+ /* local mailbox */ -+ memset(&storeargs, 0, sizeof storeargs); -+ -+ lcase(operation); -+ -+ len = strlen(operation); -+ if (len > 7 && !strcmp(operation+len-7, ".silent")) { -+ storeargs.silent = 1; -+ operation[len-7] = '\0'; -+ } -+ -+ if (!strcmp(operation, "+flags")) { -+ storeargs.operation = STORE_ADD; -+ } -+ else if (!strcmp(operation, "-flags")) { -+ storeargs.operation = STORE_REMOVE; -+ } -+ else if (!strcmp(operation, "flags")) { -+ storeargs.operation = STORE_REPLACE; -+ } -+ else { -+ prot_printf(imapd_out, "%s BAD Invalid %s attribute\r\n", tag, cmd); -+ eatline(imapd_in, ' '); -+ return; -+ } -+ -+ for (;;) { -+ c = getword(imapd_in, &flagname); -+ if (c == '(' && !flagname.s[0] && !flagsparsed && !inlist) { -+ inlist = 1; -+ continue; -+ } -+ -+ if (!flagname.s[0]) break; -+ -+ if (flagname.s[0] == '\\') { -+ lcase(flagname.s); -+ if (!strcmp(flagname.s, "\\seen")) { -+ storeargs.seen = 1; -+ } -+ else if (!strcmp(flagname.s, "\\answered")) { -+ storeargs.system_flags |= FLAG_ANSWERED; -+ } -+ else if (!strcmp(flagname.s, "\\flagged")) { -+ storeargs.system_flags |= FLAG_FLAGGED; -+ } -+ else if (!strcmp(flagname.s, "\\deleted")) { -+ storeargs.system_flags |= FLAG_DELETED; -+ } -+ else if (!strcmp(flagname.s, "\\draft")) { -+ storeargs.system_flags |= FLAG_DRAFT; -+ } -+ else { -+ prot_printf(imapd_out, "%s BAD Invalid system flag in %s command\r\n", -+ tag, cmd); -+ eatline(imapd_in, c); -+ goto freeflags; -+ } -+ } -+ else if (!imparse_isatom(flagname.s)) { -+ prot_printf(imapd_out, "%s BAD Invalid flag name %s in %s command\r\n", -+ tag, flagname.s, cmd); -+ eatline(imapd_in, c); -+ goto freeflags; -+ } -+ else { -+ if (nflags == flagalloc) { -+ flagalloc += FLAGGROW; -+ flag = (char **)xrealloc((char *)flag, -+ flagalloc*sizeof(char *)); -+ } -+ flag[nflags] = xstrdup(flagname.s); -+ nflags++; -+ } -+ -+ flagsparsed++; -+ if (c != ' ') break; -+ } -+ -+ if (!inlist && !flagsparsed) { -+ prot_printf(imapd_out, "%s BAD Missing required argument to %s\r\n", tag, cmd); -+ eatline(imapd_in, c); -+ return; -+ } -+ if (inlist && c == ')') { -+ inlist = 0; -+ c = prot_getc(imapd_in); -+ } -+ if (inlist) { -+ prot_printf(imapd_out, "%s BAD Missing close parenthesis in %s\r\n", tag, cmd); -+ eatline(imapd_in, c); -+ goto freeflags; -+ } -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ prot_printf(imapd_out, "%s BAD Unexpected extra arguments to %s\r\n", tag, cmd); -+ eatline(imapd_in, c); -+ goto freeflags; -+ } -+ -+ r = index_store(imapd_mailbox, sequence, usinguid, &storeargs, -+ flag, nflags); -+ -+ if (config_getswitch(IMAPOPT_FLUSHSEENSTATE) && -+ (storeargs.seen || storeargs.operation == STORE_REPLACE)) { -+ /* flush \Seen state to disk */ -+ index_check(imapd_mailbox, usinguid, 1); -+ } -+ else if (usinguid) { -+ index_check(imapd_mailbox, 1, 0); -+ } -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } -+ else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ -+ /* We only need to log a MAILBOX event if we've changed -+ a flag other than \Seen */ -+ if (storeargs.system_flags || nflags || -+ storeargs.operation == STORE_REPLACE) { -+ sync_log_mailbox(imapd_mailbox->name); -+ } -+ } -+ -+ freeflags: -+ while (nflags--) { -+ free(flag[nflags]); -+ } -+ if (flag) free((char *)flag); -+} -+ -+void cmd_search(char *tag, int usinguid) -+{ -+ int c; -+ int charset = 0; -+ struct searchargs *searchargs; -+ clock_t start = clock(); -+ char mytime[100]; -+ int n; -+ -+ if (backend_current) { -+ /* remote mailbox */ -+ const char *cmd = usinguid ? "UID Search" : "Search"; -+ -+ prot_printf(backend_current->out, "%s %s ", tag, cmd); -+ pipe_command(backend_current, 65536); -+ return; -+ } -+ -+ /* local mailbox */ -+ searchargs = (struct searchargs *)xzmalloc(sizeof(struct searchargs)); -+ -+ c = getsearchprogram(tag, searchargs, &charset, 1); -+ if (c == EOF) { -+ eatline(imapd_in, ' '); -+ freesearchargs(searchargs); -+ return; -+ } -+ -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ prot_printf(imapd_out, "%s BAD Unexpected extra arguments to Search\r\n", tag); -+ eatline(imapd_in, c); -+ freesearchargs(searchargs); -+ return; -+ } -+ -+ index_check(imapd_mailbox, 1, 0); -+ -+ if (charset == -1) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, -+ error_message(IMAP_UNRECOGNIZED_CHARSET)); -+ } -+ else { -+ n = index_search(imapd_mailbox, searchargs, usinguid); -+ snprintf(mytime, sizeof(mytime), "%2.3f", -+ (clock() - start) / (double) CLOCKS_PER_SEC); -+ prot_printf(imapd_out, "%s OK %s (%d msgs in %s secs)\r\n", tag, -+ error_message(IMAP_OK_COMPLETED), n, mytime); -+ } -+ -+ freesearchargs(searchargs); -+} -+ -+/* -+ * Perform a SORT/UID SORT command -+ */ -+void cmd_sort(char *tag, int usinguid) -+{ -+ int c; -+ struct sortcrit *sortcrit = NULL; -+ static struct buf arg; -+ int charset = 0; -+ struct searchargs *searchargs; -+ clock_t start = clock(); -+ char mytime[100]; -+ int n; -+ -+ if (backend_current) { -+ /* remote mailbox */ -+ char *cmd = usinguid ? "UID Sort" : "Sort"; -+ -+ prot_printf(backend_current->out, "%s %s ", tag, cmd); -+ pipe_command(backend_current, 65536); -+ return; -+ } -+ -+ /* local mailbox */ -+ c = getsortcriteria(tag, &sortcrit); -+ if (c == EOF) { -+ eatline(imapd_in, ' '); -+ freesortcrit(sortcrit); -+ return; -+ } -+ -+ /* get charset */ -+ if (c != ' ') { -+ prot_printf(imapd_out, "%s BAD Missing charset in Sort\r\n", -+ tag); -+ eatline(imapd_in, c); -+ freesortcrit(sortcrit); -+ return; -+ } -+ -+ c = getword(imapd_in, &arg); -+ if (c != ' ') { -+ prot_printf(imapd_out, "%s BAD Missing search criteria in Sort\r\n", -+ tag); -+ eatline(imapd_in, c); -+ freesortcrit(sortcrit); -+ return; -+ } -+ lcase(arg.s); -+ charset = charset_lookupname(arg.s); -+ -+ if (charset == -1) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, -+ error_message(IMAP_UNRECOGNIZED_CHARSET)); -+ eatline(imapd_in, c); -+ freesortcrit(sortcrit); -+ return; -+ } -+ -+ searchargs = (struct searchargs *)xzmalloc(sizeof(struct searchargs)); -+ -+ c = getsearchprogram(tag, searchargs, &charset, 0); -+ if (c == EOF) { -+ eatline(imapd_in, ' '); -+ freesearchargs(searchargs); -+ freesortcrit(sortcrit); -+ return; -+ } -+ -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ prot_printf(imapd_out, -+ "%s BAD Unexpected extra arguments to Sort\r\n", tag); -+ eatline(imapd_in, c); -+ freesearchargs(searchargs); -+ freesortcrit(sortcrit); -+ return; -+ } -+ -+ index_check(imapd_mailbox, 1, 0); -+ -+ n = index_sort(imapd_mailbox, sortcrit, searchargs, usinguid); -+ snprintf(mytime, sizeof(mytime), "%2.3f", -+ (clock() - start) / (double) CLOCKS_PER_SEC); -+ prot_printf(imapd_out, "%s OK %s (%d msgs in %s secs)\r\n", tag, -+ error_message(IMAP_OK_COMPLETED), n, mytime); -+ -+ freesortcrit(sortcrit); -+ freesearchargs(searchargs); -+ return; -+} -+ -+/* -+ * Perform a THREAD/UID THREAD command -+ */ -+void cmd_thread(char *tag, int usinguid) -+{ -+ static struct buf arg; -+ int c; -+ int charset = 0; -+ int alg; -+ struct searchargs *searchargs; -+ clock_t start = clock(); -+ char mytime[100]; -+ int n; -+ -+ if (backend_current) { -+ /* remote mailbox */ -+ const char *cmd = usinguid ? "UID Thread" : "Thread"; -+ -+ prot_printf(backend_current->out, "%s %s ", tag, cmd); -+ pipe_command(backend_current, 65536); -+ return; -+ } -+ -+ /* local mailbox */ -+ /* get algorithm */ -+ c = getword(imapd_in, &arg); -+ if (c != ' ') { -+ prot_printf(imapd_out, "%s BAD Missing algorithm in Thread\r\n", tag); -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ if ((alg = find_thread_algorithm(arg.s)) == -1) { -+ prot_printf(imapd_out, "%s BAD Invalid Thread algorithm %s\r\n", -+ tag, arg.s); -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ /* get charset */ -+ c = getword(imapd_in, &arg); -+ if (c != ' ') { -+ prot_printf(imapd_out, "%s BAD Missing charset in Thread\r\n", -+ tag); -+ eatline(imapd_in, c); -+ return; -+ } -+ lcase(arg.s); -+ charset = charset_lookupname(arg.s); -+ -+ if (charset == -1) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, -+ error_message(IMAP_UNRECOGNIZED_CHARSET)); -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ searchargs = (struct searchargs *)xzmalloc(sizeof(struct searchargs)); -+ -+ c = getsearchprogram(tag, searchargs, &charset, 0); -+ if (c == EOF) { -+ eatline(imapd_in, ' '); -+ freesearchargs(searchargs); -+ return; -+ } -+ -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ prot_printf(imapd_out, -+ "%s BAD Unexpected extra arguments to Thread\r\n", tag); -+ eatline(imapd_in, c); -+ freesearchargs(searchargs); -+ return; -+ } -+ -+ index_check(imapd_mailbox, 1, 0); -+ -+ n = index_thread(imapd_mailbox, alg, searchargs, usinguid); -+ snprintf(mytime, sizeof(mytime), "%2.3f", -+ (clock() - start) / (double) CLOCKS_PER_SEC); -+ prot_printf(imapd_out, "%s OK %s (%d msgs in %s secs)\r\n", tag, -+ error_message(IMAP_OK_COMPLETED), n, mytime); -+ -+ freesearchargs(searchargs); -+ return; -+} -+ -+/* -+ * Perform a COPY/UID COPY command -+ */ -+void cmd_copy(char *tag, char *sequence, char *name, int usinguid) -+{ -+ int r, myrights; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ int mbtype; -+ char *server, *acl; -+ char *copyuid; -+ -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ -+ if (!r) { -+ r = mlookup(NULL, NULL, mailboxname, &mbtype, NULL, NULL, -+ &server, &acl, NULL); -+ } -+ -+ if (!r) myrights = cyrus_acl_myrights(imapd_authstate, acl); -+ -+ if (!r && backend_current) { -+ /* remote mailbox -> local or remote mailbox */ -+ -+ /* xxx start of separate proxy-only code -+ (remove when we move to a unified environment) */ -+ struct backend *s = NULL; -+ -+ s = proxy_findserver(server, &protocol[PROTOCOL_IMAP], -+ proxy_userid, &backend_cached, -+ &backend_current, &backend_inbox, imapd_in); -+ if (!s) { -+ r = IMAP_SERVER_UNAVAILABLE; -+ goto done; -+ } -+ -+ if (s != backend_current) { -+ /* this is the hard case; we have to fetch the messages and append -+ them to the other mailbox */ -+ -+ proxy_copy(tag, sequence, name, myrights, usinguid, s); -+ return; -+ } -+ /* xxx end of separate proxy-only code */ -+ -+ /* simply send the COPY to the backend */ -+ prot_printf(backend_current->out, "%s %s %s {%d+}\r\n%s\r\n", -+ tag, usinguid ? "UID Copy" : "Copy", -+ sequence, strlen(name), name); -+ -+ return; -+ } -+ else if (!r && (mbtype & MBTYPE_REMOTE)) { -+ /* local mailbox -> remote mailbox -+ * -+ * fetch the messages and APPEND them to the backend -+ * -+ * xxx completely untested -+ */ -+ struct backend *s = NULL; -+ int res; -+ -+ index_check(imapd_mailbox, usinguid, 0); -+ -+ s = proxy_findserver(server, &protocol[PROTOCOL_IMAP], -+ proxy_userid, &backend_cached, -+ &backend_current, &backend_inbox, imapd_in); -+ if (!s) r = IMAP_SERVER_UNAVAILABLE; -+ else if (!CAPA(s, CAPA_MULTIAPPEND)) { -+ /* we need MULTIAPPEND for atomicity */ -+ r = IMAP_REMOTE_NO_MULTIAPPEND; -+ } -+ -+ if (r) goto done; -+ -+ /* start the append */ -+ prot_printf(s->out, "%s Append {%d+}\r\n%s", tag, strlen(name), name); -+ -+ /* append the messages */ -+ r = index_copy_remote(imapd_mailbox, sequence, usinguid, s->out); -+ -+ if (!r) { -+ /* ok, finish the append; we need the UIDVALIDITY and UIDs -+ to return as part of our COPYUID response code */ -+ char *appenduid, *b; -+ -+ prot_printf(s->out, "\r\n"); -+ -+ res = pipe_until_tag(s, tag, 0); -+ -+ if (res == PROXY_OK) { -+ if (myrights & ACL_READ) { -+ appenduid = strchr(s->last_result.s, '['); -+ /* skip over APPENDUID */ -+ appenduid += strlen("[appenduid "); -+ b = strchr(appenduid, ']'); -+ *b = '\0'; -+ prot_printf(imapd_out, "%s OK [COPYUID %s] %s\r\n", tag, -+ appenduid, error_message(IMAP_OK_COMPLETED)); -+ } else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ } -+ } else { -+ prot_printf(imapd_out, "%s %s", tag, s->last_result.s); -+ } -+ } else { -+ /* abort the append */ -+ prot_printf(s->out, " {0}\r\n"); -+ pipe_until_tag(s, tag, 0); -+ -+ /* report failure */ -+ prot_printf(imapd_out, "%s NO inter-server COPY failed\r\n", tag); -+ } -+ -+ return; -+ } -+ -+ /* local mailbox -> local mailbox */ -+ if (!r) { -+ r = index_copy(imapd_mailbox, sequence, usinguid, mailboxname, -+ ©uid, !config_getswitch(IMAPOPT_SINGLEINSTANCESTORE)); -+ } -+ -+ index_check(imapd_mailbox, usinguid, 0); -+ -+ done: -+ if (r && !(usinguid && r == IMAP_NO_NOSUCHMSG)) { -+ prot_printf(imapd_out, "%s NO %s%s\r\n", tag, -+ (r == IMAP_MAILBOX_NONEXISTENT && -+ mboxlist_createmailboxcheck(mailboxname, 0, 0, -+ imapd_userisadmin, -+ imapd_userid, imapd_authstate, -+ (char **)0, (char **)0) == 0) -+ ? "[TRYCREATE] " : "", error_message(r)); -+ } -+ else if (copyuid) { -+ prot_printf(imapd_out, "%s OK [COPYUID %s] %s\r\n", tag, -+ copyuid, error_message(IMAP_OK_COMPLETED)); -+ free(copyuid); -+ } -+ else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ } -+} -+ -+/* -+ * Perform an EXPUNGE command -+ * sequence == NULL if this isn't a UID EXPUNGE -+ */ -+void cmd_expunge(char *tag, char *sequence) -+{ -+ int r; -+ -+ if (backend_current) { -+ /* remote mailbox */ -+ if (sequence) { -+ prot_printf(backend_current->out, "%s UID Expunge %s\r\n", tag, -+ sequence); -+ } else { -+ prot_printf(backend_current->out, "%s Expunge\r\n", tag); -+ } -+ return; -+ } -+ -+ /* local mailbox */ -+ if (!(imapd_mailbox->myrights & ACL_EXPUNGE)) r = IMAP_PERMISSION_DENIED; -+ else if (sequence) { -+ r = mailbox_expunge(imapd_mailbox, index_expungeuidlist, sequence, 0); -+ } -+ else { -+ r = mailbox_expunge(imapd_mailbox, (mailbox_decideproc_t *)0, -+ (void *)0, 0); -+ } -+ -+ index_check(imapd_mailbox, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } -+ else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ sync_log_mailbox(imapd_mailbox->name); -+ } -+} -+ -+/* -+ * Perform a CREATE command -+ */ -+void cmd_create(char *tag, char *name, char *partition, int localonly) -+{ -+ int r = 0; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ int autocreatequota; -+ int sync_lockfd = (-1); -+ -+ if (partition && !imapd_userisadmin) { -+ r = IMAP_PERMISSION_DENIED; -+ } -+ -+ if (name[0] && name[strlen(name)-1] == imapd_namespace.hier_sep) { -+ /* We don't care about trailing hierarchy delimiters. */ -+ name[strlen(name)-1] = '\0'; -+ } -+ -+ if (!r) { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ } -+ -+ if (!r && !localonly && config_mupdate_server) { -+ int guessedpart = 0; -+ -+ /* determine if we're creating locally or remotely */ -+ if (!partition) { -+ guessedpart = 1; -+ r = mboxlist_createmailboxcheck(mailboxname, 0, 0, -+ imapd_userisadmin, -+ imapd_userid, imapd_authstate, -+ NULL, &partition); -+ } -+ -+ if (!r && !config_partitiondir(partition)) { -+ /* invalid partition, assume its a server (remote mailbox) */ -+ char *server; -+ struct backend *s = NULL; -+ int res; -+ -+ /* check for a remote partition */ -+ server = partition; -+ partition = strchr(server, '!'); -+ if (partition) *partition++ = '\0'; -+ if (guessedpart) partition = NULL; -+ -+ s = proxy_findserver(server, &protocol[PROTOCOL_IMAP], -+ proxy_userid, &backend_cached, -+ &backend_current, &backend_inbox, imapd_in); -+ if (!s) r = IMAP_SERVER_UNAVAILABLE; -+ -+ if (!r) { -+ if (!CAPA(s, CAPA_MUPDATE)) { -+ /* reserve mailbox on MUPDATE */ -+ } -+ } -+ -+ if (!r) { -+ /* ok, send the create to that server */ -+ if (partition) -+ prot_printf(s->out, -+ "%s CREATE {%d+}\r\n%s {%d+}\r\n%s\r\n", -+ tag, strlen(name), name, -+ strlen(partition), partition); -+ else -+ prot_printf(s->out, "%s CREATE {%d+}\r\n%s\r\n", -+ tag, strlen(name), name); -+ res = pipe_until_tag(s, tag, 0); -+ -+ if (!CAPA(s, CAPA_MUPDATE)) { -+ /* do MUPDATE create operations */ -+ } -+ /* make sure we've seen the update */ -+ if (ultraparanoid && res == PROXY_OK) kick_mupdate(); -+ } -+ -+ imapd_check(s, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } else { -+ /* we're allowed to reference last_result since the noop, if -+ sent, went to a different server */ -+ prot_printf(imapd_out, "%s %s", tag, s->last_result.s); -+ } -+ -+ return; -+ } -+ -+ /* local mailbox -- fall through */ -+ if (guessedpart) partition = NULL; -+ } -+ -+ /* local mailbox */ -+ if (!r) { -+ /* xxx we do forced user creates on LOCALCREATE to facilitate -+ * mailbox moves */ -+ r = mboxlist_createmailbox(mailboxname, 0, partition, -+ imapd_userisadmin, -+ imapd_userid, imapd_authstate, -+ localonly, localonly, 0); -+ -+ if (r == IMAP_PERMISSION_DENIED && !strcasecmp(name, "INBOX") && -+ (autocreatequota = config_getint(IMAPOPT_AUTOCREATEQUOTA))) { -+ -+ /* Auto create */ -+ r = mboxlist_createmailbox(mailboxname, 0, -+ partition, 1, imapd_userid, -+ imapd_authstate, 0, 0, 0); -+ -+ if (!r && autocreatequota > 0) { -+ (void) mboxlist_setquota(mailboxname, autocreatequota, 0); -+ } -+ } -+ } -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } -+ else { -+ char *userid = NULL; -+ -+ if (config_mupdate_server && -+ (config_mupdate_config != IMAP_ENUM_MUPDATE_CONFIG_STANDARD)) { -+ kick_mupdate(); -+ } -+ -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ -+ if ((userid = mboxname_isusermailbox(mailboxname, 1))) -+ sync_log_user(userid); -+ else -+ sync_log_mailbox(mailboxname); -+ } -+} -+ -+/* Callback for use by cmd_delete */ -+static int delmbox(char *name, -+ int matchlen __attribute__((unused)), -+ int maycreate __attribute__((unused)), -+ void *rock __attribute__((unused))) -+{ -+ int r; -+ -+ r = mboxlist_deletemailbox(name, imapd_userisadmin, -+ imapd_userid, imapd_authstate, -+ 0, 0, 0); -+ -+ if (!r) sync_log_mailbox(name); -+ -+ if(r) { -+ prot_printf(imapd_out, "* NO delete %s: %s\r\n", -+ name, error_message(r)); -+ } -+ -+ return 0; -+} -+ -+/* -+ * Perform a DELETE command -+ */ -+void cmd_delete(char *tag, char *name, int localonly, int force) -+{ -+ int r; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ int mbtype; -+ char *server; -+ char *p; -+ int domainlen = 0; -+ int sync_lockfd = (-1); -+ -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ -+ if (!r) { -+ r = mlookup(NULL, NULL, mailboxname, &mbtype, NULL, NULL, -+ &server, NULL, NULL); -+ } -+ -+ if (!r && (mbtype & MBTYPE_REMOTE)) { -+ /* remote mailbox */ -+ struct backend *s = NULL; -+ int res; -+ -+ if (supports_referrals) { -+ imapd_refer(tag, server, name); -+ referral_kick = 1; -+ return; -+ } -+ -+ s = proxy_findserver(server, &protocol[PROTOCOL_IMAP], -+ proxy_userid, &backend_cached, -+ &backend_current, &backend_inbox, imapd_in); -+ if (!s) r = IMAP_SERVER_UNAVAILABLE; -+ -+ if (!r) { -+ prot_printf(s->out, "%s DELETE {%d+}\r\n%s\r\n", -+ tag, strlen(name), name); -+ res = pipe_until_tag(s, tag, 0); -+ -+ if (!CAPA(s, CAPA_MUPDATE) && res == PROXY_OK) { -+ /* do MUPDATE delete operations */ -+ } -+ -+ /* make sure we've seen the update */ -+ if (ultraparanoid && res == PROXY_OK) kick_mupdate(); -+ } -+ -+ imapd_check(s, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } else { -+ /* we're allowed to reference last_result since the noop, if -+ sent, went to a different server */ -+ prot_printf(imapd_out, "%s %s", tag, s->last_result.s); -+ } -+ -+ return; -+ } -+ -+ /* local mailbox */ -+ if (!r) { -+ if (config_virtdomains && (p = strchr(mailboxname, '!'))) -+ domainlen = p - mailboxname + 1; -+ -+ r = mboxlist_deletemailbox(mailboxname, imapd_userisadmin, -+ imapd_userid, imapd_authstate, 1-force, -+ localonly, 0); -+ } -+ -+ /* was it a top-level user mailbox? */ -+ /* localonly deletes are only per-mailbox */ -+ if (!r && !localonly && -+ !strncmp(mailboxname+domainlen, "user.", 5) && -+ !strchr(mailboxname+domainlen+5, '.')) { -+ int mailboxname_len = strlen(mailboxname); -+ -+ /* If we aren't too close to MAX_MAILBOX_NAME, append .* */ -+ p = mailboxname + mailboxname_len; /* end of mailboxname */ -+ if (mailboxname_len < sizeof(mailboxname) - 3) { -+ strcpy(p, ".*"); -+ } -+ -+ /* build a list of mailboxes - we're using internal names here */ -+ mboxlist_findall(NULL, mailboxname, imapd_userisadmin, imapd_userid, -+ imapd_authstate, delmbox, NULL); -+ -+ /* take care of deleting ACLs, subscriptions, seen state and quotas */ -+ *p = '\0'; /* clip off pattern */ -+ if ((!domainlen) || -+ (domainlen+1 < (sizeof(mailboxname) - mailboxname_len))) { -+ if (domainlen) { -+ /* fully qualify the userid */ -+ snprintf(p, (sizeof(mailboxname) - mailboxname_len), "@%.*s", -+ domainlen-1, mailboxname); -+ } -+ user_deletedata(mailboxname+domainlen+5, imapd_userid, -+ imapd_authstate, 1); -+ } -+ } -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } -+ else { -+ if (config_mupdate_server && -+ (config_mupdate_config != IMAP_ENUM_MUPDATE_CONFIG_STANDARD)) { -+ kick_mupdate(); -+ } -+ -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ /* XXX should sent a RESET here to cleanup meta-data */ -+ sync_log_mailbox(mailboxname); -+ } -+} -+ -+struct renrock -+{ -+ int ol; -+ int nl; -+ int rename_user; -+ char *olduser, *newuser; -+ char *acl_olduser, *acl_newuser; -+ char *newmailboxname; -+ char *partition; -+}; -+ -+/* Callback for use by cmd_rename */ -+static int renmbox(char *name, -+ int matchlen __attribute__((unused)), -+ int maycreate __attribute__((unused)), -+ void *rock) -+{ -+ char oldextname[MAX_MAILBOX_NAME+1]; -+ char newextname[MAX_MAILBOX_NAME+1]; -+ struct renrock *text = (struct renrock *)rock; -+ int r; -+ -+ if((text->nl + strlen(name + text->ol)) > MAX_MAILBOX_NAME) -+ return 0; -+ -+ strcpy(text->newmailboxname + text->nl, name + text->ol); -+ -+ r = mboxlist_renamemailbox(name, text->newmailboxname, -+ text->partition, -+ 1, imapd_userid, imapd_authstate); -+ -+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace, -+ name, -+ imapd_userid, oldextname); -+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace, -+ text->newmailboxname, -+ imapd_userid, newextname); -+ -+ if(r) { -+ prot_printf(imapd_out, "* NO rename %s %s: %s\r\n", -+ oldextname, newextname, error_message(r)); -+ if (RENAME_STOP_ON_ERROR) return r; -+ } else { -+ /* If we're renaming a user, change quotaroot and ACL */ -+ if (text->rename_user) { -+ user_copyquotaroot(name, text->newmailboxname); -+ user_renameacl(text->newmailboxname, -+ text->acl_olduser, text->acl_newuser); -+ } -+ -+ /* Rename mailbox annotations */ -+ annotatemore_rename(name, text->newmailboxname, -+ text->rename_user ? text->olduser : NULL, -+ text->newuser); -+ -+ prot_printf(imapd_out, "* OK rename %s %s\r\n", -+ oldextname, newextname); -+ -+ sync_log_mailbox_double(name, text->newmailboxname); -+ } -+ -+ prot_flush(imapd_out); -+ -+ return 0; -+} -+ -+/* -+ * Perform a RENAME command -+ */ -+void cmd_rename(char *tag, char *oldname, char *newname, char *partition) -+{ -+ int r = 0; -+ char oldmailboxname[MAX_MAILBOX_NAME+3]; -+ char newmailboxname[MAX_MAILBOX_NAME+2]; -+ char oldmailboxname2[MAX_MAILBOX_NAME+1]; -+ char newmailboxname2[MAX_MAILBOX_NAME+1]; -+ char oldextname[MAX_MAILBOX_NAME+1]; -+ char newextname[MAX_MAILBOX_NAME+1]; -+ int sync_lockfd = (-1); -+ int omlen, nmlen; -+ char *p; -+ int recursive_rename = 1; -+ int rename_user = 0; -+ char olduser[128], newuser[128]; -+ char acl_olduser[128], acl_newuser[128]; -+ int mbtype; -+ char *server; -+ -+ if (partition && !imapd_userisadmin) { -+ r = IMAP_PERMISSION_DENIED; -+ } -+ -+ /* canonicalize names */ -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, oldname, -+ imapd_userid, oldmailboxname); -+ if (!r) -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, newname, -+ imapd_userid, newmailboxname); -+ -+ /* Keep temporary copy: master is trashed */ -+ strcpy(oldmailboxname2, oldmailboxname); -+ strcpy(newmailboxname2, newmailboxname); -+ -+ if (!r) { -+ r = mlookup(NULL, NULL, oldmailboxname, &mbtype, NULL, NULL, -+ &server, NULL, NULL); -+ } -+ -+ if (!r && (mbtype & MBTYPE_REMOTE)) { -+ /* remote mailbox */ -+ struct backend *s = NULL; -+ int res; -+ -+ s = proxy_findserver(server, &protocol[PROTOCOL_IMAP], -+ proxy_userid, &backend_cached, -+ &backend_current, &backend_inbox, imapd_in); -+ if (!s) r = IMAP_SERVER_UNAVAILABLE; -+ -+ /* xxx start of separate proxy-only code -+ (remove when we move to a unified environment) */ -+ -+ /* Cross Server Rename */ -+ if (!r && partition) { -+ char *destpart; -+ -+ if (strcmp(oldname, newname)) { -+ prot_printf(imapd_out, -+ "%s NO Cross-server or cross-partition move w/rename not supported\r\n", -+ tag); -+ return; -+ } -+ -+ /* dest partition? */ -+ -+ destpart = strchr(partition,'!'); -+ if (destpart) { -+ char newserver[MAX_MAILBOX_NAME+1]; -+ if (strlen(partition) >= sizeof(newserver)) { -+ prot_printf(imapd_out, -+ "%s NO Partition name too long\r\n", tag); -+ return; -+ } -+ strcpy(newserver,partition); -+ newserver[destpart-partition]='\0'; -+ destpart++; -+ -+ if (!strcmp(server, newserver)) { -+ /* Same Server, different partition */ -+ /* xxx this would require administrative access to the -+ * backend, which we won't get */ -+ prot_printf(imapd_out, -+ "%s NO Can't move across partitions via a proxy\r\n", -+ tag); -+ return; -+ } else { -+ /* Cross Server */ -+ /* XFER */ -+ prot_printf(s->out, -+ "%s XFER {%d+}\r\n%s {%d+}\r\n%s {%d+}\r\n%s\r\n", -+ tag, strlen(oldname), oldname, -+ strlen(newserver), newserver, -+ strlen(destpart), destpart); -+ } -+ -+ } else { -+ /* XFER */ -+ prot_printf(s->out, "%s XFER {%d+}\r\n%s {%d+}\r\n%s\r\n", -+ tag, strlen(oldname), oldname, -+ strlen(partition), partition); -+ } -+ -+ res = pipe_including_tag(s, tag, 0); -+ -+ /* make sure we've seen the update */ -+ if (ultraparanoid && res == PROXY_OK) kick_mupdate(); -+ -+ return; -+ } -+ /* xxx end of separate proxy-only code */ -+ -+ if (!r) { -+ if (!CAPA(s, CAPA_MUPDATE)) { -+ /* do MUPDATE create operations for new mailbox */ -+ } -+ -+ prot_printf(s->out, "%s RENAME {%d+}\r\n%s {%d+}\r\n%s\r\n", -+ tag, strlen(oldname), oldname, -+ strlen(newname), newname); -+ res = pipe_until_tag(s, tag, 0); -+ -+ if (!CAPA(s, CAPA_MUPDATE)) { -+ /* Activate/abort new mailbox in MUPDATE*/ -+ /* delete old mailbox from MUPDATE */ -+ } -+ -+ /* make sure we've seen the update */ -+ if (res == PROXY_OK) kick_mupdate(); -+ } -+ -+ imapd_check(s, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } else { -+ /* we're allowed to reference last_result since the noop, if -+ sent, went to a different server */ -+ prot_printf(imapd_out, "%s %s", tag, s->last_result.s); -+ } -+ -+ return; -+ } -+ -+ /* local mailbox */ -+ -+ if (!r && partition && !config_partitiondir(partition)) { -+ /* invalid partition, assume its a server (remote destination) */ -+ char *server; -+ -+ if (strcmp(oldname, newname)) { -+ prot_printf(imapd_out, -+ "%s NO Cross-server or cross-partition move w/rename not supported\r\n", -+ tag); -+ return; -+ } -+ -+ /* dest partition? */ -+ server = partition; -+ partition = strchr(server, '!'); -+ if (partition) *partition++ = '\0'; -+ -+ cmd_xfer(tag, oldname, server, partition); -+ -+ return; -+ } -+ -+ /* local destination */ -+ -+ /* if this is my inbox, don't do recursive renames */ -+ if (!strcasecmp(oldname, "inbox")) { -+ recursive_rename = 0; -+ } -+ /* check if we're an admin renaming a user */ -+ else if (config_getswitch(IMAPOPT_ALLOWUSERMOVES) && -+ mboxname_isusermailbox(oldmailboxname, 1) && -+ mboxname_isusermailbox(newmailboxname, 1) && -+ strcmp(oldmailboxname, newmailboxname) && /* different user */ -+ imapd_userisadmin) { -+ rename_user = 1; -+ } -+ -+ /* if we're renaming something inside of something else, -+ don't recursively rename stuff */ -+ omlen = strlen(oldmailboxname); -+ nmlen = strlen(newmailboxname); -+ if (omlen < nmlen) { -+ if (!strncmp(oldmailboxname, newmailboxname, omlen) && -+ newmailboxname[omlen] == '.') { -+ recursive_rename = 0; -+ } -+ } else { -+ if (!strncmp(oldmailboxname, newmailboxname, nmlen) && -+ oldmailboxname[nmlen] == '.') { -+ recursive_rename = 0; -+ } -+ } -+ -+ /* verify that the mailbox doesn't have a wildcard in it */ -+ for (p = oldmailboxname; !r && *p; p++) { -+ if (*p == '*' || *p == '%') r = IMAP_MAILBOX_BADNAME; -+ } -+ -+ /* attempt to rename the base mailbox */ -+ if (!r) { -+ r = mboxlist_renamemailbox(oldmailboxname, newmailboxname, partition, -+ imapd_userisadmin, -+ imapd_userid, imapd_authstate); -+ } -+ -+ /* If we're renaming a user, take care of changing quotaroot, ACL, -+ seen state, subscriptions and sieve scripts */ -+ if (!r && rename_user) { -+ char *domain; -+ -+ /* create canonified userids */ -+ -+ domain = strchr(oldmailboxname, '!'); -+ strcpy(olduser, domain ? domain+6 : oldmailboxname+5); -+ if (domain) -+ sprintf(olduser+strlen(olduser), "@%.*s", -+ domain - oldmailboxname, oldmailboxname); -+ strcpy(acl_olduser, olduser); -+ -+ /* Translate any separators in source old userid (for ACLs) */ -+ mboxname_hiersep_toexternal(&imapd_namespace, acl_olduser, -+ config_virtdomains ? -+ strcspn(acl_olduser, "@") : 0); -+ -+ domain = strchr(newmailboxname, '!'); -+ strcpy(newuser, domain ? domain+6 : newmailboxname+5); -+ if (domain) -+ sprintf(newuser+strlen(newuser), "@%.*s", -+ domain - newmailboxname, newmailboxname); -+ strcpy(acl_newuser, newuser); -+ -+ /* Translate any separators in destination new userid (for ACLs) */ -+ mboxname_hiersep_toexternal(&imapd_namespace, acl_newuser, -+ config_virtdomains ? -+ strcspn(acl_newuser, "@") : 0); -+ -+ user_copyquotaroot(oldmailboxname, newmailboxname); -+ user_renameacl(newmailboxname, acl_olduser, acl_newuser); -+ user_renamedata(olduser, newuser, imapd_userid, imapd_authstate); -+ -+ /* XXX report status/progress of meta-data */ -+ } -+ -+ if (!r) { -+ /* Rename mailbox annotations */ -+ annotatemore_rename(oldmailboxname, newmailboxname, -+ rename_user ? olduser : NULL, -+ newuser); -+ } -+ -+ /* rename all mailboxes matching this */ -+ if (!r && recursive_rename) { -+ struct renrock rock; -+ int ol = omlen + 1; -+ int nl = nmlen + 1; -+ -+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace, -+ oldmailboxname, -+ imapd_userid, oldextname); -+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace, -+ newmailboxname, -+ imapd_userid, newextname); -+ -+ prot_printf(imapd_out, "* OK rename %s %s\r\n", -+ oldextname, newextname); -+ prot_flush(imapd_out); -+ -+ strcat(oldmailboxname, ".*"); -+ strcat(newmailboxname, "."); -+ -+ /* setup the rock */ -+ rock.newmailboxname = newmailboxname; -+ rock.ol = ol; -+ rock.nl = nl; -+ rock.olduser = olduser; -+ rock.newuser = newuser; -+ rock.acl_olduser = acl_olduser; -+ rock.acl_newuser = acl_newuser; -+ rock.partition = partition; -+ rock.rename_user = rename_user; -+ -+ /* add submailboxes; we pretend we're an admin since we successfully -+ renamed the parent - we're using internal names here */ -+ r = mboxlist_findall(NULL, oldmailboxname, 1, imapd_userid, -+ imapd_authstate, renmbox, &rock); -+ } -+ -+ /* take care of deleting old ACLs, subscriptions, seen state and quotas */ -+ if (!r && rename_user) -+ user_deletedata(olduser, imapd_userid, imapd_authstate, 1); -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } else { -+ if (config_mupdate_server && -+ (config_mupdate_config != IMAP_ENUM_MUPDATE_CONFIG_STANDARD)) { -+ kick_mupdate(); -+ } -+ -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ sync_log_mailbox_double(oldmailboxname2, newmailboxname2); -+ } -+} -+ -+/* -+ * Perform a RECONSTRUCT command -+ */ -+void cmd_reconstruct(const char *tag, const char *name, int recursive) -+{ -+ int r = 0; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ char quotaroot[MAX_MAILBOX_NAME+1]; -+ int mbtype; -+ char *server; -+ struct mailbox mailbox; -+ -+ /* administrators only please */ -+ if (!imapd_userisadmin) { -+ r = IMAP_PERMISSION_DENIED; -+ } -+ -+ if (!r) { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ } -+ -+ if (!r) { -+ r = mlookup(tag, name, mailboxname, &mbtype, NULL, NULL, -+ &server, NULL, NULL); -+ } -+ if (r == IMAP_MAILBOX_MOVED) return; -+ -+ if (!r && (mbtype & MBTYPE_REMOTE)) { -+ /* remote mailbox */ -+ imapd_refer(tag, server, name); -+ return; -+ } -+ -+ /* local mailbox */ -+ if (!r) { -+ int pid; -+ -+ /* Reconstruct it */ -+ -+ pid = fork(); -+ if(pid == -1) { -+ r = IMAP_SYS_ERROR; -+ } else if(pid == 0) { -+ char buf[4096]; -+ int ret; -+ -+ /* Child - exec reconstruct*/ -+ syslog(LOG_NOTICE, "Reconstructing '%s' (%s) for user '%s'", -+ mailboxname, recursive ? "recursive" : "not recursive", -+ imapd_userid); -+ -+ fclose(stdin); -+ fclose(stdout); -+ fclose(stderr); -+ -+ ret = snprintf(buf, sizeof(buf), "%s/reconstruct", SERVICE_PATH); -+ if(ret < 0 || ret >= sizeof(buf)) { -+ /* in child, so fatailing won't disconnect our user */ -+ fatal("reconstruct buffer not sufficiently big", EC_CONFIG); -+ } -+ -+ if(recursive) { -+ execl(buf, buf, "-C", config_filename, "-r", "-f", -+ mailboxname, NULL); -+ } else { -+ execl(buf, buf, "-C", config_filename, mailboxname, NULL); -+ } -+ -+ /* if we are here, we have a problem */ -+ exit(-1); -+ } else { -+ int status; -+ -+ /* Parent, wait on child */ -+ if(waitpid(pid, &status, 0) < 0) r = IMAP_SYS_ERROR; -+ -+ /* Did we fail? */ -+ if(WEXITSTATUS(status) != 0) r = IMAP_SYS_ERROR; -+ } -+ } -+ -+ /* Still in parent, need to re-quota the mailbox*/ -+ -+ /* Find its quota root */ -+ if (!r) { -+ r = mailbox_open_header(mailboxname, imapd_authstate, &mailbox); -+ } -+ -+ if(!r) { -+ if(mailbox.quota.root) { -+ strcpy(quotaroot, mailbox.quota.root); -+ } else { -+ strcpy(quotaroot, mailboxname); -+ } -+ mailbox_close(&mailbox); -+ } -+ -+ /* Run quota -f */ -+ if (!r) { -+ int pid; -+ -+ pid = fork(); -+ if(pid == -1) { -+ r = IMAP_SYS_ERROR; -+ } else if(pid == 0) { -+ char buf[4096]; -+ int ret; -+ -+ /* Child - exec reconstruct*/ -+ syslog(LOG_NOTICE, -+ "Regenerating quota roots starting with '%s' for user '%s'", -+ mailboxname, imapd_userid); -+ -+ fclose(stdin); -+ fclose(stdout); -+ fclose(stderr); -+ -+ ret = snprintf(buf, sizeof(buf), "%s/quota", SERVICE_PATH); -+ if(ret < 0 || ret >= sizeof(buf)) { -+ /* in child, so fatailing won't disconnect our user */ -+ fatal("quota buffer not sufficiently big", EC_CONFIG); -+ } -+ -+ execl(buf, buf, "-C", config_filename, "-f", quotaroot, NULL); -+ -+ /* if we are here, we have a problem */ -+ exit(-1); -+ } else { -+ int status; -+ -+ /* Parent, wait on child */ -+ if(waitpid(pid, &status, 0) < 0) r = IMAP_SYS_ERROR; -+ -+ /* Did we fail? */ -+ if(WEXITSTATUS(status) != 0) r = IMAP_SYS_ERROR; -+ } -+ } -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ sync_log_user(imapd_userid); -+ } -+} -+ -+/* -+ * Perform a FIND command -+ */ -+void cmd_find(char *tag, char *namespace, char *pattern) -+{ -+ char *p; -+ lcase(namespace); -+ -+ for (p = pattern; *p; p++) { -+ if (*p == '%') *p = '?'; -+ } -+ -+ if (!strcasecmp(namespace, "mailboxes")) { -+ if (backend_inbox || (backend_inbox = proxy_findinboxserver())) { -+ /* remote INBOX */ -+ prot_printf(backend_inbox->out, -+ "%s Lsub \"\" {%d+}\r\n%s\r\n", -+ tag, strlen(pattern), pattern); -+ pipe_lsub(backend_inbox, tag, 0, "MAILBOX"); -+ } else { -+ /* local INBOX */ -+ int force = config_getswitch(IMAPOPT_ALLOWALLSUBSCRIBE); -+ -+ /* Translate any separators in pattern */ -+ mboxname_hiersep_tointernal(&imapd_namespace, pattern, -+ config_virtdomains ? -+ strcspn(pattern, "@") : 0); -+ -+ (*imapd_namespace.mboxlist_findsub)(&imapd_namespace, pattern, -+ imapd_userisadmin, imapd_userid, -+ imapd_authstate, mailboxdata, -+ NULL, force); -+ } -+ } -+ else if (!strcasecmp(namespace, "all.mailboxes")) { -+ /* Translate any separators in pattern */ -+ mboxname_hiersep_tointernal(&imapd_namespace, pattern, -+ config_virtdomains ? -+ strcspn(pattern, "@") : 0); -+ -+ (*imapd_namespace.mboxlist_findall)(&imapd_namespace, pattern, -+ imapd_userisadmin, imapd_userid, -+ imapd_authstate, mailboxdata, NULL); -+ } -+ else if (!strcasecmp(namespace, "bboards") -+ || !strcasecmp(namespace, "all.bboards")) { -+ ; -+ } -+ else { -+ prot_printf(imapd_out, "%s BAD Invalid FIND subcommand\r\n", tag); -+ return; -+ } -+ -+ imapd_check(backend_inbox, 0, 0); -+ -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+} -+ -+static int mstringdatacalls; -+ -+/* -+ * Perform a LIST or LSUB command -+ */ -+void cmd_list(char *tag, int listopts, char *reference, char *pattern) -+{ -+ char *buf = NULL; -+ int patlen = 0; -+ int reflen = 0; -+ static int ignorereference = 0; -+ clock_t start = clock(); -+ char mytime[100]; -+ int (*findall)(struct namespace *namespace, -+ const char *pattern, int isadmin, char *userid, -+ struct auth_state *auth_state, int (*proc)(), -+ void *rock); -+ int (*findsub)(struct namespace *namespace, -+ const char *pattern, int isadmin, char *userid, -+ struct auth_state *auth_state, int (*proc)(), -+ void *rock, int force); -+ -+ /* Ignore the reference argument? -+ (the behavior in 1.5.10 & older) */ -+ if (ignorereference == 0) { -+ ignorereference = config_getswitch(IMAPOPT_IGNOREREFERENCE); -+ } -+ -+ /* Reset state in mstringdata */ -+ mstringdata(NULL, NULL, 0, 0, 0); -+ -+ if (!pattern[0] && !(listopts & LIST_LSUB)) { -+ /* Special case: query top-level hierarchy separator */ -+ prot_printf(imapd_out, "* LIST (\\Noselect) \"%c\" \"\"\r\n", -+ imapd_namespace.hier_sep); -+ } else if ((listopts & (LIST_LSUB | LIST_SUBSCRIBED)) && -+ (backend_inbox || (backend_inbox = proxy_findinboxserver()))) { -+ /* remote INBOX */ -+ if ((listopts & LIST_SUBSCRIBED) && (listopts & LIST_EXT) && -+ CAPA(backend_inbox, CAPA_LISTSUBSCRIBED)) { -+ prot_printf(backend_inbox->out, "%s List (subscribed", tag); -+ if (listopts & LIST_CHILDREN) -+ prot_printf(backend_inbox->out, " children"); -+ if (listopts & LIST_REMOTE) -+ prot_printf(backend_inbox->out, " remote"); -+ prot_printf(backend_inbox->out, ") "); -+ } else { -+ prot_printf(backend_inbox->out, "%s Lsub ", tag); -+ } -+ prot_printf(backend_inbox->out, -+ "{%d+}\r\n%s {%d+}\r\n%s\r\n", -+ strlen(reference), reference, -+ strlen(pattern), pattern); -+ pipe_lsub(backend_inbox, tag, 0, (listopts & LIST_LSUB) ? "LSUB" : "LIST"); -+ } else { -+ /* Do we need to concatenate fields? */ -+ if (!ignorereference || pattern[0] == imapd_namespace.hier_sep) { -+ /* Either -+ * - name begins with dot -+ * - we're configured to honor the reference argument */ -+ -+ /* Allocate a buffer, figure out how to stick the arguments -+ together, do it, then do that instead of using pattern. */ -+ patlen = strlen(pattern); -+ reflen = strlen(reference); -+ -+ buf = xmalloc(patlen + reflen + 1); -+ buf[0] = '\0'; -+ -+ if (*reference) { -+ /* check for LIST A. .B, change to LIST "" A.B */ -+ if (reference[reflen-1] == imapd_namespace.hier_sep && -+ pattern[0] == imapd_namespace.hier_sep) { -+ reference[--reflen] = '\0'; -+ } -+ strcpy(buf, reference); -+ } -+ strcat(buf, pattern); -+ pattern = buf; -+ } -+ -+ /* Translate any separators in pattern */ -+ mboxname_hiersep_tointernal(&imapd_namespace, pattern, -+ config_virtdomains ? -+ strcspn(pattern, "@") : 0); -+ -+ /* Check to see if we should only list the personal namespace */ -+ if (!strcmp(pattern, "*") -+ && config_getswitch(IMAPOPT_FOOLSTUPIDCLIENTS)) { -+ if (buf) free(buf); -+ buf = xstrdup("INBOX*"); -+ pattern = buf; -+ findsub = mboxlist_findsub; -+ findall = mboxlist_findall; -+ } -+ else { -+ findsub = imapd_namespace.mboxlist_findsub; -+ findall = imapd_namespace.mboxlist_findall; -+ } -+ -+ if (listopts & (LIST_LSUB | LIST_SUBSCRIBED)) { -+ int force = config_getswitch(IMAPOPT_ALLOWALLSUBSCRIBE); -+ -+ (*findsub)(&imapd_namespace, pattern, -+ imapd_userisadmin, imapd_userid, imapd_authstate, -+ listdata, &listopts, force); -+ } -+ else { -+ (*findall)(&imapd_namespace, pattern, -+ imapd_userisadmin, imapd_userid, imapd_authstate, -+ listdata, &listopts); -+ } -+ -+ listdata((char *)0, 0, 0, &listopts); -+ -+ if (buf) free(buf); -+ } -+ -+ imapd_check(!(listopts & (LIST_LSUB | LIST_SUBSCRIBED)) ? -+ backend_inbox : NULL, 0, 0); -+ -+ snprintf(mytime, sizeof(mytime), "%2.3f", -+ (clock() - start) / (double) CLOCKS_PER_SEC); -+ prot_printf(imapd_out, "%s OK %s (%s secs %d calls)\r\n", tag, -+ error_message(IMAP_OK_COMPLETED), mytime, mstringdatacalls); -+} -+ -+/* -+ * Perform a SUBSCRIBE (add is nonzero) or -+ * UNSUBSCRIBE (add is zero) command -+ */ -+void cmd_changesub(char *tag, char *namespace, char *name, int add) -+{ -+ const char *cmd = add ? "Subscribe" : "Unsubscribe"; -+ int r = 0; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ int force = config_getswitch(IMAPOPT_ALLOWALLSUBSCRIBE); -+ -+ if (backend_inbox || (backend_inbox = proxy_findinboxserver())) { -+ /* remote INBOX */ -+ if (add) { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, -+ name, imapd_userid, -+ mailboxname); -+ if (!r) r = mlookup(NULL, NULL, mailboxname, -+ NULL, NULL, NULL, NULL, NULL, NULL); -+ -+ /* Doesn't exist on murder */ -+ } -+ -+ imapd_check(backend_inbox, 0, 0); -+ -+ if (!r) { -+ if (namespace) { -+ prot_printf(backend_inbox->out, -+ "%s %s {%d+}\r\n%s {%d+}\r\n%s\r\n", -+ tag, cmd, -+ strlen(namespace), namespace, -+ strlen(name), name); -+ } else { -+ prot_printf(backend_inbox->out, "%s %s {%d+}\r\n%s\r\n", -+ tag, cmd, -+ strlen(name), name); -+ } -+ if (backend_inbox != backend_current) -+ pipe_including_tag(backend_inbox, tag, 0); -+ } -+ else { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } -+ -+ return; -+ } -+ -+ /* local INBOX */ -+ if (namespace) lcase(namespace); -+ if (!namespace || !strcmp(namespace, "mailbox")) { -+ int len = strlen(name); -+ if (force && imapd_namespace.isalt && -+ (((len == strlen(imapd_namespace.prefix[NAMESPACE_USER]) - 1) && -+ !strncmp(name, imapd_namespace.prefix[NAMESPACE_USER], len)) || -+ ((len == strlen(imapd_namespace.prefix[NAMESPACE_SHARED]) - 1) && -+ !strncmp(name, imapd_namespace.prefix[NAMESPACE_SHARED], len)))) { -+ r = 0; -+ } -+ else { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ if (!r) { -+ r = mboxlist_changesub(mailboxname, imapd_userid, -+ imapd_authstate, add, force); -+ } -+ } -+ } -+ else if (!strcmp(namespace, "bboard")) { -+ r = add ? IMAP_MAILBOX_NONEXISTENT : 0; -+ } -+ else { -+ prot_printf(imapd_out, "%s BAD Invalid %s subcommand\r\n", tag, cmd); -+ return; -+ } -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s: %s\r\n", tag, cmd, error_message(r)); -+ } -+ else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ sync_log_subscribe(imapd_userid, mailboxname, add); -+ } -+} -+ -+/* -+ * Perform a GETACL command -+ */ -+void cmd_getacl(const char *tag, const char *name) -+{ -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ int r, access; -+ char *acl; -+ char *rights, *nextid; -+ char str[ACL_MAXSTR]; -+ -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ -+ if (!r) { -+ r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, NULL, &acl, NULL); -+ } -+ if (r == IMAP_MAILBOX_MOVED) return; -+ -+ if (!r) { -+ access = cyrus_acl_myrights(imapd_authstate, acl); -+ -+ if (!(access & ACL_ADMIN) && -+ !imapd_userisadmin && -+ !mboxname_userownsmailbox(imapd_userid, mailboxname)) { -+ r = (access&ACL_LOOKUP) ? -+ IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT; -+ } -+ } -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ return; -+ } -+ -+ prot_printf(imapd_out, "* ACL "); -+ printastring(name); -+ -+ while (acl) { -+ rights = strchr(acl, '\t'); -+ if (!rights) break; -+ *rights++ = '\0'; -+ -+ nextid = strchr(rights, '\t'); -+ if (!nextid) break; -+ *nextid++ = '\0'; -+ -+ prot_printf(imapd_out, " "); -+ printastring(acl); -+ prot_printf(imapd_out, " "); -+ rights = cyrus_acl_masktostr(cyrus_acl_strtomask(rights), str, 1); -+ printastring(rights); -+ acl = nextid; -+ } -+ prot_printf(imapd_out, "\r\n"); -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+} -+ -+/* -+ * Perform a LISTRIGHTS command -+ */ -+void -+cmd_listrights(tag, name, identifier) -+char *tag; -+char *name; -+char *identifier; -+{ -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ int r, rights; -+ char *acl; -+ -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ -+ if (!r) { -+ r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, NULL, &acl, NULL); -+ } -+ if (r == IMAP_MAILBOX_MOVED) return; -+ -+ if (!r) { -+ rights = cyrus_acl_myrights(imapd_authstate, acl); -+ -+ if (!rights && !imapd_userisadmin && -+ !mboxname_userownsmailbox(imapd_userid, mailboxname)) { -+ r = IMAP_MAILBOX_NONEXISTENT; -+ } -+ } -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (!r) { -+ struct auth_state *authstate = auth_newstate(identifier); -+ char *canon_identifier; -+ int canonidlen = 0; -+ int implicit; -+ char rightsdesc[100], optional[33]; -+ -+ if (global_authisa(authstate, IMAPOPT_ADMINS)) -+ canon_identifier = identifier; /* don't canonify global admins */ -+ else -+ canon_identifier = canonify_userid(identifier, imapd_userid, NULL); -+ auth_freestate(authstate); -+ -+ if (canon_identifier) canonidlen = strlen(canon_identifier); -+ -+ if (!canon_identifier) { -+ implicit = 0; -+ } -+ else if (mboxname_userownsmailbox(canon_identifier, mailboxname)) { -+ /* identifier's personal mailbox */ -+ implicit = config_implicitrights; -+ } -+ else if (mboxname_isusermailbox(mailboxname, 1)) { -+ /* anyone can post to an INBOX */ -+ implicit = ACL_POST; -+ } -+ else { -+ implicit = 0; -+ } -+ -+ /* calculate optional rights */ -+ cyrus_acl_masktostr(implicit ^ (canon_identifier ? ACL_FULL : 0), -+ optional, 1); -+ -+ /* build the rights string */ -+ if (implicit) { -+ cyrus_acl_masktostr(implicit, rightsdesc, 1); -+ } -+ else { -+ strcpy(rightsdesc, "\"\""); -+ } -+ -+ if (*optional) { -+ int i, n = strlen(optional); -+ char *p = rightsdesc + strlen(rightsdesc); -+ -+ for (i = 0; i < n; i++) { -+ *p++ = ' '; -+ *p++ = optional[i]; -+ } -+ *p = '\0'; -+ } -+ -+ prot_printf(imapd_out, "* LISTRIGHTS "); -+ printastring(name); -+ prot_putc(' ', imapd_out); -+ printastring(identifier); -+ prot_printf(imapd_out, " %s", rightsdesc); -+ -+ prot_printf(imapd_out, "\r\n%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ return; -+ } -+ -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+} -+ -+/* -+ * Perform a MYRIGHTS command -+ */ -+void cmd_myrights(const char *tag, const char *name) -+{ -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ int r, rights = 0; -+ char *acl; -+ char str[ACL_MAXSTR]; -+ -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ -+ if (!r) { -+ r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, NULL, &acl, NULL); -+ } -+ if (r == IMAP_MAILBOX_MOVED) return; -+ -+ if (!r) { -+ rights = cyrus_acl_myrights(imapd_authstate, acl); -+ -+ /* Add in implicit rights */ -+ if (imapd_userisadmin) { -+ rights |= ACL_LOOKUP|ACL_ADMIN; -+ } -+ else if (mboxname_userownsmailbox(imapd_userid, mailboxname)) { -+ rights |= config_implicitrights; -+ } -+ -+ if (!(rights & (ACL_LOOKUP|ACL_READ|ACL_INSERT|ACL_CREATE|ACL_DELETEMBOX|ACL_ADMIN))) { -+ r = IMAP_MAILBOX_NONEXISTENT; -+ } -+ } -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ return; -+ } -+ -+ prot_printf(imapd_out, "* MYRIGHTS "); -+ printastring(name); -+ prot_printf(imapd_out, " "); -+ printastring(cyrus_acl_masktostr(rights, str, 1)); -+ prot_printf(imapd_out, "\r\n%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+} -+ -+/* -+ * Perform a SETACL command -+ */ -+void cmd_setacl(char *tag, const char *name, -+ const char *identifier, const char *rights) -+{ -+ int r; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ char *server; -+ int mbtype; -+ -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ -+ /* is it remote? */ -+ if (!r) { -+ r = mlookup(tag, name, mailboxname, &mbtype, NULL, NULL, -+ &server, NULL, NULL); -+ } -+ if (r == IMAP_MAILBOX_MOVED) return; -+ -+ if (!r && (mbtype & MBTYPE_REMOTE)) { -+ /* remote mailbox */ -+ struct backend *s = NULL; -+ int res; -+ -+ s = proxy_findserver(server, &protocol[PROTOCOL_IMAP], -+ proxy_userid, &backend_cached, -+ &backend_current, &backend_inbox, imapd_in); -+ if (!s) r = IMAP_SERVER_UNAVAILABLE; -+ -+ if (!r && imapd_userisadmin && supports_referrals) { -+ /* They aren't an admin remotely, so let's refer them */ -+ imapd_refer(tag, server, name); -+ referral_kick = 1; -+ return; -+ } else if (!r) { -+ if (rights) { -+ prot_printf(s->out, -+ "%s Setacl {%d+}\r\n%s {%d+}\r\n%s {%d+}\r\n%s\r\n", -+ tag, strlen(name), name, -+ strlen(identifier), identifier, -+ strlen(rights), rights); -+ } else { -+ prot_printf(s->out, -+ "%s Deleteacl {%d+}\r\n%s {%d+}\r\n%s\r\n", -+ tag, strlen(name), name, -+ strlen(identifier), identifier); -+ } -+ res = pipe_until_tag(s, tag, 0); -+ -+ if (!CAPA(s, CAPA_MUPDATE) && res == PROXY_OK) { -+ /* setup new ACL in MUPDATE */ -+ } -+ /* make sure we've seen the update */ -+ if (ultraparanoid && res == PROXY_OK) kick_mupdate(); -+ } -+ -+ imapd_check(s, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } else { -+ /* we're allowed to reference last_result since the noop, if -+ sent, went to a different server */ -+ prot_printf(imapd_out, "%s %s", tag, s->last_result.s); -+ } -+ -+ return; -+ } -+ -+ /* local mailbox */ -+ if (!r) { -+ r = mboxlist_setacl(mailboxname, identifier, rights, -+ imapd_userisadmin, imapd_userid, imapd_authstate); -+ } -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } else { -+ if (config_mupdate_server && -+ (config_mupdate_config != IMAP_ENUM_MUPDATE_CONFIG_STANDARD)) { -+ kick_mupdate(); -+ } -+ -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ sync_log_acl(mailboxname); -+ } -+} -+ -+/* -+ * Callback for (get|set)quota, to ensure that all of the -+ * submailboxes are on the same server. -+ */ -+static int quota_cb(char *name, int matchlen __attribute__((unused)), -+ int maycreate __attribute__((unused)), void *rock) -+{ -+ int r; -+ char *this_server; -+ const char *servername = (const char *)rock; -+ -+ r = mlookup(NULL, NULL, name, NULL, NULL, NULL, &this_server, NULL, NULL); -+ if(r) return r; -+ -+ if(strcmp(servername, this_server)) { -+ /* Not on same server as the root */ -+ return IMAP_NOT_SINGULAR_ROOT; -+ } else { -+ return PROXY_OK; -+ } -+} -+ -+/* -+ * Perform a GETQUOTA command -+ */ -+void cmd_getquota(const char *tag, const char *name) -+{ -+ int r; -+ struct quota quota; -+ char quotarootbuf[MAX_MAILBOX_PATH+3]; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ int mbtype; -+ char *server_rock = NULL, *server_rock_tmp = NULL; -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (!imapd_userisadmin) r = IMAP_PERMISSION_DENIED; -+ else { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ } -+ -+ if (!r) { -+ r = mlookup(NULL, NULL, mailboxname, &mbtype, NULL, NULL, -+ &server_rock_tmp, NULL, NULL); -+ } -+ -+ if (!r && (mbtype & MBTYPE_REMOTE)) { -+ /* remote mailbox */ -+ server_rock = xstrdup(server_rock_tmp); -+ -+ snprintf(quotarootbuf, sizeof(quotarootbuf), "%s.*", mailboxname); -+ -+ r = mboxlist_findall(&imapd_namespace, quotarootbuf, -+ imapd_userisadmin, imapd_userid, -+ imapd_authstate, quota_cb, server_rock); -+ -+ if (!r) { -+ /* Do the referral */ -+ imapd_refer(tag, server_rock, name); -+ free(server_rock); -+ } else { -+ if(server_rock) free(server_rock); -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } -+ -+ return; -+ } -+ -+ /* local mailbox */ -+ if (!r) { -+ quota.root = mailboxname; -+ r = quota_read("a, NULL, 0); -+ } -+ -+ if (!r) { -+ prot_printf(imapd_out, "* QUOTA "); -+ printastring(name); -+ prot_printf(imapd_out, " ("); -+ if (quota.limit >= 0) { -+ prot_printf(imapd_out, "STORAGE " UQUOTA_T_FMT " %d", -+ quota.used/QUOTA_UNITS, quota.limit); -+ } -+ prot_printf(imapd_out, ")\r\n"); -+ } -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ return; -+ } -+ -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+} -+ -+/* -+ * Perform a GETQUOTAROOT command -+ */ -+void cmd_getquotaroot(const char *tag, const char *name) -+{ -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ char *server; -+ int mbtype; -+ struct mailbox mailbox; -+ int r; -+ int doclose = 0; -+ -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ -+ if (!r) { -+ r = mlookup(tag, name, mailboxname, &mbtype, NULL, NULL, -+ &server, NULL, NULL); -+ } -+ if (r == IMAP_MAILBOX_MOVED) return; -+ -+ if (!r && (mbtype & MBTYPE_REMOTE)) { -+ /* remote mailbox */ -+ -+ if (imapd_userisadmin) { -+ /* If they are an admin, they won't retain that privledge if we -+ * proxy for them, so we need to refer them -- even if they haven't -+ * told us they're able to handle it. */ -+ imapd_refer(tag, server, name); -+ } else { -+ struct backend *s; -+ -+ s = proxy_findserver(server, &protocol[PROTOCOL_IMAP], -+ proxy_userid, &backend_cached, -+ &backend_current, &backend_inbox, imapd_in); -+ if (!s) r = IMAP_SERVER_UNAVAILABLE; -+ -+ imapd_check(s, 0, 0); -+ -+ if (!r) { -+ prot_printf(s->out, "%s Getquotaroot {%d+}\r\n%s\r\n", -+ tag, strlen(name), name); -+ if (s != backend_current) pipe_including_tag(s, tag, 0); -+ } else { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } -+ } -+ -+ return; -+ } -+ -+ /* local mailbox */ -+ if (!r) { -+ r = mailbox_open_header(mailboxname, imapd_authstate, &mailbox); -+ } -+ -+ if (!r) { -+ doclose = 1; -+ if (!imapd_userisadmin && !(mailbox.myrights & ACL_READ)) { -+ r = (mailbox.myrights & ACL_LOOKUP) ? -+ IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT; -+ } -+ } -+ -+ if (!r) { -+ prot_printf(imapd_out, "* QUOTAROOT "); -+ printastring(name); -+ if (mailbox.quota.root) { -+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace, -+ mailbox.quota.root, -+ imapd_userid, mailboxname); -+ prot_printf(imapd_out, " "); -+ printastring(mailboxname); -+ r = quota_read(&mailbox.quota, NULL, 0); -+ if (!r) { -+ prot_printf(imapd_out, "\r\n* QUOTA "); -+ printastring(mailboxname); -+ prot_printf(imapd_out, " ("); -+ if (mailbox.quota.limit >= 0) { -+ prot_printf(imapd_out, "STORAGE " UQUOTA_T_FMT " %d", -+ mailbox.quota.used/QUOTA_UNITS, -+ mailbox.quota.limit); -+ } -+ prot_putc(')', imapd_out); -+ } -+ } -+ prot_printf(imapd_out, "\r\n"); -+ } -+ -+ if (doclose) mailbox_close(&mailbox); -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ return; -+ } -+ -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+} -+ -+/* -+ * Parse and perform a SETQUOTA command -+ * The command has been parsed up to the resource list -+ */ -+void cmd_setquota(const char *tag, const char *quotaroot) -+{ -+ int newquota = -1; -+ int badresource = 0; -+ int c; -+ int force = 0; -+ static struct buf arg; -+ char *p; -+ int r; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ int mbtype; -+ char *server_rock_tmp = NULL; -+ -+ c = prot_getc(imapd_in); -+ if (c != '(') goto badlist; -+ -+ c = getword(imapd_in, &arg); -+ if (c != ')' || arg.s[0] != '\0') { -+ for (;;) { -+ if (c != ' ') goto badlist; -+ if (strcasecmp(arg.s, "storage") != 0) badresource = 1; -+ c = getword(imapd_in, &arg); -+ if (c != ' ' && c != ')') goto badlist; -+ if (arg.s[0] == '\0') goto badlist; -+ newquota = 0; -+ for (p = arg.s; *p; p++) { -+ if (!isdigit((int) *p)) goto badlist; -+ newquota = newquota * 10 + *p - '0'; -+ if (newquota < 0) goto badlist; /* overflow */ -+ } -+ if (c == ')') break; -+ } -+ } -+ c = prot_getc(imapd_in); -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ prot_printf(imapd_out, "%s BAD Unexpected extra arguments to SETQUOTA\r\n", tag); -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ if (badresource) r = IMAP_UNSUPPORTED_QUOTA; -+ else if (!imapd_userisadmin && !imapd_userisproxyadmin) { -+ /* need to allow proxies so that mailbox moves can set initial quota -+ * roots */ -+ r = IMAP_PERMISSION_DENIED; -+ } else { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, quotaroot, -+ imapd_userid, mailboxname); -+ } -+ -+ if (!r) { -+ r = mlookup(NULL, NULL, mailboxname, &mbtype, NULL, NULL, -+ &server_rock_tmp, NULL, NULL); -+ } -+ -+ if (!r && (mbtype & MBTYPE_REMOTE)) { -+ /* remote mailbox */ -+ char quotarootbuf[MAX_MAILBOX_NAME + 3]; -+ char *server_rock = xstrdup(server_rock_tmp); -+ -+ snprintf(quotarootbuf, sizeof(quotarootbuf), "%s.*", mailboxname); -+ -+ r = mboxlist_findall(&imapd_namespace, quotarootbuf, -+ imapd_userisadmin, imapd_userid, -+ imapd_authstate, quota_cb, server_rock); -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (!r) { -+ /* Do the referral */ -+ imapd_refer(tag, server_rock, quotaroot); -+ free(server_rock); -+ } else { -+ if (server_rock) free(server_rock); -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } -+ -+ return; -+ } -+ -+ /* local mailbox */ -+ if (!r) { -+ /* are we forcing the creation of a quotaroot by having a leading +? */ -+ if (quotaroot[0] == '+') { -+ force = 1; -+ quotaroot++; -+ } -+ -+ r = mboxlist_setquota(mailboxname, newquota, force); -+ } -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ return; -+ } -+ -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ sync_log_quota(mailboxname); -+ return; -+ -+ badlist: -+ prot_printf(imapd_out, "%s BAD Invalid quota list in Setquota\r\n", tag); -+ eatline(imapd_in, c); -+} -+ -+#ifdef HAVE_SSL -+/* -+ * this implements the STARTTLS command, as described in RFC 2595. -+ * one caveat: it assumes that no external layer is currently present. -+ * if a client executes this command, information about the external -+ * layer that was passed on the command line is disgarded. this should -+ * be fixed. -+ */ -+/* imaps - whether this is an imaps transaction or not */ -+void cmd_starttls(char *tag, int imaps) -+{ -+ int result; -+ int *layerp; -+ -+ char *auth_id; -+ sasl_ssf_t ssf; -+ -+ /* SASL and openssl have different ideas about whether ssf is signed */ -+ layerp = (int *) &ssf; -+ -+ if (imapd_starttls_done == 1) -+ { -+ prot_printf(imapd_out, "%s NO TLS already active\r\n", tag); -+ return; -+ } -+ -+ result=tls_init_serverengine("imap", -+ 5, /* depth to verify */ -+ !imaps, /* can client auth? */ -+ !imaps); /* TLS only? */ -+ -+ if (result == -1) { -+ -+ syslog(LOG_ERR, "error initializing TLS"); -+ -+ if (imaps == 0) { -+ prot_printf(imapd_out, "%s NO Error initializing TLS\r\n", tag); -+ } else { -+ fatal("tls_init() failed", EC_CONFIG); -+ } -+ -+ return; -+ } -+ -+ if (imaps == 0) -+ { -+ prot_printf(imapd_out, "%s OK Begin TLS negotiation now\r\n", tag); -+ /* must flush our buffers before starting tls */ -+ prot_flush(imapd_out); -+ } -+ -+ result=tls_start_servertls(0, /* read */ -+ 1, /* write */ -+ layerp, -+ &auth_id, -+ &tls_conn); -+ -+ /* if error */ -+ if (result==-1) { -+ if (imaps == 0) { -+ prot_printf(imapd_out, "%s NO Starttls negotiation failed\r\n", -+ tag); -+ syslog(LOG_NOTICE, "STARTTLS negotiation failed: %s", -+ imapd_clienthost); -+ return; -+ } else { -+ syslog(LOG_NOTICE, "imaps TLS negotiation failed: %s", -+ imapd_clienthost); -+ fatal("tls_start_servertls() failed", EC_TEMPFAIL); -+ return; -+ } -+ } -+ -+ /* tell SASL about the negotiated layer */ -+ result = sasl_setprop(imapd_saslconn, SASL_SSF_EXTERNAL, &ssf); -+ if (result != SASL_OK) { -+ fatal("sasl_setprop() failed: cmd_starttls()", EC_TEMPFAIL); -+ } -+ saslprops.ssf = ssf; -+ -+ result = sasl_setprop(imapd_saslconn, SASL_AUTH_EXTERNAL, auth_id); -+ if (result != SASL_OK) { -+ fatal("sasl_setprop() failed: cmd_starttls()", EC_TEMPFAIL); -+ } -+ if(saslprops.authid) { -+ free(saslprops.authid); -+ saslprops.authid = NULL; -+ } -+ if(auth_id) -+ saslprops.authid = xstrdup(auth_id); -+ -+ /* tell the prot layer about our new layers */ -+ prot_settls(imapd_in, tls_conn); -+ prot_settls(imapd_out, tls_conn); -+ -+ imapd_starttls_done = 1; -+} -+#else -+void cmd_starttls(char *tag, int imaps) -+{ -+ fatal("cmd_starttls() executed, but starttls isn't implemented!", -+ EC_SOFTWARE); -+} -+#endif /* HAVE_SSL */ -+ -+/* -+ * Parse and perform a STATUS command -+ * The command has been parsed up to the attribute list -+ */ -+void cmd_status(char *tag, char *name) -+{ -+ int c; -+ int statusitems = 0; -+ static struct buf arg; -+ struct mailbox mailbox; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ int mbtype; -+ char *server; -+ int r = 0; -+ int doclose = 0; -+ -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ -+ if (!r) { -+ r = mlookup(tag, name, mailboxname, &mbtype, NULL, NULL, -+ &server, NULL, NULL); -+ } -+ if (r == IMAP_MAILBOX_MOVED) { -+ /* Eat the argument */ -+ eatline(imapd_in, prot_getc(imapd_in)); -+ return; -+ } -+ -+ if (!r && (mbtype & MBTYPE_REMOTE)) { -+ /* remote mailbox */ -+ -+ if (supports_referrals -+ && config_getswitch(IMAPOPT_PROXYD_ALLOW_STATUS_REFERRAL)) { -+ imapd_refer(tag, server, name); -+ /* Eat the argument */ -+ eatline(imapd_in, prot_getc(imapd_in)); -+ } -+ else { -+ struct backend *s; -+ -+ s = proxy_findserver(server, &protocol[PROTOCOL_IMAP], -+ proxy_userid, &backend_cached, -+ &backend_current, &backend_inbox, imapd_in); -+ if (!s) r = IMAP_SERVER_UNAVAILABLE; -+ -+ imapd_check(s, 0, 0); -+ -+ if (!r) { -+ prot_printf(s->out, "%s Status {%d+}\r\n%s ", tag, -+ strlen(name), name); -+ if (!pipe_command(s, 65536)) { -+ if (s != backend_current) pipe_including_tag(s, tag, 0); -+ } -+ } else { -+ eatline(imapd_in, prot_getc(imapd_in)); -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } -+ } -+ -+ return; -+ } -+ -+ /* local mailbox */ -+ -+ /* -+ * Perform a full checkpoint of any open mailbox, in case we're -+ * doing a STATUS check of the current mailbox. -+ */ -+ imapd_check(NULL, 0, 1); -+ -+ c = prot_getc(imapd_in); -+ if (c != '(') goto badlist; -+ -+ c = getword(imapd_in, &arg); -+ if (arg.s[0] == '\0') goto badlist; -+ for (;;) { -+ lcase(arg.s); -+ if (!strcmp(arg.s, "messages")) { -+ statusitems |= STATUS_MESSAGES; -+ } -+ else if (!strcmp(arg.s, "recent")) { -+ statusitems |= STATUS_RECENT; -+ } -+ else if (!strcmp(arg.s, "uidnext")) { -+ statusitems |= STATUS_UIDNEXT; -+ } -+ else if (!strcmp(arg.s, "uidvalidity")) { -+ statusitems |= STATUS_UIDVALIDITY; -+ } -+ else if (!strcmp(arg.s, "unseen")) { -+ statusitems |= STATUS_UNSEEN; -+ } -+ else { -+ prot_printf(imapd_out, "%s BAD Invalid Status attribute %s\r\n", -+ tag, arg.s); -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ if (c == ' ') c = getword(imapd_in, &arg); -+ else break; -+ } -+ -+ if (c != ')') { -+ prot_printf(imapd_out, -+ "%s BAD Missing close parenthesis in Status\r\n", tag); -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ c = prot_getc(imapd_in); -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ prot_printf(imapd_out, -+ "%s BAD Unexpected extra arguments to Status\r\n", tag); -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ if (!r) { -+ r = mailbox_open_header(mailboxname, imapd_authstate, &mailbox); -+ } -+ -+ if (!r) { -+ doclose = 1; -+ r = mailbox_open_index(&mailbox); -+ } -+ if (!r && !(mailbox.myrights & ACL_READ)) { -+ r = (imapd_userisadmin || (mailbox.myrights & ACL_LOOKUP)) ? -+ IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT; -+ } -+ -+ if (!r) { -+ r = index_status(&mailbox, name, statusitems); -+ } -+ -+ if (doclose) mailbox_close(&mailbox); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ return; -+ } -+ -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ return; -+ -+ badlist: -+ prot_printf(imapd_out, "%s BAD Invalid status list in Status\r\n", tag); -+ eatline(imapd_in, c); -+} -+ -+#ifdef ENABLE_X_NETSCAPE_HACK -+/* -+ * Reply to Netscape's crock with a crock of my own -+ */ -+void cmd_netscrape(char *tag) -+{ -+ const char *url; -+ -+ url = config_getstring(IMAPOPT_NETSCAPEURL); -+ -+ /* I only know of three things to reply with: */ -+ prot_printf(imapd_out, -+ "* OK [NETSCAPE] Carnegie Mellon Cyrus IMAP\r\n" -+ "* VERSION %s\r\n", -+ CYRUS_VERSION); -+ if (url) prot_printf(imapd_out, "* ACCOUNT-URL %s\r\n", url); -+ prot_printf(imapd_out, "%s OK %s\r\n", -+ tag, error_message(IMAP_OK_COMPLETED)); -+} -+#endif /* ENABLE_X_NETSCAPE_HACK */ -+ -+/* Callback for cmd_namespace to be passed to mboxlist_findall. -+ * For each top-level mailbox found, print a bit of the response -+ * if it is a shared namespace. The rock is used as an integer in -+ * order to ensure the namespace response is correct on a server with -+ * no shared namespace. -+ */ -+static int namespacedata(char *name, -+ int matchlen __attribute__((unused)), -+ int maycreate __attribute__((unused)), -+ void *rock) -+{ -+ int* sawone = (int*) rock; -+ -+ if (!name) { -+ return 0; -+ } -+ -+ if ((!strncasecmp(name, "INBOX", 5) && (!name[5] || name[5] == '.'))) { -+ /* The user has a "personal" namespace. */ -+ sawone[NAMESPACE_INBOX] = 1; -+ } else if (mboxname_isusermailbox(name, 0)) { -+ /* The user can see the "other users" namespace. */ -+ sawone[NAMESPACE_USER] = 1; -+ } else { -+ /* The user can see the "shared" namespace. */ -+ sawone[NAMESPACE_SHARED] = 1; -+ } -+ -+ return 0; -+} -+ -+/* -+ * Print out a response to the NAMESPACE command defined by -+ * RFC 2342. -+ */ -+void cmd_namespace(tag) -+ char* tag; -+{ -+ int sawone[3] = {0, 0, 0}; -+ char* pattern; -+ -+ if (SLEEZY_NAMESPACE) { -+ char inboxname[MAX_MAILBOX_NAME+1]; -+ -+ if (strlen(imapd_userid) + 5 > MAX_MAILBOX_NAME) -+ sawone[NAMESPACE_INBOX] = 0; -+ else { -+ (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, "INBOX", -+ imapd_userid, inboxname); -+ sawone[NAMESPACE_INBOX] = -+ !mboxlist_lookup(inboxname, NULL, NULL); -+ } -+ sawone[NAMESPACE_USER] = 1; -+ sawone[NAMESPACE_SHARED] = 1; -+ } else { -+ pattern = xstrdup("%"); -+ /* now find all the exciting toplevel namespaces - -+ * we're using internal names here -+ */ -+ mboxlist_findall(NULL, pattern, imapd_userisadmin, imapd_userid, -+ imapd_authstate, namespacedata, (void*) sawone); -+ free(pattern); -+ } -+ -+ prot_printf(imapd_out, "* NAMESPACE"); -+ if (sawone[NAMESPACE_INBOX]) { -+ prot_printf(imapd_out, " ((\"%s\" \"%c\"))", -+ imapd_namespace.prefix[NAMESPACE_INBOX], -+ imapd_namespace.hier_sep); -+ } else { -+ prot_printf(imapd_out, " NIL"); -+ } -+ if (sawone[NAMESPACE_USER]) { -+ prot_printf(imapd_out, " ((\"%s\" \"%c\"))", -+ imapd_namespace.prefix[NAMESPACE_USER], -+ imapd_namespace.hier_sep); -+ } else { -+ prot_printf(imapd_out, " NIL"); -+ } -+ if (sawone[NAMESPACE_SHARED]) { -+ prot_printf(imapd_out, " ((\"%s\" \"%c\"))", -+ imapd_namespace.prefix[NAMESPACE_SHARED], -+ imapd_namespace.hier_sep); -+ } else { -+ prot_printf(imapd_out, " NIL"); -+ } -+ prot_printf(imapd_out, "\r\n"); -+ -+ imapd_check(NULL, 0, 0); -+ -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+} -+ -+/* -+ * Parse annotate fetch data. -+ * -+ * This is a generic routine which parses just the annotation data. -+ * Any surrounding command text must be parsed elsewhere, ie, -+ * GETANNOTATION, FETCH. -+ */ -+ -+int getannotatefetchdata(char *tag, -+ struct strlist **entries, struct strlist **attribs) -+{ -+ int c; -+ static struct buf arg; -+ -+ *entries = *attribs = NULL; -+ -+ c = prot_getc(imapd_in); -+ if (c == EOF) { -+ prot_printf(imapd_out, -+ "%s BAD Missing annotation entry\r\n", tag); -+ goto baddata; -+ } -+ else if (c == '(') { -+ /* entry list */ -+ do { -+ c = getqstring(imapd_in, imapd_out, &arg); -+ if (c == EOF) { -+ prot_printf(imapd_out, -+ "%s BAD Missing annotation entry\r\n", tag); -+ goto baddata; -+ } -+ -+ /* add the entry to the list */ -+ appendstrlist(entries, arg.s); -+ -+ } while (c == ' '); -+ -+ if (c != ')') { -+ prot_printf(imapd_out, -+ "%s BAD Missing close paren in annotation entry list \r\n", -+ tag); -+ goto baddata; -+ } -+ -+ c = prot_getc(imapd_in); -+ } -+ else { -+ /* single entry -- add it to the list */ -+ prot_ungetc(c, imapd_in); -+ c = getqstring(imapd_in, imapd_out, &arg); -+ if (c == EOF) { -+ prot_printf(imapd_out, -+ "%s BAD Missing annotation entry\r\n", tag); -+ goto baddata; -+ } -+ -+ appendstrlist(entries, arg.s); -+ } -+ -+ if (c != ' ' || (c = prot_getc(imapd_in)) == EOF) { -+ prot_printf(imapd_out, -+ "%s BAD Missing annotation attribute(s)\r\n", tag); -+ goto baddata; -+ } -+ -+ if (c == '(') { -+ /* attrib list */ -+ do { -+ c = getnstring(imapd_in, imapd_out, &arg); -+ if (c == EOF) { -+ prot_printf(imapd_out, -+ "%s BAD Missing annotation attribute(s)\r\n", tag); -+ goto baddata; -+ } -+ -+ /* add the attrib to the list */ -+ appendstrlist(attribs, arg.s); -+ -+ } while (c == ' '); -+ -+ if (c != ')') { -+ prot_printf(imapd_out, -+ "%s BAD Missing close paren in " -+ "annotation attribute list\r\n", tag); -+ goto baddata; -+ } -+ -+ c = prot_getc(imapd_in); -+ } -+ else { -+ /* single attrib */ -+ prot_ungetc(c, imapd_in); -+ c = getqstring(imapd_in, imapd_out, &arg); -+ if (c == EOF) { -+ prot_printf(imapd_out, -+ "%s BAD Missing annotation attribute\r\n", tag); -+ goto baddata; -+ } -+ -+ appendstrlist(attribs, arg.s); -+ } -+ -+ return c; -+ -+ baddata: -+ if (c != EOF) prot_ungetc(c, imapd_in); -+ return EOF; -+} -+ -+/* -+ * Parse annotate store data. -+ * -+ * This is a generic routine which parses just the annotation data. -+ * Any surrounding command text must be parsed elsewhere, ie, -+ * SETANNOTATION, STORE, APPEND. -+ */ -+ -+int getannotatestoredata(char *tag, struct entryattlist **entryatts) -+{ -+ int c, islist = 0; -+ static struct buf entry, attrib, value; -+ struct attvaluelist *attvalues = NULL; -+ -+ *entryatts = NULL; -+ -+ c = prot_getc(imapd_in); -+ if (c == EOF) { -+ prot_printf(imapd_out, -+ "%s BAD Missing annotation entry\r\n", tag); -+ goto baddata; -+ } -+ else if (c == '(') { -+ /* entry list */ -+ islist = 1; -+ } -+ else { -+ /* single entry -- put the char back */ -+ prot_ungetc(c, imapd_in); -+ } -+ -+ do { -+ /* get entry */ -+ c = getqstring(imapd_in, imapd_out, &entry); -+ if (c == EOF) { -+ prot_printf(imapd_out, -+ "%s BAD Missing annotation entry\r\n", tag); -+ goto baddata; -+ } -+ -+ /* parse att-value list */ -+ if (c != ' ' || (c = prot_getc(imapd_in)) != '(') { -+ prot_printf(imapd_out, -+ "%s BAD Missing annotation attribute-values list\r\n", -+ tag); -+ goto baddata; -+ } -+ -+ do { -+ /* get attrib */ -+ c = getqstring(imapd_in, imapd_out, &attrib); -+ if (c == EOF) { -+ prot_printf(imapd_out, -+ "%s BAD Missing annotation attribute\r\n", tag); -+ goto baddata; -+ } -+ -+ /* get value */ -+ if (c != ' ' || -+ (c = getnstring(imapd_in, imapd_out, &value)) == EOF) { -+ prot_printf(imapd_out, -+ "%s BAD Missing annotation value\r\n", tag); -+ goto baddata; -+ } -+ -+ /* add the attrib-value pair to the list */ -+ appendattvalue(&attvalues, attrib.s, value.s); -+ -+ } while (c == ' '); -+ -+ if (c != ')') { -+ prot_printf(imapd_out, -+ "%s BAD Missing close paren in annotation " -+ "attribute-values list\r\n", tag); -+ goto baddata; -+ } -+ -+ /* add the entry to the list */ -+ appendentryatt(entryatts, entry.s, attvalues); -+ attvalues = NULL; -+ -+ c = prot_getc(imapd_in); -+ -+ } while (c == ' '); -+ -+ if (islist) { -+ if (c != ')') { -+ prot_printf(imapd_out, -+ "%s BAD Missing close paren in annotation entry list \r\n", -+ tag); -+ goto baddata; -+ } -+ -+ c = prot_getc(imapd_in); -+ } -+ -+ return c; -+ -+ baddata: -+ if (attvalues) freeattvalues(attvalues); -+ if (c != EOF) prot_ungetc(c, imapd_in); -+ return EOF; -+} -+ -+/* -+ * Output an entry/attribute-value list response. -+ * -+ * This is a generic routine which outputs just the annotation data. -+ * Any surrounding response text must be output elsewhere, ie, -+ * GETANNOTATION, FETCH. -+ */ -+void annotate_response(struct entryattlist *l) -+{ -+ int islist; /* do we have more than one entry? */ -+ -+ if (!l) return; -+ -+ islist = (l->next != NULL); -+ -+ if (islist) prot_printf(imapd_out, "("); -+ -+ while (l) { -+ prot_printf(imapd_out, "\"%s\"", l->entry); -+ -+ /* do we have attributes? solicited vs. unsolicited */ -+ if (l->attvalues) { -+ struct attvaluelist *av = l->attvalues; -+ -+ prot_printf(imapd_out, " ("); -+ while (av) { -+ prot_printf(imapd_out, "\"%s\" ", av->attrib); -+ if (!strcasecmp(av->value, "NIL")) -+ prot_printf(imapd_out, "NIL"); -+ else -+ prot_printf(imapd_out, "\"%s\"", av->value); -+ -+ if ((av = av->next) == NULL) -+ prot_printf(imapd_out, ")"); -+ else -+ prot_printf(imapd_out, " "); -+ } -+ } -+ -+ if ((l = l->next) != NULL) -+ prot_printf(imapd_out, " "); -+ } -+ -+ if (islist) prot_printf(imapd_out, ")"); -+} -+ -+/* -+ * Perform a GETANNOTATION command -+ * -+ * The command has been parsed up to the entries -+ */ -+void cmd_getannotation(char *tag, char *mboxpat) -+{ -+ int c, r = 0; -+ struct strlist *entries = NULL, *attribs = NULL; -+ -+ c = getannotatefetchdata(tag, &entries, &attribs); -+ if (c == EOF) { -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ /* check for CRLF */ -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ prot_printf(imapd_out, -+ "%s BAD Unexpected extra arguments to Getannotation\r\n", -+ tag); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ -+ r = annotatemore_fetch(mboxpat, entries, attribs, &imapd_namespace, -+ imapd_userisadmin || imapd_userisproxyadmin, -+ imapd_userid, imapd_authstate, imapd_out); -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } else { -+ prot_printf(imapd_out, "%s OK %s\r\n", -+ tag, error_message(IMAP_OK_COMPLETED)); -+ } -+ -+ freeargs: -+ if (entries) freestrlist(entries); -+ if (attribs) freestrlist(attribs); -+ -+ return; -+} -+ -+/* -+ * Perform a SETANNOTATION command -+ * -+ * The command has been parsed up to the entry-att list -+ */ -+void cmd_setannotation(char *tag, char *mboxpat) -+{ -+ int c, r = 0; -+ struct entryattlist *entryatts = NULL; -+ -+ c = getannotatestoredata(tag, &entryatts); -+ if (c == EOF) { -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ /* check for CRLF */ -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ prot_printf(imapd_out, -+ "%s BAD Unexpected extra arguments to Setannotation\r\n", -+ tag); -+ eatline(imapd_in, c); -+ goto freeargs; -+ } -+ -+ r = annotatemore_store(mboxpat, -+ entryatts, &imapd_namespace, imapd_userisadmin, -+ imapd_userid, imapd_authstate); -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ } -+ -+ freeargs: -+ if (entryatts) freeentryatts(entryatts); -+ return; -+} -+ -+/* -+ * Parse a search program -+ */ -+int getsearchprogram(tag, searchargs, charset, parsecharset) -+char *tag; -+struct searchargs *searchargs; -+int *charset; -+int parsecharset; -+{ -+ int c; -+ -+ do { -+ c = getsearchcriteria(tag, searchargs, charset, parsecharset); -+ parsecharset = 0; -+ } while (c == ' '); -+ return c; -+} -+ -+/* -+ * Parse a search criteria -+ */ -+int getsearchcriteria(tag, searchargs, charset, parsecharset) -+char *tag; -+struct searchargs *searchargs; -+int *charset; -+int parsecharset; -+{ -+ static struct buf criteria, arg; -+ struct searchargs *sub1, *sub2; -+ char *p, *str; -+ int c, flag; -+ unsigned size; -+ time_t start, end; -+ -+ c = getword(imapd_in, &criteria); -+ lcase(criteria.s); -+ switch (criteria.s[0]) { -+ case '\0': -+ if (c != '(') goto badcri; -+ c = getsearchprogram(tag, searchargs, charset, 0); -+ if (c == EOF) return EOF; -+ if (c != ')') { -+ prot_printf(imapd_out, "%s BAD Missing required close paren in Search command\r\n", -+ tag); -+ if (c != EOF) prot_ungetc(c, imapd_in); -+ return EOF; -+ } -+ c = prot_getc(imapd_in); -+ break; -+ -+ case '0': case '1': case '2': case '3': case '4': -+ case '5': case '6': case '7': case '8': case '9': -+ case '*': -+ if (imparse_issequence(criteria.s)) { -+ appendstrlist(&searchargs->sequence, criteria.s); -+ } -+ else goto badcri; -+ break; -+ -+ case 'a': -+ if (!strcmp(criteria.s, "answered")) { -+ searchargs->system_flags_set |= FLAG_ANSWERED; -+ } -+ else if (!strcmp(criteria.s, "all")) { -+ break; -+ } -+ else goto badcri; -+ break; -+ -+ case 'b': -+ if (!strcmp(criteria.s, "before")) { -+ if (c != ' ') goto missingarg; -+ c = getsearchdate(&start, &end); -+ if (c == EOF) goto baddate; -+ if (!searchargs->before || searchargs->before > start) { -+ searchargs->before = start; -+ } -+ } -+ else if (!strcmp(criteria.s, "bcc")) { -+ if (c != ' ') goto missingarg; -+ c = getastring(imapd_in, imapd_out, &arg); -+ if (c == EOF) goto missingarg; -+ str = charset_convert(arg.s, *charset, NULL, 0); -+ if (strchr(str, EMPTY)) { -+ /* Force failure */ -+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET); -+ } -+ else { -+ appendstrlistpat(&searchargs->bcc, str); -+ } -+ } -+ else if (!strcmp(criteria.s, "body")) { -+ if (c != ' ') goto missingarg; -+ c = getastring(imapd_in, imapd_out, &arg); -+ if (c == EOF) goto missingarg; -+ str = charset_convert(arg.s, *charset, NULL, 0); -+ if (strchr(str, EMPTY)) { -+ /* Force failure */ -+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET); -+ } -+ else { -+ appendstrlistpat(&searchargs->body, str); -+ } -+ } -+ else goto badcri; -+ break; -+ -+ case 'c': -+ if (!strcmp(criteria.s, "cc")) { -+ if (c != ' ') goto missingarg; -+ c = getastring(imapd_in, imapd_out, &arg); -+ if (c == EOF) goto missingarg; -+ str = charset_convert(arg.s, *charset, NULL, 0); -+ if (strchr(str, EMPTY)) { -+ /* Force failure */ -+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET); -+ } -+ else { -+ appendstrlistpat(&searchargs->cc, str); -+ } -+ } -+ else if (parsecharset && !strcmp(criteria.s, "charset")) { -+ if (c != ' ') goto missingarg; -+ c = getastring(imapd_in, imapd_out, &arg); -+ if (c != ' ') goto missingarg; -+ lcase(arg.s); -+ *charset = charset_lookupname(arg.s); -+ } -+ else goto badcri; -+ break; -+ -+ case 'd': -+ if (!strcmp(criteria.s, "deleted")) { -+ searchargs->system_flags_set |= FLAG_DELETED; -+ } -+ else if (!strcmp(criteria.s, "draft")) { -+ searchargs->system_flags_set |= FLAG_DRAFT; -+ } -+ else goto badcri; -+ break; -+ -+ case 'f': -+ if (!strcmp(criteria.s, "flagged")) { -+ searchargs->system_flags_set |= FLAG_FLAGGED; -+ } -+ else if (!strcmp(criteria.s, "from")) { -+ if (c != ' ') goto missingarg; -+ c = getastring(imapd_in, imapd_out, &arg); -+ if (c == EOF) goto missingarg; -+ str = charset_convert(arg.s, *charset, NULL, 0); -+ if (strchr(str, EMPTY)) { -+ /* Force failure */ -+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET); -+ } -+ else { -+ appendstrlistpat(&searchargs->from, str); -+ } -+ } -+ else goto badcri; -+ break; -+ -+ case 'h': -+ if (!strcmp(criteria.s, "header")) { -+ struct strlist **patlist; -+ -+ if (c != ' ') goto missingarg; -+ c = getastring(imapd_in, imapd_out, &arg); -+ if (c != ' ') goto missingarg; -+ lcase(arg.s); -+ -+ /* some headers can be reduced to search terms */ -+ if (!strcmp(arg.s, "bcc")) { -+ patlist = &searchargs->bcc; -+ } -+ else if (!strcmp(arg.s, "cc")) { -+ patlist = &searchargs->cc; -+ } -+ else if (!strcmp(arg.s, "to")) { -+ patlist = &searchargs->to; -+ } -+ else if (!strcmp(arg.s, "from")) { -+ patlist = &searchargs->from; -+ } -+ else if (!strcmp(arg.s, "subject")) { -+ patlist = &searchargs->subject; -+ } -+ -+ /* we look message-id up in the envelope */ -+ else if (!strcmp(arg.s, "message-id")) { -+ patlist = &searchargs->messageid; -+ } -+ -+ /* all other headers we handle normally */ -+ else { -+ if (searchargs->cache_atleast < BIT32_MAX) { -+ bit32 this_ver = -+ mailbox_cached_header(arg.s); -+ if(this_ver > searchargs->cache_atleast) -+ searchargs->cache_atleast = this_ver; -+ } -+ appendstrlist(&searchargs->header_name, arg.s); -+ patlist = &searchargs->header; -+ } -+ -+ c = getastring(imapd_in, imapd_out, &arg); -+ if (c == EOF) goto missingarg; -+ str = charset_convert(arg.s, *charset, NULL, 0); -+ if (strchr(str, EMPTY)) { -+ /* Force failure */ -+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET); -+ } -+ else { -+ appendstrlistpat(patlist, str); -+ } -+ } -+ else goto badcri; -+ break; -+ -+ case 'k': -+ if (!strcmp(criteria.s, "keyword")) { -+ if (c != ' ') goto missingarg; -+ c = getword(imapd_in, &arg); -+ if (!imparse_isatom(arg.s)) goto badflag; -+ lcase(arg.s); -+ for (flag=0; flag < MAX_USER_FLAGS; flag++) { -+ if (imapd_mailbox->flagname[flag] && -+ !strcasecmp(imapd_mailbox->flagname[flag], arg.s)) break; -+ } -+ if (flag == MAX_USER_FLAGS) { -+ /* Force failure */ -+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET); -+ break; -+ } -+ searchargs->user_flags_set[flag/32] |= 1<<(flag&31); -+ } -+ else goto badcri; -+ break; -+ -+ case 'l': -+ if (!strcmp(criteria.s, "larger")) { -+ if (c != ' ') goto missingarg; -+ c = getword(imapd_in, &arg); -+ size = 0; -+ for (p = arg.s; *p && isdigit((int) *p); p++) { -+ size = size * 10 + *p - '0'; -+ /* if (size < 0) goto badnumber; */ -+ } -+ if (!arg.s || *p) goto badnumber; -+ if (size > searchargs->larger) searchargs->larger = size; -+ } -+ else goto badcri; -+ break; -+ -+ case 'n': -+ if (!strcmp(criteria.s, "not")) { -+ if (c != ' ') goto missingarg; -+ sub1 = (struct searchargs *)xzmalloc(sizeof(struct searchargs)); -+ c = getsearchcriteria(tag, sub1, charset, 0); -+ if (c == EOF) { -+ freesearchargs(sub1); -+ return EOF; -+ } -+ -+ appendsearchargs(searchargs, sub1, (struct searchargs *)0); -+ } -+ else if (!strcmp(criteria.s, "new")) { -+ searchargs->flags |= (SEARCH_SEEN_UNSET|SEARCH_RECENT_SET); -+ } -+ else goto badcri; -+ break; -+ -+ case 'o': -+ if (!strcmp(criteria.s, "or")) { -+ if (c != ' ') goto missingarg; -+ sub1 = (struct searchargs *)xzmalloc(sizeof(struct searchargs)); -+ c = getsearchcriteria(tag, sub1, charset, 0); -+ if (c == EOF) { -+ freesearchargs(sub1); -+ return EOF; -+ } -+ if (c != ' ') goto missingarg; -+ sub2 = (struct searchargs *)xzmalloc(sizeof(struct searchargs)); -+ c = getsearchcriteria(tag, sub2, charset, 0); -+ if (c == EOF) { -+ freesearchargs(sub1); -+ freesearchargs(sub2); -+ return EOF; -+ } -+ appendsearchargs(searchargs, sub1, sub2); -+ } -+ else if (!strcmp(criteria.s, "old")) { -+ searchargs->flags |= SEARCH_RECENT_UNSET; -+ } -+ else if (!strcmp(criteria.s, "on")) { -+ if (c != ' ') goto missingarg; -+ c = getsearchdate(&start, &end); -+ if (c == EOF) goto baddate; -+ if (!searchargs->before || searchargs->before > end) { -+ searchargs->before = end; -+ } -+ if (!searchargs->after || searchargs->after < start) { -+ searchargs->after = start; -+ } -+ } -+ else goto badcri; -+ break; -+ -+ case 'r': -+ if (!strcmp(criteria.s, "recent")) { -+ searchargs->flags |= SEARCH_RECENT_SET; -+ } -+ else goto badcri; -+ break; -+ -+ case 's': -+ if (!strcmp(criteria.s, "seen")) { -+ searchargs->flags |= SEARCH_SEEN_SET; -+ } -+ else if (!strcmp(criteria.s, "sentbefore")) { -+ if (c != ' ') goto missingarg; -+ c = getsearchdate(&start, &end); -+ if (c == EOF) goto baddate; -+ if (!searchargs->sentbefore || searchargs->sentbefore > start) { -+ searchargs->sentbefore = start; -+ } -+ } -+ else if (!strcmp(criteria.s, "senton")) { -+ if (c != ' ') goto missingarg; -+ c = getsearchdate(&start, &end); -+ if (c == EOF) goto baddate; -+ if (!searchargs->sentbefore || searchargs->sentbefore > end) { -+ searchargs->sentbefore = end; -+ } -+ if (!searchargs->sentafter || searchargs->sentafter < start) { -+ searchargs->sentafter = start; -+ } -+ } -+ else if (!strcmp(criteria.s, "sentsince")) { -+ if (c != ' ') goto missingarg; -+ c = getsearchdate(&start, &end); -+ if (c == EOF) goto baddate; -+ if (!searchargs->sentafter || searchargs->sentafter < start) { -+ searchargs->sentafter = start; -+ } -+ } -+ else if (!strcmp(criteria.s, "since")) { -+ if (c != ' ') goto missingarg; -+ c = getsearchdate(&start, &end); -+ if (c == EOF) goto baddate; -+ if (!searchargs->after || searchargs->after < start) { -+ searchargs->after = start; -+ } -+ } -+ else if (!strcmp(criteria.s, "smaller")) { -+ if (c != ' ') goto missingarg; -+ c = getword(imapd_in, &arg); -+ size = 0; -+ for (p = arg.s; *p && isdigit((int) *p); p++) { -+ size = size * 10 + *p - '0'; -+ /* if (size < 0) goto badnumber; */ -+ } -+ if (!arg.s || *p) goto badnumber; -+ if (size == 0) size = 1; -+ if (!searchargs->smaller || size < searchargs->smaller) -+ searchargs->smaller = size; -+ } -+ else if (!strcmp(criteria.s, "subject")) { -+ if (c != ' ') goto missingarg; -+ c = getastring(imapd_in, imapd_out, &arg); -+ if (c == EOF) goto missingarg; -+ str = charset_convert(arg.s, *charset, NULL, 0); -+ if (strchr(str, EMPTY)) { -+ /* Force failure */ -+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET); -+ } -+ else { -+ appendstrlistpat(&searchargs->subject, str); -+ } -+ } -+ else goto badcri; -+ break; -+ -+ case 't': -+ if (!strcmp(criteria.s, "to")) { -+ if (c != ' ') goto missingarg; -+ c = getastring(imapd_in, imapd_out, &arg); -+ if (c == EOF) goto missingarg; -+ str = charset_convert(arg.s, *charset, NULL, 0); -+ if (strchr(str, EMPTY)) { -+ /* Force failure */ -+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET); -+ } -+ else { -+ appendstrlistpat(&searchargs->to, str); -+ } -+ } -+ else if (!strcmp(criteria.s, "text")) { -+ if (c != ' ') goto missingarg; -+ c = getastring(imapd_in, imapd_out, &arg); -+ if (c == EOF) goto missingarg; -+ str = charset_convert(arg.s, *charset, NULL, 0); -+ if (strchr(str, EMPTY)) { -+ /* Force failure */ -+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET); -+ } -+ else { -+ appendstrlistpat(&searchargs->text, str); -+ } -+ } -+ else goto badcri; -+ break; -+ -+ case 'u': -+ if (!strcmp(criteria.s, "uid")) { -+ if (c != ' ') goto missingarg; -+ c = getword(imapd_in, &arg); -+ if (!imparse_issequence(arg.s)) goto badcri; -+ appendstrlist(&searchargs->uidsequence, arg.s); -+ } -+ else if (!strcmp(criteria.s, "unseen")) { -+ searchargs->flags |= SEARCH_SEEN_UNSET; -+ } -+ else if (!strcmp(criteria.s, "unanswered")) { -+ searchargs->system_flags_unset |= FLAG_ANSWERED; -+ } -+ else if (!strcmp(criteria.s, "undeleted")) { -+ searchargs->system_flags_unset |= FLAG_DELETED; -+ } -+ else if (!strcmp(criteria.s, "undraft")) { -+ searchargs->system_flags_unset |= FLAG_DRAFT; -+ } -+ else if (!strcmp(criteria.s, "unflagged")) { -+ searchargs->system_flags_unset |= FLAG_FLAGGED; -+ } -+ else if (!strcmp(criteria.s, "unkeyword")) { -+ if (c != ' ') goto missingarg; -+ c = getword(imapd_in, &arg); -+ if (!imparse_isatom(arg.s)) goto badflag; -+ lcase(arg.s); -+ for (flag=0; flag < MAX_USER_FLAGS; flag++) { -+ if (imapd_mailbox->flagname[flag] && -+ !strcasecmp(imapd_mailbox->flagname[flag], arg.s)) break; -+ } -+ if (flag != MAX_USER_FLAGS) { -+ searchargs->user_flags_unset[flag/32] |= 1<<(flag&31); -+ } -+ } -+ else goto badcri; -+ break; -+ -+ default: -+ badcri: -+ prot_printf(imapd_out, "%s BAD Invalid Search criteria\r\n", tag); -+ if (c != EOF) prot_ungetc(c, imapd_in); -+ return EOF; -+ } -+ -+ return c; -+ -+ missingarg: -+ prot_printf(imapd_out, "%s BAD Missing required argument to Search %s\r\n", -+ tag, criteria.s); -+ if (c != EOF) prot_ungetc(c, imapd_in); -+ return EOF; -+ -+ badflag: -+ prot_printf(imapd_out, "%s BAD Invalid flag name %s in Search command\r\n", -+ tag, arg.s); -+ if (c != EOF) prot_ungetc(c, imapd_in); -+ return EOF; -+ -+ baddate: -+ prot_printf(imapd_out, "%s BAD Invalid date in Search command\r\n", tag); -+ if (c != EOF) prot_ungetc(c, imapd_in); -+ return EOF; -+ -+ badnumber: -+ prot_printf(imapd_out, "%s BAD Invalid number in Search command\r\n", tag); -+ if (c != EOF) prot_ungetc(c, imapd_in); -+ return EOF; -+} -+ -+void cmd_dump(char *tag, char *name, int uid_start) -+{ -+ int r = 0; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ char *path, *mpath, *acl; -+ -+ /* administrators only please */ -+ if (!imapd_userisadmin) { -+ r = IMAP_PERMISSION_DENIED; -+ } -+ -+ if (!r) { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ } -+ -+ if (!r) { -+ r = mlookup(tag, name, mailboxname, NULL, &path, &mpath, -+ NULL, &acl, NULL); -+ } -+ if (r == IMAP_MAILBOX_MOVED) return; -+ -+ if(!r) { -+ r = dump_mailbox(tag, mailboxname, path, mpath, acl, uid_start, -+ imapd_in, imapd_out, imapd_authstate); -+ } -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ } -+} -+ -+void cmd_undump(char *tag, char *name) -+{ -+ int r = 0; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ char *path, *mpath, *acl; -+ -+ /* administrators only please */ -+ if (!imapd_userisadmin) { -+ r = IMAP_PERMISSION_DENIED; -+ } -+ -+ if (!r) { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ } -+ -+ if (!r) { -+ r = mlookup(tag, name, mailboxname, NULL, &path, &mpath, -+ NULL, &acl, NULL); -+ } -+ if (r == IMAP_MAILBOX_MOVED) return; -+ -+ if(!r) { -+ /* save this stuff from additional mlookups */ -+ char *safe_path = xstrdup(path); -+ char *safe_mpath = mpath ? xstrdup(mpath) : NULL; -+ char *safe_acl = xstrdup(acl); -+ r = undump_mailbox(mailboxname, safe_path, safe_mpath, safe_acl, -+ imapd_in, imapd_out, imapd_authstate); -+ free(safe_path); -+ if (safe_mpath) free(safe_mpath); -+ free(safe_acl); -+ } -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s%s\r\n", -+ tag, -+ (r == IMAP_MAILBOX_NONEXISTENT && -+ mboxlist_createmailboxcheck(mailboxname, 0, 0, -+ imapd_userisadmin, -+ imapd_userid, imapd_authstate, -+ NULL, NULL) == 0) -+ ? "[TRYCREATE] " : "", error_message(r)); -+ } else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ } -+} -+ -+static int getresult(struct protstream *p, char *tag) -+{ -+ char buf[4096]; -+ char *str = (char *) buf; -+ -+ while(1) { -+ if (!prot_fgets(str, sizeof(buf), p)) { -+ return IMAP_SERVER_UNAVAILABLE; -+ } -+ if (!strncmp(str, tag, strlen(tag))) { -+ str += strlen(tag); -+ if(!*str) { -+ /* We got a tag, but no response */ -+ return IMAP_SERVER_UNAVAILABLE; -+ } -+ str++; -+ if (!strncasecmp(str, "OK ", 3)) { return 0; } -+ if (!strncasecmp(str, "NO ", 3)) { return IMAP_REMOTE_DENIED; } -+ return IMAP_SERVER_UNAVAILABLE; /* huh? */ -+ } -+ /* skip this line, we don't really care */ -+ } -+} -+ -+/* given 2 protstreams and a mailbox, gets the acl and then wipes it */ -+static int trashacl(struct protstream *pin, struct protstream *pout, -+ char *mailbox) -+{ -+ int i=0, j=0; -+ char tagbuf[128]; -+ int c; /* getword() returns an int */ -+ struct buf tag, cmd, tmp, user; -+ int r = 0; -+ -+ memset(&tag, 0, sizeof(struct buf)); -+ memset(&cmd, 0, sizeof(struct buf)); -+ memset(&tmp, 0, sizeof(struct buf)); -+ memset(&user, 0, sizeof(struct buf)); -+ -+ prot_printf(pout, "ACL0 GETACL {%d+}\r\n%s\r\n", -+ strlen(mailbox), mailbox); -+ -+ while(1) { -+ c = getword(pin, &tag); -+ if (c == EOF) { -+ r = IMAP_SERVER_UNAVAILABLE; -+ break; -+ } -+ -+ c = getword(pin, &cmd); -+ if (c == EOF) { -+ r = IMAP_SERVER_UNAVAILABLE; -+ break; -+ } -+ -+ if(c == '\r') { -+ c = prot_getc(pin); -+ if(c != '\n') { -+ r = IMAP_SERVER_UNAVAILABLE; -+ goto cleanup; -+ } -+ } -+ if(c == '\n') goto cleanup; -+ -+ if (tag.s[0] == '*' && !strncmp(cmd.s, "ACL", 3)) { -+ while(c != '\n') { -+ /* An ACL response, we should send a DELETEACL command */ -+ c = getastring(pin, pout, &tmp); -+ if (c == EOF) { -+ r = IMAP_SERVER_UNAVAILABLE; -+ goto cleanup; -+ } -+ -+ if(c == '\r') { -+ c = prot_getc(pin); -+ if(c != '\n') { -+ r = IMAP_SERVER_UNAVAILABLE; -+ goto cleanup; -+ } -+ } -+ if(c == '\n') goto cleanup; -+ -+ c = getastring(pin, pout, &user); -+ if (c == EOF) { -+ r = IMAP_SERVER_UNAVAILABLE; -+ goto cleanup; -+ } -+ -+ snprintf(tagbuf, sizeof(tagbuf), "ACL%d", ++i); -+ -+ prot_printf(pout, "%s DELETEACL {%d+}\r\n%s {%d+}\r\n%s\r\n", -+ tagbuf, strlen(mailbox), mailbox, -+ strlen(user.s), user.s); -+ if(c == '\r') { -+ c = prot_getc(pin); -+ if(c != '\n') { -+ r = IMAP_SERVER_UNAVAILABLE; -+ goto cleanup; -+ } -+ } -+ /* if the next character is \n, we'll exit the loop */ -+ } -+ continue; -+ } else if (!strncmp(tag.s, "ACL0", 4)) { -+ /* end of this command */ -+ if (!strcasecmp(cmd.s, "OK")) { break; } -+ if (!strcasecmp(cmd.s, "NO")) { r = IMAP_REMOTE_DENIED; break; } -+ r = IMAP_SERVER_UNAVAILABLE; -+ break; -+ } -+ } -+ -+ cleanup: -+ -+ /* Now cleanup after all the DELETEACL commands */ -+ if(!r) { -+ while(j < i) { -+ c = getword(pin, &tag); -+ if (c == EOF) { -+ r = IMAP_SERVER_UNAVAILABLE; -+ break; -+ } -+ -+ eatline(pin, c); -+ -+ if(!strncmp("ACL", tag.s, 3)) { -+ j++; -+ } -+ } -+ } -+ -+ if(r) eatline(pin, c); -+ -+ freebuf(&user); -+ freebuf(&tmp); -+ freebuf(&cmd); -+ freebuf(&tag); -+ -+ return r; -+} -+ -+static int dumpacl(struct protstream *pin, struct protstream *pout, -+ char *mailbox, char *acl_in) -+{ -+ int r = 0; -+ int c; /* getword() returns an int */ -+ char tag[128]; -+ int tagnum = 1; -+ char *rights, *nextid; -+ int mailboxlen = strlen(mailbox); -+ char *acl_safe = acl_in ? xstrdup(acl_in) : NULL; -+ char *acl = acl_safe; -+ struct buf inbuf; -+ -+ memset(&inbuf, 0, sizeof(struct buf)); -+ -+ while (acl) { -+ rights = strchr(acl, '\t'); -+ if (!rights) break; -+ *rights++ = '\0'; -+ -+ nextid = strchr(rights, '\t'); -+ if (!nextid) break; -+ *nextid++ = '\0'; -+ -+ snprintf(tag, sizeof(tag), "SACL%d", tagnum++); -+ -+ prot_printf(pout, "%s SETACL {%d+}\r\n%s {%d+}\r\n%s {%d+}\r\n%s\r\n", -+ tag, -+ mailboxlen, mailbox, -+ strlen(acl), acl, -+ strlen(rights), rights); -+ -+ while(1) { -+ c = getword(pin, &inbuf); -+ if (c == EOF) { -+ r = IMAP_SERVER_UNAVAILABLE; -+ break; -+ } -+ if(strncmp(tag, inbuf.s, strlen(tag))) { -+ eatline(pin, c); -+ continue; -+ } else { -+ /* this is our line */ -+ break; -+ } -+ } -+ -+ /* Are we OK? */ -+ -+ c = getword(pin, &inbuf); -+ if (c == EOF) { -+ r = IMAP_SERVER_UNAVAILABLE; -+ break; -+ } -+ -+ if(strncmp("OK", inbuf.s, 2)) { -+ r = IMAP_REMOTE_DENIED; -+ break; -+ } -+ -+ /* Eat the line and get the next one */ -+ eatline(pin, c); -+ acl = nextid; -+ } -+ -+ freebuf(&inbuf); -+ if(acl_safe) free(acl_safe); -+ -+ return r; -+} -+ -+static int do_xfer_single(char *toserver, char *topart, -+ char *name, char *mailboxname, -+ int mbflags, -+ char *path, char *mpath, char *part, char *acl, -+ int prereserved, -+ mupdate_handle *h_in, -+ struct backend *be_in) -+{ -+ int r = 0, rerr = 0; -+ char buf[MAX_PARTITION_LEN+HOSTNAME_SIZE+2]; -+ struct backend *be = NULL; -+ mupdate_handle *mupdate_h = NULL; -+ int backout_mupdate = 0; -+ int backout_remotebox = 0; -+ int backout_remoteflag = 0; -+ -+ /* Make sure we're given a sane value */ -+ if(topart && !imparse_isatom(topart)) { -+ return IMAP_PARTITION_UNKNOWN; -+ } -+ -+ if(!strcmp(toserver, config_servername)) { -+ return IMAP_BAD_SERVER; -+ } -+ -+ /* Okay, we have the mailbox, now the order of steps is: -+ * -+ * 1) Connect to remote server. -+ * 2) LOCALCREATE on remote server -+ * 2.5) Set mailbox as REMOTE on local server -+ * 3) mupdate.DEACTIVATE(mailbox, remoteserver) xxx what partition? -+ * 4) undump mailbox from local to remote -+ * 5) Sync remote acl -+ * 6) mupdate.ACTIVATE(mailbox, remoteserver) -+ * ** MAILBOX NOW LIVING ON REMOTE SERVER -+ * 6.5) force remote server to push the final mupdate entry to ensure -+ * that the state of the world is correct (required if we do not -+ * know the remote partition, but worst case it will be caught -+ * when they next sync) -+ * 7) local delete of mailbox -+ * 8) remove local remote mailbox entry?????? -+ */ -+ -+ /* Step 1: Connect to remote server */ -+ if(!r && !be_in) { -+ /* Just authorize as the IMAP server, so pass "" as our authzid */ -+ be = backend_connect(NULL, toserver, &protocol[PROTOCOL_IMAP], -+ "", NULL, NULL); -+ if(!be) r = IMAP_SERVER_UNAVAILABLE; -+ if(r) syslog(LOG_ERR, -+ "Could not move mailbox: %s, Backend connect failed", -+ mailboxname); -+ } else if(!r) { -+ be = be_in; -+ } -+ -+ /* Step 1a: Connect to mupdate (as needed) */ -+ if(h_in) { -+ mupdate_h = h_in; -+ } else if (config_mupdate_server) { -+ r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL); -+ if(r) { -+ syslog(LOG_ERR, -+ "Could not move mailbox: %s, MUPDATE connect failed", -+ mailboxname); -+ goto done; -+ } -+ -+ } -+ -+ /* Step 2: LOCALCREATE on remote server */ -+ if(!r) { -+ if(topart) { -+ /* need to send partition as an atom */ -+ prot_printf(be->out, "LC1 LOCALCREATE {%d+}\r\n%s %s\r\n", -+ strlen(name), name, topart); -+ } else { -+ prot_printf(be->out, "LC1 LOCALCREATE {%d+}\r\n%s\r\n", -+ strlen(name), name); -+ } -+ r = getresult(be->in, "LC1"); -+ if(r) syslog(LOG_ERR, "Could not move mailbox: %s, LOCALCREATE failed", -+ mailboxname); -+ else backout_remotebox = 1; -+ } -+ -+ /* Step 2.5: Set mailbox as REMOTE on local server */ -+ if(!r) { -+ snprintf(buf, sizeof(buf), "%s!%s", toserver, part); -+ r = mboxlist_update(mailboxname, mbflags|MBTYPE_MOVING, buf, acl, 1); -+ if(r) syslog(LOG_ERR, "Could not move mailbox: %s, " \ -+ "mboxlist_update failed", mailboxname); -+ } -+ -+ /* Step 3: mupdate.DEACTIVATE(mailbox, newserver) */ -+ /* (only if mailbox has not been already deactivated by our caller) */ -+ if(!r && mupdate_h && !prereserved) { -+ backout_remoteflag = 1; -+ -+ /* Note we are making the reservation on OUR host so that recovery -+ * make sense */ -+ snprintf(buf, sizeof(buf), "%s!%s", config_servername, part); -+ r = mupdate_deactivate(mupdate_h, mailboxname, buf); -+ if(r) syslog(LOG_ERR, -+ "Could not move mailbox: %s, MUPDATE DEACTIVATE failed", -+ mailboxname); -+ } -+ -+ /* Step 4: Dump local -> remote */ -+ if(!r) { -+ backout_mupdate = 1; -+ -+ prot_printf(be->out, "D01 UNDUMP {%d+}\r\n%s ", strlen(name), name); -+ -+ r = dump_mailbox(NULL, mailboxname, path, mpath, acl, -+ 0, be->in, be->out, imapd_authstate); -+ -+ if(r) -+ syslog(LOG_ERR, -+ "Could not move mailbox: %s, dump_mailbox() failed", -+ mailboxname); -+ } -+ -+ if(!r) { -+ r = getresult(be->in, "D01"); -+ if(r) syslog(LOG_ERR, "Could not move mailbox: %s, UNDUMP failed", -+ mailboxname); -+ } -+ -+ /* Step 5: Set ACL on remote */ -+ if(!r) { -+ r = trashacl(be->in, be->out, name); -+ if(r) syslog(LOG_ERR, "Could not clear remote acl on %s", -+ mailboxname); -+ } -+ if(!r) { -+ r = dumpacl(be->in, be->out, name, acl); -+ if(r) syslog(LOG_ERR, "Could not set remote acl on %s", -+ mailboxname); -+ } -+ -+ /* Step 6: mupdate.activate(mailbox, remote) */ -+ /* We do this from the local server first so that recovery is easier */ -+ if(!r && mupdate_h) { -+ /* Note the flag that we don't have a valid partiton at the moment */ -+ snprintf(buf, sizeof(buf), "%s!MOVED", toserver); -+ r = mupdate_activate(mupdate_h, mailboxname, buf, acl); -+ } -+ -+ /* MAILBOX NOW LIVES ON REMOTE */ -+ if(!r) { -+ backout_remotebox = 0; -+ backout_mupdate = 0; -+ backout_remoteflag = 0; -+ -+ /* 6.5) Kick remote server to correct mupdate entry */ -+ /* Note that we don't really care if this succeeds or not */ -+ if (mupdate_h) { -+ prot_printf(be->out, "MP1 MUPDATEPUSH {%d+}\r\n%s\r\n", -+ strlen(name), name); -+ rerr = getresult(be->in, "MP1"); -+ if(rerr) { -+ syslog(LOG_ERR, -+ "Could not trigger remote push to mupdate server" \ -+ "during move of %s", -+ mailboxname); -+ } -+ } -+ } -+ -+ /* 7) local delete of mailbox -+ * & remove local "remote" mailboxlist entry */ -+ if(!r) { -+ /* Note that we do not check the ACL, and we don't update MUPDATE */ -+ /* note also that we need to remember to let proxyadmins do this */ -+ r = mboxlist_deletemailbox(mailboxname, -+ imapd_userisadmin || imapd_userisproxyadmin, -+ imapd_userid, imapd_authstate, 0, 1, 0); -+ if(r) syslog(LOG_ERR, -+ "Could not delete local mailbox during move of %s", -+ mailboxname); -+ -+ if (!r) { -+ /* Delete mailbox annotations */ -+ annotatemore_delete(mailboxname); -+ } -+ } -+ -+done: -+ if(r && mupdate_h && backout_mupdate) { -+ rerr = 0; -+ /* xxx if the mupdate server is what failed, then this won't -+ help any! */ -+ snprintf(buf, sizeof(buf), "%s!%s", config_servername, part); -+ rerr = mupdate_activate(mupdate_h, mailboxname, buf, acl); -+ if(rerr) { -+ syslog(LOG_ERR, -+ "Could not back out mupdate during move of %s (%s)", -+ mailboxname, error_message(rerr)); -+ } -+ } -+ if(r && backout_remotebox) { -+ rerr = 0; -+ prot_printf(be->out, "LD1 LOCALDELETE {%d+}\r\n%s\r\n", -+ strlen(name), name); -+ rerr = getresult(be->in, "LD1"); -+ if(rerr) { -+ syslog(LOG_ERR, -+ "Could not back out remote mailbox during move of %s (%s)", -+ name, error_message(rerr)); -+ } -+ } -+ if(r && backout_remoteflag) { -+ rerr = 0; -+ -+ rerr = mboxlist_update(mailboxname, mbflags, part, acl, 1); -+ if(rerr) syslog(LOG_ERR, "Could not unset remote flag on mailbox: %s", -+ mailboxname); -+ } -+ -+ /* release the handles we got locally if necessary */ -+ if(mupdate_h && !h_in) -+ mupdate_disconnect(&mupdate_h); -+ if(be && !be_in) -+ backend_disconnect(be); -+ -+ return r; -+} -+ -+struct xfer_user_rock -+{ -+ char *toserver; -+ char *topart; -+ mupdate_handle *h; -+ struct backend *be; -+}; -+ -+static int xfer_user_cb(char *name, -+ int matchlen __attribute__((unused)), -+ int maycreate __attribute__((unused)), -+ void *rock) -+{ -+ mupdate_handle *mupdate_h = ((struct xfer_user_rock *)rock)->h; -+ char *toserver = ((struct xfer_user_rock *)rock)->toserver; -+ char *topart = ((struct xfer_user_rock *)rock)->topart; -+ struct backend *be = ((struct xfer_user_rock *)rock)->be; -+ char externalname[MAX_MAILBOX_NAME+1]; -+ int mbflags; -+ int r = 0; -+ char *inpath, *inmpath, *inpart, *inacl; -+ char *path = NULL, *mpath = NULL, *part = NULL, *acl = NULL; -+ -+ if (!r) { -+ /* NOTE: NOT mlookup() because we don't want to issue a referral */ -+ /* xxx but what happens if they are remote -+ * mailboxes? */ -+ r = mboxlist_detail(name, &mbflags, -+ &inpath, &inmpath, &inpart, &inacl, NULL); -+ } -+ -+ if (!r) { -+ path = xstrdup(inpath); -+ if (inmpath) mpath = xstrdup(inmpath); -+ part = xstrdup(inpart); -+ acl = xstrdup(inacl); -+ } -+ -+ if (!r) { -+ r = (*imapd_namespace.mboxname_toexternal)(&imapd_namespace, -+ name, -+ imapd_userid, -+ externalname); -+ } -+ -+ if(!r) { -+ r = do_xfer_single(toserver, topart, externalname, name, mbflags, -+ path, mpath, part, acl, 0, mupdate_h, be); -+ } -+ -+ if(path) free(path); -+ if(mpath) free(mpath); -+ if(part) free(part); -+ if(acl) free(acl); -+ -+ return r; -+} -+ -+ -+void cmd_xfer(char *tag, char *name, char *toserver, char *topart) -+{ -+ int r = 0; -+ char buf[MAX_PARTITION_LEN+HOSTNAME_SIZE+2]; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ int mbflags; -+ int moving_user = 0; -+ int backout_mupdate = 0; -+ mupdate_handle *mupdate_h = NULL; -+ char *inpath, *inmpath, *inpart, *inacl; -+ char *path = NULL, *mpath = NULL, *part = NULL, *acl = NULL; -+ char *p, *mbox = mailboxname; -+ -+ /* administrators only please */ -+ /* however, proxys can do this, if their authzid is an admin */ -+ if (!imapd_userisadmin && !imapd_userisproxyadmin) { -+ r = IMAP_PERMISSION_DENIED; -+ } -+ -+ if (!r) { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, -+ name, -+ imapd_userid, -+ mailboxname); -+ } -+ -+ /* NOTE: Since XFER can only be used by an admin, and we always connect -+ * to the destination backend as an admin, we take advantage of the fact -+ * that admins *always* use a consistent mailbox naming scheme. -+ * So, 'name' should be used in any command we send to a backend, and -+ * 'mailboxname' is the internal name to be used for mupdate and findall. -+ */ -+ -+ if (config_virtdomains && (p = strchr(mailboxname, '!'))) { -+ /* pointer to mailbox w/o domain prefix */ -+ mbox = p + 1; -+ } -+ -+ if(!strncmp(mbox, "user.", 5) && !strchr(mbox+5, '.')) { -+ if ((strlen(mbox+5) == (strlen(imapd_userid) - (mbox - mailboxname))) && -+ !strncmp(mbox+5, imapd_userid, strlen(mbox+5))) { -+ /* don't move your own inbox, that could be troublesome */ -+ r = IMAP_MAILBOX_NOTSUPPORTED; -+ } else if (!config_getswitch(IMAPOPT_ALLOWUSERMOVES)) { -+ /* not configured to allow user moves */ -+ r = IMAP_MAILBOX_NOTSUPPORTED; -+ } else { -+ moving_user = 1; -+ } -+ } -+ -+ if (!r) { -+ r = mlookup(tag, name, mailboxname, &mbflags, -+ &inpath, &inmpath, &inpart, &inacl, NULL); -+ } -+ if (r == IMAP_MAILBOX_MOVED) return; -+ -+ if (!r) { -+ path = xstrdup(inpath); -+ if (inmpath) mpath = xstrdup(inmpath); -+ part = xstrdup(inpart); -+ acl = xstrdup(inacl); -+ } -+ -+ /* if we are not moving a user, just move the one mailbox */ -+ if(!r && !moving_user) { -+ r = do_xfer_single(toserver, topart, name, mailboxname, mbflags, -+ path, mpath, part, acl, 0, NULL, NULL); -+ } else if (!r) { -+ struct backend *be = NULL; -+ -+ /* we need to reserve the users inbox - connect to mupdate */ -+ if(!r && config_mupdate_server) { -+ r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL); -+ if(r) { -+ syslog(LOG_ERR, -+ "Could not move mailbox: %s, MUPDATE connect failed", -+ mailboxname); -+ goto done; -+ } -+ } -+ -+ /* Get a single connection to the remote backend */ -+ be = backend_connect(NULL, toserver, &protocol[PROTOCOL_IMAP], -+ "", NULL, NULL); -+ if(!be) { -+ r = IMAP_SERVER_UNAVAILABLE; -+ syslog(LOG_ERR, -+ "Could not move mailbox: %s, " \ -+ "Initial backend connect failed", -+ mailboxname); -+ } -+ -+ /* deactivate their inbox */ -+ if(!r && mupdate_h) { -+ /* Note we are making the reservation on OUR host so that recovery -+ * make sense */ -+ snprintf(buf, sizeof(buf), "%s!%s", config_servername, part); -+ r = mupdate_deactivate(mupdate_h, mailboxname, buf); -+ if(r) syslog(LOG_ERR, -+ "Could deactivate mailbox: %s, during move", -+ mailboxname); -+ else backout_mupdate = 1; -+ } -+ -+ /* If needed, set an uppermost quota root */ -+ if(!r) { -+ struct quota quota; -+ -+ quota.root = mailboxname; -+ r = quota_read("a, NULL, 0); -+ -+ if(!r) { -+ /* note use of + to force the setting of a nonexistant -+ * quotaroot */ -+ prot_printf(be->out, "Q01 SETQUOTA {%d+}\r\n" \ -+ "+%s (STORAGE %d)\r\n", -+ strlen(name)+1, name, quota.limit); -+ r = getresult(be->in, "Q01"); -+ if(r) syslog(LOG_ERR, -+ "Could not move mailbox: %s, " \ -+ "failed setting initial quota root\r\n", -+ mailboxname); -+ } -+ else if (r == IMAP_QUOTAROOT_NONEXISTENT) r = 0; -+ } -+ -+ -+ /* recursively move all sub-mailboxes, using internal names */ -+ if(!r) { -+ struct xfer_user_rock rock; -+ -+ rock.toserver = toserver; -+ rock.topart = topart; -+ rock.h = mupdate_h; -+ rock.be = be; -+ -+ snprintf(buf, sizeof(buf), "%s.*", mailboxname); -+ r = mboxlist_findall(NULL, buf, 1, imapd_userid, -+ imapd_authstate, xfer_user_cb, -+ &rock); -+ } -+ -+ /* xxx how do you back out if one of the above moves fails? */ -+ -+ /* move this mailbox */ -+ /* ...and seen file, and subs file, and sieve scripts... */ -+ if(!r) { -+ r = do_xfer_single(toserver, topart, name, mailboxname, mbflags, -+ path, mpath, part, acl, 1, mupdate_h, be); -+ } -+ -+ if(be) { -+ backend_disconnect(be); -+ free(be); -+ } -+ -+ if(r && mupdate_h && backout_mupdate) { -+ int rerr = 0; -+ /* xxx if the mupdate server is what failed, then this won't -+ help any! */ -+ snprintf(buf, sizeof(buf), "%s!%s", config_servername, part); -+ rerr = mupdate_activate(mupdate_h, mailboxname, buf, acl); -+ if(rerr) { -+ syslog(LOG_ERR, -+ "Could not back out mupdate during move of %s (%s)", -+ mailboxname, error_message(rerr)); -+ } -+ } else if(!r) { -+ /* this was a successful user delete, and we need to delete -+ certain user meta-data (but not seen state!) */ -+ user_deletedata(mailboxname+5, imapd_userid, imapd_authstate, 0); -+ } -+ -+ if(!r && mupdate_h) { -+ mupdate_disconnect(&mupdate_h); -+ } -+ } -+ -+ done: -+ if(part) free(part); -+ if(path) free(path); -+ if(mpath) free(mpath); -+ if(acl) free(acl); -+ -+ imapd_check(NULL, 0, 0); -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", -+ tag, -+ error_message(r)); -+ } else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ } -+ -+ return; -+} -+ -+/* -+ * Parse a "date", for SEARCH criteria -+ * The time_t's pointed to by 'start' and 'end' are set to the -+ * times of the start and end of the parsed date. -+ */ -+int getsearchdate(start, end) -+time_t *start, *end; -+{ -+ int c; -+ struct tm tm; -+ int quoted = 0; -+ char month[4]; -+ -+ memset(&tm, 0, sizeof tm); -+ -+ c = prot_getc(imapd_in); -+ if (c == '\"') { -+ quoted++; -+ c = prot_getc(imapd_in); -+ } -+ -+ /* Day of month */ -+ if (!isdigit(c)) goto baddate; -+ tm.tm_mday = c - '0'; -+ c = prot_getc(imapd_in); -+ if (isdigit(c)) { -+ tm.tm_mday = tm.tm_mday * 10 + c - '0'; -+ c = prot_getc(imapd_in); -+ } -+ -+ if (c != '-') goto baddate; -+ c = prot_getc(imapd_in); -+ -+ /* Month name */ -+ if (!isalpha(c)) goto baddate; -+ month[0] = c; -+ c = prot_getc(imapd_in); -+ if (!isalpha(c)) goto baddate; -+ month[1] = c; -+ c = prot_getc(imapd_in); -+ if (!isalpha(c)) goto baddate; -+ month[2] = c; -+ c = prot_getc(imapd_in); -+ month[3] = '\0'; -+ lcase(month); -+ -+ for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) { -+ if (!strcmp(month, monthname[tm.tm_mon])) break; -+ } -+ if (tm.tm_mon == 12) goto baddate; -+ -+ if (c != '-') goto baddate; -+ c = prot_getc(imapd_in); -+ -+ /* Year */ -+ if (!isdigit(c)) goto baddate; -+ tm.tm_year = c - '0'; -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ tm.tm_year = tm.tm_year * 10 + c - '0'; -+ c = prot_getc(imapd_in); -+ if (isdigit(c)) { -+ if (tm.tm_year < 19) goto baddate; -+ tm.tm_year -= 19; -+ tm.tm_year = tm.tm_year * 10 + c - '0'; -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ tm.tm_year = tm.tm_year * 10 + c - '0'; -+ c = prot_getc(imapd_in); -+ } -+ -+ if (quoted) { -+ if (c != '\"') goto baddate; -+ c = prot_getc(imapd_in); -+ } -+ -+ tm.tm_isdst = -1; -+ *start = mktime(&tm); -+ -+ tm.tm_sec = tm.tm_min = 59; -+ tm.tm_hour = 23; -+ tm.tm_isdst = -1; -+ *end = mktime(&tm); -+ -+ return c; -+ -+ baddate: -+ prot_ungetc(c, imapd_in); -+ return EOF; -+} -+ -+#define SORTGROWSIZE 10 -+ -+/* -+ * Parse sort criteria -+ */ -+int getsortcriteria(char *tag, struct sortcrit **sortcrit) -+{ -+ int c; -+ static struct buf criteria; -+ int nsort, n; -+ -+ *sortcrit = NULL; -+ -+ c = prot_getc(imapd_in); -+ if (c != '(') goto missingcrit; -+ -+ c = getword(imapd_in, &criteria); -+ if (criteria.s[0] == '\0') goto missingcrit; -+ -+ nsort = 0; -+ n = 0; -+ for (;;) { -+ if (n >= nsort - 1) { /* leave room for implicit criterion */ -+ /* (Re)allocate an array for sort criteria */ -+ nsort += SORTGROWSIZE; -+ *sortcrit = -+ (struct sortcrit *) xrealloc(*sortcrit, -+ nsort * sizeof(struct sortcrit)); -+ /* Zero out the newly added sortcrit */ -+ memset((*sortcrit)+n, 0, SORTGROWSIZE * sizeof(struct sortcrit)); -+ } -+ -+ lcase(criteria.s); -+ if (!strcmp(criteria.s, "reverse")) { -+ (*sortcrit)[n].flags |= SORT_REVERSE; -+ goto nextcrit; -+ } -+ else if (!strcmp(criteria.s, "arrival")) -+ (*sortcrit)[n].key = SORT_ARRIVAL; -+ else if (!strcmp(criteria.s, "cc")) -+ (*sortcrit)[n].key = SORT_CC; -+ else if (!strcmp(criteria.s, "date")) -+ (*sortcrit)[n].key = SORT_DATE; -+ else if (!strcmp(criteria.s, "from")) -+ (*sortcrit)[n].key = SORT_FROM; -+ else if (!strcmp(criteria.s, "size")) -+ (*sortcrit)[n].key = SORT_SIZE; -+ else if (!strcmp(criteria.s, "subject")) -+ (*sortcrit)[n].key = SORT_SUBJECT; -+ else if (!strcmp(criteria.s, "to")) -+ (*sortcrit)[n].key = SORT_TO; -+#if 0 -+ else if (!strcmp(criteria.s, "annotation")) { -+ (*sortcrit)[n].key = SORT_ANNOTATION; -+ if (c != ' ') goto missingarg; -+ c = getstring(imapd_in, &arg); -+ if (c != ' ') goto missingarg; -+ (*sortcrit)[n].args.annot.entry = xstrdup(arg.s); -+ c = getstring(imapd_in, &arg); -+ if (c == EOF) goto missingarg; -+ (*sortcrit)[n].args.annot.attrib = xstrdup(arg.s); -+ } -+#endif -+ else { -+ prot_printf(imapd_out, "%s BAD Invalid Sort criterion %s\r\n", -+ tag, criteria.s); -+ if (c != EOF) prot_ungetc(c, imapd_in); -+ return EOF; -+ } -+ -+ n++; -+ -+ nextcrit: -+ if (c == ' ') c = getword(imapd_in, &criteria); -+ else break; -+ } -+ -+ if ((*sortcrit)[n].flags & SORT_REVERSE && !(*sortcrit)[n].key) { -+ prot_printf(imapd_out, -+ "%s BAD Missing Sort criterion to reverse\r\n", tag); -+ if (c != EOF) prot_ungetc(c, imapd_in); -+ return EOF; -+ } -+ -+ if (c != ')') { -+ prot_printf(imapd_out, -+ "%s BAD Missing close parenthesis in Sort\r\n", tag); -+ if (c != EOF) prot_ungetc(c, imapd_in); -+ return EOF; -+ } -+ -+ /* Terminate the list with the implicit sort criterion */ -+ (*sortcrit)[n++].key = SORT_SEQUENCE; -+ -+ c = prot_getc(imapd_in); -+ -+ return c; -+ -+ missingcrit: -+ prot_printf(imapd_out, "%s BAD Missing Sort criteria\r\n", tag); -+ if (c != EOF) prot_ungetc(c, imapd_in); -+ return EOF; -+#if 0 /* For annotations stuff above */ -+ missingarg: -+ prot_printf(imapd_out, "%s BAD Missing argument to Sort criterion %s\r\n", -+ tag, criteria.s); -+ if (c != EOF) prot_ungetc(c, imapd_in); -+ return EOF; -+#endif -+} -+ -+#ifdef ENABLE_LISTEXT -+/* -+ * Parse LIST options. -+ * The command has been parsed up to and including the opening '('. -+ */ -+int getlistopts(char *tag, int *listopts) -+{ -+ int c; -+ static struct buf arg; -+ -+ *listopts = LIST_EXT; -+ -+ for (;;) { -+ c = getword(imapd_in, &arg); -+ if (!arg.s[0]) break; -+ -+ lcase(arg.s); -+ if (!strcmp(arg.s, "subscribed")) { -+ *listopts |= LIST_SUBSCRIBED; -+ } -+ else if (!strcmp(arg.s, "children")) { -+ *listopts |= LIST_CHILDREN; -+ } -+ else if (!strcmp(arg.s, "remote")) { -+ *listopts |= LIST_REMOTE; -+ } -+ else { -+ prot_printf(imapd_out, "%s BAD Invalid List option %s\r\n", -+ tag, arg.s); -+ return EOF; -+ } -+ -+ if (c != ' ') break; -+ } -+ -+ if (c != ')') { -+ prot_printf(imapd_out, -+ "%s BAD Missing close parenthesis in List\r\n", tag); -+ return EOF; -+ } -+ -+ c = prot_getc(imapd_in); -+ -+ return c; -+} -+#endif /* ENABLE_LISTEXT */ -+ -+/* -+ * Parse a date_time, for the APPEND command -+ */ -+int getdatetime(date) -+time_t *date; -+{ -+ int c; -+ struct tm tm; -+ int old_format = 0; -+ char month[4], zone[4], *p; -+ time_t tmp_gmtime; -+ int zone_off; -+ -+ memset(&tm, 0, sizeof tm); -+ -+ c = prot_getc(imapd_in); -+ if (c != '\"') goto baddate; -+ -+ /* Day of month */ -+ c = prot_getc(imapd_in); -+ if (c == ' ') c = '0'; -+ if (!isdigit(c)) goto baddate; -+ tm.tm_mday = c - '0'; -+ c = prot_getc(imapd_in); -+ if (isdigit(c)) { -+ tm.tm_mday = tm.tm_mday * 10 + c - '0'; -+ c = prot_getc(imapd_in); -+ if(tm.tm_mday <= 0 || tm.tm_mday > 31) -+ goto baddate; -+ } -+ -+ if (c != '-') goto baddate; -+ c = prot_getc(imapd_in); -+ -+ /* Month name */ -+ if (!isalpha(c)) goto baddate; -+ month[0] = c; -+ c = prot_getc(imapd_in); -+ if (!isalpha(c)) goto baddate; -+ month[1] = c; -+ c = prot_getc(imapd_in); -+ if (!isalpha(c)) goto baddate; -+ month[2] = c; -+ c = prot_getc(imapd_in); -+ month[3] = '\0'; -+ lcase(month); -+ -+ for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) { -+ if (!strcmp(month, monthname[tm.tm_mon])) break; -+ } -+ if (tm.tm_mon == 12) goto baddate; -+ /* xxx this doesn't quite work in leap years */ -+ if (tm.tm_mday > max_monthdays[tm.tm_mon]) goto baddate; -+ -+ if (c != '-') goto baddate; -+ c = prot_getc(imapd_in); -+ -+ /* Year */ -+ if (!isdigit(c)) goto baddate; -+ tm.tm_year = c - '0'; -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ tm.tm_year = tm.tm_year * 10 + c - '0'; -+ c = prot_getc(imapd_in); -+ if (isdigit(c)) { -+ if (tm.tm_year < 19) goto baddate; -+ tm.tm_year -= 19; -+ tm.tm_year = tm.tm_year * 10 + c - '0'; -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ tm.tm_year = tm.tm_year * 10 + c - '0'; -+ c = prot_getc(imapd_in); -+ } -+ else old_format++; -+ -+ /* Hour */ -+ if (c != ' ') goto baddate; -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ tm.tm_hour = c - '0'; -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ tm.tm_hour = tm.tm_hour * 10 + c - '0'; -+ c = prot_getc(imapd_in); -+ if (tm.tm_hour > 23) goto baddate; -+ -+ /* Minute */ -+ if (c != ':') goto baddate; -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ tm.tm_min = c - '0'; -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ tm.tm_min = tm.tm_min * 10 + c - '0'; -+ c = prot_getc(imapd_in); -+ if (tm.tm_min > 59) goto baddate; -+ -+ /* Second */ -+ if (c != ':') goto baddate; -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ tm.tm_sec = c - '0'; -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ tm.tm_sec = tm.tm_sec * 10 + c - '0'; -+ c = prot_getc(imapd_in); -+ if (tm.tm_min > 60) goto baddate; -+ -+ /* Time zone */ -+ if (old_format) { -+ if (c != '-') goto baddate; -+ c = prot_getc(imapd_in); -+ -+ if (!isalpha(c)) goto baddate; -+ zone[0] = c; -+ c = prot_getc(imapd_in); -+ -+ if (c == '\"') { -+ /* Military (single-char) zones */ -+ zone[1] = '\0'; -+ lcase(zone); -+ if (zone[0] <= 'm') { -+ zone_off = (zone[0] - 'a' + 1)*60; -+ } -+ else if (zone[0] < 'z') { -+ zone_off = ('m' - zone[0])*60; -+ } -+ else zone_off = 0; -+ } -+ else { -+ /* UT (universal time) */ -+ zone[1] = c; -+ c = prot_getc(imapd_in); -+ if (c == '\"') { -+ zone[2] = '\0'; -+ lcase(zone); -+ if (!strcmp(zone, "ut")) goto baddate; -+ zone_off = 0; -+ } -+ else { -+ /* 3-char time zone */ -+ zone[2] = c; -+ c = prot_getc(imapd_in); -+ if (c != '\"') goto baddate; -+ zone[3] = '\0'; -+ lcase(zone); -+ p = strchr("aecmpyhb", zone[0]); -+ if (c != '\"' || zone[2] != 't' || !p) goto baddate; -+ zone_off = (strlen(p) - 12)*60; -+ if (zone[1] == 'd') zone_off -= 60; -+ else if (zone[1] != 's') goto baddate; -+ } -+ } -+ } -+ else { -+ if (c != ' ') goto baddate; -+ c = prot_getc(imapd_in); -+ -+ if (c != '+' && c != '-') goto baddate; -+ zone[0] = c; -+ -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ zone_off = c - '0'; -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ zone_off = zone_off * 10 + c - '0'; -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ zone_off = zone_off * 6 + c - '0'; -+ c = prot_getc(imapd_in); -+ if (!isdigit(c)) goto baddate; -+ zone_off = zone_off * 10 + c - '0'; -+ -+ if (zone[0] == '-') zone_off = -zone_off; -+ -+ c = prot_getc(imapd_in); -+ if (c != '\"') goto baddate; -+ -+ } -+ -+ c = prot_getc(imapd_in); -+ -+ tm.tm_isdst = -1; -+ -+ tmp_gmtime = mkgmtime(&tm); -+ if(tmp_gmtime == -1) goto baddate; -+ -+ *date = tmp_gmtime - zone_off*60; -+ -+ return c; -+ -+ baddate: -+ prot_ungetc(c, imapd_in); -+ return EOF; -+} -+ -+/* -+ * Print 's' as a quoted-string or literal (but not an atom) -+ */ -+void -+printstring(s) -+const char *s; -+{ -+ const char *p; -+ int len = 0; -+ -+ /* Look for any non-QCHAR characters */ -+ for (p = s; *p && len < 1024; p++) { -+ len++; -+ if (*p & 0x80 || *p == '\r' || *p == '\n' -+ || *p == '\"' || *p == '%' || *p == '\\') break; -+ } -+ -+ /* if it's too long, literal it */ -+ if (*p || len >= 1024) { -+ prot_printf(imapd_out, "{%u}\r\n%s", strlen(s), s); -+ } else { -+ prot_printf(imapd_out, "\"%s\"", s); -+ } -+} -+ -+/* -+ * Print 's' as an atom, quoted-string, or literal -+ */ -+void -+printastring(s) -+const char *s; -+{ -+ const char *p; -+ int len = 0; -+ -+ if (imparse_isatom(s)) { -+ prot_printf(imapd_out, "%s", s); -+ return; -+ } -+ -+ /* Look for any non-QCHAR characters */ -+ for (p = s; *p && len < 1024; p++) { -+ len++; -+ if (*p & 0x80 || *p == '\r' || *p == '\n' -+ || *p == '\"' || *p == '%' || *p == '\\') break; -+ } -+ -+ /* if it's too long, literal it */ -+ if (*p || len >= 1024) { -+ prot_printf(imapd_out, "{%u}\r\n%s", strlen(s), s); -+ } else { -+ prot_printf(imapd_out, "\"%s\"", s); -+ } -+} -+ -+/* -+ * Append 'section', 'fields', 'trail' to the fieldlist 'l'. -+ */ -+void -+appendfieldlist(struct fieldlist **l, char *section, -+ struct strlist *fields, char *trail, -+ void *d, size_t size) -+{ -+ struct fieldlist **tail = l; -+ -+ while (*tail) tail = &(*tail)->next; -+ -+ *tail = (struct fieldlist *)xmalloc(sizeof(struct fieldlist)); -+ (*tail)->section = xstrdup(section); -+ (*tail)->fields = fields; -+ (*tail)->trail = xstrdup(trail); -+ if(d && size) { -+ (*tail)->rock = xmalloc(size); -+ memcpy((*tail)->rock, d, size); -+ } else { -+ (*tail)->rock = NULL; -+ } -+ (*tail)->next = 0; -+} -+ -+ -+/* -+ * Free the fieldlist 'l' -+ */ -+void freefieldlist(struct fieldlist *l) -+{ -+ struct fieldlist *n; -+ -+ while (l) { -+ n = l->next; -+ free(l->section); -+ freestrlist(l->fields); -+ free(l->trail); -+ if (l->rock) free(l->rock); -+ free((char *)l); -+ l = n; -+ } -+} -+ -+/* -+ * Append the searchargs 's1' and 's2' to the sublist of 's' -+ */ -+void -+appendsearchargs(s, s1, s2) -+struct searchargs *s, *s1, *s2; -+{ -+ struct searchsub **tail = &s->sublist; -+ -+ while (*tail) tail = &(*tail)->next; -+ -+ *tail = (struct searchsub *)xmalloc(sizeof(struct searchsub)); -+ (*tail)->sub1 = s1; -+ (*tail)->sub2 = s2; -+ (*tail)->next = 0; -+} -+ -+ -+/* -+ * Free the searchargs 's' -+ */ -+void -+freesearchargs(s) -+struct searchargs *s; -+{ -+ struct searchsub *sub, *n; -+ -+ if (!s) return; -+ -+ freestrlist(s->sequence); -+ freestrlist(s->uidsequence); -+ freestrlist(s->from); -+ freestrlist(s->to); -+ freestrlist(s->cc); -+ freestrlist(s->bcc); -+ freestrlist(s->subject); -+ freestrlist(s->body); -+ freestrlist(s->text); -+ freestrlist(s->header_name); -+ freestrlist(s->header); -+ -+ for (sub = s->sublist; sub; sub = n) { -+ n = sub->next; -+ freesearchargs(sub->sub1); -+ freesearchargs(sub->sub2); -+ free(sub); -+ } -+ free(s); -+} -+ -+/* -+ * Free an array of sortcrit -+ */ -+static void freesortcrit(struct sortcrit *s) -+{ -+ int i = 0; -+ -+ if (!s) return; -+ do { -+ switch (s[i].key) { -+ case SORT_ANNOTATION: -+ free(s[i].args.annot.entry); -+ free(s[i].args.annot.attrib); -+ break; -+ } -+ i++; -+ } while (s[i].key != SORT_SEQUENCE); -+ free(s); -+} -+ -+/* -+ * Issue a MAILBOX untagged response -+ */ -+static int mailboxdata(char *name, -+ int matchlen __attribute__((unused)), -+ int maycreate __attribute__((unused)), -+ void *rock __attribute__((unused))) -+{ -+ char mboxname[MAX_MAILBOX_PATH+1]; -+ -+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace, name, -+ imapd_userid, mboxname); -+ prot_printf(imapd_out, "* MAILBOX %s\r\n", mboxname); -+ return 0; -+} -+ -+/* -+ * Issue a LIST or LSUB untagged response -+ */ -+static void mstringdata(char *cmd, char *name, int matchlen, int maycreate, -+ int listopts) -+{ -+ static char lastname[MAX_MAILBOX_PATH+1]; -+ static int lastnamedelayed = 0; -+ static int lastnamenoinferiors = 0; -+ static int nonexistent = 0; -+ static int sawuser = 0; -+ int lastnamehassub = 0; -+ int c, mbtype; -+ char mboxname[MAX_MAILBOX_PATH+1]; -+ -+ /* We have to reset the sawuser flag before each list command. -+ * Handle it as a dirty hack. -+ */ -+ if (cmd == NULL) { -+ sawuser = 0; -+ mstringdatacalls = 0; -+ return; -+ } -+ mstringdatacalls++; -+ -+ if (lastnamedelayed) { -+ /* Check if lastname has children */ -+ if (name && strncmp(lastname, name, strlen(lastname)) == 0 && -+ name[strlen(lastname)] == '.') { -+ lastnamehassub = 1; -+ } -+ prot_printf(imapd_out, "* %s (", cmd); -+ if (nonexistent == IMAP_MAILBOX_RESERVED) { -+ /* LISTEXT wants \\PlaceHolder instead of \\Noselect */ -+ if (listopts & LIST_EXT) -+ prot_printf(imapd_out, "\\PlaceHolder"); -+ else -+ prot_printf(imapd_out, "\\Noselect"); -+ } else if (nonexistent) { -+ prot_printf(imapd_out, "\\NonExistent"); -+ } -+ if (lastnamenoinferiors) { -+ prot_printf(imapd_out, "%s\\Noinferiors", nonexistent ? " " : ""); -+ } -+ else if ((listopts & LIST_CHILDREN) && -+ /* we can't determine \HasNoChildren for subscriptions */ -+ (lastnamehassub || -+ !(listopts & (LIST_LSUB | LIST_SUBSCRIBED)))) { -+ prot_printf(imapd_out, "%s%s", nonexistent ? " " : "", -+ lastnamehassub ? "\\HasChildren" : "\\HasNoChildren"); -+ } -+ prot_printf(imapd_out, ") \"%c\" ", imapd_namespace.hier_sep); -+ -+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace, lastname, -+ imapd_userid, mboxname); -+ printstring(mboxname); -+ prot_printf(imapd_out, "\r\n"); -+ lastnamedelayed = lastnamenoinferiors = nonexistent = 0; -+ } -+ -+ /* Special-case to flush any final state */ -+ if (!name) { -+ lastname[0] = '\0'; -+ return; -+ } -+ -+ /* Suppress any output of a partial match */ -+ if ((name[matchlen] -+ && strncmp(lastname, name, matchlen) == 0 -+ && (lastname[matchlen] == '\0' || lastname[matchlen] == '.'))) { -+ return; -+ } -+ -+ /* -+ * We can get a partial match for "user" multiple times with -+ * other matches inbetween. Handle it as a special case -+ */ -+ if (matchlen == 4 && strncasecmp(name, "user", 4) == 0) { -+ if (sawuser) return; -+ sawuser = 1; -+ } -+ -+ strlcpy(lastname, name, sizeof(lastname)); -+ lastname[matchlen] = '\0'; -+ nonexistent = 0; -+ -+ /* Now we need to see if this mailbox exists */ -+ /* first convert "INBOX" to "user." */ -+ if (!strncasecmp(lastname, "inbox", 5)) { -+ (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, "INBOX", -+ imapd_userid, mboxname); -+ strlcat(mboxname, lastname+5, sizeof(mboxname)); -+ } -+ else -+ strlcpy(mboxname, lastname, sizeof(mboxname)); -+ -+ /* Look it up */ -+ nonexistent = mboxlist_detail(mboxname, &mbtype, -+ NULL, NULL, NULL, NULL, NULL); -+ if(!nonexistent && (mbtype & MBTYPE_RESERVE)) -+ nonexistent = IMAP_MAILBOX_RESERVED; -+ -+ if (!name[matchlen]) { -+ lastnamedelayed = 1; -+ if (!maycreate) lastnamenoinferiors = 1; -+ return; -+ } -+ -+ c = name[matchlen]; -+ if (c) name[matchlen] = '\0'; -+ prot_printf(imapd_out, "* %s (", cmd); -+ if (c) { -+ /* Handle namespace prefix as a special case */ -+ if (!strcmp(name, "user") || -+ !strcmp(name, imapd_namespace.prefix[NAMESPACE_SHARED])) { -+ prot_printf(imapd_out, "\\Noselect"); -+ if (listopts & LIST_EXT) -+ prot_printf(imapd_out, " \\PlaceHolder"); -+ } -+ else { -+ if (nonexistent) -+ prot_printf(imapd_out, "\\NonExistent"); -+ /* LISTEXT uses \PlaceHolder instead of \Noselect */ -+ if (listopts & LIST_EXT) -+ prot_printf(imapd_out, "%s\\PlaceHolder", nonexistent ? " " : ""); -+ else -+ prot_printf(imapd_out, "%s\\Noselect", nonexistent ? " " : ""); -+ } -+ if (listopts & LIST_CHILDREN) -+ prot_printf(imapd_out, " \\HasChildren"); -+ } -+ prot_printf(imapd_out, ") \"%c\" ", imapd_namespace.hier_sep); -+ -+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace, name, -+ imapd_userid, mboxname); -+ printstring(mboxname); -+ prot_printf(imapd_out, "\r\n"); -+ if (c) name[matchlen] = c; -+ return; -+} -+ -+/* -+ * Issue a LIST untagged response -+ */ -+static int listdata(char *name, int matchlen, int maycreate, void *rock) -+{ -+ int listopts = *((int *)rock); -+ -+ mstringdata(((listopts & LIST_LSUB) ? "LSUB" : "LIST"), -+ name, matchlen, maycreate, listopts); -+ -+ return 0; -+} -+ -+/* Reset the given sasl_conn_t to a sane state */ -+static int reset_saslconn(sasl_conn_t **conn) -+{ -+ int ret; -+ sasl_security_properties_t *secprops = NULL; -+ -+ sasl_dispose(conn); -+ /* do initialization typical of service_main */ -+ ret = sasl_server_new("imap", config_servername, -+ NULL, NULL, NULL, -+ NULL, 0, conn); -+ if(ret != SASL_OK) return ret; -+ -+ if(saslprops.ipremoteport) -+ ret = sasl_setprop(*conn, SASL_IPREMOTEPORT, -+ saslprops.ipremoteport); -+ if(ret != SASL_OK) return ret; -+ -+ if(saslprops.iplocalport) -+ ret = sasl_setprop(*conn, SASL_IPLOCALPORT, -+ saslprops.iplocalport); -+ if(ret != SASL_OK) return ret; -+ -+ secprops = mysasl_secprops(SASL_SEC_NOPLAINTEXT); -+ ret = sasl_setprop(*conn, SASL_SEC_PROPS, secprops); -+ if(ret != SASL_OK) return ret; -+ /* end of service_main initialization excepting SSF */ -+ -+ /* If we have TLS/SSL info, set it */ -+ if(saslprops.ssf) { -+ ret = sasl_setprop(*conn, SASL_SSF_EXTERNAL, &saslprops.ssf); -+ } else { -+ ret = sasl_setprop(*conn, SASL_SSF_EXTERNAL, &extprops_ssf); -+ } -+ if(ret != SASL_OK) return ret; -+ -+ if(saslprops.authid) { -+ ret = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, saslprops.authid); -+ if(ret != SASL_OK) return ret; -+ } -+ /* End TLS/SSL Info */ -+ -+ return SASL_OK; -+} -+ -+void cmd_mupdatepush(char *tag, char *name) -+{ -+ int r = 0; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ char *part, *acl; -+ mupdate_handle *mupdate_h = NULL; -+ char buf[MAX_PARTITION_LEN + HOSTNAME_SIZE + 2]; -+ -+ if (!imapd_userisadmin) { -+ r = IMAP_PERMISSION_DENIED; -+ } -+ if (!config_mupdate_server) { -+ r = IMAP_SERVER_UNAVAILABLE; -+ } -+ -+ if (!r) { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, -+ imapd_userid, mailboxname); -+ } -+ -+ if (!r) { -+ r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, -+ &part, &acl, NULL); -+ } -+ if (r == IMAP_MAILBOX_MOVED) return; -+ -+ /* Push mailbox to mupdate server */ -+ if (!r) { -+ r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL); -+ } -+ -+ if (!r) { -+ snprintf(buf, sizeof(buf), "%s!%s", config_servername, part); -+ -+ r = mupdate_activate(mupdate_h, mailboxname, buf, acl); -+ } -+ -+ if(mupdate_h) { -+ mupdate_disconnect(&mupdate_h); -+ } -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r)); -+ } -+ else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ } -+} -+ -+#ifdef HAVE_SSL -+/* Convert the ASCII hex into binary data -+ * -+ * 'bin' MUST be able to accomodate at least strlen(hex)/2 bytes -+ */ -+void hex2bin(const char *hex, unsigned char *bin, unsigned int *binlen) -+{ -+ int i; -+ const char *c; -+ unsigned char msn, lsn; -+ -+ for (c = hex, i = 0; *c && isxdigit((int) *c); c++) { -+ msn = (*c > '9') ? tolower((int) *c) - 'a' + 10 : *c - '0'; -+ c++; -+ lsn = (*c > '9') ? tolower((int) *c) - 'a' + 10 : *c - '0'; -+ -+ bin[i++] = (unsigned char) (msn << 4) | lsn; -+ } -+ *binlen = i; -+} -+ -+enum { -+ URLAUTH_ALG_HMAC_SHA1 = 0 /* HMAC-SHA1 */ -+}; -+ -+void cmd_urlfetch(char *tag) -+{ -+ struct mboxkey *mboxkey_db; -+ int c, r, doclose; -+ static struct buf arg; -+ struct imapurl url; -+ char mailboxname[MAX_MAILBOX_NAME+1]; -+ struct mailbox mboxstruct, *mailbox; -+ unsigned msgno; -+ unsigned int token_len; -+ int mbtype; -+ char *newserver; -+ time_t now = time(NULL); -+ -+ prot_printf(imapd_out, "* URLFETCH"); -+ -+ do { -+ c = getastring(imapd_in, imapd_out, &arg); -+ prot_putc(' ', imapd_out); -+ printstring(arg.s); -+ prot_putc(' ', imapd_out); -+ -+ r = doclose = 0; -+ imapurl_fromURL(&url, arg.s); -+ -+ /* validate the URL */ -+ if (!url.user || !url.server || !url.mailbox || !url.uid || -+ (url.urlauth.access && !(url.urlauth.mech && url.urlauth.token))) { -+ /* missing info */ -+ r = IMAP_BADURL; -+ } else if (strcmp(url.server, config_servername)) { -+ /* wrong server */ -+ r = IMAP_BADURL; -+ } else if (url.urlauth.expire && -+ url.urlauth.expire < mktime(gmtime(&now))) { -+ /* expired */ -+ r = IMAP_BADURL; -+ } else if (url.urlauth.access) { -+ /* check mechanism & authorization */ -+ int authorized = 0; -+ -+ if (!strcasecmp(url.urlauth.mech, "INTERNAL")) { -+ if (!strncasecmp(url.urlauth.access, "submit+", 7) && -+ global_authisa(imapd_authstate, IMAPOPT_SUBMITSERVERS)) { -+ /* authorized submit server */ -+ authorized = 1; -+ } else if (!strncasecmp(url.urlauth.access, "user+", 5) && -+ !strcmp(url.urlauth.access+5, imapd_userid)) { -+ /* currently authorized user */ -+ authorized = 1; -+ } else if (!strcasecmp(url.urlauth.access, "authuser") && -+ strcmp(imapd_userid, "anonymous")) { -+ /* any non-anonymous authorized user */ -+ authorized = 1; -+ } else if (!strcasecmp(url.urlauth.access, "anonymous")) { -+ /* anyone */ -+ authorized = 1; -+ } -+ } -+ -+ if (!authorized) r = IMAP_BADURL; -+ } -+ -+ if (!r) { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, -+ url.mailbox, -+ url.user, mailboxname); -+ } -+ if (!r) { -+ r = mlookup(NULL, NULL, mailboxname, &mbtype, NULL, NULL, -+ &newserver, NULL, NULL); -+ } -+ -+ if (!r && (mbtype & MBTYPE_REMOTE)) { -+ /* remote mailbox */ -+ struct backend *be; -+ -+ be = proxy_findserver(newserver, &protocol[PROTOCOL_IMAP], -+ proxy_userid, &backend_cached, -+ &backend_current, &backend_inbox, imapd_in); -+ if (!be) { -+ r = IMAP_SERVER_UNAVAILABLE; -+ } else { -+ /* XXX proxy command to backend */ -+ } -+ -+ free(url.freeme); -+ -+ continue; -+ } -+ -+ /* local mailbox */ -+ if (!r) { -+ if (url.urlauth.token) { -+ /* validate the URLAUTH token */ -+ hex2bin(url.urlauth.token, -+ (unsigned char *) url.urlauth.token, &token_len); -+ -+ /* first byte is the algorithm used to create token */ -+ switch (url.urlauth.token[0]) { -+ case URLAUTH_ALG_HMAC_SHA1: { -+ const char *key; -+ size_t keylen; -+ unsigned char vtoken[EVP_MAX_MD_SIZE]; -+ unsigned int vtoken_len; -+ -+ r = mboxkey_open(url.user, 0, &mboxkey_db); -+ r = mboxkey_read(mboxkey_db, mailboxname, &key, &keylen); -+ HMAC(EVP_sha1(), key, keylen, arg.s, url.urlauth.rump_len, -+ vtoken, &vtoken_len); -+ mboxkey_close(mboxkey_db); -+ -+ if (memcmp(vtoken, url.urlauth.token+1, vtoken_len)) { -+ r = IMAP_BADURL; -+ } -+ -+ break; -+ } -+ default: -+ r = IMAP_BADURL; -+ break; -+ } -+ } -+ -+ if (!r) { -+ if (!imapd_mailbox || strcmp(imapd_mailbox->name, mailboxname)) { -+ /* not the currently selected mailbox, so try to open it */ -+ -+ r = mailbox_open_header(mailboxname, imapd_authstate, -+ &mboxstruct); -+ -+ if (!r) { -+ doclose = 1; -+ r = mailbox_open_index(&mboxstruct); -+ } -+ -+ if (!r && !url.urlauth.access && -+ !(mboxstruct.myrights & ACL_READ)) { -+ r = (imapd_userisadmin || -+ (mboxstruct.myrights & ACL_LOOKUP)) ? -+ IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT; -+ } -+ -+ if (!r) { -+ mailbox = &mboxstruct; -+ index_operatemailbox(mailbox); -+ } -+ } else { -+ mailbox = imapd_mailbox; -+ } -+ } -+ -+ if (r) { -+ /* nothing to do, handled up top */ -+ } else if (url.uidvalidity && -+ (mailbox->uidvalidity != url.uidvalidity)) { -+ r = IMAP_BADURL; -+ } else if (!url.uid || !(msgno = index_finduid(url.uid)) || -+ (index_getuid(msgno) != url.uid)) { -+ r = IMAP_BADURL; -+ } else { -+ r = index_urlfetch(mailbox, msgno, url.section, -+ url.start_octet, url.octet_count, -+ imapd_out, NULL); -+ } -+ -+ free(url.freeme); -+ -+ if (doclose) { -+ mailbox_close(&mboxstruct); -+ if (imapd_mailbox) index_operatemailbox(imapd_mailbox); -+ } -+ } -+ -+ if (r) prot_printf(imapd_out, "NIL"); -+ -+ } while (c == ' '); -+ -+ prot_printf(imapd_out, "\r\n"); -+ -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ prot_printf(imapd_out, -+ "%s BAD Unexpected extra arguments to URLFETCH\r\n", tag); -+ eatline(imapd_in, c); -+ } -+ else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ } -+} -+ -+/* Convert the binary data into ASCII hex -+ * -+ * 'hex' MUST be able to accomodate at least 2*binlen+1 bytes -+ */ -+void bin2hex(unsigned char *bin, int binlen, char *hex) -+{ -+ int i; -+ unsigned char c; -+ -+ for (i = 0; i < binlen; i++) { -+ c = (bin[i] >> 4) & 0xf; -+ hex[i*2] = (c > 9) ? ('a' + c - 10) : ('0' + c); -+ c = bin[i] & 0xf; -+ hex[i*2+1] = (c > 9) ? ('a' + c - 10) : ('0' + c); -+ } -+ hex[i*2] = '\0'; -+} -+ -+#define MBOX_KEY_LEN 16 /* 128 bits */ -+ -+void cmd_genurlauth(char *tag) -+{ -+ struct mboxkey *mboxkey_db; -+ int first = 1; -+ int c, r, doclose; -+ static struct buf arg1, arg2; -+ struct imapurl url; -+ char mailboxname[MAX_MAILBOX_NAME+1], *urlauth = NULL; -+ char newkey[MBOX_KEY_LEN]; -+ const char *key; -+ size_t keylen; -+ unsigned char token[EVP_MAX_MD_SIZE+1]; /* +1 for algorithm */ -+ unsigned int token_len; -+ int mbtype; -+ char *newserver; -+ time_t now = time(NULL); -+ -+ r = mboxkey_open(imapd_userid, MBOXKEY_CREATE, &mboxkey_db); -+ if (r) { -+ prot_printf(imapd_out, -+ "%s NO Can not open mailbox key db for %s: %s\r\n", -+ tag, imapd_userid, error_message(r)); -+ return; -+ } -+ -+ do { -+ c = getastring(imapd_in, imapd_out, &arg1); -+ if (c != ' ') { -+ prot_printf(imapd_out, -+ "%s BAD Missing required argument to Genurlauth\r\n", -+ tag); -+ eatline(imapd_in, c); -+ return; -+ } -+ c = getword(imapd_in, &arg2); -+ if (strcasecmp(arg2.s, "INTERNAL")) { -+ prot_printf(imapd_out, -+ "%s BAD Unknown auth mechanism to Genurlauth %s\r\n", -+ tag, arg2.s); -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ r = 0; -+ imapurl_fromURL(&url, arg1.s); -+ -+ /* validate the URL */ -+ if (!url.user || !url.server || !url.mailbox || !url.uid || -+ !url.urlauth.access) { -+ r = IMAP_BADURL; -+ } else if (strcmp(url.user, imapd_userid)) { -+ /* not using currently authorized user's namespace */ -+ r = IMAP_BADURL; -+ } else if (strcmp(url.server, config_servername)) { -+ /* wrong server */ -+ r = IMAP_BADURL; -+ } else if (url.urlauth.expire && -+ url.urlauth.expire < mktime(gmtime(&now))) { -+ /* already expired */ -+ r = IMAP_BADURL; -+ } -+ -+ if (!r) { -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, -+ url.mailbox, -+ imapd_userid, mailboxname); -+ } -+ if (!r) { -+ r = mlookup(NULL, NULL, mailboxname, &mbtype, NULL, NULL, -+ &newserver, NULL, NULL); -+ } -+ if (r) { -+ prot_printf(imapd_out, -+ "%s BAD Poorly specified URL to Genurlauth %s\r\n", -+ tag, arg1.s); -+ eatline(imapd_in, c); -+ return; -+ } -+ -+ if (mbtype & MBTYPE_REMOTE) { -+ /* XXX proxy to backend */ -+ continue; -+ } -+ -+ /* lookup key */ -+ r = mboxkey_read(mboxkey_db, mailboxname, &key, &keylen); -+ if (r) { -+ syslog(LOG_ERR, "DBERROR: error fetching mboxkey: %s", -+ cyrusdb_strerror(r)); -+ } -+ else if (!key) { -+ /* create a new key */ -+ RAND_bytes(newkey, MBOX_KEY_LEN); -+ key = newkey; -+ keylen = MBOX_KEY_LEN; -+ r = mboxkey_write(mboxkey_db, mailboxname, key, keylen); -+ if (r) { -+ syslog(LOG_ERR, "DBERROR: error writing new mboxkey: %s", -+ cyrusdb_strerror(r)); -+ } -+ } -+ -+ if (r) { -+ eatline(imapd_in, c); -+ prot_printf(imapd_out, -+ "%s NO Error authorizing %s: %s\r\n", -+ tag, arg1.s, cyrusdb_strerror(r)); -+ return; -+ } -+ -+ /* first byte is the algorithm used to create token */ -+ token[0] = URLAUTH_ALG_HMAC_SHA1; -+ HMAC(EVP_sha1(), key, keylen, arg1.s, strlen(arg1.s), -+ token+1, &token_len); -+ token_len++; -+ -+ urlauth = xrealloc(urlauth, strlen(arg1.s) + 10 + -+ 2 * (EVP_MAX_MD_SIZE+1) + 1); -+ strcpy(urlauth, arg1.s); -+ strcat(urlauth, ":internal:"); -+ bin2hex(token, token_len, urlauth+strlen(urlauth)); -+ -+ if (first) { -+ prot_printf(imapd_out, "* GENURLAUTH"); -+ first = 0; -+ } -+ prot_putc(' ', imapd_out); -+ printstring(urlauth); -+ } while (c == ' '); -+ -+ if (!first) prot_printf(imapd_out, "\r\n"); -+ -+ if (c == '\r') c = prot_getc(imapd_in); -+ if (c != '\n') { -+ prot_printf(imapd_out, -+ "%s BAD Unexpected extra arguments to GENURLAUTH\r\n", tag); -+ eatline(imapd_in, c); -+ } -+ else { -+ prot_printf(imapd_out, "%s OK %s\r\n", tag, -+ error_message(IMAP_OK_COMPLETED)); -+ } -+ -+ mboxkey_close(mboxkey_db); -+} -+ -+void cmd_resetkey(char *tag, char *mailbox, -+ char *mechanism __attribute__((unused))) -+/* XXX we don't support any external mechanisms, so we ignore it */ -+{ -+ int r; -+ -+ if (mailbox) { -+ /* delete key for specified mailbox */ -+ char mailboxname[MAX_MAILBOX_NAME+1], *newserver; -+ int mbtype; -+ struct mboxkey *mboxkey_db; -+ -+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, -+ mailbox, -+ imapd_userid, mailboxname); -+ if (!r) { -+ r = mlookup(NULL, NULL, mailboxname, &mbtype, NULL, NULL, -+ &newserver, NULL, NULL); -+ } -+ if (r) { -+ prot_printf(imapd_out, "%s NO Error removing key: %s\r\n", -+ tag, error_message(r)); -+ return; -+ } -+ -+ if (mbtype & MBTYPE_REMOTE) { -+ /* XXX proxy to backend */ -+ return; -+ } -+ -+ r = mboxkey_open(imapd_userid, MBOXKEY_CREATE, &mboxkey_db); -+ if (!r) { -+ r = mboxkey_write(mboxkey_db, mailboxname, NULL, 0); -+ mboxkey_close(mboxkey_db); -+ } -+ -+ if (r) { -+ prot_printf(imapd_out, "%s NO Error removing key: %s\r\n", -+ tag, cyrusdb_strerror(r)); -+ } else { -+ prot_printf(imapd_out, -+ "%s OK [URLMECH INTERNAL] key removed\r\n", tag); -+ } -+ } -+ else { -+ /* delete ALL keys */ -+ /* XXX what do we do about multiple backends? */ -+ r = mboxkey_delete_user(imapd_userid); -+ if (r) { -+ prot_printf(imapd_out, "%s NO Error removing keys: %s\r\n", -+ tag, cyrusdb_strerror(r)); -+ } else { -+ prot_printf(imapd_out, "%s OK All keys removed\r\n", tag); -+ } -+ } -+} -+#endif /* HAVE_SSL */ ---- cyrus-imapd-2.3.7/imap/lmtpd.c.autocreate0 2006-05-23 15:09:36.000000000 +0200 -+++ cyrus-imapd-2.3.7/imap/lmtpd.c 2006-07-23 12:35:41.000000000 +0200 -@@ -115,6 +115,8 @@ - static FILE *spoolfile(message_data_t *msgdata); - static void removespool(message_data_t *msgdata); - -+static int autocreate_inbox(const char *user, const char *domain); -+ - /* current namespace */ - static struct namespace lmtpd_namespace; - -@@ -937,6 +939,86 @@ - exit(code); - } - -+ -+/* -+ * Autocreate Inbox and subfolders upon login -+ */ -+int autocreate_inbox(const char *user, const char *domain) -+{ -+ struct auth_state *auth_state; -+ char inboxname[MAX_MAILBOX_NAME+1]; -+ char *rcpt_userid = NULL; -+ int autocreatequota; -+ int r = 0; -+ -+ if (user == NULL) -+ return IMAP_MAILBOX_NONEXISTENT; -+ -+ if (domain != NULL) { -+ int k; -+ -+ rcpt_userid = (char *) xmalloc((strlen(user) + strlen(domain) + 2) * sizeof(char)); -+ k = strlcpy(rcpt_userid, user, strlen(user) + 1); -+ *(rcpt_userid + k) = '@'; -+ strlcpy(rcpt_userid + k + 1, domain, strlen(domain) + 1); -+ } else { -+ rcpt_userid = (char *) user; -+ } -+ -+ -+ /* -+ * Exclude anonymous -+ */ -+ if (!strcmp(rcpt_userid, "anonymous")) { -+ if (rcpt_userid != user) { -+ free(rcpt_userid); -+ } -+ -+ return IMAP_MAILBOX_NONEXISTENT; -+ } -+ -+ /* -+ * Check for autocreatequota and createonpost -+ */ -+ if (!(autocreatequota = config_getint(IMAPOPT_AUTOCREATEQUOTA)) || -+ !(config_getswitch(IMAPOPT_CREATEONPOST))) { -+ -+ if (rcpt_userid != user) { -+ free(rcpt_userid); -+ } -+ -+ return IMAP_MAILBOX_NONEXISTENT; -+ } -+ -+ -+ /* -+ * Exclude admin's accounts -+ */ -+ auth_state = auth_newstate(rcpt_userid); -+ -+ if (global_authisa(auth_state, IMAPOPT_ADMINS)) { -+ if (rcpt_userid != user) { -+ free(rcpt_userid); -+ } -+ -+ return IMAP_MAILBOX_NONEXISTENT; -+ } -+ -+ r = (*lmtpd_namespace.mboxname_tointernal) (&lmtpd_namespace, -+ "INBOX", rcpt_userid, inboxname); -+ -+ if (!r) -+ r = mboxlist_autocreateinbox(&lmtpd_namespace, rcpt_userid, -+ auth_state, inboxname, autocreatequota); -+ -+ if (rcpt_userid != user) { -+ free(rcpt_userid); -+ } -+ -+ return r; -+} -+ -+ - static int verify_user(const char *user, const char *domain, char *mailbox, - long quotacheck, struct auth_state *authstate) - { -@@ -980,6 +1062,15 @@ - */ - r = mlookup(namebuf, &server, &acl, NULL); - -+ /* If user mailbox does not exist, then invoke autocreate inbox function */ -+ if (r == IMAP_MAILBOX_NONEXISTENT) { -+ r = autocreate_inbox(user, domain); -+ -+ /* Try to locate the mailbox again */ -+ if (!r) -+ r = mlookup(namebuf, &server, &acl, NULL); -+ } -+ - if (r == IMAP_MAILBOX_NONEXISTENT && !user && - config_getswitch(IMAPOPT_LMTP_FUZZY_MAILBOX_MATCH) && - /* see if we have a mailbox whose name is close */ -@@ -1006,6 +1097,7 @@ - aclcheck, (quotacheck < 0) - || config_getswitch(IMAPOPT_LMTP_STRICT_QUOTA) ? - quotacheck : 0); -+ - } - } - ---- cyrus-imapd-2.3.7/imap/mboxlist.c.autocreate0 2006-05-22 22:37:25.000000000 +0200 -+++ cyrus-imapd-2.3.7/imap/mboxlist.c 2006-07-23 12:35:41.000000000 +0200 -@@ -81,6 +81,12 @@ - #include "mboxlist.h" - #include "quota.h" - -+#ifdef USE_SIEVE -+extern int autoadd_sieve(char *userid, -+ const char *source_script); -+#endif -+ -+ - #define DB config_mboxlist_db - #define SUBDB config_subscription_db - -@@ -98,11 +104,29 @@ - static int mboxlist_changequota(const char *name, int matchlen, int maycreate, - void *rock); - -+static int mboxlist_autochangesub(char *name, int matchlen, int maycreate, -+ void *rock); -+ -+static int mboxlist_autosubscribe_sharedfolders(struct namespace *namespace, -+ char *userid, char *auth_userid, -+ struct auth_state *auth_state); -+ - struct change_rock { - struct quota *quota; - struct txn **tid; - }; - -+/* -+ * Struct needed to be passed as void *rock to -+ * mboxlist_autochangesub(); -+ */ -+struct changesub_rock_st { -+ char *userid; -+ char *auth_userid; -+ struct auth_state *auth_state; -+}; -+ -+ - #define FNAME_SUBSSUFFIX ".sub" - - /* -@@ -3241,3 +3265,349 @@ - - return DB->abort(mbdb, tid); - } -+ -+/* -+ * Automatically subscribe user to *ALL* shared folders, -+ * one has permissions to be subscribed to. -+ * INBOX subfolders are excluded. -+ */ -+static int mboxlist_autochangesub(char *name, int matchlen, int maycreate, -+ void *rock) { -+ -+ struct changesub_rock_st *changesub_rock = (struct changesub_rock_st *) rock; -+ char *userid = changesub_rock->userid; -+ char *auth_userid = changesub_rock->auth_userid; -+ struct auth_state *auth_state = changesub_rock->auth_state; -+ int r; -+ -+ -+ if((strlen(name) == 5 && !strncmp(name, "INBOX", 5)) || /* Exclude INBOX */ -+ (strlen(name) > 5 && !strncmp(name, "INBOX.",6)) || /* Exclude INBOX subfolders */ -+ (strlen(name) > 4 && !strncmp(name, "user.", 5))) /* Exclude other users' folders */ -+ return 0; -+ -+ -+ r = mboxlist_changesub(name, userid, auth_state, 1, 0); -+ -+ if (r) { -+ syslog(LOG_WARNING, -+ "autosubscribe: User %s to folder %s, subscription failed: %s", -+ auth_userid, name, error_message(r)); -+ } else { -+ syslog(LOG_NOTICE, -+ "autosubscribe: User %s to folder %s, subscription succeeded", -+ auth_userid, name); -+ } -+ -+ return 0; -+} -+ -+#define SEP '|' -+ -+/* -+ * Automatically subscribe user to a shared folder. -+ * Subscription is done successfully, if the shared -+ * folder exists and the user has the necessary -+ * permissions. -+ */ -+static int mboxlist_autosubscribe_sharedfolders(struct namespace *namespace, -+ char *userid, char *auth_userid, -+ struct auth_state *auth_state) { -+ -+ const char *sub ; -+ char *p, *q, *next_sub; -+ char folder[MAX_MAILBOX_NAME+1], name[MAX_MAILBOX_NAME+1], mailboxname[MAX_MAILBOX_NAME+1]; -+ int len; -+ int r = 0; -+ int subscribe_all_sharedfolders = 0; -+ -+ subscribe_all_sharedfolders = config_getswitch(IMAPOPT_AUTOSUBSCRIBE_ALL_SHAREDFOLDERS); -+ -+ /* -+ * If subscribeallsharedfolders is set to yes in imapd.conf, then -+ * subscribe user to every shared folder one has the apropriate -+ * permissions. -+ */ -+ if(subscribe_all_sharedfolders) { -+ char pattern[MAX_MAILBOX_PATH+1]; -+ struct changesub_rock_st changesub_rock; -+ -+ strcpy(pattern, "*"); -+ changesub_rock.userid = userid; -+ changesub_rock.auth_userid = auth_userid; -+ changesub_rock.auth_state = auth_state; -+ -+ r = mboxlist_findall(namespace, pattern, 0, userid, -+ auth_state, mboxlist_autochangesub, &changesub_rock); -+ -+ return r; -+ } -+ -+ if ((sub=config_getstring(IMAPOPT_AUTOSUBSCRIBESHAREDFOLDERS)) == NULL) -+ return r; -+ -+ next_sub = (char *) sub; -+ while (*next_sub) { -+ for (p = next_sub ; isspace((int) *p) || *p == SEP ; p++); -+ for (next_sub = p ; *next_sub && *next_sub != SEP ; next_sub++); -+ for (q = next_sub ; q > p && (isspace((int) *q) || *q == SEP || !*q) ; q--); -+ if (!*p ) continue; -+ -+ len = q - p + 1; -+ /* Check for folder length */ -+ if (len > sizeof(folder)-1) -+ continue; -+ -+ if (!r) { -+ strncpy(folder, p, len); -+ folder[len] = '\0'; -+ -+ strlcpy(name, namespace->prefix[NAMESPACE_SHARED], sizeof(name)); -+ len = strlcat(name, folder, sizeof(name)); -+ -+ r = (namespace->mboxname_tointernal) (namespace, name, userid, -+ mailboxname); -+ } -+ -+ if (!r) -+ r = mboxlist_changesub(mailboxname, userid, auth_state, 1, 0); -+ -+ if (!r) { -+ syslog(LOG_NOTICE, "autosubscribe: User %s to %s succeeded", -+ userid, folder); -+ } else { -+ syslog(LOG_WARNING, "autosubscribe: User %s to %s failed: %s", -+ userid, folder, error_message(r)); -+ r = 0; -+ } -+ } -+ -+ return r; -+} -+ -+ -+ -+int mboxlist_autocreateinbox(struct namespace *namespace, -+ char *userid, -+ struct auth_state *auth_state, -+ char *mailboxname, int autocreatequota) { -+ char name [MAX_MAILBOX_NAME+1]; -+ char folder [MAX_MAILBOX_NAME+1]; -+ char *auth_userid = NULL; -+ char *partition = NULL; -+ const char *crt; -+ const char *sub; -+ char *p, *q, *next_crt, *next_sub; -+ int len; -+ int r = 0; -+ int numcrt = 0; -+ int numsub = 0; -+#ifdef USE_SIEVE -+ const char *source_script; -+#endif -+ -+ -+ -+ auth_userid = auth_canonuser(auth_state); -+ if (auth_userid == NULL) { -+ /* -+ * Couldn't get cannon userid -+ */ -+ syslog(LOG_ERR, -+ "autocreateinbox: Could not get canonified userid for user %s", userid); -+ return IMAP_PARTITION_UNKNOWN; -+ } -+ -+ /* Added this for debug information. */ -+ syslog(LOG_DEBUG, "autocreateinbox: autocreate inbox for user %s was called", auth_userid); -+ -+ /* -+ * While this is not needed for admins -+ * and imap_admins accounts, it would be -+ * better to separate *all* admins and -+ * proxyservers from normal accounts -+ * (accounts that have mailboxes). -+ * UOA Specific note(1): Even if we do not -+ * exclude these servers-classes here, -+ * UOA specific code, will neither return -+ * role, nor create INBOX, because none of these -+ * administrative accounts belong to the -+ * mailRecipient objectclass, or have imapPartition. -+ * UOA Specific note(2): Another good reason for doing -+ * this, is to prevent the code, from getting into -+ * cyrus_ldap.c because of the continues MSA logins to LMTPd. -+ */ -+ -+ /* -+ * admins and the coresponding imap -+ * service, had already been excluded. -+ */ -+ -+ /* -+ * Do we really need group membership -+ * for admins or service_admins? -+ */ -+ if (global_authisa(auth_state, IMAPOPT_ADMINS)) return 0; -+ -+ /* -+ * Do we really need group membership -+ * for proxyservers? -+ */ -+ if (global_authisa(auth_state, IMAPOPT_PROXYSERVERS)) return 0; -+ -+ /* -+ * Check if user belongs to the autocreate_users group. This option -+ * controls for whom the mailbox may be automatically created. Default -+ * value for this option is 'anyone'. So, if not declared, all mailboxes -+ * will be created. -+ */ -+ if (!global_authisa(auth_state, IMAPOPT_AUTOCREATE_USERS)) { -+ syslog(LOG_DEBUG, "autocreateinbox: User %s does not belong to the autocreate_users. No mailbox is created", -+ auth_userid); -+ return IMAP_MAILBOX_NONEXISTENT; -+ } -+ -+#if 0 -+ /* -+ * Get Partition info or return. -+ * (Here you should propably use -+ * you own "get_partition(char *userid)" -+ * function. Otherwise all new INBOXes will be -+ * created into whatever partition has been declared -+ * as default in your imapd.conf) -+ */ -+ -+ partition = get_partition(userid); -+ if (partition == NULL) { -+ /* -+ * Couldn't get partition info -+ */ -+ syslog(LOG_ERR, -+ "Could not get imapPartition info for user %s", userid); -+ return IMAP_PARTITION_UNKNOWN; -+ } -+#endif -+ -+ r = mboxlist_createmailbox(mailboxname, MAILBOX_FORMAT_NORMAL, NULL, -+ 1, userid, auth_state, 0, 0, 0); -+ -+ if (!r && autocreatequota > 0) -+ r = mboxlist_setquota(mailboxname, autocreatequota, 0); -+ -+ if (!r) -+ r = mboxlist_changesub(mailboxname, userid, -+ auth_state, 1, 1); -+ -+ if (!r) { -+ syslog(LOG_NOTICE, "autocreateinbox: User %s, INBOX was successfully created in partition %s", -+ auth_userid, partition == NULL ? "default" : partition); -+ } else { -+ syslog(LOG_ERR, "autocreateinbox: User %s, INBOX failed. %s", -+ auth_userid, error_message(r)); -+ } -+ -+#if 0 -+ /* Allocated from get_partition, and not needed any more */ -+ free_partition(partition); -+#endif -+ -+ if (r) return r; -+ -+ /* INBOX's subfolders */ -+ if ((crt=config_getstring(IMAPOPT_AUTOCREATEINBOXFOLDERS))) -+ sub=config_getstring(IMAPOPT_AUTOSUBSCRIBEINBOXFOLDERS); -+ -+ /* Roll through crt */ -+ next_crt = (char *) crt; -+ while (next_crt!=NULL && *next_crt) { -+ for (p = next_crt ; isspace((int) *p) || *p == SEP ; p++); -+ for (next_crt = p ; *next_crt && *next_crt != SEP ; next_crt++); -+ for (q = next_crt ; q > p && (isspace((int) *q) || *q == SEP || !*q); q--); -+ -+ if (!*p) continue; -+ -+ len = q - p + 1; -+ -+ /* First time we check for length */ -+ if (len > sizeof(folder) - 5) -+ r = IMAP_MAILBOX_BADNAME; -+ -+ if (!r) { -+ strncpy(folder, p, len); -+ folder[len] = '\0'; -+ -+ strlcpy(name, namespace->prefix[NAMESPACE_INBOX], sizeof(name)); -+ len = strlcat(name, folder, sizeof(name)); -+ } -+ -+ if (!r) -+ r = (namespace->mboxname_tointernal) (namespace, name, userid, -+ mailboxname); -+ if (!r) -+ r = mboxlist_createmailbox(mailboxname, MAILBOX_FORMAT_NORMAL, NULL, -+ 1, userid, auth_state, 0, 0, 0); -+ -+ if (!r) { -+ numcrt++; -+ syslog(LOG_NOTICE, "autocreateinbox: User %s, subfolder %s creation succeeded.", -+ auth_userid, name); -+ } else { -+ syslog(LOG_WARNING, "autocreateinbox: User %s, subfolder %s creation failed. %s", -+ auth_userid, name, error_message(r)); -+ r=0; -+ continue; -+ } -+ -+ /* Roll through sub */ -+ next_sub = (char *) sub; -+ while (next_sub!=NULL && *next_sub) { -+ for (p = next_sub ; isspace((int) *p) || *p == SEP ; p++); -+ for (next_sub = p ; *next_sub && *next_sub != SEP ; next_sub++); -+ for (q = next_sub ; q > p && (isspace((int) *q) || *q == SEP || !*q) ; q--); -+ if (!*p ) continue; -+ -+ len = q - p + 1; -+ -+ if (len != strlen(folder) || strncmp(folder, p, len)) -+ continue; -+ -+ r = mboxlist_changesub(mailboxname, userid, auth_state, 1, 1); -+ -+ if (!r) { -+ numsub++; -+ syslog(LOG_NOTICE,"autocreateinbox: User %s, subscription to %s succeeded", -+ auth_userid, name); -+ } else -+ syslog(LOG_WARNING, "autocreateinbox: User %s, subscription to %s failed. %s", -+ auth_userid, name, error_message(r)); -+ -+ break; -+ } -+ } -+ -+ if (crt!=NULL && *crt) -+ syslog(LOG_INFO, "User %s, Inbox subfolders, created %d, subscribed %d", -+ auth_userid, numcrt, numsub); -+ -+ /* -+ * Check if shared folders are available for subscription. -+ */ -+ mboxlist_autosubscribe_sharedfolders(namespace, userid, auth_userid, auth_state); -+ -+#ifdef USE_SIEVE -+ /* -+ * Here the autocreate sieve script feature is iniated from. -+ */ -+ source_script = config_getstring(IMAPOPT_AUTOCREATE_SIEVE_SCRIPT); -+ -+ if (source_script) { -+ if (!autoadd_sieve(userid, source_script)) -+ syslog(LOG_NOTICE, "autocreate_sieve: User %s, default sieve script creation succeeded", auth_userid); -+ else -+ syslog(LOG_WARNING, "autocreate_sieve: User %s, default sieve script creation failed", auth_userid); -+ } -+#endif -+ -+ return r; -+} -+ ---- cyrus-imapd-2.3.7/imap/mboxlist.h.autocreate0 2005-02-21 20:25:40.000000000 +0100 -+++ cyrus-imapd-2.3.7/imap/mboxlist.h 2006-07-23 12:35:41.000000000 +0200 -@@ -203,4 +203,10 @@ - int mboxlist_commit(struct txn *tid); - int mboxlist_abort(struct txn *tid); - -+int mboxlist_autocreateinbox(struct namespace *namespace, -+ char *userid, -+ struct auth_state *auth_state, -+ char *mailboxname, int autocreatequota); -+ -+ - #endif ---- cyrus-imapd-2.3.7/imap/pop3d.c.autocreate0 2006-05-26 17:50:09.000000000 +0200 -+++ cyrus-imapd-2.3.7/imap/pop3d.c 2006-07-23 12:35:41.000000000 +0200 -@@ -156,6 +156,8 @@ - static char popd_apop_chal[45 + MAXHOSTNAMELEN + 1]; /* */ - static void cmd_apop(char *response); - -+static int autocreate_inbox(char *inboxname, char *userid); -+ - static void cmd_auth(char *arg); - static void cmd_capa(void); - static void cmd_pass(char *pass); -@@ -1224,6 +1226,7 @@ - popd_userid = xstrdup(userbuf); - prot_printf(popd_out, "+OK Name is a valid mailbox\r\n"); - } -+ - } - - void cmd_pass(char *pass) -@@ -1498,6 +1501,43 @@ - } - - /* -+ * Autocreate Inbox and subfolders upon login -+ */ -+int autocreate_inbox(char *inboxname, char *auth_userid) -+{ -+ struct auth_state *auth_state; -+ int autocreatequota; -+ int r; -+ -+ if (inboxname == NULL || auth_userid == NULL) -+ return IMAP_MAILBOX_NONEXISTENT; -+ -+ /* -+ * Exclude anonymous -+ */ -+ if (!strcmp(popd_userid, "anonymous")) -+ return IMAP_MAILBOX_NONEXISTENT; -+ -+ /* -+ * Check for autocreatequota -+ */ -+ if (!(autocreatequota = config_getint(IMAPOPT_AUTOCREATEQUOTA))) -+ return IMAP_MAILBOX_NONEXISTENT; -+ -+ /* -+ * Exclude admin's accounts -+ */ -+ auth_state = auth_newstate(popd_userid); -+ if (global_authisa(auth_state, IMAPOPT_ADMINS)) -+ return IMAP_MAILBOX_NONEXISTENT; -+ -+ r = mboxlist_autocreateinbox(&popd_namespace, auth_userid, -+ auth_state, inboxname, autocreatequota); -+ return r; -+} -+ -+ -+/* - * Complete the login process by opening and locking the user's inbox - */ - int openinbox(void) -@@ -1526,6 +1566,12 @@ - - if (!r) r = mboxlist_detail(inboxname, &type, NULL, NULL, - &server, &acl, NULL); -+ -+ /* Try once again after autocreate_inbox */ -+ if (r == IMAP_MAILBOX_NONEXISTENT && !(r = autocreate_inbox(inboxname, userid))) -+ r = mboxlist_detail(inboxname, &type, NULL, NULL, -+ &server, &acl, NULL); -+ - if (!r && (config_popuseacl = config_getswitch(IMAPOPT_POPUSEACL)) && - (!acl || - !((myrights = cyrus_acl_myrights(popd_authstate, acl)) & ACL_READ))) { ---- cyrus-imapd-2.3.7/notifyd/Makefile.in.autocreate0 2004-05-31 20:22:59.000000000 +0200 -+++ cyrus-imapd-2.3.7/notifyd/Makefile.in 2006-07-23 12:35:41.000000000 +0200 -@@ -69,10 +69,11 @@ - SERVICE=../master/service.o - - IMAP_LIBS = @IMAP_LIBS@ @LIB_RT@ -+SIEVE_LIBS = @SIEVE_LIBS@ - IMAP_COM_ERR_LIBS = @IMAP_COM_ERR_LIBS@ - LIB_WRAP = @LIB_WRAP@ - LIBS = @ZEPHYR_LIBS@ @LIBS@ $(IMAP_COM_ERR_LIBS) --DEPLIBS=../imap/mutex_fake.o ../imap/libimap.a ../lib/libcyrus.a ../lib/libcyrus_min.a @DEPLIBS@ -+DEPLIBS=../imap/mutex_fake.o ../imap/libimap.a $(SIEVE_LIBS) ../lib/libcyrus.a ../lib/libcyrus_min.a @DEPLIBS@ - - PURIFY=/usr/local/bin/purify - PUREOPT=-best-effort ---- cyrus-imapd-2.3.7/notifyd/notifyd.c.autocreate0 2005-04-13 17:43:36.000000000 +0200 -+++ cyrus-imapd-2.3.7/notifyd/notifyd.c 2006-07-23 12:35:41.000000000 +0200 -@@ -97,7 +97,7 @@ - - #define NOTIFY_MAXSIZE 8192 - --int do_notify() -+static int do_notify() - { - struct sockaddr_un sun_data; - socklen_t sunlen = sizeof(sun_data); ---- cyrus-imapd-2.3.7/ptclient/Makefile.in.autocreate0 2006-06-19 18:00:18.000000000 +0200 -+++ cyrus-imapd-2.3.7/ptclient/Makefile.in 2006-07-23 12:35:41.000000000 +0200 -@@ -57,10 +57,11 @@ - AFS_LDFLAGS = @AFS_LDFLAGS@ @COM_ERR_LDFLAGS@ - AFS_LIBS = @AFS_LIBS@ - IMAP_LIBS = @IMAP_LIBS@ @LIB_RT@ -+SIEVE_LIBS = @SIEVE_LIBS@ - LIBS = $(IMAP_LIBS) @COM_ERR_LIBS@ - LIB_SASL = @LIB_SASL@ - LIB_WRAP = @LIB_WRAP@ --DEPLIBS = ../imap/libimap.a ../lib/libcyrus.a ../lib/libcyrus_min.a @DEPLIBS@ -+DEPLIBS = ../imap/libimap.a $(SIEVE_LIBS) ../lib/libcyrus.a ../lib/libcyrus_min.a @DEPLIBS@ - UTIL_LIBS = ../imap/mutex_fake.o ../imap/cli_fatal.o - - LDAP_LIBS=@LDAP_LIBS@ ---- cyrus-imapd-2.3.7/lib/auth.c.autocreate0 2005-02-16 22:06:50.000000000 +0100 -+++ cyrus-imapd-2.3.7/lib/auth.c 2006-07-23 12:35:41.000000000 +0200 -@@ -117,3 +117,11 @@ - - auth->freestate(auth_state); - } -+ -+char *auth_canonuser(struct auth_state *auth_state) -+{ -+ struct auth_mech *auth = auth_fromname(); -+ -+ return auth->auth_canonuser(auth_state); -+} -+ ---- cyrus-imapd-2.3.7/lib/auth.h.autocreate0 2005-02-16 22:06:50.000000000 +0100 -+++ cyrus-imapd-2.3.7/lib/auth.h 2006-07-23 12:35:41.000000000 +0200 -@@ -54,6 +54,7 @@ - const char *identifier); - struct auth_state *(*newstate)(const char *identifier); - void (*freestate)(struct auth_state *auth_state); -+ char *(*auth_canonuser)(struct auth_state *auth_state); - }; - - extern struct auth_mech *auth_mechs[]; -@@ -76,5 +77,6 @@ - const char *identifier); - struct auth_state *auth_newstate(const char *identifier); - void auth_freestate(struct auth_state *auth_state); -+char *auth_canonuser(struct auth_state *auth_state); - - #endif /* INCLUDED_AUTH_H */ ---- cyrus-imapd-2.3.7/lib/auth_krb.c.autocreate0 2005-12-14 14:52:09.000000000 +0100 -+++ cyrus-imapd-2.3.7/lib/auth_krb.c 2006-07-23 12:35:41.000000000 +0200 -@@ -339,6 +339,15 @@ - free((char *)auth_state); - } - -+static char *mycanonuser(struct auth_state *auth_state) -+{ -+ if (auth_state) -+ return auth_state->userid; -+ -+ return NULL; -+} -+ -+ - #else /* HAVE_KRB */ - - static int mymemberof( -@@ -367,6 +376,13 @@ - fatal("Authentication mechanism (krb) not compiled in", EC_CONFIG); - } - -+static char *mycanonuser( -+ struct auth_state *auth_state __attribute__((unused))) -+{ -+ fatal("Authentication mechanism (krb) not compiled in", EC_CONFIG); -+} -+ -+ - #endif - - struct auth_mech auth_krb = -@@ -377,4 +393,5 @@ - &mymemberof, - &mynewstate, - &myfreestate, -+ &mycanonuser, - }; ---- cyrus-imapd-2.3.7/lib/auth_krb5.c.autocreate0 2005-02-16 22:06:50.000000000 +0100 -+++ cyrus-imapd-2.3.7/lib/auth_krb5.c 2006-07-23 12:35:41.000000000 +0200 -@@ -197,6 +197,14 @@ - free(auth_state); - } - -+static char *mycanonuser(struct auth_state *auth_state) -+{ -+ if (auth_state) -+ return auth_state->userid; -+ -+ return NULL; -+} -+ - #else /* HAVE_GSSAPI_H */ - - static int mymemberof( -@@ -225,6 +233,12 @@ - fatal("Authentication mechanism (krb5) not compiled in", EC_CONFIG); - } - -+static char *mycanonuser( -+ struct auth_state *auth_state __attribute__((unused))) -+{ -+ fatal("Authentication mechanism (krb5) not compiled in", EC_CONFIG); -+} -+ - #endif - - struct auth_mech auth_krb5 = -@@ -235,4 +249,5 @@ - &mymemberof, - &mynewstate, - &myfreestate, -+ &mycanonuser, - }; ---- cyrus-imapd-2.3.7/lib/auth_pts.c.autocreate0 2006-03-17 17:05:15.000000000 +0100 -+++ cyrus-imapd-2.3.7/lib/auth_pts.c 2006-07-23 12:35:41.000000000 +0200 -@@ -503,6 +503,14 @@ - free(auth_state); - } - -+static char *mycanonuser(struct auth_state *auth_state) -+{ -+ if (auth_state) -+ return auth_state->userid.id; -+ -+ return NULL; -+} -+ - struct auth_mech auth_pts = - { - "pts", /* name */ -@@ -511,4 +519,5 @@ - &mymemberof, - &mynewstate, - &myfreestate, -+ &mycanonuser, - }; ---- cyrus-imapd-2.3.7/lib/auth_unix.c.autocreate0 2005-02-16 22:06:50.000000000 +0100 -+++ cyrus-imapd-2.3.7/lib/auth_unix.c 2006-07-23 12:35:41.000000000 +0200 -@@ -264,6 +264,16 @@ - free((char *)auth_state); - } - -+static char *mycanonuser(auth_state) -+ struct auth_state *auth_state; -+{ -+ if (auth_state) -+ return auth_state->userid; -+ -+ return NULL; -+} -+ -+ - - struct auth_mech auth_unix = - { -@@ -273,4 +283,5 @@ - &mymemberof, - &mynewstate, - &myfreestate, -+ &mycanonuser, - }; ---- cyrus-imapd-2.3.7/lib/imapoptions.autocreate0 2006-06-27 17:58:42.000000000 +0200 -+++ cyrus-imapd-2.3.7/lib/imapoptions 2006-07-23 12:35:41.000000000 +0200 -@@ -172,6 +172,55 @@ - /* Number of seconds to wait before returning a timeout failure when - performing a client connection (e.g. in a murder environment) */ - -+{ "createonpost", 0, SWITCH } -+/* If yes, when lmtpd receives an incoming mail for an INBOX that does not exist, -+ then the INBOX is automatically created by lmtpd. */ -+ -+{ "autocreateinboxfolders", NULL, STRING } -+/* If a user does not have an INBOX created then the INBOX as well as some INBOX -+ subfolders are created under two conditions. -+ 1. The user logins via the IMAP or the POP3 protocol. (autocreatequota option must have a nonzero value) -+ 2. A message arrives for the user through the LMTPD protocol.(createonpost option must be yes) -+ autocreateinboxfolders is a list of INBOX's subfolders separated by a "|", that -+ are automatically created by the server under the previous two situations. */ -+ -+{ "autosubscribeinboxfolders", NULL, STRING } -+/* 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. */ -+ -+{ "autosubscribesharedfolders", NULL, STRING } -+/* 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. */ -+ -+{ "autosubscribe_all_sharedfolders", 0, SWITCH } -+/* If set to yes, the user is automatically subscribed to all shared folders, one has permission -+ to subscribe to. */ -+ -+{ "autocreate_sieve_script", NULL, STRING } -+/* The full path of a file that contains a sieve script. This script automatically becomes a -+ user's initial default sieve filter script. When this option is not defined, no default -+ sieve filter is created. The file must be readable by the cyrus daemon. */ -+ -+{ "autocreate_sieve_compiledscript", NULL, STRING } -+/* 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 */ -+ -+{ "generate_compiled_sieve_script", 0, SWITCH } -+/* 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_users", "anyone", STRING } -+/* A space separated list of users and/or groups that are allowed their INBOX to be -+ automatically created. */ -+ - { "configdirectory", NULL, STRING } - /* The pathname of the IMAP configuration directory. This field is - required. */ ---- /dev/null 2006-07-21 18:50:55.248316500 +0200 -+++ cyrus-imapd-2.3.7/lib/imapoptions.orig 2006-07-23 12:35:41.000000000 +0200 -@@ -0,0 +1,1006 @@ -+# 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) 1998-2000 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 other legal -+.\" details, please contact -+.\" Office of Technology Transfer -+.\" Carnegie Mellon University -+.\" 5000 Forbes Avenue -+.\" Pittsburgh, PA 15213-3890 -+.\" (412) 268-4387, fax: (412) 268-7395 -+.\" tech-transfer@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. -+.\" -+.\" $Id: cyrus-imapd-2.3.7-autocreate-0.10-0.diff,v 1.1 2006/07/23 12:03:32 prockai Exp $ -+.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 -+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. -+.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 -+``''. Some options default to the empty string, these -+are listed with ``''. -+*/ -+ -+# OPTIONS -+ -+{ "admins", "", STRING } -+/* 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 } -+/* 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 } -+/* Cell to use for AFS PTS lookups. Defaults to the local cell. */ -+ -+{ "allowallsubscribe", 0, SWITCH } -+/* 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 (eg. -+ Microsoft Outlook). */ -+ -+{ "allowanonymouslogin", 0, SWITCH } -+/* Permit logins by the user "anonymous" using any password. Also -+ allows use of the SASL ANONYMOUS mechanism. */ -+ -+{ "allowapop", 1, SWITCH } -+/* 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 (eg. sasldb), and that the system can provide enough entropy -+ (eg. from /dev/urandom) to create a challenge in the banner. */ -+ -+{ "allownewnews", 0, SWITCH } -+/* 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", 1, SWITCH } -+/* Allow the use of cleartext passwords on the wire. */ -+ -+{ "allowusermoves", 0, SWITCH } -+/* 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 can not 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", 0, SWITCH } -+/* 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. */ -+ -+{ "annotation_db", "skiplist", STRINGLIST("berkeley", "berkeley-hash", "skiplist")} -+/* The cyrusdb backend to use for mailbox annotations. */ -+ -+{ "auth_mech", "unix", STRINGLIST("unix", "pts", "krb", "krb5")} -+/* The authorization mechanism to use. */ -+ -+{ "autocreatequota", 0, INT } -+/* If nonzero, normal users may create their own IMAP accounts by -+ creating the mailbox INBOX. The user's quota is set to the value -+ if it is positive, otherwise the user has unlimited quota. */ -+ -+{ "berkeley_cachesize", 512, INT } -+/* Size (in kilobytes) of the shared memory buffer pool (cache) used -+ by the berkeley environment. The minimum allowed value is 20. The -+ maximum allowed value is 4194303 (4GB). */ -+ -+{ "berkeley_locks_max", 50000, INT } -+/* Maximum number of locks to be held or requested in the berkeley -+ environment. */ -+ -+{ "berkeley_txns_max", 100, INT } -+/* Maximum number of transactions to be supported in the berkeley -+ environment. */ -+ -+{ "client_timeout", 10, INT } -+/* Number of seconds to wait before returning a timeout failure when -+ performing a client connection (e.g. in a murder environment) */ -+ -+{ "configdirectory", NULL, STRING } -+/* The pathname of the IMAP configuration directory. This field is -+ required. */ -+ -+{ "debug_command", NULL, STRING } -+/* 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 (without path). The second is the pid (integer) and the third -+ is the service ID. Example: /usr/local/bin/gdb /usr/cyrus/bin/%s %d */ -+ -+{ "defaultacl", "anyone lrs", STRING } -+/* The Access Control List (ACL) placed on a newly-created (non-user) -+ mailbox that does not have a parent mailbox. */ -+ -+{ "defaultdomain", NULL, STRING } -+/* The default domain for virtual domain support */ -+ -+{ "defaultpartition", "default", STRING } -+/* The partition name used by default for new mailboxes. */ -+ -+{ "deleteright", "c", STRING } -+/* 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. */ -+ -+{ "duplicate_db", "berkeley-nosync", STRINGLIST("berkeley", "berkeley-nosync", "berkeley-hash", "berkeley-hash-nosync", "skiplist")} -+/* The cyrusdb backend to use for the duplicate delivery suppression -+ and sieve. */ -+ -+{ "duplicatesuppression", 1, SWITCH } -+/* 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. */ -+ -+{ "expunge_mode", "immediate", ENUM("immediate", "delayed") } -+/* The mode in which messages (and their corresponding cache entries) -+ are expunged. "Immediate" mode is the default behavior in which the -+ message files and cache entries are purged at the time of the -+ EXPUNGE. In "delayed" mode, the messages are removed from the -+ mailbox index at the time of the EXPUNGE (hiding them from the -+ client), but the message files and cache entries are left behind, -+ to be purged at a later time by "cyr_expire". 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. */ -+ -+{ "flushseenstate", 0, SWITCH } -+/* If enabled, changes to the seen state will be flushed to disk -+ immediately, otherwise changes will be cached and flushed when the -+ mailbox is closed. This option may be used to fix the problem of -+ previously read messages being marked as unread in Microsoft -+ Outlook, at the expense of a loss of performance/scalability. */ -+ -+{ "foolstupidclients", 0, SWITCH } -+/* 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 } -+/* 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 _mechs option to set preferred -+ mechanisms for a specific host */ -+ -+{ "fulldirhash", 0, SWITCH } -+/* If enabled, uses an improved directory hashing scheme which hashes -+ the entire username instead of using just the first letter. 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 can NOT be changed on a live system. The -+ server must be quiesced and then the directories moved with the -+ \fBrehash\fR utility. */ -+ -+{ "hashimapspool", 0, SWITCH } -+/* 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 } -+/* 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 } -+/* The password to use for authentication to the backend server hostname -+ (where hostname is the short hostname of the server) - Cyrus Murder */ -+ -+{ "idlesocket", "{configdirectory}/socket/idle", STRING } -+/* Unix domain socket that idled listens on. */ -+ -+{ "ignorereference", 0, SWITCH } -+/* For backwards compatibility with Cyrus 1.5.10 and earlier -- ignore -+ the reference argument in LIST or LSUB commands. */ -+ -+{ "imapidlepoll", 60, INT } -+/* The interval (in seconds) for polling for mailbox changes and -+ ALERTs while running the IDLE command. This option is used when -+ idled is not enabled or can not be contacted. The minimum value is -+ 1. A value of 0 will disable IDLE. */ -+ -+{ "imapidresponse", 1, SWITCH } -+/* 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 } -+/* 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. */ -+ -+{ "implicit_owner_rights", "lca", STRING } -+/* 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 } -+/* Directive which includes the specified file as part of the -+ configuration. If the path to the file is not absolute, CYRUS_PATH -+ is prepended. */ -+ -+{ "ldap_authz", NULL, STRING } -+/* SASL authorization ID for the LDAP server */ -+ -+{ "ldap_base", "", STRING } -+/* Contains the LDAP base dn for the LDAP ptloader module */ -+ -+{ "ldap_bind_dn", NULL, STRING } -+/* 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") } -+/* Specify how aliases dereferencing is handled during search. */ -+ -+{ "ldap_filter", "(uid=%u)", STRING } -+/* 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 -+ %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 } -+/* LDAP base dn for ldap_group_filter. */ -+ -+{ "ldap_group_filter", "(cn=%u)", STRING } -+/* Specify a filter that searches for group identifiers. -+ See ldap_filter for more options. */ -+ -+{ "ldap_group_scope", "sub", STRINGLIST("sub", "one", "base") } -+/* Specify search scope for ldap_group_filter. */ -+ -+{ "ldap_id", NULL, STRING } -+/* SASL authentication ID for the LDAP server */ -+ -+{ "ldap_mech", NULL, STRING } -+/* SASL mechanism for LDAP authentication */ -+ -+{ "ldap_member_attribute", NULL, STRING } -+/* See ldap_member_method. */ -+ -+{ "ldap_member_base", "", STRING } -+/* LDAP base dn for ldap_member_filter. */ -+ -+{ "ldap_member_filter", "(member=%D)", STRING } -+/* Specify a filter for "ldap_member_method: filter". -+ See ldap_filter for more options. */ -+ -+{ "ldap_member_method", "attribute", STRINGLIST("attribute", "filter") } -+/* 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") } -+/* Specify search scope for ldap_member_filter. */ -+ -+{ "ldap_password", NULL, STRING } -+/* Password for the connection to the LDAP server (SASL and simple bind). -+ Do not use for anonymous simple binds */ -+ -+{ "ldap_realm", NULL, STRING } -+/* SASL realm for LDAP authentication */ -+ -+{ "ldap_referrals", 0, SWITCH } -+/* Specify whether or not the client should follow referrals. */ -+ -+{ "ldap_restart", 1, SWITCH } -+/* Specify whether or not LDAP I/O operations are automatically restarted -+ if they abort prematurely. */ -+ -+{ "ldap_sasl", 1, SWITCH } -+/* Use SASL for LDAP binds in the LDAP PTS module. */ -+ -+{ "ldap_sasl_authc", NULL, STRING } -+/* Deprecated. Use ldap_id */ -+ -+{ "ldap_sasl_authz", NULL, STRING } -+/* Deprecated. Use ldap_authz */ -+ -+{ "ldap_sasl_mech", NULL, STRING } -+/* Deprecated. Use ldap_mech */ -+ -+{ "ldap_sasl_password", NULL, STRING } -+/* Deprecated. User ldap_password */ -+ -+{ "ldap_sasl_realm", NULL, STRING } -+/* Deprecated. Use ldap_realm */ -+ -+{ "ldap_scope", "sub", STRINGLIST("sub", "one", "base") } -+/* Specify search scope. */ -+ -+{ "ldap_servers", "ldap://localhost/", STRING } -+/* Deprecated. Use ldap_uri */ -+ -+{ "ldap_size_limit", 1, INT } -+/* Specify a number of entries for a search request to return. */ -+ -+{ "ldap_start_tls", 0, SWITCH } -+/* Use StartTLS extended operation. Do not use ldaps: ldap_uri when -+ this option is enabled. */ -+ -+{ "ldap_time_limit", 5, INT } -+/* Specify a number of seconds for a search request to complete. */ -+ -+{ "ldap_timeout", 5, INT } -+/* Specify a number of seconds a search can take before timing out. */ -+ -+{ "ldap_tls_cacert_dir", NULL, STRING } -+/* Path to directory with CA (Certificate Authority) certificates. */ -+ -+{ "ldap_tls_cacert_file", NULL, STRING } -+/* File containing CA (Certificate Authority) certificate(s). */ -+ -+{ "ldap_tls_cert", NULL, STRING } -+/* File containing the client certificate. */ -+ -+{ "ldap_tls_check_peer", 0, SWITCH } -+/* Require and verify server certificate. If this option is yes, -+ you must specify ldap_tls_cacert_file or ldap_tls_cacert_dir. */ -+ -+{ "ldap_tls_ciphers", NULL, STRING } -+/* List of SSL/TLS ciphers to allow. The format of the string is -+ described in ciphers(1). */ -+ -+{ "ldap_tls_key", NULL, STRING } -+/* File containing the private client key. */ -+ -+{ "ldap_uri", NULL, STRING } -+/* Contains a list of the URLs of all the LDAP servers when using the -+ LDAP PTS module. */ -+ -+{ "ldap_version", 3, INT } -+/* Specify the LDAP protocol version. If ldap_start_tls and/or -+ ldap_use_sasl are enabled, ldap_version will be automatically -+ set to 3. */ -+ -+{ "lmtp_downcase_rcpt", 0, SWITCH } -+/* If enabled, lmtpd will convert the recipient address to lowercase -+ (up to a '+' character, if present). */ -+ -+{ "lmtp_fuzzy_mailbox_match", 0, SWITCH } -+/* 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 } -+/* 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_strict_quota", 0, SWITCH } -+/* 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. */ -+ -+{ "lmtpsocket", "{configdirectory}/socket/lmtp", STRING } -+/* Unix domain socket that lmtpd listens on, used by deliver(8). This should -+ match the path specified in cyrus.conf(5). */ -+ -+# xxx how does this tie into virtual domains? -+{ "loginrealms", "", STRING } -+/* 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 } -+/* If enabled, any authentication identity which has \fBa\fR rights on a -+ user's INBOX may log in as that user. */ -+ -+{ "logtimestamps", 0, SWITCH } -+/* Include notations in the protocol telemetry logs indicating the number of -+ seconds since the last command or response. */ -+ -+{ "mailnotifier", NULL, STRING } -+/* Notifyd(8) method to use for "MAIL" notifications. If not set, "MAIL" -+ notifications are disabled. */ -+ -+{ "maxmessagesize", 0, INT } -+/* 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). */ -+ -+{ "mboxkey_db", "skiplist", STRINGLIST("berkeley", "skiplist") } -+/* The cyrusdb backend to use for mailbox keys. */ -+ -+{ "mboxlist_db", "skiplist", STRINGLIST("flat", "berkeley", "berkeley-hash", "skiplist")} -+/* The cyrusdb backend to use for the mailbox list. */ -+ -+{ "metapartition_files", "", BITFIELD("header", "index", "cache", "expunge", "squat") } -+/* 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 } -+/* 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 } -+/* The SASL username (Authentication Name) to use when authenticating to the -+ mupdate server (if needed). */ -+ -+{ "mupdate_config", "standard", ENUM("standard", "unified", "replicated") } -+/* 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. */ -+ -+{ "md5_dir", NULL, STRING } -+/* Top level directory for MD5 store manipulated by make_md5. File -+ structure within this directory is one file for each user on the system, -+ hashed on the first letter of the userid (e.g: /var/imap/md5/d/dpc22). */ -+ -+{ "md5_user_map", NULL, STRING } -+/* Map file (cdb) to allow partial make_md5 runs. Maps username to UID */ -+ -+# xxx badly worded -+{ "mupdate_connections_max", 128, INT } -+/* 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 } -+/* The SASL password (if needed) to use when authenticating to the -+ mupdate server. */ -+ -+{ "mupdate_port", 3905, INT } -+/* The port of the mupdate server for the Cyrus Murder */ -+ -+{ "mupdate_realm", NULL, STRING } -+/* The SASL realm (if needed) to use when authenticating to the mupdate -+ server. */ -+ -+{ "mupdate_retry_delay", 20, INT } -+/* The base time to wait between connection retries to the mupdate server. */ -+ -+{ "mupdate_server", NULL, STRING } -+/* The mupdate server for the Cyrus Murder */ -+ -+{ "mupdate_username", "", STRING } -+/* The SASL username (Authorization Name) to use when authenticating to -+ the mupdate server */ -+ -+{ "mupdate_workers_max", 50, INT } -+/* The maximum number of mupdate worker threads (overall) */ -+ -+{ "mupdate_workers_maxspare", 10, INT } -+/* The maximum number of idle mupdate worker threads */ -+ -+{ "mupdate_workers_minspare", 2, INT } -+/* The minimum number of idle mupdate worker threads */ -+ -+{ "mupdate_workers_start", 5, INT } -+/* The number of mupdate worker threads to start */ -+ -+{ "netscapeurl", "http://asg.web.cmu.edu/cyrus/imapd/netscape-admin.html", STRING } -+/* If enabled at compile time, this specifies a URL to reply when -+ Netscape asks the server where the mail administration HTTP server -+ is. The default is a site at CMU with a hopefully informative -+ message; administrators should set this to a local resource with -+ some information of greater use. */ -+ -+{ "newsmaster", "news", STRING } -+/* 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 } -+/* 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 } -+/* 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 a \fITo:\fR header to each -+ incoming usenet article. This \fITo:\fR header will contain email -+ delivery addresses corresponding to each newsgroup in the -+ \fINewsgroups:\fR header. By default, a \fITo:\fR header is not -+ added to usenet articles. */ -+ -+{ "newsprefix", NULL, STRING } -+/* Prefix to be prepended to newsgroup names to make the corresponding -+ IMAP mailbox names. */ -+ -+{ "nntptimeout", 3, INT } -+/* Set the length of the NNTP server's inactivity autologout timer, -+ in minutes. The minimum value is 3, the default. */ -+ -+{ "notifysocket", "{configdirectory}/socket/notify", STRING } -+/* Unix domain socket that the new mail notification daemon listens on. */ -+ -+# Commented out - there's no such thing as "partition-name", but we need -+# this for the man page -+# { "partition-name", NULL, STRING } -+/* The pathname of the partition \fIname\fR. At least one field, for the -+ partition named in the \fBdefaultpartition\fR option, is required. -+ For example, if the value of the \fBdefaultpartion\fR option is -+ \fBdefault\fR, then the \fBpartition-default\fR field is required. */ -+ -+{ "plaintextloginpause", 0, INT } -+/* Number of seconds 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.) */ -+ -+{ "plaintextloginalert", NULL, STRING } -+/* Message to send to client after a successful plaintext login. */ -+ -+{ "popexpiretime", -1, INT } -+/* The number of days 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). "NEVER", the default, may be specified with a negative -+ number. 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. */ -+ -+{ "popminpoll", 0, INT } -+/* Set the minimum amount of time the server forces users to wait -+ between successive POP logins, in minutes. */ -+ -+{ "popsubfolders", 0, SWITCH } -+/* Allow access to subfolders of INBOX via POP3 by using -+ userid+subfolder syntax as the authentication/authorization id. */ -+ -+{ "poppollpadding", 1, INT } -+/* 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", 10, INT } -+/* Set the length of the POP server's inactivity autologout timer, -+ in minutes. The minimum value is 10, the default. */ -+ -+{ "popuseacl", 0, SWITCH } -+/* 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' -+ and 'd' for the owner of the mailbox. The 'r' right allows the -+ user to open the mailbox and list/retrieve messages. The 'd' right -+ allows the user to delete messages. */ -+ -+{ "postmaster", "postmaster", STRING } -+/* Username that is used as the 'From' address in rejection MDNs produced -+ by sieve. */ -+ -+{ "postspec", NULL, STRING } -+ -+{ "postuser", "", STRING } -+/* 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. */ -+ -+{ "proxy_authname", "proxy", STRING } -+/* The authentication name to use when authenticating to a backend server -+ in the Cyrus Murder. */ -+ -+{ "proxy_password", NULL, STRING } -+/* 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 } -+/* The authentication realm to use when authenticating to a backend server -+ in the Cyrus Murder */ -+ -+{ "proxyd_allow_status_referral", 0, SWITCH } -+/* 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. */ -+ -+{ "proxyservers", NULL, STRING } -+/* 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. */ -+ -+{ "pts_module", "afskrb", STRINGLIST("afskrb", "ldap") } -+/* The PTS module to use. */ -+ -+{ "ptloader_sock", NULL, STRING } -+/* Unix domain socket that ptloader listens on. -+ (defaults to configdir/ptclient/ptsock) */ -+ -+{ "ptscache_db", "berkeley", STRINGLIST("berkeley", "berkeley-hash", "skiplist")} -+/* The cyrusdb backend to use for the pts cache. */ -+ -+{ "ptscache_timeout", 10800, INT } -+/* The timeout (in seconds) for the PTS cache database when using the -+ auth_krb_pts authorization method (default: 3 hours). */ -+ -+{ "ptskrb5_convert524", 1, SWITCH } -+/* 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 } -+/* 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) */ -+ -+{ "quota_db", "quotalegacy", STRINGLIST("flat", "berkeley", "berkeley-hash", "skiplist", "quotalegacy")} -+/* The cyrusdb backend to use for quotas. */ -+ -+{ "quotawarn", 90, INT } -+/* The percent of quota utilization over which the server generates -+ warnings. */ -+ -+{ "quotawarnkb", 0, INT } -+/* The maximum amount of free space (in kB) in which to give a quota -+ warning (if this value is 0, or if the quota is smaller than this -+ amount, than warnings are always given). */ -+ -+{ "reject8bit", 0, SWITCH } -+/* If enabled, lmtpd rejects messages with 8-bit characters in the -+ headers. Otherwise, 8-bit characters are changed to `X'. (A -+ proper solution to non-ASCII characters in headers is offered by -+ RFC 2047 and its predecessors.) */ -+ -+{ "rfc2046_strict", 0, SWITCH } -+/* 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. */ -+ -+{ "rfc3028_strict", 1, SWITCH } -+/* 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. */ -+ -+# Commented out - used by libsasl -+# { "sasl_auto_transition", 0, SWITCH } -+/* If enabled, the SASL library will automatically create authentication -+ secrets when given a plaintext password. See the SASL documentation. */ -+ -+{ "sasl_maximum_layer", 256, INT } -+/* Maximum SSF (security strength factor) that the server will allow a -+ client to negotiate. */ -+ -+{ "sasl_minimum_layer", 0, INT } -+/* 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 } -+/* Any SASL option can be set by preceding it with "sasl_". This -+ file overrides the SASL configuration file. */ -+ -+# Commented out - used by libsasl -+# { "sasl_pwcheck_method", NULL, STRING } -+/* The mechanism used by the server to verify plaintext passwords. -+ Possible values include "auxprop", "saslauthd", and "pwcheck". */ -+ -+{ "seenstate_db", "skiplist", STRINGLIST("flat", "berkeley", "berkeley-hash", "skiplist")} -+/* The cyrusdb backend to use for the seen state. */ -+ -+{ "sendmail", "/usr/lib/sendmail", STRING } -+/* The pathname of the sendmail executable. Sieve invokes sendmail -+ for sending rejections, redirects and vacation responses. */ -+ -+{ "servername", NULL, STRING } -+/* 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. */ -+ -+{ "sharedprefix", "Shared Folders", STRING } -+/* If using the alternate IMAP namespace, the prefix for the shared -+ namespace. The hierarchy delimiter will be automatically appended. */ -+ -+{ "sieve_extensions", "fileinto reject vacation imapflags notify envelope relational regex subaddress copy", BITFIELD("fileinto", "reject", "vacation", "imapflags", "notify", "include", "envelope", "body", "relational", "regex", "subaddress", "copy") } -+/* 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 } -+/* Maximum size (in kilobytes) any sieve script can be, enforced at -+ submission by timsieved(8). */ -+ -+{ "sieve_maxscripts", 5, INT } -+/* Maximum number of sieve scripts any user may have, enforced at -+ submission by timsieved(8). */ -+ -+{ "sievedir", "/usr/sieve", STRING } -+/* If sieveusehomedir is false, this directory is searched for Sieve -+ scripts. */ -+ -+{ "sievenotifier", NULL, STRING } -+/* 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 } -+/* If enabled, lmtpd will look for Sieve scripts in user's home -+ directories: ~user/.sieve. */ -+ -+{ "singleinstancestore", 1, SWITCH } -+/* 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_unsafe", 0, SWITCH } -+/* If enabled, this option forces the skiplist cyrusdb backend to -+ not sync writes to the disk. Enabling this option is NOT RECOMMENDED. */ -+ -+{ "soft_noauth", 1, SWITCH } -+/* If enabled, lmtpd returns temporary failures if the client does not -+ successfully authenticate. Otherwise lmtpd returns permanent failures -+ (causing the mail to bounce immediately). */ -+ -+{ "srvtab", "", STRING } -+/* 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 } -+/* 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", "berkeley", "berkeley-hash", "skiplist")} -+/* The cyrusdb backend to use for the subscriptions list. */ -+ -+{ "sync_authname", NULL, STRING } -+/* The authentication name to use when authenticating to a sync server. */ -+ -+{ "sync_host", NULL, STRING } -+/* Name of the host (replica running sync_server(8)) to which -+ replication actions will be sent by sync_client(8). */ -+ -+{ "sync_log", 0, SWITCH } -+/* 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_machineid", -1, INT } -+/* Machine ID of this server which must be unique within a cluster. -+ Any negative number, the default, will disable the use of UUIDs for -+ replication. */ -+ -+{ "sync_password", NULL, STRING } -+/* The default password to use when authenticating to a sync server. */ -+ -+{ "sync_realm", NULL, STRING } -+/* The authentication realm to use when authenticating to a sync server. */ -+ -+{ "sync_repeat_interval", 1, INT } -+/* Minimum interval (in seconds) between replication runs in rolling -+ replication mode. If a replication run takes longer than this -+ time, we repeat immediately. */ -+ -+{ "sync_shutdown_file", NULL, STRING } -+/* Simple latch used to tell sync_client(8) that it should shut down at the -+ next opportunity. Safer than sending signals to running processes */ -+ -+{ "syslog_prefix", NULL, STRING } -+/* String to be appended to the process name in syslog entries. */ -+ -+{ "temp_path", "/tmp", STRING } -+/* The pathname to store temporary files in */ -+ -+{ "timeout", 30, INT } -+/* The length of the IMAP server's inactivity autologout timer, -+ in minutes. The minimum value is 30, the default. */ -+ -+{ "tls_ca_file", NULL, STRING } -+/* File containing one or more Certificate Authority (CA) certificates. */ -+ -+{ "tls_ca_path", NULL, STRING } -+/* Path to directory with certificates of CAs. This directory must -+ have filenames with the hashed value of the certificate (see -+ openssl(XXX)). */ -+ -+{ "tlscache_db", "berkeley-nosync", STRINGLIST("berkeley", "berkeley-nosync", "berkeley-hash", "berkeley-hash-nosync", "skiplist")} -+/* The cyrusdb backend to use for the TLS cache. */ -+ -+{ "tls_cert_file", NULL, STRING } -+/* File containing the certificate presented for server authentication -+ during STARTTLS. A value of "disabled" will disable SSL/TLS. */ -+ -+{ "tls_cipher_list", "DEFAULT", STRING } -+/* The list of SSL/TLS ciphers to allow. The format of the string is -+ described in ciphers(1). */ -+ -+{ "tls_key_file", NULL, STRING } -+/* File containing the private key belonging to the server -+ certificate. A value of "disabled" will disable SSL/TLS. */ -+ -+{ "tls_require_cert", 0, SWITCH } -+/* Require a client certificate for ALL services (imap, pop3, lmtp, sieve). */ -+ -+{ "tls_session_timeout", 1440, INT } -+/* The length of time (in minutes) that a TLS session will be cached -+ for later reuse. The maximum value is 1440 (24 hours), the -+ default. A value of 0 will disable session caching. */ -+ -+{ "umask", "077", STRING } -+/* The umask value used by various Cyrus IMAP programs. */ -+ -+{ "username_tolower", 1, SWITCH } -+/* Convert usernames to all lowercase before login/authenticate. This -+ is useful with authentication backends which ignore case during -+ username lookups (such as LDAP). */ -+ -+{ "userprefix", "Other Users", STRING } -+/* 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 } -+/* 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", 0, SWITCH } -+/* Use the UNIX separator character '/' for delimiting levels of -+ mailbox hierarchy. The default is to use the netnews separator -+ character '.'. */ -+ -+{ "virtdomains", "off", ENUM("off", "userid", "on") } -+/* Enable virtual domain support. If enabled, the user's domain will -+ be determined by splitting a fully qualified userid at the last '@' -+ or '%' symbol. If the userid is unqualified, and the virtdomains -+ option is set to "on", then the domain will be determined by doing -+ a reverse lookup on the IP address of the incoming network -+ interface, otherwise the user is assumed to be in the default -+ domain (if set). */ -+ -+/* -+.SH SEE ALSO -+.PP -+\fBimapd(8)\fR, \fBpop3d(8)\fR, \fBnntpd(8)\fR, \fBlmtpd(8)\fR, -+\fBtimsieved(8)\fR, \fBidled(8)\fR, \fBnotifyd(8)\fR, -+\fBdeliver(8)\fR, \fBmaster(8)\fR, \fBciphers(1)\fR -+*/ ---- /dev/null 2006-07-21 18:50:55.248316500 +0200 -+++ cyrus-imapd-2.3.7/README.autocreate 2006-07-23 12:35:41.000000000 +0200 -@@ -0,0 +1,211 @@ -+Cyrus IMAP autocreate Inbox patch -+---------------------------------- -+ -+NOTE : This patch has been created at the University of Athens. For more info, as well -+as more patches on Cyrus IMAPD server, please visit http://email.uoa.gr/ -+ -+The design of Cyrus IMAP server does not predict the automatic creation of users' -+INBOX folders. The creation of a user's INBOX is considered to be an external task, -+that has to be completed as part of the user email account creation procedure. -+Hence, to create a new email account the site administrator has to: -+ -+ a) Include the new account in the user database for the authentication procedure -+ (e.g. sasldb, shadow, mysql, ldap). -+ b) Create the corresponding INBOX folder. -+ -+Alternatively, the user, if succesfully authenticated, may create his own INBOX folder, -+as long as the configuration of the site allows it (see "autocreatequota" in imapd.conf). -+Unlike what not careful readers may think, enabling the "autocreatequota" option, doesn't -+lead to the automatic INBOX folder creation by Cyrus IMAP server. -+In fact, "autocreate" means that the IMAP clients are allowed to automatically create -+the user INBOX. -+ -+This patch adds the functionality of automatic creation of the users' INBOX folders into -+the Cyrus IMAP server. It is implemented as two features, namely the "create on login" -+and "create on post". -+ -+ -+ -+Create on login -+=============== -+This feauture provides automatic creation of a user's INBOX folder when all of the -+following requirements are met: -+ -+i) The user has succesfully passed the authentication procedure. -+ -+ii) The user's authorisation ID (typically the same as the user's -+authentication ID) doesn't belong to the imap_admins or admins -+accounts (see imapd.conf). -+ -+iii) The "autocreatequota" option in the imap configuration file -+has been set to a non zero value. -+ -+iv) The corresponding to the user's authorisation ID INBOX folder -+does not exist. -+ -+The user's first login is the most typical case when all four requirements are met. -+Note that if the authenticated ID is allowed to proxy to another account for which -+all of the above requirements are met, the corresponding INBOX folder for that account -+will be created. -+ -+ -+ -+Create on post -+============== -+This feauture provides automatic creation of a user's INBOX folder when all of the -+following requirements are met. -+ -+i) An email message addressed to the user has been received. -+ -+ii) The recipient is not any of the imap_admins or admins accounts. -+Note that passing emails to admins or imap_admins accounts from -+the MTA to LMTP should be avoided in any case. -+ -+iii) The recipient's INBOX does not exist. -+ -+iv) The "autocreatequota" option in the imap configuration file -+has been set to a non zero value. -+ -+v) The "createonpost" option in the imap configuration file -+has been switched on. -+ -+ -+Besides the automatic creation of INBOX folder, additional functionalities are -+provided: -+ -+ (A) Automatic creation of INBOX subfolders controlled by "autocreateinboxfolders" -+configuration option. eg -+ -+autocreateinboxfolders: sent|drafts|spam|templates -+ -+ (B) Automatic subscription of INBOX subfolders controlled by "autosubscribeinboxfolders" -+configuration option. eg -+ -+autosubscribeinboxfolders: sent|spam -+ -+Obviously, only subscription to subfolders included in the "autocreateinboxfolder" -+list is meaningful. -+ -+ (C) Automatic subscription to shared folders (bulletin boards). The user gets -+automatically subscribed to the shared folders declared in the "autosubscribesharedfolders" -+configuration option in imapd.conf. -+eg autosubscribesharedfolders: public_folder | public_folder.subfolder -+ -+In order the above action to succeed, the shared folder has to pre-exist the INBOX creation -+and the user must have the appropriate permissions in order to be able to subscribe to the -+shared folder. -+ -+* A new config option has been added. 'autosubscribe_all_sharedfolders' is a yes/no -+option. When set to yes, the user is automatically subscribed to all shared folders one -+has permission to subscribe to. Please, note that when this option is set to yes, then -+'autosubscribesharedfolders' option is overriden. -+ -+ (D) Automatic creation of a predefined default sieve script. -+ -+This is very useful when a default sieve script is used for every user. Usually, a -+default anti-spam script may me be written in a file and copied to each user -+sieve scripts upon the INBOX creation. The imapd.conf options that have been added -+are 'autocreate_sieve_script', 'autocreate_sieve_compiledscript' and -+'generate_compiled_sieve_script'. -+ -+autocreate_sieve_script configuration option refers to the full path of the file -+that contains the sieve script. The default value is null and if no file is defined, -+then no default script is created upon INBOX creation. (The feature is disabled) -+eg autocreate_sieve_script: /etc/default_sieve_script -+ -+autocreate_sieve_compiledscript configuration option refers to the full path of the -+file that contains the bytecode compiled sieve script. If this filename is defined -+in imapd.conf and the file exists, then it is automatically copied in the user's sieve -+directory. If it is not defined, then a bytecode sieve script gets on the fly compiled -+by the daemon. -+eg autocreate_sieve_compiledscript: /etc/default_sieve_script.bc -+ -+generate_compiled_sieve_script is a boolean option that triggers the compilation of the -+source sieve script to bytecode sieve script. The file that the bytecode script will -+be saved is pointed by autocreate_sieve_compiledscript. -+ -+Ways of compiling a sieve script : -+1. Compile a sieve script using the standard sievec utility, distributed by CMU -+2. Compile a sieve script using the compile_sieve utility, released by UoA. This -+ tool is almost identical to the sievec utility, with the difference that it -+ reads the input and output file from autocreate_sieve_script and -+ autocreate_sieve_compiledscript options in imapd.conf -+3. Let cyrus create a compiled sieve script using a source script. Cyrus can be -+ instructed to save the compiled script any time a compiled script does not exist. -+ -+NOTES : -+1. In order this functionality to work, the following requirements must have been met: -+ - 'sieveusehomedir' option must be 'no' in the configuration (default). -+ - 'sievedir' option must have a valid value. -+2. Currently, this patch checks the validity of the source script while generating a -+ bytecode compiled script, but not the validity of the bytecode sieve script file. -+ The administrator should make sure that the provided files contain a valid sieve -+ script as well as the compiled script is updated every time the source script changes. -+ -+ -+ (E) The administrator may control for which users and/or groups may the INBOXes -+automatically be created. The autocreate_users option restricts the groups -+for which the patch will create the mailboxes. -+ -+The default value of autocreate_users is anyone. So, if not set at all, the patch will -+work for all users. However, one may set: -+ -+autocreate_users: user1 user2 group:group1 group:group2 -+ -+In that case, the INBOX will be created only for user1, user2 and the users that belong -+to group1 and group2. -+ -+More refined control per service is provided by the options imap_autocreate_users, -+pop3_autocreate_users and lmtp_autocreate_users. These options override the -+autocreate_users option and offer per service control. -+ -+Example: -+One may want to restrict the create on post functionality only for a specific group -+of users. To achieve this, the following lines must be added in the imapd.conf file: -+ -+createonpost: yes -+lmtp_autocreate_users: group:groupname -+ -+ -+ -+Issues to be considered -+======================= -+ -+I) In order to use the create on post feauture one should be absolutely sure that: -+a) The MTA checks the validity of the email recipient before sending the email to -+LMTP. This is an RFC821 requirement. This usually expands to "the mta should be -+able to use the account database as user mailbox database". -+b) Only authorised accounts/services can talk to LMTP. -+ -+II) Especially in the case of imap logins, the current patch implementation checks -+for the INBOX folder existence upon login, causing an extra mailbox lookup in most -+of the cases. -+A better approach would be to chase the "IMAP_MAILBOX_NONEXISTENT" error code and -+check if the error is associated with an INBOX folder. However, this would mess up -+Cyrus code. The way it was implemented may not have been the most performance -+optimised, but it produces a much cleaner and simple patch. -+ -+ -+ -+Virtual Domains Support -+======================= -+ -+Virtual domains are supported by all versions of the patch for cyrus-imapd-2.2.1-BETA and -+later. However, it is not possible to declare different INBOX subfolders to be created or -+shared folders to be subscribed to for every domain. -+ -+ -+ -+Things to be done -+================= -+ -+1. Support MUPDATE -+ -+It is within the developers' intentions to support the mupdate protocol, but serious -+design issues on future cyrus releases have to resolved first. -+ -+2. Select different attributes (quota, partition, sieve filter, etc) depending on the group -+a user belongs to. -+ -+For more information and updates please visit http://email.uoa.gr/projects/cyrus/autocreate -+ diff --git a/cyrus-imapd-2.3.7-rmquota+deletemailbox.patch b/cyrus-imapd-2.3.7-rmquota+deletemailbox.patch deleted file mode 100644 index d024152..0000000 --- a/cyrus-imapd-2.3.7-rmquota+deletemailbox.patch +++ /dev/null @@ -1,537 +0,0 @@ ---- cyrus-imapd-2.3.7/imap/ctl_cyrusdb.c.rmquota 2005-02-16 22:06:18.000000000 +0100 -+++ cyrus-imapd-2.3.7/imap/ctl_cyrusdb.c 2006-07-23 12:52:14.000000000 +0200 -@@ -133,7 +133,7 @@ - /* if it is MBTYPE_RESERVED, unset it & call mboxlist_delete */ - if(!r && (mbtype & MBTYPE_RESERVE)) { - if(!r) { -- r = mboxlist_deletemailbox(name, 1, NULL, NULL, 0, 0, 1); -+ r = mboxlist_deletemailbox(name, 1, NULL, NULL, 0, 0, 1, 1); - if(r) { - /* log the error */ - syslog(LOG_ERR, ---- cyrus-imapd-2.3.7/imap/ctl_mboxlist.c.rmquota 2006-04-06 17:42:10.000000000 +0200 -+++ cyrus-imapd-2.3.7/imap/ctl_mboxlist.c 2006-07-23 12:52:14.000000000 +0200 -@@ -457,7 +457,7 @@ - - wipe_head = wipe_head->next; - -- ret = mboxlist_deletemailbox(me->mailbox, 1, "", NULL, 0, 1, 1); -+ ret = mboxlist_deletemailbox(me->mailbox, 1, "", NULL, 0, 1, 1, 1); - if(ret) { - fprintf(stderr, "couldn't delete defunct mailbox %s\n", - me->mailbox); ---- cyrus-imapd-2.3.7/imap/imapd.c.rmquota 2006-07-23 12:52:14.000000000 +0200 -+++ cyrus-imapd-2.3.7/imap/imapd.c 2006-07-23 12:55:14.000000000 +0200 -@@ -4985,7 +4985,7 @@ - - r = mboxlist_deletemailbox(name, imapd_userisadmin, - imapd_userid, imapd_authstate, -- 0, 0, 0); -+ 0, 0, 0, 1); - - if (!r) sync_log_mailbox(name); - -@@ -5009,6 +5009,12 @@ - char *p; - int domainlen = 0; - int sync_lockfd = (-1); -+ int keepQuota = 1; -+ -+ if(name && *name == '+') { -+ keepQuota = 0; -+ name++; -+ } - - r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, - imapd_userid, mailboxname); -@@ -5067,7 +5073,7 @@ - - r = mboxlist_deletemailbox(mailboxname, imapd_userisadmin, - imapd_userid, imapd_authstate, 1-force, -- localonly, 0); -+ localonly, 0, keepQuota); - } - - /* was it a top-level user mailbox? */ -@@ -6426,6 +6432,7 @@ - { - int newquota = -1; - int badresource = 0; -+ int rmquota = 0; - int c; - int force = 0; - static struct buf arg; -@@ -6442,7 +6449,8 @@ - if (c != ')' || arg.s[0] != '\0') { - for (;;) { - if (c != ' ') goto badlist; -- if (strcasecmp(arg.s, "storage") != 0) badresource = 1; -+ if (strcasecmp(arg.s, "remove") == 0) rmquota = 1; -+ else if (strcasecmp(arg.s, "storage") != 0) badresource = 1; - c = getword(imapd_in, &arg); - if (c != ' ' && c != ')') goto badlist; - if (arg.s[0] == '\0') goto badlist; -@@ -6511,7 +6519,10 @@ - - /* local mailbox */ - if (!r || (r == IMAP_MAILBOX_NONEXISTENT)) { -- r = mboxlist_setquota(mailboxname, newquota, force); -+ if (!rmquota) -+ r = mboxlist_setquota(mailboxname, newquota, force); -+ else -+ r = mboxlist_unsetquota(mailboxname); - } - - imapd_check(NULL, 0, 0); -@@ -8224,7 +8235,7 @@ - /* note also that we need to remember to let proxyadmins do this */ - r = mboxlist_deletemailbox(mailboxname, - imapd_userisadmin || imapd_userisproxyadmin, -- imapd_userid, imapd_authstate, 0, 1, 0); -+ imapd_userid, imapd_authstate, 0, 1, 0, 1); - if(r) syslog(LOG_ERR, - "Could not delete local mailbox during move of %s", - mailboxname); ---- cyrus-imapd-2.3.7/imap/mailbox.c.rmquota 2006-06-02 20:55:06.000000000 +0200 -+++ cyrus-imapd-2.3.7/imap/mailbox.c 2006-07-23 12:52:14.000000000 +0200 -@@ -2689,27 +2689,7 @@ - - seen_delete_mailbox(mailbox); - -- if (delete_quota_root && !rquota) { -- quota_delete(&mailbox->quota, &tid); -- free(mailbox->quota.root); -- mailbox->quota.root = NULL; -- } else if (!rquota) { -- /* Free any quota being used by this mailbox */ -- if (mailbox->quota.used >= mailbox->quota_mailbox_used) { -- mailbox->quota.used -= mailbox->quota_mailbox_used; -- } -- else { -- mailbox->quota.used = 0; -- } -- r = quota_write(&mailbox->quota, &tid); -- if (r) { -- syslog(LOG_ERR, -- "LOSTQUOTA: unable to record free of " UQUOTA_T_FMT " bytes in quota %s", -- mailbox->quota_mailbox_used, mailbox->quota.root); -- } -- else -- quota_commit(&tid); -- } -+ mailbox_updatequota(mailbox,NULL); - - /* remove data (message file) directory */ - path = mailbox->path; -@@ -3331,3 +3311,49 @@ - if (*p == '.') *p = '/'; - } - } -+ -+ -+/* This function is used to update the quota. Can be used to replace -+ * identical parts of the code, and can be quite handy some times -+ * The tid is used in order to make possible to make the quota update -+ * being a part of a bigger transaction to the quota db */ -+int mailbox_updatequota(struct mailbox *mailbox, struct txn **tid) -+{ -+ int r = 0, havetid = 0; -+ struct txn **ltid = NULL; -+ -+ if(tid) { -+ ltid = tid; -+ havetid = 1; -+ } -+ /* Ensure that we are locked */ -+ if(!mailbox->header_lock_count) return IMAP_INTERNAL; -+ -+ -+ if(mailbox->quota.root) { -+ r = quota_read(&mailbox->quota, ltid, 1); -+ if( r == 0 ) { -+ if (mailbox->quota.used >= mailbox->quota_mailbox_used) { -+ mailbox->quota.used -= mailbox->quota_mailbox_used; -+ } -+ else { -+ mailbox->quota.used = 0; -+ } -+ r = quota_write(&mailbox->quota, ltid); -+ if (r) { -+ syslog(LOG_ERR, -+ "LOSTQUOTA: unable to record free of %lu bytes in quota %s", -+ mailbox->quota_mailbox_used, mailbox->quota.root); -+ } -+ else if(!havetid) -+ quota_commit(tid); -+ } -+ /* It is not a big mistake not to have quota .. just remove from the mailbox */ -+ else if ( r == IMAP_QUOTAROOT_NONEXISTENT) { -+ free(mailbox->quota.root); -+ r = 0; -+ } -+ } -+ return r; -+} -+ ---- cyrus-imapd-2.3.7/imap/mailbox.h.rmquota 2006-06-02 18:41:57.000000000 +0200 -+++ cyrus-imapd-2.3.7/imap/mailbox.h 2006-07-23 12:52:14.000000000 +0200 -@@ -364,6 +364,8 @@ - struct mailbox *mailboxp); - extern int mailbox_delete(struct mailbox *mailbox, int delete_quota_root); - -+extern int mailbox_updatequota(struct mailbox *mailbox, struct txn **tid); -+ - extern int mailbox_rename_copy(struct mailbox *oldmailbox, - const char *newname, char *newpartition, - bit32 *olduidvalidityp, bit32 *newuidvalidityp, ---- cyrus-imapd-2.3.7/imap/mboxlist.c.rmquota 2006-07-23 12:52:14.000000000 +0200 -+++ cyrus-imapd-2.3.7/imap/mboxlist.c 2006-07-23 12:52:14.000000000 +0200 -@@ -99,6 +99,11 @@ - static int mboxlist_opensubs(); - static void mboxlist_closesubs(); - -+static int child_cb(char *name, -+ int matchlen __attribute__((unused)), -+ int maycreate __attribute__((unused)), -+ void *rock); -+ - static int mboxlist_rmquota(const char *name, int matchlen, int maycreate, - void *rock); - static int mboxlist_changequota(const char *name, int matchlen, int maycreate, -@@ -113,6 +118,7 @@ - - struct change_rock { - struct quota *quota; -+ struct quota *oldquota; - struct txn **tid; - }; - -@@ -911,9 +917,9 @@ - */ - int mboxlist_deletemailbox(const char *name, int isadmin, char *userid, - struct auth_state *auth_state, int checkacl, -- int local_only, int force) -+ int local_only, int force, int keepQuota) - { -- int r; -+ int r, has_children = 0; - char *acl; - long access; - struct mailbox mailbox; -@@ -924,6 +930,7 @@ - int mbtype; - const char *p; - mupdate_handle *mupdate_h = NULL; -+ char *quotaroot = NULL; - - if(!isadmin && force) return IMAP_PERMISSION_DENIED; - -@@ -1036,13 +1043,44 @@ - - if ((r && !force) || isremote) goto done; - -- if (!r || force) r = mailbox_delete(&mailbox, deletequotaroot); -+ if (!r || force) { -+ /* first we have to keep the previous quota root in order to delete it */ -+ if(mailbox.quota.root) -+ quotaroot = xstrdup(mailbox.quota.root); -+ r = mailbox_delete(&mailbox, deletequotaroot); -+ } - - /* - * See if we have to remove mailbox's quota root - */ -- if (!r && mailbox.quota.root != NULL) { -+ if (!r && quotaroot != NULL) { - /* xxx look for any other mailboxes in this quotaroot */ -+ /* If we have not asked to remove the quota (default behaviour), we check -+ * whether there are any subfolders beneeth the quota root. If there aren't -+ * any subfolders the reasonable thing is to delete the quota */ -+ if(keepQuota) { -+ char pattern[MAX_MAILBOX_PATH+1]; -+ strlcpy(pattern, quotaroot, sizeof(pattern)); -+ if (config_virtdomains && name[strlen(name)-1] == '!') { -+ strlcat(pattern, "*", sizeof(pattern)); -+ } -+ else { -+ strlcat(pattern, ".*", sizeof(pattern)); -+ } -+ /* find if there are subfolders. Then we want to -+ * keep the existing quota */ -+ mboxlist_findall(NULL, pattern, isadmin, userid, -+ auth_state, child_cb, (void *) &has_children); -+ -+ if(!has_children) -+ if(!mboxlist_mylookup(quotaroot, NULL, NULL, NULL, NULL, NULL, NULL, 0 )) -+ has_children = 1; -+ } -+ /* If we want to remove the quota explicitely or the quota root folder has no subfolders -+ * we execute the rmquota patch */ -+ if(!keepQuota || !has_children ) -+ mboxlist_unsetquota(quotaroot); -+ free(quotaroot); - } - - done: -@@ -2498,6 +2536,7 @@ - if (r) return r; - - crock.quota = "a; -+ crock.oldquota = NULL; - crock.tid = &tid; - /* top level mailbox */ - if(have_mailbox) -@@ -2516,17 +2555,21 @@ - */ - int mboxlist_unsetquota(const char *root) - { -+ char newquota[MAX_MAILBOX_PATH+1]; - char pattern[MAX_MAILBOX_PATH+1]; - struct quota quota; -- int r=0; -+ struct change_rock crock; -+ int r=0, k=0; - - if (!root[0] || root[0] == '.' || strchr(root, '/') - || strchr(root, '*') || strchr(root, '%') || strchr(root, '?')) { - return IMAP_MAILBOX_BADNAME; - } -+ -+ crock.tid=NULL; - - quota.root = (char *) root; -- r = quota_read("a, NULL, 0); -+ r = quota_read("a, crock.tid, 0); - if (r == IMAP_QUOTAROOT_NONEXISTENT) { - /* already unset */ - return 0; -@@ -2543,13 +2586,45 @@ - } - else - strlcat(pattern, ".*", sizeof(pattern)); -- -- /* top level mailbox */ -- mboxlist_rmquota(root, 0, 0, (void *)root); -- /* submailboxes - we're using internal names here */ -- mboxlist_findall(NULL, pattern, 1, 0, 0, mboxlist_rmquota, (void *)root); - -- r = quota_delete("a, NULL); -+ r = quota_delete("a, crock.tid); -+ -+ /* If we cannot delete the quota then abort the operation */ -+ if(!r) { -+ /* quota_findroot performs several checks that we can -+ * assume that are already done, and don't have to perform -+ * them again. One of them is that it returns 1 only if -+ * quotaroot exists. -+ */ -+ if(quota_findroot(newquota, sizeof(newquota), root)) { -+ struct quota rootquota; -+ rootquota.root = newquota; -+ k = quota_read(&rootquota, crock.tid, 0); -+ if (!k) { -+ crock.quota = &rootquota; -+ crock.oldquota = "a; -+ /* top level mailbox */ -+ k = mboxlist_changequota(root, 0, 0, &crock); -+ } -+ /* submailboxes - we're using internal names here */ -+ if (!k) -+ k = mboxlist_findall(NULL, pattern, 1, 0, 0, mboxlist_changequota, &crock); -+ if(!k) -+ k = quota_write(&rootquota, crock.tid); -+ -+ } -+ else { -+ /* top level mailbox */ -+ mboxlist_rmquota(root, 0, 0, (void *)root); -+ /* submailboxes - we're using internal names here */ -+ mboxlist_findall(NULL, pattern, 1, 0, 0, mboxlist_rmquota, (void *)root); -+ } -+ } -+ -+ if(!r && !k) -+ quota_commit(crock.tid); -+ else -+ quota_abort(crock.tid); - - return r; - } -@@ -2647,6 +2722,7 @@ - struct mailbox mailbox; - struct change_rock *crock = (struct change_rock *) rock; - struct quota *mboxlist_newquota = crock->quota; -+ struct quota *mboxlist_oldquota = crock->oldquota; - struct txn **tid = crock->tid; - - assert(rock != NULL); -@@ -2664,27 +2740,24 @@ - if (r) goto error; - - if (mailbox.quota.root) { -- if (strlen(mailbox.quota.root) >= strlen(mboxlist_newquota->root)) { -- /* Part of a child quota root */ -- mailbox_close(&mailbox); -- return 0; -- } -- -- r = quota_read(&mailbox.quota, tid, 1); -- if (r) goto error; -- if (mailbox.quota.used >= mailbox.quota_mailbox_used) { -- mailbox.quota.used -= mailbox.quota_mailbox_used; -+ if(mboxlist_oldquota) { -+ if (strlen(mailbox.quota.root) > strlen(mboxlist_oldquota->root)) { -+ /* Part of a child quota root */ -+ mailbox_close(&mailbox); -+ return 0; -+ } - } - else { -- mailbox.quota.used = 0; -- } -- r = quota_write(&mailbox.quota, tid); -- if (r) { -- syslog(LOG_ERR, -- "LOSTQUOTA: unable to record free of " UQUOTA_T_FMT " bytes in quota %s", -- mailbox.quota_mailbox_used, mailbox.quota.root); -+ if (strlen(mailbox.quota.root) >= strlen(mboxlist_newquota->root)) { -+ /* Part of a child quota root */ -+ mailbox_close(&mailbox); -+ return 0; -+ } - } -- free(mailbox.quota.root); -+ -+ r = mailbox_updatequota(&mailbox,tid); -+ if (r) -+ goto error; - } - - mailbox.quota.root = xstrdup(mboxlist_newquota->root); -@@ -2694,18 +2767,24 @@ - mboxlist_newquota->used += mailbox.quota_mailbox_used; - mailbox_close(&mailbox); - return 0; -- -+ - error: - mailbox_close(&mailbox); -+ syslog(LOG_ERR, "LOSTQUOTA: unable to change quota root for %s to %s: %s. \ -+ Command aborted. Run reconstruct to make sure mailboxes \ -+ are in consistent state", -+ name, mboxlist_newquota->root, error_message(r)); -+ return 1; - error_noclose: - syslog(LOG_ERR, "LOSTQUOTA: unable to change quota root for %s to %s: %s", -- name, mboxlist_newquota->root, error_message(r)); -+ name, mboxlist_newquota->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; - } - -+ - /* must be called after cyrus_init */ - void mboxlist_init(int myflags) - { ---- cyrus-imapd-2.3.7/imap/mboxlist.h.rmquota 2006-07-23 12:52:14.000000000 +0200 -+++ cyrus-imapd-2.3.7/imap/mboxlist.h 2006-07-23 12:52:14.000000000 +0200 -@@ -125,7 +125,7 @@ - * the planet */ - int mboxlist_deletemailbox(const char *name, int isadmin, char *userid, - struct auth_state *auth_state, int checkacl, -- int local_only, int force); -+ int local_only, int force, int keepQuota); - - /* Rename/move a mailbox (hierarchical) */ - int mboxlist_renamemailbox(char *oldname, char *newname, char *partition, ---- cyrus-imapd-2.3.7/imap/mupdate.c.rmquota 2006-03-15 19:56:31.000000000 +0100 -+++ cyrus-imapd-2.3.7/imap/mupdate.c 2006-07-23 12:52:14.000000000 +0200 -@@ -2297,7 +2297,7 @@ - remote_boxes.head = r->next; - } else if (ret < 0) { - /* Local without corresponding remote, delete it */ -- mboxlist_deletemailbox(l->mailbox, 1, "", NULL, 0, 0, 0); -+ mboxlist_deletemailbox(l->mailbox, 1, "", NULL, 0, 0, 0, 1); - local_boxes.head = l->next; - } else /* (ret > 0) */ { - /* Remote without corresponding local, insert it */ -@@ -2312,7 +2312,7 @@ - if(l && !r) { - /* we have more deletes to do */ - while(l) { -- mboxlist_deletemailbox(l->mailbox, 1, "", NULL, 0, 0, 0); -+ mboxlist_deletemailbox(l->mailbox, 1, "", NULL, 0, 0, 0, 1); - local_boxes.head = l->next; - l = local_boxes.head; - } ---- cyrus-imapd-2.3.7/imap/nntpd.c.rmquota 2006-05-26 17:50:07.000000000 +0200 -+++ cyrus-imapd-2.3.7/imap/nntpd.c 2006-07-23 12:52:14.000000000 +0200 -@@ -3356,7 +3356,7 @@ - /* XXX should we delete right away, or wait until empty? */ - - r = mboxlist_deletemailbox(mailboxname, 0, -- newsmaster, newsmaster_authstate, 1, 0, 0); -+ newsmaster, newsmaster_authstate, 1, 0, 0, 1); - - if (!r) sync_log_mailbox(mailboxname); - ---- cyrus-imapd-2.3.7/imap/sync_reset.c.rmquota 2005-12-13 16:31:10.000000000 +0100 -+++ cyrus-imapd-2.3.7/imap/sync_reset.c 2006-07-23 12:52:14.000000000 +0200 -@@ -254,7 +254,7 @@ - if (r) goto fail; - - for (item = list->head ; item ; item = item->next) { -- r=mboxlist_deletemailbox(item->name, 1, NULL, sync_authstate, 1, 0, 0); -+ r=mboxlist_deletemailbox(item->name, 1, NULL, sync_authstate, 1, 0, 0, 1); - - if (r) goto fail; - } -@@ -270,7 +270,7 @@ - if (r) goto fail; - - for (item = list->head ; item ; item = item->next) { -- r=mboxlist_deletemailbox(item->name, 1, NULL, sync_authstate, 1, 0, 0); -+ r=mboxlist_deletemailbox(item->name, 1, NULL, sync_authstate, 1, 0, 0, 1); - - if (r) goto fail; - } -@@ -278,7 +278,7 @@ - - /* Nuke inbox (recursive nuke possible?) */ - snprintf(buf, sizeof(buf)-1, "user.%s", user); -- r = mboxlist_deletemailbox(buf, 1, "cyrus", sync_authstate, 1, 0, 0); -+ r = mboxlist_deletemailbox(buf, 1, "cyrus", sync_authstate, 1, 0, 0, 1); - if (r && (r != IMAP_MAILBOX_NONEXISTENT)) goto fail; - - if ((r=user_deletedata(user, sync_userid, sync_authstate, 1))) ---- cyrus-imapd-2.3.7/imap/sync_server.c.rmquota 2006-06-12 20:56:42.000000000 +0200 -+++ cyrus-imapd-2.3.7/imap/sync_server.c 2006-07-23 12:52:14.000000000 +0200 -@@ -1576,7 +1576,7 @@ - - for (item = list->head ; item ; item = item->next) { - r=mboxlist_deletemailbox(item->name, sync_userisadmin, sync_userid, -- sync_authstate, 0, 0, 1); -+ sync_authstate, 0, 0, 1, 1); - - if (r) goto fail; - } -@@ -1586,7 +1586,7 @@ - (sync_namespacep->mboxname_tointernal)(sync_namespacep, "INBOX", - user, buf); - r = mboxlist_deletemailbox(buf, sync_userisadmin, sync_userid, -- sync_authstate, 0, 0, 1); -+ sync_authstate, 0, 0, 1, 1); - if (r && (r != IMAP_MAILBOX_NONEXISTENT)) goto fail; - - if ((r=user_deletedata(user, sync_userid, sync_authstate, 1))) -@@ -2508,7 +2508,7 @@ - - /* Delete with admin priveleges */ - r = mboxlist_deletemailbox(name, sync_userisadmin, sync_userid, -- sync_authstate, 0, 0, 1); -+ sync_authstate, 0, 0, 1, 1); - - if (r) - prot_printf(sync_out, "NO Failed to delete %s: %s\r\n", diff --git a/cyrus-imapd.cron-daily b/cyrus-imapd.cron-daily index 341673e..3ec831c 100644 --- a/cyrus-imapd.cron-daily +++ b/cyrus-imapd.cron-daily @@ -23,7 +23,7 @@ fi [ -x /usr/lib/cyrus-imapd/ctl_mboxlist ] || exit 0 # rotate mailbox lists -seq $[ $ROTATE - 1 ] 1 | while read i; do +seq $[ $ROTATE - 1 ] -1 1 | while read i; do [ -f ${MBOXLIST}.${i}.gz ] && mv -f ${MBOXLIST}.${i}.gz ${MBOXLIST}.$[ $i + 1 ].gz done [ -f ${MBOXLIST}.gz ] && mv -f ${MBOXLIST}.gz ${MBOXLIST}.1.gz diff --git a/cyrus-imapd.spec b/cyrus-imapd.spec index 40b26e0..475ed42 100644 --- a/cyrus-imapd.spec +++ b/cyrus-imapd.spec @@ -1,6 +1,6 @@ Name: cyrus-imapd Version: 2.3.8 -Release: 1%{?dist} +Release: 2%{?dist} # ********************** BUILD TIME OPTIONS START ********************** @@ -813,6 +813,9 @@ fi %{_mandir}/man1/* %changelog +* Wed Apr 04 2007 Tomas Janousek - 2.3.8-2 +- fixed mboxlist backup rotation (#197054) + * Mon Mar 12 2007 Tomas Janousek - 2.3.8-1 - update to latest upstream