Anonymous | Login | 2024-12-03 10:22 MST |
Main | My View | View Issues | Change Log | Roadmap | Repositories |
View Issue Details [ Jump to Notes ] | [ Issue History ] [ Print ] | ||||||||
ID | Project | Category | View Status | Date Submitted | Last Update | ||||
0001910 | ClearOS | cyrus-imapd | public | 2014-08-13 14:57 | 2015-04-28 04:09 | ||||
Reporter | user2 | ||||||||
Assigned To | user2 | ||||||||
Priority | normal | Severity | feature | Reproducibility | have not tried | ||||
Status | closed | Resolution | fixed | ||||||
Platform | OS | OS Version | |||||||
Product Version | |||||||||
Target Version | 7.1.0 Beta 1 | Fixed in Version | |||||||
Summary | 0001910: Upstream IMAP server does not include autocreate patches (sponsor required?) | ||||||||
Description | By default, the Cyrus IMAP server does not auto-create mailboxes. Instead, the mailbox needs to be created by an administrator using the "cyradm" command, e.g.: # cyradm --user root 127.0.0.1 > cm users/test (or something like that) In ClearOS 5, the nasty "kolabd/ldapsync" script handled this. In ClearOS 6, Red Hat included a patch that allowed the auto-creation of mailboxes, so no messy synchronization was required. Unfortunately, this patch has been dropped in RHEL 7, and the original author has not released a patch for the Cyrus 2.4.x version. There is an alternative patch for this version here (but in 403s): http://blog.vx.sk/archives/13-Autocreate-and-autosieve-patches-for-Cyrus-IMAP-Server-24.html [^] Since we spend most of our development/support efforts on Zarafa, it might be best to find a sponsor for this feature. | ||||||||
Tags | No tags attached. | ||||||||
Attached Files | cyrus-imapd-2.4.4-autocreate-0.10-0.patch [^] (74,039 bytes) 2015-03-24 18:29 [Show Content] [Hide Content]diff -Naur cyrus-imapd-2.4.4.orig/README.autocreate cyrus-imapd-2.4.4/README.autocreate --- cyrus-imapd-2.4.4.orig/README.autocreate 1970-01-01 01:00:00.000000000 +0100 +++ cyrus-imapd-2.4.4/README.autocreate 2010-11-16 08:48:37.704981331 +0100 @@ -0,0 +1,213 @@ +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/ + +NOTE : Patch updated for Cyrus IMAP 2.4.x by Martin Matuska <mm@FreeBSD.org> + +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 -Naur cyrus-imapd-2.4.4.orig/imap/Makefile.in cyrus-imapd-2.4.4/imap/Makefile.in --- cyrus-imapd-2.4.4.orig/imap/Makefile.in 2010-11-16 08:48:20.817612092 +0100 +++ cyrus-imapd-2.4.4/imap/Makefile.in 2010-11-16 08:49:21.892354019 +0100 @@ -66,7 +66,7 @@ IMAP_COM_ERR_LIBS = @IMAP_COM_ERR_LIBS@ LIB_WRAP = @LIB_WRAP@ LIBS = $(IMAP_LIBS) $(IMAP_COM_ERR_LIBS) -DEPLIBS = ../lib/libcyrus.a ../lib/libcyrus_min.a @DEPLIBS@ +DEPLIBS = $(SIEVE_LIBS) ../lib/libcyrus.a ../lib/libcyrus_min.a @DEPLIBS@ CFLAGS = @CFLAGS@ LDFLAGS = @LDFLAGS@ @COM_ERR_LDFLAGS@ @@ -102,7 +102,7 @@ imapparse.o telemetry.o user.o notify.o idle.o quota_db.o \ sync_log.o $(SEEN) mboxkey.o backend.o tls.o message_guid.o \ statuscache_db.o userdeny_db.o sequence.o upgrade_index.o \ - dlist.o version.o + dlist.o version.o autosieve.o IMAPDOBJS=pushstats.o imapd.o proxy.o imap_proxy.o index.o @@ -118,7 +118,7 @@ fud smmapd reconstruct quota mbpath ipurge cyr_dbtool cyr_synclog \ cyrdump chk_cyrus cvt_cyrusdb deliver ctl_mboxlist \ ctl_deliver ctl_cyrusdb squatter mbexamine cyr_expire arbitron \ - unexpunge cyr_df cyr_sequence cyr_userseen @IMAP_PROGS@ + unexpunge compile_sieve cyr_df cyr_sequence cyr_userseen @IMAP_PROGS@ BUILTSOURCES = imap_err.c imap_err.h pushstats.c pushstats.h \ lmtpstats.c lmtpstats.h mupdate_err.c mupdate_err.h \ @@ -180,9 +180,9 @@ mupdate_err.h: mupdate_err.c ### Services -idled: idled.o mutex_fake.o libimap.a $(DEPLIBS) +idled: idled.o mutex_fake.o libimap.a $(SIEVE_LIBS) $(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) @@ -365,6 +365,10 @@ sync_reset sync_reset.o sync_support.o \ libimap.a mutex_fake.o $(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 clean: diff -Naur cyrus-imapd-2.4.4.orig/imap/autosieve.c cyrus-imapd-2.4.4/imap/autosieve.c --- cyrus-imapd-2.4.4.orig/imap/autosieve.c 1970-01-01 01:00:00.000000000 +0100 +++ cyrus-imapd-2.4.4/imap/autosieve.c 2010-11-16 08:48:37.722056581 +0100 @@ -0,0 +1,590 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <fcntl.h> +#include <ctype.h> +#include <time.h> +#include <syslog.h> +#include <com_err.h> +#include <config.h> + +#include "global.h" +#include "util.h" +#include "xmalloc.h" +#include "xstrlcpy.h" +#include "xstrlcat.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, config_fulldirhash), domain, dir_hash_c(user,config_fulldirhash), user) >= buflen) { + free(user); + return 1; + } + } else { + if(snprintf(sieve_script_dir, buflen, "%s/%c/%s/", + sieve_dir, dir_hash_c(user,config_fulldirhash), 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 + */ + diff -Naur cyrus-imapd-2.4.4.orig/imap/compile_sieve.c cyrus-imapd-2.4.4/imap/compile_sieve.c --- cyrus-imapd-2.4.4.orig/imap/compile_sieve.c 1970-01-01 01:00:00.000000000 +0100 +++ cyrus-imapd-2.4.4/imap/compile_sieve.c 2010-11-16 08:48:37.724064645 +0100 @@ -0,0 +1,365 @@ +/* This tool compiles the sieve script from a command +line so that it can be used wby the autoadd patch */ +#include <stdio.h> +#include <stdlib.h> + +#include <config.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <fcntl.h> +#include <ctype.h> +#include <time.h> +#include <com_err.h> + +#include "global.h" + +#include "util.h" +#include "xmalloc.h" +#include "xstrlcpy.h" +#include "xstrlcat.h" +#include "mailbox.h" +#include "imap_err.h" +#include "sieve_interface.h" +#include "script.h" + +#include <pwd.h> + +#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 <altconfig>] [-i <infile> -o <outfile>]\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, opt; + char *source_script = NULL; + char *compiled_source_script = NULL; + 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 <infile> 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 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_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 1; + } + + + /* 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 1; + } + 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 1; + } + + /* 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; +} + + + + + + diff -Naur cyrus-imapd-2.4.4.orig/imap/imapd.c cyrus-imapd-2.4.4/imap/imapd.c --- cyrus-imapd-2.4.4.orig/imap/imapd.c 2010-11-16 08:48:20.829659918 +0100 +++ cyrus-imapd-2.4.4/imap/imapd.c 2010-11-16 08:48:37.728081052 +0100 @@ -269,6 +269,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); @@ -2102,8 +2103,47 @@ mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid, config_virtdomains ? strcspn(imapd_userid, "@") : 0); + + autocreate_inbox(); +} + +/* + * 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 */ @@ -5880,6 +5920,8 @@ goto freeargs; } + autocreate_inbox(); + return; freeargs: diff -Naur cyrus-imapd-2.4.4.orig/imap/lmtpd.c cyrus-imapd-2.4.4/imap/lmtpd.c --- cyrus-imapd-2.4.4.orig/imap/lmtpd.c 2010-11-16 08:48:20.824640875 +0100 +++ cyrus-imapd-2.4.4/imap/lmtpd.c 2010-11-16 08:48:37.729084805 +0100 @@ -119,6 +119,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; @@ -976,6 +978,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, quota_t quotacheck, struct auth_state *authstate) { @@ -1019,6 +1101,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 */ @@ -1045,6 +1136,7 @@ aclcheck, (quotacheck < 0) || config_getswitch(IMAPOPT_LMTP_STRICT_QUOTA) ? quotacheck : 0); + } } diff -Naur cyrus-imapd-2.4.4.orig/imap/mboxlist.c cyrus-imapd-2.4.4/imap/mboxlist.c --- cyrus-imapd-2.4.4.orig/imap/mboxlist.c 2010-11-16 08:48:20.818616124 +0100 +++ cyrus-imapd-2.4.4/imap/mboxlist.c 2010-11-16 08:48:37.732096343 +0100 @@ -84,6 +84,12 @@ #include "quota.h" #include "sync_log.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 @@ -100,6 +106,19 @@ void *rock); static int mboxlist_changequota(const 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 needed to be passed as void *rock to + * mboxlist_autochangesub(); + */ +struct changesub_rock_st { + char *userid; + char *auth_userid; + struct auth_state *auth_state; +}; char *mboxlist_makeentry(int mbtype, const char *part, const char *acl) { @@ -2996,3 +3015,349 @@ return(count); } + +/* + * 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, 0, 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, 0, 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; +} + diff -Naur cyrus-imapd-2.4.4.orig/imap/mboxlist.h cyrus-imapd-2.4.4/imap/mboxlist.h --- cyrus-imapd-2.4.4.orig/imap/mboxlist.h 2010-11-16 08:48:20.828655886 +0100 +++ cyrus-imapd-2.4.4/imap/mboxlist.h 2010-11-16 08:48:37.732096343 +0100 @@ -221,4 +221,9 @@ const char *userid, struct auth_state *authstate); +int mboxlist_autocreateinbox(struct namespace *namespace,char *userid, + struct auth_state *auth_state, char *mailboxname, + int autocreatequota); + + #endif diff -Naur cyrus-imapd-2.4.4.orig/imap/pop3d.c cyrus-imapd-2.4.4/imap/pop3d.c --- cyrus-imapd-2.4.4.orig/imap/pop3d.c 2010-11-16 08:48:20.820623909 +0100 +++ cyrus-imapd-2.4.4/imap/pop3d.c 2010-11-16 08:48:37.734104407 +0100 @@ -181,6 +181,8 @@ static char popd_apop_chal[45 + MAXHOSTNAMELEN + 1]; /* <rand.time@hostname> */ 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); @@ -1389,6 +1391,7 @@ popd_userid = xstrdup(userbuf); prot_printf(popd_out, "+OK Name is a valid mailbox\r\n"); } + } void cmd_pass(char *pass) @@ -1692,6 +1695,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) @@ -1720,6 +1760,12 @@ userid, inboxname); if (!r) r = mboxlist_lookup(inboxname, &mbentry, NULL); + + /* Try once again after autocreate_inbox */ + if (r == IMAP_MAILBOX_NONEXISTENT && + !(r = autocreate_inbox(inboxname, userid))) + r = mboxlist_lookup(inboxname, &mbentry, NULL); + if (!r && (config_popuseacl = config_getswitch(IMAPOPT_POPUSEACL)) && (!mbentry.acl || !((myrights = cyrus_acl_myrights(popd_authstate, mbentry.acl)) & ACL_READ))) { diff -Naur cyrus-imapd-2.4.4.orig/lib/auth.c cyrus-imapd-2.4.4/lib/auth.c --- cyrus-imapd-2.4.4.orig/lib/auth.c 2010-11-16 08:48:20.875850421 +0100 +++ cyrus-imapd-2.4.4/lib/auth.c 2010-11-16 08:48:37.755228749 +0100 @@ -118,3 +118,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); +} + diff -Naur cyrus-imapd-2.4.4.orig/lib/auth.h cyrus-imapd-2.4.4/lib/auth.h --- cyrus-imapd-2.4.4.orig/lib/auth.h 2010-11-16 08:48:20.881874054 +0100 +++ cyrus-imapd-2.4.4/lib/auth.h 2010-11-16 08:48:37.755228749 +0100 @@ -55,6 +55,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[]; @@ -77,5 +78,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 */ diff -Naur cyrus-imapd-2.4.4.orig/lib/auth_krb.c cyrus-imapd-2.4.4/lib/auth_krb.c --- cyrus-imapd-2.4.4.orig/lib/auth_krb.c 2010-11-16 08:48:20.885889903 +0100 +++ cyrus-imapd-2.4.4/lib/auth_krb.c 2010-11-16 08:48:37.756232781 +0100 @@ -341,6 +341,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( @@ -372,6 +381,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 = @@ -382,4 +398,5 @@ &mymemberof, &mynewstate, &myfreestate, + &mycanonuser, }; diff -Naur cyrus-imapd-2.4.4.orig/lib/auth_krb5.c cyrus-imapd-2.4.4/lib/auth_krb5.c --- cyrus-imapd-2.4.4.orig/lib/auth_krb5.c 2010-11-16 08:48:20.877858205 +0100 +++ cyrus-imapd-2.4.4/lib/auth_krb5.c 2010-11-16 08:48:37.756232781 +0100 @@ -199,6 +199,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( @@ -230,6 +238,13 @@ 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); + return NULL; +} + #endif struct auth_mech auth_krb5 = @@ -240,4 +255,5 @@ &mymemberof, &mynewstate, &myfreestate, + &mycanonuser, }; diff -Naur cyrus-imapd-2.4.4.orig/lib/auth_pts.c cyrus-imapd-2.4.4/lib/auth_pts.c --- cyrus-imapd-2.4.4.orig/lib/auth_pts.c 2010-11-16 08:48:20.877858205 +0100 +++ cyrus-imapd-2.4.4/lib/auth_pts.c 2010-11-16 08:48:37.757236534 +0100 @@ -512,6 +512,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 */ @@ -520,4 +528,5 @@ &mymemberof, &mynewstate, &myfreestate, + &mycanonuser, }; diff -Naur cyrus-imapd-2.4.4.orig/lib/auth_unix.c cyrus-imapd-2.4.4/lib/auth_unix.c --- cyrus-imapd-2.4.4.orig/lib/auth_unix.c 2010-11-16 08:48:20.880870301 +0100 +++ cyrus-imapd-2.4.4/lib/auth_unix.c 2010-11-16 08:48:37.757236534 +0100 @@ -315,6 +315,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 = { @@ -324,4 +334,5 @@ &mymemberof, &mynewstate, &myfreestate, + &mycanonuser, }; diff -Naur cyrus-imapd-2.4.4.orig/lib/imapoptions cyrus-imapd-2.4.4/lib/imapoptions --- cyrus-imapd-2.4.4.orig/lib/imapoptions 2010-11-16 08:48:20.878862238 +0100 +++ cyrus-imapd-2.4.4/lib/imapoptions 2010-11-16 08:48:37.759244877 +0100 @@ -245,6 +245,55 @@ /* Time in seconds. Any imap command that takes longer than this time is logged. */ +{ "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. */ diff -Naur cyrus-imapd-2.4.4.orig/notifyd/Makefile.in cyrus-imapd-2.4.4/notifyd/Makefile.in --- cyrus-imapd-2.4.4.orig/notifyd/Makefile.in 2010-11-16 08:48:20.897937729 +0100 +++ cyrus-imapd-2.4.4/notifyd/Makefile.in 2010-11-16 08:48:37.769295255 +0100 @@ -71,10 +71,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 diff -Naur cyrus-imapd-2.4.4.orig/notifyd/notifyd.c cyrus-imapd-2.4.4/notifyd/notifyd.c --- cyrus-imapd-2.4.4.orig/notifyd/notifyd.c 2010-11-16 08:48:20.897937729 +0100 +++ cyrus-imapd-2.4.4/notifyd/notifyd.c 2010-11-16 08:48:37.770300684 +0100 @@ -98,7 +98,7 @@ #define NOTIFY_MAXSIZE 8192 -int do_notify() +static int do_notify() { struct sockaddr_un sun_data; socklen_t sunlen = sizeof(sun_data); diff -Naur cyrus-imapd-2.4.4.orig/ptclient/Makefile.in cyrus-imapd-2.4.4/ptclient/Makefile.in --- cyrus-imapd-2.4.4.orig/ptclient/Makefile.in 2010-11-16 08:48:20.800544944 +0100 +++ cyrus-imapd-2.4.4/ptclient/Makefile.in 2010-11-16 08:48:37.770300684 +0100 @@ -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.4.4-autosieve-0.6.0.patch [^] (7,870 bytes) 2015-03-24 18:30 [Show Content] [Hide Content] diff -Naur cyrus-imapd-2.4.4.orig/README.autosievefolder cyrus-imapd-2.4.4/README.autosievefolder --- cyrus-imapd-2.4.4.orig/README.autosievefolder 1970-01-01 01:00:00.000000000 +0100 +++ cyrus-imapd-2.4.4/README.autosievefolder 2010-11-15 10:40:56.299163485 +0100 @@ -0,0 +1,43 @@ +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 + +NOTE : Patch updated to Cyrus IMAPD 2.4.x by Martin Matuska <mm@FreeBSD.org> + + 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.4.4.orig/imap/lmtp_sieve.c cyrus-imapd-2.4.4/imap/lmtp_sieve.c --- cyrus-imapd-2.4.4.orig/imap/lmtp_sieve.c 2010-11-11 23:15:33.000000000 +0100 +++ cyrus-imapd-2.4.4/imap/lmtp_sieve.c 2010-11-15 10:40:13.127210740 +0100 @@ -88,6 +88,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]; @@ -496,7 +499,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; @@ -947,3 +963,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, 0, 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.4.4.orig/lib/imapoptions cyrus-imapd-2.4.4/lib/imapoptions --- cyrus-imapd-2.4.4.orig/lib/imapoptions 2010-11-11 23:15:33.000000000 +0100 +++ cyrus-imapd-2.4.4/lib/imapoptions 2010-11-15 10:40:13.129220481 +0100 @@ -1096,6 +1096,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 cyrus-imapd-2.4.12-autosieve-0.6.0.patch [^] (7,626 bytes) 2015-03-24 18:30 [Show Content] [Hide Content] --- cyrus-imapd-2.4.12.orig/README.autosievefolder 1970-01-01 01:00:00.000000000 +0100 +++ cyrus-imapd-2.4.12/README.autosievefolder 2011-10-31 10:07:44.890693235 +0100 @@ -0,0 +1,43 @@ +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 + +NOTE : Patch updated to Cyrus IMAPD 2.4.x by Martin Matuska <mm@FreeBSD.org> + + 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 + --- cyrus-imapd-2.4.12.orig/imap/lmtp_sieve.c 2011-10-31 10:07:03.922690650 +0100 +++ cyrus-imapd-2.4.12/imap/lmtp_sieve.c 2011-10-31 10:08:36.752691723 +0100 @@ -88,6 +88,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]; @@ -503,7 +506,20 @@ sd->username, mdata->notifyheader, namebuf, md->date, 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, md->date, quotaoverride, 0); + } + if (!ret) { snmp_increment(SIEVE_FILEINTO, 1); return SIEVE_OK; @@ -973,3 +989,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, 0, 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; +} + --- cyrus-imapd-2.4.12.orig/lib/imapoptions 2011-10-31 10:07:03.969690694 +0100 +++ cyrus-imapd-2.4.12/lib/imapoptions 2011-10-31 10:13:23.876700183 +0100 @@ -1145,6 +1145,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 | ||||||||
Notes | |
(0001398) bchambers (administrator) 2015-03-24 18:28 |
I asked the original author for the patches for 2.4 and he kindly replied: http://www.vx.sk/download/patches/cyrus-imapd/ [^] |
(0001400) user2 2015-03-25 08:06 |
Sweet. I'll add it to my patch Wednesday tasks today. |
Issue History | |||
Date Modified | Username | Field | Change |
2014-08-13 14:57 | user2 | New Issue | |
2014-08-13 14:57 | user2 | Status | new => confirmed |
2014-08-20 16:14 | user2 | Target Version | 7.0.0 Alpha 2 => 7.1.0 Beta 2 |
2015-03-08 19:12 | user2 | Relationship added | has duplicate 0002258 |
2015-03-08 19:13 | user2 | Description Updated | View Revisions |
2015-03-24 18:28 | bchambers | Note Added: 0001398 | |
2015-03-24 18:29 | bchambers | File Added: cyrus-imapd-2.4.4-autocreate-0.10-0.patch | |
2015-03-24 18:30 | bchambers | File Added: cyrus-imapd-2.4.4-autosieve-0.6.0.patch | |
2015-03-24 18:30 | bchambers | File Added: cyrus-imapd-2.4.12-autosieve-0.6.0.patch | |
2015-03-25 08:06 | user2 | Note Added: 0001400 | |
2015-03-25 14:12 | user2 | Category | app-imap - IMAP and POP Server => cyrus-imapd |
2015-03-26 09:10 | user2 | Note Added: 0001401 | |
2015-03-26 09:11 | user2 | Note Edited: 0001401 | View Revisions |
2015-03-26 09:13 | user2 | Note Deleted: 0001401 | |
2015-04-14 08:28 | user2 | Target Version | 7.1.0 Beta 2 => 7.1.0 Beta 1 |
2015-04-14 10:08 | user2 | Status | confirmed => resolved |
2015-04-14 10:08 | user2 | Resolution | open => fixed |
2015-04-14 10:08 | user2 | Assigned To | => user2 |
2015-04-28 04:09 | user2 | Status | resolved => closed |