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.


Reply via email to