Hi I've developed a patch that extends the "/vendor/cmu/cyrus-imapd/sieve" mailbox annotation to be meaningful for a user's inbox in addition to shared mailboxes. For a user's inbox, the sieve-annotation specifies the name of a global sieve script that is executed when new mail is delivery to that user _before_ his private sieve-default-script is executed.
I need this to easily file Junk-mails into a users junk folder without messing with his private sieve script. My patch changes the "sieve_execute_t *exe" parameter of sieve_execute_bytecode to "sieve_execute_t **exes" (exes has to be a null-terminated array of pointers to a sieve_execute_t structure). sieve_execute_bytecode calls sieve_eval_bc for each entry of that array, passing 1 for is_incl for all but the last entry. This makes a script defined via the "/vendor/cmu/cyrus-imapd/sieve" annotation on a users's inbox behave like it was included at the top of the user's own sieve script. Patch is attached. I'd like to see this included in cyrus 2.3, and am ready to improve that patch if it's not acceptable for inclusion in it's present form. I'm not subscribed to this list, so please reply to me personally. greetings, Florian Pflug
diff -Naur orig/cyrus-imapd-2.3.7/imap/lmtp_sieve.c cyrus-imapd-2.3.7/imap/lmtp_sieve.c --- orig/cyrus-imapd-2.3.7/imap/lmtp_sieve.c 2006-05-23 15:09:35.000000000 +0200 +++ cyrus-imapd-2.3.7/imap/lmtp_sieve.c 2006-12-14 18:28:23.000000000 +0100 @@ -859,33 +859,67 @@ { char namebuf[MAX_MAILBOX_NAME+1] = ""; struct annotation_data attrib; - const char *script = NULL; + const char *script_main = NULL; + const char *script_prepend = NULL; char fname[MAX_MAILBOX_PATH+1]; - sieve_execute_t *bc = NULL; + sieve_execute_t *bc_prepend = NULL; + sieve_execute_t *bc_main = NULL; + sieve_execute_t *bcs_empty[3] = {NULL, NULL, NULL} ; + sieve_execute_t **bcs = bcs_empty ; script_data_t sdata; char userbuf[MAX_MAILBOX_NAME+1] = ""; char authuserbuf[MAX_MAILBOX_NAME+1]; int r = 0; + if (domain) snprintf(namebuf, sizeof(namebuf), "%s!", domain); + else namebuf[0] = '\0'; + if (!user) { /* shared mailbox, check for annotation */ - if (domain) snprintf(namebuf, sizeof(namebuf), "%s!", domain); strlcat(namebuf, mailbox, sizeof(namebuf)); - if (annotatemore_lookup(namebuf, + if (!annotatemore_lookup(namebuf, "/vendor/cmu/cyrus-imapd/sieve", "", &attrib) != 0 || !attrib.value) { - /* no sieve script annotation */ - return 1; /* do normal delivery actions */ + script_main = attrib.value ; + } + } + else { + /* User. check for prepend annotation */ + strlcat(namebuf, "user.", sizeof(namebuf)); + strlcat(namebuf, user, sizeof(namebuf)); + + if (!annotatemore_lookup(namebuf, + "/vendor/cmu/cyrus-imapd/sieve", "", + &attrib) != 0 || !attrib.value) { + script_prepend = attrib.value; } - - script = attrib.value; } - if (sieve_find_script(user, domain, script, fname, sizeof(fname)) != 0 || - sieve_script_load(fname, &bc) != SIEVE_OK) { - /* no sieve script */ - return 1; /* do normal delivery actions */ + /* Note that sieve_find_script returns a user's default script + if script_main is null, and searches for global scripts if + user is null */ + if (sieve_find_script(user, domain, script_main, fname, sizeof(fname)) != 0 || + sieve_script_load(fname, &bc_main) != SIEVE_OK) { + bc_main = NULL ; + } + /* prepend-script are always global scripts. The user could just use + include otherwise */ + if (script_prepend == NULL || + sieve_find_script(NULL, domain, script_prepend, fname, sizeof(fname)) != 0 || + sieve_script_load(fname, &bc_prepend) != SIEVE_OK) { + bc_prepend = NULL ; + } + + if (bc_prepend == NULL) { + if (bc_main == NULL) + return 1; /*No sieve scripts, do normal actions */ + else + bcs[0] = bc_main ; + } + else { + bcs[0] = bc_prepend ; + bcs[1] = bc_main ; } if (user) strlcpy(userbuf, user, sizeof(userbuf)); @@ -908,7 +942,7 @@ sdata.authstate = msgdata->authstate; } - r = sieve_execute_bytecode(bc, interp, + r = sieve_execute_bytecode(bcs, interp, (void *) &sdata, (void *) msgdata); if ((r == SIEVE_OK) && (msgdata->m->id)) { @@ -928,7 +962,8 @@ /* free everything */ if (user && sdata.authstate) auth_freestate(sdata.authstate); - sieve_script_unload(&bc); + while (*bcs != NULL) + sieve_script_unload(bcs++) ; /* if there was an error, r is non-zero and we'll do normal delivery */ diff -Naur orig/cyrus-imapd-2.3.7/sieve/script.c cyrus-imapd-2.3.7/sieve/script.c --- orig/cyrus-imapd-2.3.7/sieve/script.c 2005-10-05 17:56:23.000000000 +0200 +++ cyrus-imapd-2.3.7/sieve/script.c 2006-12-14 18:28:47.000000000 +0100 @@ -873,9 +873,10 @@ sieve_imapflags_t * imapflags, action_list_t *actions, notify_list_t *notify_list, const char **errmsg); -int sieve_execute_bytecode(sieve_execute_t *exe, sieve_interp_t *interp, +int sieve_execute_bytecode(sieve_execute_t **exes, sieve_interp_t *interp, void *script_context, void *message_context) { + sieve_execute_t **exe = exes ; action_list_t *actions = NULL; notify_list_t *notify_list = NULL; /* notify_action_t *notify_action;*/ @@ -912,9 +913,15 @@ actions_string, errmsg); } else { - ret = sieve_eval_bc(exe, 0, interp, &body_cache, - script_context, message_context, - &imapflags, actions, notify_list, &errmsg); + /* Loop over executables until an error or a stop occurs */ + ret = 0 ; + while ((*exe != NULL) && (ret == 0)) { + /* Pretend that all but the last exe is an include. */ + ret = sieve_eval_bc(*exe, (exe[1] == NULL) ? 0 : 1, interp, + &body_cache, script_context, message_context, + &imapflags, actions, notify_list, &errmsg); + exe++ ; + } if (ret < 0) { ret = do_sieve_error(SIEVE_RUN_ERROR, interp, &body_cache, diff -Naur orig/cyrus-imapd-2.3.7/sieve/test.c cyrus-imapd-2.3.7/sieve/test.c --- orig/cyrus-imapd-2.3.7/sieve/test.c 2005-10-05 17:56:25.000000000 +0200 +++ cyrus-imapd-2.3.7/sieve/test.c 2006-12-12 15:59:31.000000000 +0100 @@ -730,6 +730,7 @@ { sieve_interp_t *i; sieve_execute_t *exe = NULL; + sieve_execute_t **exes = {exe, NULL} ; message_data_t *m; char *script = NULL, *message = NULL; int c, force_fail = 0, usage_error = 0; @@ -884,7 +885,7 @@ exit(1); } - res = sieve_execute_bytecode(exe, i, NULL, m); + res = sieve_execute_bytecode(exes, i, NULL, m); if (res != SIEVE_OK) { printf("sieve_execute_bytecode() returns %d\n", res); exit(1);