Introduction ------------ The following is an implementation of the facility to allow mail delivered directly to a shared/public folder to be filtered with sieve.
For example, you might have a shared folder "public.interestingmessages" and deliver mail to it using an address of "[EMAIL PROTECTED]". The facility is enabled using a new option in /etc/imapd.conf - "sievesystemscripts". If this option is not set then public folder filtering will not be enabled - ie. if you don't change anything, the behaviour of the system is unchanged. "sievesystemscripts" should be set to a directory accessible by the deliver process - but not accessible by ordinary users! Deliver will then look for a sieve script in this directory. If one exists then it will be used to filter the email sent to the public folders. The script should be sent up in the usual way - called something sensible such as "public.script" then linked to with the symlink "default". Note that timsieved will not be able to access this directory. This is probably a good thing - but you will need to have access to your cyrus server host in order to set things up. Public folder filtering is performed with the security context of "anyone". So for example, your script will only be able to "fileinto" a folder with an appropriate ACL for "anyone". This is probably the most contentious point - but I can't see anything wrong in practice - public folders by there nature are accessible to anyone, and I'm only really interested in fileto and reject actions (setting flags might be an issue?). As the concept of "anyone" already exists, there is no need to mess about creating new "dummy" users etc. And using "anyone" seems to work quite neatly. Applications ------------ The obvious application is to do some kind of spam filtering on your public folders. In addition, I have various automatically generated reports from customer systems. The systems are configured with to send to a single well know email address - which maps to a public folder on our mail store. This has become rather cluttered over time and was being manually filtered.. which was somewhat time consuming. The sieve script is set to filter on the basis of the sender address, allow mail to be automatically organised by customer and/or function. An additional benefit is that the "well known" email address does not have to be changed. Example ------- imapd.conf .............................. configdirectory: /var/imap partition-default: /home/imap admins: root cyrus allowanonymouslogin: no # To use the PAM for authentication (but not /etc/passwd or shadow), change # the following line to specify "pam" instead of "sasldb". # sasl_pwcheck_method: sasldb sasl_pwcheck_method: pam # Sieve can send email sendmail: /usr/sbin/sendmail sieveusehomedir: false sievedir: /var/lib/sieve sievesystemscripts: /var/lib/sieve/system ............................................ They key thing here is the addition of sievesystemscripts ............... $ ls -l /var/lib/sieve/system total 4 lrwxrwxrwx 1 cyrus mail 11 Nov 5 12:55 default -> spam.script -rw------- 1 cyrus mail 499 Nov 7 15:28 spam.script ............... cat spam.script ......................... require ["fileinto","reject"]; if address :all :is ["From"] ["[EMAIL PROTECTED]"] { fileinto "public.help.customer1.fax"; } if address :all :is ["From"] ["[EMAIL PROTECTED]"] { fileinto "public.help.customer2.fax"; } if address :all :is ["From"] ["[EMAIL PROTECTED]"] { fileinto "public.help.customer1.amanda"; } if address :localpart :contains ["To"] ["bb+public.test"] { reject "This folder has been disabled"; } if header :is "X-SPAM" "yes" { fileinto "public.spam"; } ........................................... [Note, in case you are wondering - spam *really* does have the header "X-SPAM" set to yes. Spam detection happens on the way in to our network, and mail deemed to be spam is labeled. This is a way to separate mechanism from policy. Individual users can do what they want with spam - either discard it or file it. And I can set an appropriate policy for the public folders.] Implementation -------------- My example implementation is against cyrus 2.0.16, not against CVS head. This is because I am running 2.0.16, so I've been able to test it. I will redo the diff against CVS if the basic idea seems OK... but I won't be able to test it so well. The patch is not very invasive - only imap/lmptd.c is touched (plus the documentation). It probably ought to be applied to the lmtpproxyd.c - but again, I won't bother to test that as I won't be using it. I've tried to follow the same coding conventions that already exist in the source, so hopefully it should be fairly easy to understand. ................................. diff -Naur cyrus-imapd-2.0.16-orig/doc/install-sieve.html cyrus-imapd-2.0.16/doc/install-sieve.html --- cyrus-imapd-2.0.16-orig/doc/install-sieve.html Fri Jan 5 01:59:03 2001 +++ cyrus-imapd-2.0.16/doc/install-sieve.html Wed Nov 7 15:57:56 2001 @@ -44,6 +44,19 @@ sieve script stored in the home directory of the user as "<tt>~/.sieve</tt>". +<h3>Filtering mail delivered directly to shared/public folders</h3> + +<p>It is possible to filter mail delivered directly to shared/public folders, +for example, with the email address <tt>[EMAIL PROTECTED]</tt>. +<p>This is enabled if the "<tt>sievesystemscripts</tt>" option is set to +point to a directory containing a sieve script. +The onus is on the cyrus administrator to create this directory and +place a sieve script in it. +Typically, the script will be a file linked to with the symbolic link +"default". +<p>The script is run with the security context of "<tt>anyone</tt>". +This means, for example that <tt>fileinto</tt> will only work +if "<tt>anyone</tt>" has suitable rights on the target folder. <h3>Testing the sieve server</h3> <ol> diff -Naur cyrus-imapd-2.0.16-orig/imap/lmtpd.c cyrus-imapd-2.0.16/imap/lmtpd.c --- cyrus-imapd-2.0.16-orig/imap/lmtpd.c Mon May 21 19:21:47 2001 +++ cyrus-imapd-2.0.16/imap/lmtpd.c Wed Nov 7 15:51:15 2001 @@ -737,7 +737,10 @@ int ret = 1; if (sd->mailboxname) { - strcpy(namebuf, "INBOX."); + /* Sieving a public or user folder ? */ + if ( strcmp( sd->username, "anyone" )) { + strcpy(namebuf, "INBOX."); + } strcat(namebuf, sd->mailboxname); ret = deliver_mailbox(md->data, &mydata->stage, md->size, @@ -751,7 +754,13 @@ if (!sd->authstate) return SIEVE_FAIL; - strcpy(namebuf, "INBOX"); + /* sieving a public or user folder ? */ + if ( strcmp( sd->username, "anyone" ) ) { + strcpy(namebuf, "INBOX"); + } + else { + strcpy( namebuf, sd->mailboxname ); + } ret = deliver_mailbox(md->data, &mydata->stage, md->size, kc->imapflags->flag, kc->imapflags->nflags, @@ -1046,22 +1055,33 @@ return NULL; } - if (sieve_usehomedir) { /* look in homedir */ - struct passwd *pent = getpwnam(user); + if ( !strcmp( user, "anyone" ) ) { + const char *sievesystemscripts = config_getstring( "sievesystemscripts", +(const char *) 0 ); - if (pent == NULL) { - return NULL; - } + if ( sievesystemscripts == (const char *) 0 ) { + return NULL; /* Must have a script dir for this to work */ + } - /* check ~USERNAME/.sieve */ - snprintf(buf, sizeof(buf), "%s/%s", pent->pw_dir, ".sieve"); - } else { /* look in sieve_dir */ - char hash; + snprintf(buf, sizeof(buf), "%s/default", sievesystemscripts ); + } + else { + if (sieve_usehomedir) { /* look in homedir */ + struct passwd *pent = getpwnam(user); - hash = (char) tolower((int) *user); - if (!islower((int) hash)) { hash = 'q'; } + if (pent == NULL) { + return NULL; + } - snprintf(buf, sizeof(buf), "%s/%c/%s/default", sieve_dir, hash, user); + /* check ~USERNAME/.sieve */ + snprintf(buf, sizeof(buf), "%s/%s", pent->pw_dir, ".sieve"); + } else { /* look in sieve_dir */ + char hash; + + hash = (char) tolower((int) *user); + if (!islower((int) hash)) { hash = 'q'; } + + snprintf(buf, sizeof(buf), "%s/%c/%s/default", sieve_dir, hash, user); + } } return (fopen(buf, "r")); @@ -1160,6 +1180,7 @@ { int n, nrcpts; mydata_t mydata; + char namebuf[MAX_MAILBOX_PATH]; assert(msgdata); nrcpts = msg_getnumrcpt(msgdata); @@ -1176,28 +1197,30 @@ for (n = 0; n < nrcpts; n++) { char *rcpt = xstrdup(msg_getrcpt(msgdata, n)); char *plus; + char *script_user; int quotaoverride = msg_getrcpt_ignorequota(msgdata, n); int r = 0; mydata.cur_rcpt = n; plus = strchr(rcpt, '+'); if (plus) *plus++ = '\0'; - /* case 1: shared mailbox request */ - if (plus && !strcmp(rcpt, BB)) { - r = deliver_mailbox(msgdata->data, - &mydata.stage, - msgdata->size, - NULL, 0, - mydata.authuser, mydata.authstate, - msgdata->id, NULL, mydata.notifyheader, - plus, quotaoverride, 0); - } - /* case 2: ordinary user, might have Sieve script */ + if (plus && !strcmp(rcpt, BB)) { + /* case 1: shared mailbox request */ + script_user = "anyone"; + } else if (!strchr(rcpt, '.') && strlen(rcpt) + 30 <= MAX_MAILBOX_PATH) { - FILE *f = sieve_find_script(rcpt); - char namebuf[MAX_MAILBOX_PATH]; + /* case 2: ordinary user, might have Sieve script */ + script_user = rcpt; + } + else { + /* Make sure that nothing can go wrong! */ + script_user = ""; + } + + if ( *script_user ) { + FILE *f = sieve_find_script(script_user); #ifdef USE_SIEVE if (f != NULL) { @@ -1206,9 +1229,9 @@ sdata = (script_data_t *) xmalloc(sizeof(script_data_t)); - sdata->username = rcpt; + sdata->username = script_user; sdata->mailboxname = plus; - sdata->authstate = auth_newstate(rcpt, (char *)0); + sdata->authstate = auth_newstate(script_user, (char *)0); /* slap the mailboxname back on so we hash the envelope & id when we figure out whether or not to keep the message */ @@ -1248,6 +1271,7 @@ /* if there was an error, r is non-zero and we'll do normal delivery */ + } else { /* no sieve script */ r = 1; /* do normal delivery actions */ @@ -1255,7 +1279,24 @@ #else /* USE_SIEVE */ r = 1; /* normal delivery */ #endif /* USE_SIEVE */ - + } + else { + r = 1; + } + +/* Check to see if it was a public folder */ + if ( !strcmp( script_user, "anyone" ) ) { + if ( r ) { + r = deliver_mailbox(msgdata->data, + &mydata.stage, + msgdata->size, + NULL, 0, + mydata.authuser, mydata.authstate, + msgdata->id, NULL, mydata.notifyheader, + plus, quotaoverride, 0); + } + } + else { if (r && plus && strlen(rcpt) + strlen(plus) + 30 <= MAX_MAILBOX_PATH) { /* normal delivery to + mailbox */ diff -Naur cyrus-imapd-2.0.16-orig/man/imapd.conf.5 cyrus-imapd-2.0.16/man/imapd.conf.5 --- cyrus-imapd-2.0.16-orig/man/imapd.conf.5 Sat Jun 23 02:01:50 2001 +++ cyrus-imapd-2.0.16/man/imapd.conf.5 Wed Nov 7 15:49:17 2001 @@ -187,6 +187,11 @@ .IP "\fBsievedir:\fR /usr/sieve" 5 If sieveusehomedir is false, this directory is searched for Sieve scripts. +.IP "\fBsievesystemscripts:\fR <no default>" 5 +If set, deliver will look for a Sieve script in this directory. +The script will be used to filter mail delivered directly to +public/shared folders. +The script will be run with the security context of "anyone". .IP "\fBsendmail:\fR /usr/lib/sendmail" 5 The pathname of the sendmail executable. Sieve uses sendmail for sending rejections, redirects and vacation responses. @@ -229,4 +234,4 @@ .SH SEE ALSO .PP \fBimapd(8)\fR, \fBpop3d(8)\fR, \fBlmtpd(8)\fR, \fBtimsieved(8)\fR, -\fBidled(8)\fR, \fBdeliver(8)\fR, \fBmaster(8)\fR +\fBidled(8)\fR, \fBdeliver(8)\fR, \fBcyrusmaster(8)\fR ------------------------------------- So far it has had a day or so of testing.... caveat emptor.. Regards, Ian.