diff -Naur cyrus-imapd-2.1.17/README.autocreate cyrus-imapd-2.1.17.autocreate-uncompiled/README.autocreate --- cyrus-imapd-2.1.17/README.autocreate 1970-01-01 02:00:00 +0200 +++ cyrus-imapd-2.1.17.autocreate-uncompiled/README.autocreate 2004-12-20 18:42:55 +0200 @@ -0,0 +1,147 @@ +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 e-mail account creation procedure. +Hence, to create a new e-mail 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 uncareful 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 authorization 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 authorizationID 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 authenticatedID 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 e-mail message addressed to the user has been received. + +ii) The recipient is not any of the imap_admins or admins accounts. +Note that passing e-mails 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. e.g + +autocreateinboxfolders: sent|drafts|spam|templates + +B) Automatic subscription of INBOX subfolders controlled by "autosubscribeinboxfolders" +configuration option. e.g + +autosubscribeinboxfolders: sent|spam + +Obviously, only subscription to subfolders included in the "autocreateinboxfolder" +list is meaningfull. + +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. +e.g 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 apropriate 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 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 option that has been added +is 'autocreate_sieve_script'. This refers to the full path of the file that +contains the sieve script. +eg. autocreate_sieve_script: /etc/default_sieve_script + +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. + +NOTE : Currently, this patch does not check the validity of the sieve script file. +The administrator should make sure that the provided file contains a valid sieve +script. + +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 e-mail recipient before sending the e-mail 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 authorized 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 +optimized, but it produces a much cleaner and simple patch. + + + + +Things to be done +================= + +1. Support MURDER architecture. +2. Support virtual domains. + + +For more information and updates please visit http://email.uoa.gr/autocreate + diff -Naur cyrus-imapd-2.1.17/imap/Makefile.in cyrus-imapd-2.1.17.autocreate-uncompiled/imap/Makefile.in --- cyrus-imapd-2.1.17/imap/Makefile.in 2003-09-09 17:54:53 +0300 +++ cyrus-imapd-2.1.17.autocreate-uncompiled/imap/Makefile.in 2004-12-20 18:42:56 +0200 @@ -100,7 +100,7 @@ config.o imap_err.o mupdate_err.o proc.o setproctitle.o convert_code.o \ duplicate.o saslclient.o saslserver.o acapmbox.o signals.o annotate.o \ search_engines.o squat.o squat_internal.o mbdump.o \ - imapparse.o telemetry.o user.o notify.o $(SEEN) $(IDLE) + imapparse.o telemetry.o user.o notify.o autosieve.o $(SEEN) $(IDLE) IMAPDOBJS=pushstats.o backend.o imapd.o index.o tls.o version.o diff -Naur cyrus-imapd-2.1.17/imap/autosieve.c cyrus-imapd-2.1.17.autocreate-uncompiled/imap/autosieve.c --- cyrus-imapd-2.1.17/imap/autosieve.c 1970-01-01 02:00:00 +0200 +++ cyrus-imapd-2.1.17.autocreate-uncompiled/imap/autosieve.c 2004-12-21 12:12:19 +0200 @@ -0,0 +1,208 @@ +/* + * This file is create by the University of Athens and implements + * the automatic sieve filter script creation upon the user mailbox + * creation. + * + * For more information on the patch, please read the README.autocreate + * file, distributed with the patch. + */ + +#define CHECK_SIEVESCRIPT 0 + + +#include +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "imapconf.h" +#include "mailbox.h" +#include "imap_err.h" + +#if CHECK_SIEVESCRIPT +#include "../timsieved/scripttest.h" +#define TIMSIEVE_FAIL -1 +#endif + + +static int cyrus_mkdir(char *path, int mode) +{ + int save_errno; + char *p = path; + struct stat sbuf; + + while ((p = strchr(p+1, '/'))) { + *p = '\0'; + if (mkdir(path, mode) == -1 && errno != EEXIST) { + save_errno = errno; + if (stat(path, &sbuf) == -1) { + errno = save_errno; + syslog(LOG_ERR, "IOERROR: creating directory %s: %m", path); + return IMAP_IOERROR; + } + } + *p = '/'; + } + return 0; +} + +static int script_name(char *sievename, size_t buflen, char *filename) +{ + char *p; + int r; + + p = strrchr(filename, '/'); + if (p == NULL) + p = filename; + else + p++; + + r = strlcpy(sievename, p, buflen) - buflen; + return (r >= 0 || r == -buflen ? 1 : 0); +} + + +int autoadd_sieve(char *userid, char *source_script) +{ + char *sieve_dir; + char sievename[1024]; + char sievepath[1024]; + char sieve_tmpname[1024]; + char sieve_script[1024]; + char sieve_default[1024]; + char *err = NULL; + FILE *in_stream, *out_fp; + char buf[4096]; + mode_t oldmask; + int len; + + /* Check that sievedir exists and we are not using sievehome dirs */ + if (config_getswitch("sieveusehomedir", 0) || !(sieve_dir = (char *)config_getstring("sievedir",NULL))) { + syslog(LOG_WARNING, "autoadd_sieve: autocreate_sieve_script functional only with sievedir"); + return 1; + } + + if (script_name(sievename, sizeof(sievename), source_script)) { + syslog(LOG_ERR, "autoadd_sieve: Invalid sieve script %s", source_script); + return 1; + } + + /* Compare max possible length with a 1024 buf */ + if (strlen(sieve_dir)+ 3 + strlen(userid) + 1 + strlen(sievename) + 15 > 1024) { + syslog(LOG_ERR, "autoadd_sieve: Invalid sieve path %s, %s, %s", sieve_dir, sievename, userid); + return 1; + } + + sprintf(sievepath, "%s/%c/%s/", sieve_dir, (char) dir_hash_c(userid), userid); + sprintf(sieve_tmpname, "%s%s.script.%s", sievepath, sievename, ".NEW"); + sprintf(sieve_script, "%s%s.script", sievepath, sievename); + sprintf(sieve_default, "%s%s", sievepath, "default"); + + + /* open the source script. if there is a problem with that exit */ + in_stream = fopen(source_script,"r"); + + if (!in_stream) { + syslog(LOG_ERR, "autoadd_sieve: Unable to open %s source sieve script.", source_script); + return 1; + } + +#if CHECK_SIEVESCRIPT + /* Checking the script */ + if (is_script_parsable(in_stream,&err) == TIMSIEVE_FAIL) { + if(err && *err) { + syslog(LOG_ERR,"autoadd_sieve: Error while parsing script %s.",err); + free(err); + } else { + syslog(LOG_ERR,"autoadd_sieve: Error while parsing script"); + } + fclose(in_stream); + return 1; + } +#endif + + if (!stat(sieve_default,NULL)) { + syslog(LOG_ERR, "autoadd_sieve: Default link already exists. Probably already created"); + fclose(in_stream); + return 1; + } + + /* at this point we start the modifications of the filesystem */ + + if (cyrus_mkdir(sievepath, 0750)) { + syslog(LOG_ERR, "autoadd_sieve: Unable to create directory %s", sievepath); + fclose(in_stream); + return 1; + } + + /* Copy the initial script */ + oldmask=umask(022); + if((out_fp = fopen(sieve_tmpname,"w")) == NULL) { + syslog(LOG_ERR, "autoadd_sieve: Unable to open %s destination sieve script.", sieve_tmpname); + umask(oldmask); + fclose(in_stream); + return 1; + } + umask(oldmask); + + while ((len = fread(buf,sizeof(char), sizeof(buf), in_stream))) { + if (fwrite(buf,sizeof(char),len,out_fp) != len) { + syslog(LOG_ERR, "autoadd_sieve: Error while writing to sieve script file: %s",sieve_tmpname); + fclose(out_fp); + unlink(sieve_tmpname); + fclose(in_stream); + return 1; + } + } + + if (feof(in_stream)) { + fclose(out_fp); + } else { /* ferror */ + fclose(out_fp); + unlink(sieve_tmpname); + fclose(in_stream); + return 1; + } + + /* We don't need the input file anymore */ + fclose(in_stream); + + if (rename(sieve_tmpname, sieve_script)) { + if (errno != ENOENT) { + unlink(sieve_tmpname); + } + return 1; + } + + if (symlink(sieve_script, sieve_default)) { + if ( errno != EEXIST) { + syslog(LOG_ERR, "autoadd_sieve: Problem making the default link."); + unlink(sieve_script); + return 1; + } + } + + return 0; + +} + + +/* + * 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 + */ + diff -Naur cyrus-imapd-2.1.17/imap/imapd.c cyrus-imapd-2.1.17.autocreate-uncompiled/imap/imapd.c --- cyrus-imapd-2.1.17/imap/imapd.c 2004-11-23 21:12:45 +0200 +++ cyrus-imapd-2.1.17.autocreate-uncompiled/imap/imapd.c 2004-12-20 18:42:55 +0200 @@ -143,6 +143,7 @@ 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); void cmd_authenticate(char *tag, char *authtype); @@ -1693,6 +1694,48 @@ } /* + * 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("autocreatequota", 0))) { + /* This is actually 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, 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) @@ -1825,6 +1868,9 @@ mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid); freebuf(&passwdbuf); + + autocreate_inbox(); + return; } @@ -1946,6 +1992,9 @@ /* Translate any separators in userid */ mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid); + + autocreate_inbox(); + return; } diff -Naur cyrus-imapd-2.1.17/imap/lmtpd.c cyrus-imapd-2.1.17.autocreate-uncompiled/imap/lmtpd.c --- cyrus-imapd-2.1.17/imap/lmtpd.c 2003-10-01 21:20:37 +0300 +++ cyrus-imapd-2.1.17.autocreate-uncompiled/imap/lmtpd.c 2004-12-20 18:42:55 +0200 @@ -148,6 +148,8 @@ struct lmtp_func mylmtp = { &deliver, &verify_user, &shut_down, &spoolfile, &removespool, 0, 1, 0 }; +static int autocreate_inbox(char *rcpt_userid); + static void logdupelem(); static void usage(); static void setup_sieve(); @@ -1349,6 +1351,50 @@ exit(code); } + + +/* + * Autocreate Inbox and subfolders upon login + */ +int autocreate_inbox(char *rcpt_userid) +{ + struct auth_state *authstate; + char inboxname[MAX_MAILBOX_NAME+1]; + int rcptisadmin ; + int autocreatequota; + int r; + + /* + * Exclude anonymous + */ + if (!strcmp(rcpt_userid, "anonymous")) + return IMAP_MAILBOX_NONEXISTENT; + + /* + * Check for autocreatequota and createonpost + */ + if (!(autocreatequota = config_getint("autocreatequota", 0)) || + !(config_getswitch("createonpost", 0))) + return IMAP_MAILBOX_NONEXISTENT; + + /* + * Exclude admin's accounts + */ + authstate = auth_newstate(rcpt_userid, NULL); + rcptisadmin = authisa(authstate, "imap", "admins"); + if (rcptisadmin) + return IMAP_MAILBOX_NONEXISTENT; + + r = (*lmtpd_namespace.mboxname_tointernal) (&lmtpd_namespace, + "INBOX", rcpt_userid, inboxname); + if (!r) + r = mboxlist_autocreateinbox(&lmtpd_namespace, rcpt_userid, + authstate, inboxname, autocreatequota); + return r; +} + + + static int verify_user(const char *user, long quotacheck, struct auth_state *authstate) { @@ -1382,6 +1428,8 @@ - don't care about message size (1 msg over quota allowed) */ r = append_check(buf, MAILBOX_FORMAT_NORMAL, authstate, 0, quotacheck > 0 ? 0 : quotacheck); + if (r == IMAP_MAILBOX_NONEXISTENT) + r = autocreate_inbox(buf+5); } } diff -Naur cyrus-imapd-2.1.17/imap/mboxlist.c cyrus-imapd-2.1.17.autocreate-uncompiled/imap/mboxlist.c --- cyrus-imapd-2.1.17/imap/mboxlist.c 2004-02-27 00:56:56 +0200 +++ cyrus-imapd-2.1.17.autocreate-uncompiled/imap/mboxlist.c 2004-12-23 16:12:46 +0200 @@ -88,6 +88,17 @@ struct db *mbdb; +/* + * 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; +}; + + static int mboxlist_dbopen = 0; static int mboxlist_opensubs(); @@ -97,6 +108,13 @@ void *rock); 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); +extern int autoadd_sieve(char *userid, char *source_script); + #define FNAME_SUBSSUFFIX ".sub" @@ -2805,3 +2823,338 @@ mboxlist_closesubs(subs); return r; } + +/* + * 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 and subfolders */ + && strncmp(name, "user.", 5))) { /* Exclude other users' folders */ + + 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]; + char name [MAX_MAILBOX_NAME+1]; + char mailboxname [MAX_MAILBOX_NAME+1]; + int len; + int r = 0; + int subscribe_all_sharedfolders = 0; + + subscribe_all_sharedfolders = config_getswitch("autosubscribe_all_sharedfolders", 0); + 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; + } + + sub=config_getstring("autosubscribesharedfolders", ""); + + 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", + auth_userid, folder); + } + else { + syslog(LOG_WARNING, "autosubscribe: User %s to %s failed: %s", + auth_userid, folder, error_message(r)); + r = 0; + } + } + + return r; +} + +/* + * This function provides the autocreate inbox functionality. + * This patch is submitted by the University of Athens email + * development team. + * For more information visit http://email.uoa.gr or send + * emails at email (at) edunet.gr + */ +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 + char *source_script; +#endif + + /* + * 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 (authisa(auth_state, "lmtp", "admins")) return 0; + if (authisa(auth_state, "sieve", "admins")) return 0; + + /* + * Do we really need group membership + * for proxyservers? + */ + if (authisa(auth_state, "imap", "proxyservers")) return 0; + if (authisa(auth_state, "sieve", "proxyservers")) return 0; + + auth_userid = auth_canonuser(auth_state); + if (auth_userid == NULL) { + /* + * Couldn't get cannon userid + */ + syslog(LOG_ERR, + "Could not get cannon userid for user %s", userid); + return IMAP_PARTITION_UNKNOWN; + } + +#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(auth_userid); + if (partition == NULL) { + /* + * Couldn't get partition info + */ + syslog(LOG_ERR, + "Could not get imapPartition info for user %s", auth_userid); + return IMAP_PARTITION_UNKNOWN; + } +#endif + + r = mboxlist_createmailbox(mailboxname, MAILBOX_FORMAT_NORMAL, partition, + 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); + + syslog(r == 0 ? LOG_NOTICE : LOG_ERR, + "User %s, INBOX autocreate in partition-%s: %s", auth_userid, + partition == NULL ? "default" : partition, + r == 0 ? "succeeded" : 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("autocreateinboxfolders", ""))) + sub=config_getstring("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; + + /* + * 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(folder) - strlen(userid) - 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 (len >= MAX_MAILBOX_NAME) + r = IMAP_MAILBOX_BADNAME; + } + + 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)); + + if (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); + + /* + * Here the automatic creation of a default sieve script is initiated. + * This is a new and rather independent feature in autocreate inbox patch. + */ +#ifdef USE_SIEVE + if ((source_script = (char *) config_getstring("autocreate_sieve_script", NULL)) != NULL) { + if (!autoadd_sieve(userid, source_script)) + syslog(LOG_NOTICE, "autocreatesieve: User %s, default sieve script created", auth_userid); + else + syslog(LOG_WARNING, "autocreatesieve: User %s, default sieve script failed", auth_userid); + } +#endif + + return r; +} + diff -Naur cyrus-imapd-2.1.17/imap/mboxlist.h cyrus-imapd-2.1.17.autocreate-uncompiled/imap/mboxlist.h --- cyrus-imapd-2.1.17/imap/mboxlist.h 2003-08-09 02:08:52 +0300 +++ cyrus-imapd-2.1.17.autocreate-uncompiled/imap/mboxlist.h 2004-12-20 18:42:56 +0200 @@ -191,4 +191,12 @@ /* done with database stuff */ void mboxlist_done(void); +int mboxlist_autocreateinbox(struct namespace *namespace, + char *userid, + struct auth_state *auth_state, + char *mailboxname, int autocreatequota); + + + + #endif diff -Naur cyrus-imapd-2.1.17/imap/pop3d.c cyrus-imapd-2.1.17.autocreate-uncompiled/imap/pop3d.c --- cyrus-imapd-2.1.17/imap/pop3d.c 2004-03-26 04:03:55 +0200 +++ cyrus-imapd-2.1.17.autocreate-uncompiled/imap/pop3d.c 2004-12-20 18:42:56 +0200 @@ -141,6 +141,7 @@ static struct namespace popd_namespace; static char popd_apop_chal[45 + MAXHOSTNAMELEN + 1]; /* */ +static int autocreate_inbox(char *inboxname, char *auth_userid); static void cmd_apop(char *response); static void cmd_auth(); @@ -1262,6 +1263,41 @@ } /* + * Autocreate Inbox and subfolders upon login + */ +int autocreate_inbox(char *inboxname, char *auth_userid) +{ + struct auth_state *authstate; + int userisadmin ; + int autocreatequota; + int r; + + /* + * Exclude anonymous + */ + if (!strcmp(popd_userid, "anonymous")) + return IMAP_MAILBOX_NONEXISTENT; + + /* + * Check for autocreatequota + */ + if (!(autocreatequota = config_getint("autocreatequota", 0))) + return IMAP_MAILBOX_NONEXISTENT; + + /* + * Exclude admin's accounts + */ + authstate = auth_newstate(auth_userid, NULL); + userisadmin = authisa(authstate, "imap", "admins"); + if (userisadmin) + return IMAP_MAILBOX_NONEXISTENT; + + r = mboxlist_autocreateinbox(&popd_namespace, popd_userid, + authstate, inboxname, autocreatequota); + return r; +} + +/* * Complete the login process by opening and locking the user's inbox */ int openinbox(void) @@ -1270,6 +1306,7 @@ int r, msg; struct index_record record; int minpoll; + char *auth_userid; popd_login_time = time(0); @@ -1279,6 +1316,16 @@ strcpy(inboxname, "user."); strcat(inboxname, popd_userid); r = mailbox_open_header(inboxname, 0, &mboxstruct); + + /* Try once again after autocreate_inbox */ + if (r == IMAP_MAILBOX_NONEXISTENT) { + auth_userid = xstrdup(popd_userid); + mboxname_hiersep_toexternal(&popd_namespace, auth_userid); + if (!(r = autocreate_inbox(inboxname, auth_userid))) + r = mailbox_open_header(inboxname, 0, &mboxstruct); + free(auth_userid); + } + if (r) { free(popd_userid); popd_userid = 0; diff -Naur cyrus-imapd-2.1.17/lib/auth.h cyrus-imapd-2.1.17.autocreate-uncompiled/lib/auth.h --- cyrus-imapd-2.1.17/lib/auth.h 2003-02-13 22:15:38 +0200 +++ cyrus-imapd-2.1.17.autocreate-uncompiled/lib/auth.h 2004-12-20 18:42:56 +0200 @@ -61,4 +61,6 @@ const char *cacheid); extern void auth_freestate(struct auth_state *auth_state); +extern char *auth_canonuser(struct auth_state *auth_state); + #endif /* INCLUDED_AUTH_H */ diff -Naur cyrus-imapd-2.1.17/lib/auth_krb.c cyrus-imapd-2.1.17.autocreate-uncompiled/lib/auth_krb.c --- cyrus-imapd-2.1.17/lib/auth_krb.c 2003-02-13 22:15:38 +0200 +++ cyrus-imapd-2.1.17.autocreate-uncompiled/lib/auth_krb.c 2004-12-20 18:42:56 +0200 @@ -351,3 +351,11 @@ free((char *)auth_state); } +char * +auth_canonuser(struct auth_state *auth_state) +{ + if (auth_state) + return auth_state->userid; + return NULL; +} + diff -Naur cyrus-imapd-2.1.17/lib/auth_unix.c cyrus-imapd-2.1.17.autocreate-uncompiled/lib/auth_unix.c --- cyrus-imapd-2.1.17/lib/auth_unix.c 2003-05-13 18:33:26 +0300 +++ cyrus-imapd-2.1.17.autocreate-uncompiled/lib/auth_unix.c 2004-12-20 18:42:56 +0200 @@ -266,4 +266,12 @@ free((char *)auth_state); } +char * +auth_canonuser(struct auth_state *auth_state) +{ + if (auth_state) + return auth_state->userid; + return NULL; +} + diff -Naur cyrus-imapd-2.1.17/man/imapd.conf.5 cyrus-imapd-2.1.17.autocreate-uncompiled/man/imapd.conf.5 --- cyrus-imapd-2.1.17/man/imapd.conf.5 2003-09-03 17:38:26 +0300 +++ cyrus-imapd-2.1.17.autocreate-uncompiled/man/imapd.conf.5 2004-12-20 18:42:57 +0200 @@ -201,6 +201,32 @@ 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. +.IP "\fBcreateonpost:\fR no" 5 +If yes, when lmtpd receives an incoming mail for an INBOX that does not exist, +then the INBOX is automatically created by lmtpd. +.IP "\fBautocreateinboxfolders:\fR " 5 +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 yes) + autocreateinboxfolders is a list of INBOX's subfolders separated by a "|", that are automatically created by the server +under the previous two situations. +.IP "\fBautosubscribeinboxfolders:\fR " 5 +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. +.IP "\fBautosubscribesharedfolders:\fR " 5 +A list of shared folder (bulletin board) names, separated by "|" that the users get automatically subscribed to, +when their INBOX is created. +These folders must exist before the user mailbox is created and the user must have the apropriate permissions, +in order to get subscribed to the shared folder. +.IP "\fBautosubscribe_all_sharedfolders:\fR " 5 +If set to yes then the user is automatically subscribed to all shared folders, one has permission +to subscribe to. +.IP "\fBautocreate_sieve_script:\fR " 5 +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. .IP "\fBlogtimestamps:\fR no" 5 Include notations in the protocol telemetry logs indicating the number of seconds since the last command or response.