Back to index

citadel  8.12
serv_sieve.c
Go to the documentation of this file.
00001 /*
00002  * This module glues libSieve to the Citadel server in order to implement
00003  * the Sieve mailbox filtering language (RFC 3028).
00004  *
00005  * Copyright (c) 1987-2012 by the citadel.org team
00006  *
00007  * This program is open source software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License version 3.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  */
00015 
00016 #include "sysdep.h"
00017 #include <stdlib.h>
00018 #include <unistd.h>
00019 #include <stdio.h>
00020 #include <fcntl.h>
00021 #include <ctype.h>
00022 #include <pwd.h>
00023 #include <errno.h>
00024 #include <sys/types.h>
00025 
00026 #if TIME_WITH_SYS_TIME
00027 # include <sys/time.h>
00028 # include <time.h>
00029 #else
00030 # if HAVE_SYS_TIME_H
00031 #  include <sys/time.h>
00032 # else
00033 #  include <time.h>
00034 # endif
00035 #endif
00036 
00037 #include <sys/wait.h>
00038 #include <string.h>
00039 #include <limits.h>
00040 #include <libcitadel.h>
00041 #include "citadel.h"
00042 #include "server.h"
00043 #include "citserver.h"
00044 #include "support.h"
00045 #include "config.h"
00046 #include "database.h"
00047 #include "msgbase.h"
00048 #include "internet_addressing.h"
00049 #include "ctdl_module.h"
00050 #include "serv_sieve.h"
00051 
00052 struct RoomProcList *sieve_list = NULL;
00053 char *msiv_extensions = NULL;
00054 int SieveDebugEnable = 0;
00055 
00056 #define DBGLOG(LEVEL) if ((LEVEL != LOG_DEBUG) || (SieveDebugEnable != 0))
00057 
00058 #define SV_syslog(LEVEL, FORMAT, ...)                          \
00059        DBGLOG(LEVEL) syslog(LEVEL,                      \
00060                           "Sieve: " FORMAT, __VA_ARGS__)
00061 
00062 #define SVM_syslog(LEVEL, FORMAT)         \
00063        DBGLOG(LEVEL) syslog(LEVEL,        \
00064                           "Sieve: " FORMAT);
00065 
00066 
00067 /*
00068  * Callback function to send libSieve trace messages to Citadel log facility
00069  */
00070 int ctdl_debug(sieve2_context_t *s, void *my)
00071 {
00072        SV_syslog(LOG_DEBUG, "%s", sieve2_getvalue_string(s, "message"));
00073        return SIEVE2_OK;
00074 }
00075 
00076 
00077 /*
00078  * Callback function to log script parsing errors
00079  */
00080 int ctdl_errparse(sieve2_context_t *s, void *my)
00081 {
00082        SV_syslog(LOG_WARNING, "Error in script, line %d: %s",
00083                 sieve2_getvalue_int(s, "lineno"),
00084                 sieve2_getvalue_string(s, "message")
00085        );
00086        return SIEVE2_OK;
00087 }
00088 
00089 
00090 /*
00091  * Callback function to log script execution errors
00092  */
00093 int ctdl_errexec(sieve2_context_t *s, void *my)
00094 {
00095        SV_syslog(LOG_WARNING, "Error executing script: %s",
00096                 sieve2_getvalue_string(s, "message")
00097               );
00098        return SIEVE2_OK;
00099 }
00100 
00101 
00102 /*
00103  * Callback function to redirect a message to a different folder
00104  */
00105 int ctdl_redirect(sieve2_context_t *s, void *my)
00106 {
00107        struct ctdl_sieve *cs = (struct ctdl_sieve *)my;
00108        struct CtdlMessage *msg = NULL;
00109        struct recptypes *valid = NULL;
00110        char recp[256];
00111 
00112        safestrncpy(recp, sieve2_getvalue_string(s, "address"), sizeof recp);
00113 
00114        SV_syslog(LOG_DEBUG, "Action is REDIRECT, recipient <%s>", recp);
00115 
00116        valid = validate_recipients(recp, NULL, 0);
00117        if (valid == NULL) {
00118               SV_syslog(LOG_WARNING, "REDIRECT failed: bad recipient <%s>", recp);
00119               return SIEVE2_ERROR_BADARGS;
00120        }
00121        if (valid->num_error > 0) {
00122               SV_syslog(LOG_WARNING, "REDIRECT failed: bad recipient <%s>", recp);
00123               free_recipients(valid);
00124               return SIEVE2_ERROR_BADARGS;
00125        }
00126 
00127        msg = CtdlFetchMessage(cs->msgnum, 1);
00128        if (msg == NULL) {
00129               SV_syslog(LOG_WARNING, "REDIRECT failed: unable to fetch msg %ld", cs->msgnum);
00130               free_recipients(valid);
00131               return SIEVE2_ERROR_BADARGS;
00132        }
00133 
00134        CtdlSubmitMsg(msg, valid, NULL, 0);
00135        cs->cancel_implicit_keep = 1;
00136        free_recipients(valid);
00137        CtdlFreeMessage(msg);
00138        return SIEVE2_OK;
00139 }
00140 
00141 
00142 /*
00143  * Callback function to indicate that a message *will* be kept in the inbox
00144  */
00145 int ctdl_keep(sieve2_context_t *s, void *my)
00146 {
00147        struct ctdl_sieve *cs = (struct ctdl_sieve *)my;
00148        
00149        SVM_syslog(LOG_DEBUG, "Action is KEEP");
00150 
00151        cs->keep = 1;
00152        cs->cancel_implicit_keep = 1;
00153        return SIEVE2_OK;
00154 }
00155 
00156 
00157 /*
00158  * Callback function to file a message into a different mailbox
00159  */
00160 int ctdl_fileinto(sieve2_context_t *s, void *my)
00161 {
00162        struct ctdl_sieve *cs = (struct ctdl_sieve *)my;
00163        const char *dest_folder = sieve2_getvalue_string(s, "mailbox");
00164        int c;
00165        char foldername[256];
00166        char original_room_name[ROOMNAMELEN];
00167 
00168        SV_syslog(LOG_DEBUG, "Action is FILEINTO, destination is <%s>", dest_folder);
00169 
00170        /* FILEINTO 'INBOX' is the same thing as KEEP */
00171        if ( (!strcasecmp(dest_folder, "INBOX")) || (!strcasecmp(dest_folder, MAILROOM)) ) {
00172               cs->keep = 1;
00173               cs->cancel_implicit_keep = 1;
00174               return SIEVE2_OK;
00175        }
00176 
00177        /* Remember what room we came from */
00178        safestrncpy(original_room_name, CC->room.QRname, sizeof original_room_name);
00179 
00180        /* First try a mailbox name match (check personal mail folders first) */
00181        snprintf(foldername, sizeof foldername, "%010ld.%s", cs->usernum, dest_folder);
00182        c = CtdlGetRoom(&CC->room, foldername);
00183 
00184        /* Then a regular room name match (public and private rooms) */
00185        if (c != 0) {
00186               safestrncpy(foldername, dest_folder, sizeof foldername);
00187               c = CtdlGetRoom(&CC->room, foldername);
00188        }
00189 
00190        if (c != 0) {
00191               SV_syslog(LOG_WARNING, "FILEINTO failed: target <%s> does not exist", dest_folder);
00192               return SIEVE2_ERROR_BADARGS;
00193        }
00194 
00195        /* Yes, we actually have to go there */
00196        CtdlUserGoto(NULL, 0, 0, NULL, NULL);
00197 
00198        c = CtdlSaveMsgPointersInRoom(NULL, &cs->msgnum, 1, 0, NULL, 0);
00199 
00200        /* Go back to the room we came from */
00201        if (strcasecmp(original_room_name, CC->room.QRname)) {
00202               CtdlUserGoto(original_room_name, 0, 0, NULL, NULL);
00203        }
00204 
00205        if (c == 0) {
00206               cs->cancel_implicit_keep = 1;
00207               return SIEVE2_OK;
00208        }
00209        else {
00210               return SIEVE2_ERROR_BADARGS;
00211        }
00212 }
00213 
00214 
00215 /*
00216  * Callback function to indicate that a message should be discarded.
00217  */
00218 int ctdl_discard(sieve2_context_t *s, void *my)
00219 {
00220        struct ctdl_sieve *cs = (struct ctdl_sieve *)my;
00221 
00222        SVM_syslog(LOG_DEBUG, "Action is DISCARD");
00223 
00224        /* Cancel the implicit keep.  That's all there is to it. */
00225        cs->cancel_implicit_keep = 1;
00226        return SIEVE2_OK;
00227 }
00228 
00229 
00230 
00231 /*
00232  * Callback function to indicate that a message should be rejected
00233  */
00234 int ctdl_reject(sieve2_context_t *s, void *my)
00235 {
00236        struct ctdl_sieve *cs = (struct ctdl_sieve *)my;
00237        char *reject_text = NULL;
00238 
00239        SVM_syslog(LOG_DEBUG, "Action is REJECT");
00240 
00241        /* If we don't know who sent the message, do a DISCARD instead. */
00242        if (IsEmptyStr(cs->sender)) {
00243               SVM_syslog(LOG_INFO, "Unknown sender.  Doing DISCARD instead of REJECT.");
00244               return ctdl_discard(s, my);
00245        }
00246 
00247        /* Assemble the reject message. */
00248        reject_text = malloc(strlen(sieve2_getvalue_string(s, "message")) + 1024);
00249        if (reject_text == NULL) {
00250               return SIEVE2_ERROR_FAIL;
00251        }
00252 
00253        sprintf(reject_text, 
00254               "Content-type: text/plain\n"
00255               "\n"
00256               "The message was refused by the recipient's mail filtering program.\n"
00257               "The reason given was as follows:\n"
00258               "\n"
00259               "%s\n"
00260               "\n"
00261        ,
00262               sieve2_getvalue_string(s, "message")
00263        );
00264 
00265        quickie_message(     /* This delivers the message */
00266               NULL,
00267               cs->envelope_to,
00268               cs->sender,
00269               NULL,
00270               reject_text,
00271               FMT_RFC822,
00272               "Delivery status notification"
00273        );
00274 
00275        free(reject_text);
00276        cs->cancel_implicit_keep = 1;
00277        return SIEVE2_OK;
00278 }
00279 
00280 
00281 
00282 /*
00283  * Callback function to indicate that a vacation message should be generated
00284  */
00285 int ctdl_vacation(sieve2_context_t *s, void *my)
00286 {
00287        struct ctdl_sieve *cs = (struct ctdl_sieve *)my;
00288        struct sdm_vacation *vptr;
00289        int days = 1;
00290        const char *message;
00291        char *vacamsg_text = NULL;
00292        char vacamsg_subject[1024];
00293 
00294        SVM_syslog(LOG_DEBUG, "Action is VACATION");
00295 
00296        message = sieve2_getvalue_string(s, "message");
00297        if (message == NULL) return SIEVE2_ERROR_BADARGS;
00298 
00299        if (sieve2_getvalue_string(s, "subject") != NULL) {
00300               safestrncpy(vacamsg_subject, sieve2_getvalue_string(s, "subject"), sizeof vacamsg_subject);
00301        }
00302        else {
00303               snprintf(vacamsg_subject, sizeof vacamsg_subject, "Re: %s", cs->subject);
00304        }
00305 
00306        days = sieve2_getvalue_int(s, "days");
00307        if (days < 1) days = 1;
00308        if (days > MAX_VACATION) days = MAX_VACATION;
00309 
00310        /* Check to see whether we've already alerted this sender that we're on vacation. */
00311        for (vptr = cs->u->first_vacation; vptr != NULL; vptr = vptr->next) {
00312               if (!strcasecmp(vptr->fromaddr, cs->sender)) {
00313                      if ( (time(NULL) - vptr->timestamp) < (days * 86400) ) {
00314                             SV_syslog(LOG_DEBUG, "Already alerted <%s> recently.", cs->sender);
00315                             return SIEVE2_OK;
00316                      }
00317               }
00318        }
00319 
00320        /* Assemble the reject message. */
00321        vacamsg_text = malloc(strlen(message) + 1024);
00322        if (vacamsg_text == NULL) {
00323               return SIEVE2_ERROR_FAIL;
00324        }
00325 
00326        sprintf(vacamsg_text, 
00327               "Content-type: text/plain charset=utf-8\n"
00328               "\n"
00329               "%s\n"
00330               "\n"
00331        ,
00332               message
00333        );
00334 
00335        quickie_message(     /* This delivers the message */
00336               NULL,
00337               cs->envelope_to,
00338               cs->sender,
00339               NULL,
00340               vacamsg_text,
00341               FMT_RFC822,
00342               vacamsg_subject
00343        );
00344 
00345        free(vacamsg_text);
00346 
00347        /* Now update the list to reflect the fact that we've alerted this sender.
00348         * If they're already in the list, just update the timestamp.
00349         */
00350        for (vptr = cs->u->first_vacation; vptr != NULL; vptr = vptr->next) {
00351               if (!strcasecmp(vptr->fromaddr, cs->sender)) {
00352                      vptr->timestamp = time(NULL);
00353                      return SIEVE2_OK;
00354               }
00355        }
00356 
00357        /* If we get to this point, create a new record.
00358         */
00359        vptr = malloc(sizeof(struct sdm_vacation));
00360        memset(vptr, 0, sizeof(struct sdm_vacation));
00361        vptr->timestamp = time(NULL);
00362        safestrncpy(vptr->fromaddr, cs->sender, sizeof vptr->fromaddr);
00363        vptr->next = cs->u->first_vacation;
00364        cs->u->first_vacation = vptr;
00365 
00366        return SIEVE2_OK;
00367 }
00368 
00369 
00370 /*
00371  * Callback function to parse addresses per local system convention
00372  * It is disabled because we don't support subaddresses.
00373  */
00374 #if 0
00375 int ctdl_getsubaddress(sieve2_context_t *s, void *my)
00376 {
00377        struct ctdl_sieve *cs = (struct ctdl_sieve *)my;
00378 
00379        /* libSieve does not take ownership of the memory used here.  But, since we
00380         * are just pointing to locations inside a struct which we are going to free
00381         * later, we're ok.
00382         */
00383        sieve2_setvalue_string(s, "user", cs->recp_user);
00384        sieve2_setvalue_string(s, "detail", "");
00385        sieve2_setvalue_string(s, "localpart", cs->recp_user);
00386        sieve2_setvalue_string(s, "domain", cs->recp_node);
00387        return SIEVE2_OK;
00388 }
00389 #endif
00390 
00391 
00392 /*
00393  * Callback function to parse message envelope
00394  */
00395 int ctdl_getenvelope(sieve2_context_t *s, void *my)
00396 {
00397        struct ctdl_sieve *cs = (struct ctdl_sieve *)my;
00398 
00399        SVM_syslog(LOG_DEBUG, "Action is GETENVELOPE");
00400        SV_syslog(LOG_DEBUG, "EnvFrom: %s", cs->envelope_from);
00401        SV_syslog(LOG_DEBUG, "EnvTo: %s", cs->envelope_to);
00402 
00403        if (cs->envelope_from != NULL) {
00404               if ((cs->envelope_from[0] != '@')&&(cs->envelope_from[strlen(cs->envelope_from)-1] != '@')) {
00405                      sieve2_setvalue_string(s, "from", cs->envelope_from);
00406               }
00407               else {
00408                      sieve2_setvalue_string(s, "from", "invalid_envelope_from@example.org");
00409               }
00410        }
00411        else {
00412               sieve2_setvalue_string(s, "from", "null_envelope_from@example.org");
00413        }
00414 
00415 
00416        if (cs->envelope_to != NULL) {
00417               if ((cs->envelope_to[0] != '@') && (cs->envelope_to[strlen(cs->envelope_to)-1] != '@')) {
00418                      sieve2_setvalue_string(s, "to", cs->envelope_to);
00419               }
00420               else {
00421                      sieve2_setvalue_string(s, "to", "invalid_envelope_to@example.org");
00422               }
00423        }
00424        else {
00425               sieve2_setvalue_string(s, "to", "null_envelope_to@example.org");
00426        }
00427 
00428        return SIEVE2_OK;
00429 }
00430 
00431 
00432 /*
00433  * Callback function to fetch message body
00434  * (Uncomment the code if we implement this extension)
00435  *
00436 int ctdl_getbody(sieve2_context_t *s, void *my)
00437 {
00438        return SIEVE2_ERROR_UNSUPPORTED;
00439 }
00440  *
00441  */
00442 
00443 
00444 /*
00445  * Callback function to fetch message size
00446  */
00447 int ctdl_getsize(sieve2_context_t *s, void *my)
00448 {
00449        struct ctdl_sieve *cs = (struct ctdl_sieve *)my;
00450        struct MetaData smi;
00451 
00452        GetMetaData(&smi, cs->msgnum);
00453        
00454        if (smi.meta_rfc822_length > 0L) {
00455               sieve2_setvalue_int(s, "size", (int)smi.meta_rfc822_length);
00456               return SIEVE2_OK;
00457        }
00458 
00459        return SIEVE2_ERROR_UNSUPPORTED;
00460 }
00461 
00462 
00463 /*
00464  * Return a pointer to the active Sieve script.
00465  * (Caller does NOT own the memory and should not free the returned pointer.)
00466  */
00467 char *get_active_script(struct sdm_userdata *u) {
00468        struct sdm_script *sptr;
00469 
00470        for (sptr=u->first_script; sptr!=NULL; sptr=sptr->next) {
00471               if (sptr->script_active > 0) {
00472                      SV_syslog(LOG_DEBUG, "get_active_script() is using script '%s'", sptr->script_name);
00473                      return(sptr->script_content);
00474               }
00475        }
00476 
00477        SVM_syslog(LOG_DEBUG, "get_active_script() found no active script");
00478        return(NULL);
00479 }
00480 
00481 
00482 /*
00483  * Callback function to retrieve the sieve script
00484  */
00485 int ctdl_getscript(sieve2_context_t *s, void *my) {
00486        struct ctdl_sieve *cs = (struct ctdl_sieve *)my;
00487 
00488        char *active_script = get_active_script(cs->u);
00489        if (active_script != NULL) {
00490               sieve2_setvalue_string(s, "script", active_script);
00491               return SIEVE2_OK;
00492        }
00493 
00494        return SIEVE2_ERROR_GETSCRIPT;
00495 }
00496 
00497 /*
00498  * Callback function to retrieve message headers
00499  */
00500 int ctdl_getheaders(sieve2_context_t *s, void *my) {
00501 
00502        struct ctdl_sieve *cs = (struct ctdl_sieve *)my;
00503 
00504        SVM_syslog(LOG_DEBUG, "ctdl_getheaders() was called");
00505        sieve2_setvalue_string(s, "allheaders", cs->rfc822headers);
00506        return SIEVE2_OK;
00507 }
00508 
00509 
00510 
00511 /*
00512  * Add a room to the list of those rooms which potentially require sieve processing
00513  */
00514 void sieve_queue_room(struct ctdlroom *which_room) {
00515        struct RoomProcList *ptr;
00516 
00517        ptr = (struct RoomProcList *) malloc(sizeof (struct RoomProcList));
00518        if (ptr == NULL) return;
00519 
00520        safestrncpy(ptr->name, which_room->QRname, sizeof ptr->name);
00521        begin_critical_section(S_SIEVELIST);
00522        ptr->next = sieve_list;
00523        sieve_list = ptr;
00524        end_critical_section(S_SIEVELIST);
00525        SV_syslog(LOG_DEBUG, "<%s> queued for Sieve processing", which_room->QRname);
00526 }
00527 
00528 
00529 
00530 /*
00531  * Perform sieve processing for one message (called by sieve_do_room() for each message)
00532  */
00533 void sieve_do_msg(long msgnum, void *userdata) {
00534        struct sdm_userdata *u = (struct sdm_userdata *) userdata;
00535        sieve2_context_t *sieve2_context;
00536        struct ctdl_sieve my;
00537        int res;
00538        struct CtdlMessage *msg;
00539        int i;
00540        size_t headers_len = 0;
00541        int len = 0;
00542 
00543        if (u == NULL)
00544        {
00545               SV_syslog(LOG_EMERG, "Can't process message <%ld> without userdata!", msgnum);
00546               return;
00547        }
00548 
00549        sieve2_context = u->sieve2_context;
00550 
00551        SV_syslog(LOG_DEBUG, "Performing sieve processing on msg <%ld>", msgnum);
00552 
00553        /*
00554         * Make sure you include message body so you can get those second-level headers ;)
00555         */
00556        msg = CtdlFetchMessage(msgnum, 1);
00557        if (msg == NULL) return;
00558 
00559        /*
00560         * Grab the message headers so we can feed them to libSieve.
00561         * Use HEADERS_ONLY rather than HEADERS_FAST in order to include second-level headers.
00562         */
00563        CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
00564        CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ONLY, 0, 1, 0);
00565        headers_len = StrLength(CC->redirect_buffer);
00566        my.rfc822headers = SmashStrBuf(&CC->redirect_buffer);
00567 
00568        /*
00569         * libSieve clobbers the stack if it encounters badly formed
00570         * headers.  Sanitize our headers by stripping nonprintable
00571         * characters.
00572         */
00573        for (i=0; i<headers_len; ++i) {
00574               if (!isascii(my.rfc822headers[i])) {
00575                      my.rfc822headers[i] = '_';
00576               }
00577        }
00578 
00579        my.keep = 0;                       /* Set to 1 to declare an *explicit* keep */
00580        my.cancel_implicit_keep = 0;              /* Some actions will cancel the implicit keep */
00581        my.usernum = atol(CC->room.QRname);       /* Keep track of the owner of the room's namespace */
00582        my.msgnum = msgnum;                /* Keep track of the message number in our local store */
00583        my.u = u;                          /* Hand off a pointer to the rest of this info */
00584 
00585        /* Keep track of the recipient so we can do handling based on it later */
00586        process_rfc822_addr(msg->cm_fields['R'], my.recp_user, my.recp_node, my.recp_name);
00587 
00588        /* Keep track of the sender so we can use it for REJECT and VACATION responses */
00589        if (msg->cm_fields['F'] != NULL) {
00590               safestrncpy(my.sender, msg->cm_fields['F'], sizeof my.sender);
00591        }
00592        else if ( (msg->cm_fields['A'] != NULL) && (msg->cm_fields['N'] != NULL) ) {
00593               snprintf(my.sender, sizeof my.sender, "%s@%s", msg->cm_fields['A'], msg->cm_fields['N']);
00594        }
00595        else if (msg->cm_fields['A'] != NULL) {
00596               safestrncpy(my.sender, msg->cm_fields['A'], sizeof my.sender);
00597        }
00598        else {
00599               strcpy(my.sender, "");
00600        }
00601 
00602        /* Keep track of the subject so we can use it for VACATION responses */
00603        if (msg->cm_fields['U'] != NULL) {
00604               safestrncpy(my.subject, msg->cm_fields['U'], sizeof my.subject);
00605        }
00606        else {
00607               strcpy(my.subject, "");
00608        }
00609 
00610        /* Keep track of the envelope-from address (use body-from if not found) */
00611        if (msg->cm_fields['P'] != NULL) {
00612               safestrncpy(my.envelope_from, msg->cm_fields['P'], sizeof my.envelope_from);
00613               stripallbut(my.envelope_from, '<', '>');
00614        }
00615        else if (msg->cm_fields['F'] != NULL) {
00616               safestrncpy(my.envelope_from, msg->cm_fields['F'], sizeof my.envelope_from);
00617               stripallbut(my.envelope_from, '<', '>');
00618        }
00619        else {
00620               strcpy(my.envelope_from, "");
00621        }
00622 
00623        len = strlen(my.envelope_from);
00624        for (i=0; i<len; ++i) {
00625               if (isspace(my.envelope_from[i])) my.envelope_from[i] = '_';
00626        }
00627        if (haschar(my.envelope_from, '@') == 0) {
00628               strcat(my.envelope_from, "@");
00629               strcat(my.envelope_from, config.c_fqdn);
00630        }
00631 
00632        /* Keep track of the envelope-to address (use body-to if not found) */
00633        if (msg->cm_fields['V'] != NULL) {
00634               safestrncpy(my.envelope_to, msg->cm_fields['V'], sizeof my.envelope_to);
00635               stripallbut(my.envelope_to, '<', '>');
00636        }
00637        else if (msg->cm_fields['R'] != NULL) {
00638               safestrncpy(my.envelope_to, msg->cm_fields['R'], sizeof my.envelope_to);
00639               if (msg->cm_fields['D'] != NULL) {
00640                      strcat(my.envelope_to, "@");
00641                      strcat(my.envelope_to, msg->cm_fields['D']);
00642               }
00643               stripallbut(my.envelope_to, '<', '>');
00644        }
00645        else {
00646               strcpy(my.envelope_to, "");
00647        }
00648 
00649        len = strlen(my.envelope_to);
00650        for (i=0; i<len; ++i) {
00651               if (isspace(my.envelope_to[i])) my.envelope_to[i] = '_';
00652        }
00653        if (haschar(my.envelope_to, '@') == 0) {
00654               strcat(my.envelope_to, "@");
00655               strcat(my.envelope_to, config.c_fqdn);
00656        }
00657 
00658        CtdlFreeMessage(msg);
00659        
00660        SVM_syslog(LOG_DEBUG, "Calling sieve2_execute()");
00661        res = sieve2_execute(sieve2_context, &my);
00662        if (res != SIEVE2_OK) {
00663               SV_syslog(LOG_CRIT, "sieve2_execute() returned %d: %s", res, sieve2_errstr(res));
00664        }
00665 
00666        free(my.rfc822headers);
00667        my.rfc822headers = NULL;
00668 
00669        /*
00670         * Delete the message from the inbox unless either we were told not to, or
00671         * if no other action was successfully taken.
00672         */
00673        if ( (!my.keep) && (my.cancel_implicit_keep) ) {
00674               SVM_syslog(LOG_DEBUG, "keep is 0 -- deleting message from inbox");
00675               CtdlDeleteMessages(CC->room.QRname, &msgnum, 1, "");
00676        }
00677 
00678        SV_syslog(LOG_DEBUG, "Completed sieve processing on msg <%ld>", msgnum);
00679        u->lastproc = msgnum;
00680 
00681        return;
00682 }
00683 
00684 
00685 
00686 /*
00687  * Given the on-disk representation of our Sieve config, load
00688  * it into an in-memory data structure.
00689  */
00690 void parse_sieve_config(char *conf, struct sdm_userdata *u) {
00691        char *ptr;
00692        char *c, *vacrec;
00693        char keyword[256];
00694        struct sdm_script *sptr;
00695        struct sdm_vacation *vptr;
00696 
00697        ptr = conf;
00698        while (c = ptr, ptr = bmstrcasestr(ptr, CTDLSIEVECONFIGSEPARATOR), ptr != NULL) {
00699               *ptr = 0;
00700               ptr += strlen(CTDLSIEVECONFIGSEPARATOR);
00701 
00702               extract_token(keyword, c, 0, '|', sizeof keyword);
00703 
00704               if (!strcasecmp(keyword, "lastproc")) {
00705                      u->lastproc = extract_long(c, 1);
00706               }
00707 
00708               else if (!strcasecmp(keyword, "script")) {
00709                      sptr = malloc(sizeof(struct sdm_script));
00710                      extract_token(sptr->script_name, c, 1, '|', sizeof sptr->script_name);
00711                      sptr->script_active = extract_int(c, 2);
00712                      remove_token(c, 0, '|');
00713                      remove_token(c, 0, '|');
00714                      remove_token(c, 0, '|');
00715                      sptr->script_content = strdup(c);
00716                      sptr->next = u->first_script;
00717                      u->first_script = sptr;
00718               }
00719 
00720               else if (!strcasecmp(keyword, "vacation")) {
00721 
00722                      if (c != NULL) while (vacrec=c, c=strchr(c, '\n'), (c != NULL)) {
00723 
00724                             *c = 0;
00725                             ++c;
00726 
00727                             if (strncasecmp(vacrec, "vacation|", 9)) {
00728                                    vptr = malloc(sizeof(struct sdm_vacation));
00729                                    extract_token(vptr->fromaddr, vacrec, 0, '|', sizeof vptr->fromaddr);
00730                                    vptr->timestamp = extract_long(vacrec, 1);
00731                                    vptr->next = u->first_vacation;
00732                                    u->first_vacation = vptr;
00733                             }
00734                      }
00735               }
00736 
00737               /* ignore unknown keywords */
00738        }
00739 }
00740 
00741 /*
00742  * We found the Sieve configuration for this user.
00743  * Now do something with it.
00744  */
00745 void get_sieve_config_backend(long msgnum, void *userdata) {
00746        struct sdm_userdata *u = (struct sdm_userdata *) userdata;
00747        struct CtdlMessage *msg;
00748        char *conf;
00749 
00750        u->config_msgnum = msgnum;
00751        msg = CtdlFetchMessage(msgnum, 1);
00752        if (msg == NULL) {
00753               u->config_msgnum = (-1) ;
00754               return;
00755        }
00756 
00757        conf = msg->cm_fields['M'];
00758        msg->cm_fields['M'] = NULL;
00759        CtdlFreeMessage(msg);
00760 
00761        if (conf != NULL) {
00762               parse_sieve_config(conf, u);
00763               free(conf);
00764        }
00765 
00766 }
00767 
00768 
00769 /* 
00770  * Write our citadel sieve config back to disk
00771  * 
00772  * (Set yes_write_to_disk to nonzero to make it actually write the config;
00773  * otherwise it just frees the data structures.)
00774  */
00775 void rewrite_ctdl_sieve_config(struct sdm_userdata *u, int yes_write_to_disk) {
00776        StrBuf *text;
00777        struct sdm_script *sptr;
00778        struct sdm_vacation *vptr;
00779        
00780        text = NewStrBufPlain(NULL, SIZ);
00781        StrBufPrintf(text,
00782                    "Content-type: application/x-citadel-sieve-config\n"
00783                    "\n"
00784                    CTDLSIEVECONFIGSEPARATOR
00785                    "lastproc|%ld"
00786                    CTDLSIEVECONFIGSEPARATOR
00787                    ,
00788                    u->lastproc
00789               );
00790 
00791        while (u->first_script != NULL) {
00792               StrBufAppendPrintf(text,
00793                                "script|%s|%d|%s" CTDLSIEVECONFIGSEPARATOR,
00794                                u->first_script->script_name,
00795                                u->first_script->script_active,
00796                                u->first_script->script_content
00797                      );
00798               sptr = u->first_script;
00799               u->first_script = u->first_script->next;
00800               free(sptr->script_content);
00801               free(sptr);
00802        }
00803 
00804        if (u->first_vacation != NULL) {
00805 
00806               StrBufAppendPrintf(text, "vacation|\n");
00807               while (u->first_vacation != NULL) {
00808                      if ( (time(NULL) - u->first_vacation->timestamp) < (MAX_VACATION * 86400)) {
00809                             StrBufAppendPrintf(text, "%s|%ld\n",
00810                                              u->first_vacation->fromaddr,
00811                                              u->first_vacation->timestamp
00812                                    );
00813                      }
00814                      vptr = u->first_vacation;
00815                      u->first_vacation = u->first_vacation->next;
00816                      free(vptr);
00817               }
00818               StrBufAppendPrintf(text, CTDLSIEVECONFIGSEPARATOR);
00819        }
00820 
00821        if (yes_write_to_disk)
00822        {
00823               /* Save the config */
00824               quickie_message("Citadel", NULL, NULL, u->config_roomname,
00825                             ChrPtr(text),
00826                             4,
00827                             "Sieve configuration"
00828               );
00829               
00830               /* And delete the old one */
00831               if (u->config_msgnum > 0) {
00832                      CtdlDeleteMessages(u->config_roomname, &u->config_msgnum, 1, "");
00833               }
00834        }
00835 
00836        FreeStrBuf (&text);
00837 
00838 }
00839 
00840 
00841 /*
00842  * This is our callback registration table for libSieve.
00843  */
00844 sieve2_callback_t ctdl_sieve_callbacks[] = {
00845        { SIEVE2_ACTION_REJECT,            ctdl_reject          },
00846        { SIEVE2_ACTION_VACATION,   ctdl_vacation        },
00847        { SIEVE2_ERRCALL_PARSE,            ctdl_errparse        },
00848        { SIEVE2_ERRCALL_RUNTIME,   ctdl_errexec         },
00849        { SIEVE2_ACTION_FILEINTO,   ctdl_fileinto        },
00850        { SIEVE2_ACTION_REDIRECT,   ctdl_redirect        },
00851        { SIEVE2_ACTION_DISCARD,    ctdl_discard         },
00852        { SIEVE2_ACTION_KEEP,              ctdl_keep            },
00853        { SIEVE2_SCRIPT_GETSCRIPT,  ctdl_getscript              },
00854        { SIEVE2_DEBUG_TRACE,              ctdl_debug           },
00855        { SIEVE2_MESSAGE_GETALLHEADERS,    ctdl_getheaders             },
00856        { SIEVE2_MESSAGE_GETSIZE,   ctdl_getsize         },
00857        { SIEVE2_MESSAGE_GETENVELOPE,      ctdl_getenvelope     },
00858 /*
00859  * These actions are unsupported by Citadel so we don't declare them.
00860  *
00861        { SIEVE2_ACTION_NOTIFY,            ctdl_notify          },
00862        { SIEVE2_MESSAGE_GETSUBADDRESS,    ctdl_getsubaddress   },
00863        { SIEVE2_MESSAGE_GETBODY,   ctdl_getbody         },
00864  *
00865  */
00866        { 0 }
00867 };
00868 
00869 
00870 /*
00871  * Perform sieve processing for a single room
00872  */
00873 void sieve_do_room(char *roomname) {
00874        
00875        struct sdm_userdata u;
00876        sieve2_context_t *sieve2_context = NULL;  /* Context for sieve parser */
00877        int res;                                  /* Return code from libsieve calls */
00878        long orig_lastproc = 0;
00879 
00880        memset(&u, 0, sizeof u);
00881 
00882        /* See if the user who owns this 'mailbox' has any Sieve scripts that
00883         * require execution.
00884         */
00885        snprintf(u.config_roomname, sizeof u.config_roomname, "%010ld.%s", atol(roomname), USERCONFIGROOM);
00886        if (CtdlGetRoom(&CC->room, u.config_roomname) != 0) {
00887               SV_syslog(LOG_DEBUG, "<%s> does not exist.  No processing is required.", u.config_roomname);
00888               return;
00889        }
00890 
00891        /*
00892         * Find the sieve scripts and control record and do something
00893         */
00894        u.config_msgnum = (-1);
00895        CtdlForEachMessage(MSGS_LAST, 1, NULL, SIEVECONFIG, NULL,
00896               get_sieve_config_backend, (void *)&u );
00897 
00898        if (u.config_msgnum < 0) {
00899               SVM_syslog(LOG_DEBUG, "No Sieve rules exist.  No processing is required.");
00900               return;
00901        }
00902 
00903        /*
00904         * Check to see whether the script is empty and should not be processed.
00905         * A script is considered non-empty if it contains at least one semicolon.
00906         */
00907        if (
00908               (get_active_script(&u) == NULL)
00909               || (strchr(get_active_script(&u), ';') == NULL)
00910        ) {
00911               SVM_syslog(LOG_DEBUG, "Sieve script is empty.  No processing is required.");
00912               return;
00913        }
00914 
00915        SV_syslog(LOG_DEBUG, "Rules found.  Performing Sieve processing for <%s>", roomname);
00916 
00917        if (CtdlGetRoom(&CC->room, roomname) != 0) {
00918               SV_syslog(LOG_CRIT, "ERROR: cannot load <%s>", roomname);
00919               return;
00920        }
00921 
00922        /* Initialize the Sieve parser */
00923        
00924        res = sieve2_alloc(&sieve2_context);
00925        if (res != SIEVE2_OK) {
00926               SV_syslog(LOG_CRIT, "sieve2_alloc() returned %d: %s", res, sieve2_errstr(res));
00927               return;
00928        }
00929 
00930        res = sieve2_callbacks(sieve2_context, ctdl_sieve_callbacks);
00931        if (res != SIEVE2_OK) {
00932               SV_syslog(LOG_CRIT, "sieve2_callbacks() returned %d: %s", res, sieve2_errstr(res));
00933               goto BAIL;
00934        }
00935 
00936        /* Validate the script */
00937 
00938        struct ctdl_sieve my;              /* dummy ctdl_sieve struct just to pass "u" slong */
00939        memset(&my, 0, sizeof my);
00940        my.u = &u;
00941        res = sieve2_validate(sieve2_context, &my);
00942        if (res != SIEVE2_OK) {
00943               SV_syslog(LOG_CRIT, "sieve2_validate() returned %d: %s", res, sieve2_errstr(res));
00944               goto BAIL;
00945        }
00946 
00947        /* Do something useful */
00948        u.sieve2_context = sieve2_context;
00949        orig_lastproc = u.lastproc;
00950        CtdlForEachMessage(MSGS_GT, u.lastproc, NULL, NULL, NULL,
00951               sieve_do_msg,
00952               (void *) &u
00953        );
00954 
00955 BAIL:
00956        res = sieve2_free(&sieve2_context);
00957        if (res != SIEVE2_OK) {
00958               SV_syslog(LOG_CRIT, "sieve2_free() returned %d: %s", res, sieve2_errstr(res));
00959        }
00960 
00961        /* Rewrite the config if we have to */
00962        rewrite_ctdl_sieve_config(&u, (u.lastproc > orig_lastproc) ) ;
00963 }
00964 
00965 
00966 /*
00967  * Perform sieve processing for all rooms which require it
00968  */
00969 void perform_sieve_processing(void) {
00970        struct RoomProcList *ptr = NULL;
00971 
00972        if (sieve_list != NULL) {
00973               SVM_syslog(LOG_DEBUG, "Begin Sieve processing");
00974               while (sieve_list != NULL) {
00975                      char spoolroomname[ROOMNAMELEN];
00976                      safestrncpy(spoolroomname, sieve_list->name, sizeof spoolroomname);
00977                      begin_critical_section(S_SIEVELIST);
00978 
00979                      /* pop this record off the list */
00980                      ptr = sieve_list;
00981                      sieve_list = sieve_list->next;
00982                      free(ptr);
00983 
00984                      /* invalidate any duplicate entries to prevent double processing */
00985                      for (ptr=sieve_list; ptr!=NULL; ptr=ptr->next) {
00986                             if (!strcasecmp(ptr->name, spoolroomname)) {
00987                                    ptr->name[0] = 0;
00988                             }
00989                      }
00990 
00991                      end_critical_section(S_SIEVELIST);
00992                      if (spoolroomname[0] != 0) {
00993                             sieve_do_room(spoolroomname);
00994                      }
00995               }
00996        }
00997 }
00998 
00999 
01000 void msiv_load(struct sdm_userdata *u) {
01001        char hold_rm[ROOMNAMELEN];
01002 
01003        strcpy(hold_rm, CC->room.QRname);       /* save current room */
01004 
01005        /* Take a spin through the user's personal address book */
01006        if (CtdlGetRoom(&CC->room, USERCONFIGROOM) == 0) {
01007        
01008               u->config_msgnum = (-1);
01009               strcpy(u->config_roomname, CC->room.QRname);
01010               CtdlForEachMessage(MSGS_LAST, 1, NULL, SIEVECONFIG, NULL,
01011                      get_sieve_config_backend, (void *)u );
01012 
01013        }
01014 
01015        if (strcmp(CC->room.QRname, hold_rm)) {
01016               CtdlGetRoom(&CC->room, hold_rm);    /* return to saved room */
01017        }
01018 }
01019 
01020 void msiv_store(struct sdm_userdata *u, int yes_write_to_disk) {
01021 /*
01022  * Initialise the sieve configs last processed message number.
01023  * We don't need to get the highest message number for the users inbox since the systems
01024  * highest message number will be higher than that and loer than this scripts message number
01025  * This prevents this new script from processing any old messages in the inbox.
01026  * Most importantly it will prevent vacation messages being sent to lots of old messages
01027  * in the inbox.
01028  */
01029        u->lastproc = CtdlGetCurrentMessageNumber();
01030        rewrite_ctdl_sieve_config(u, yes_write_to_disk);
01031 }
01032 
01033 
01034 /*
01035  * Select the active script.
01036  * (Set script_name to an empty string to disable all scripts)
01037  * 
01038  * Returns 0 on success or nonzero for error.
01039  */
01040 int msiv_setactive(struct sdm_userdata *u, char *script_name) {
01041        int ok = 0;
01042        struct sdm_script *s;
01043 
01044        /* First see if the supplied value is ok */
01045 
01046        if (IsEmptyStr(script_name)) {
01047               ok = 1;
01048        }
01049        else {
01050               for (s=u->first_script; s!=NULL; s=s->next) {
01051                      if (!strcasecmp(s->script_name, script_name)) {
01052                             ok = 1;
01053                      }
01054               }
01055        }
01056 
01057        if (!ok) return(-1);
01058 
01059        /* Now set the active script */
01060        for (s=u->first_script; s!=NULL; s=s->next) {
01061               if (!strcasecmp(s->script_name, script_name)) {
01062                      s->script_active = 1;
01063               }
01064               else {
01065                      s->script_active = 0;
01066               }
01067        }
01068        
01069        return(0);
01070 }
01071 
01072 
01073 /*
01074  * Fetch a script by name.
01075  *
01076  * Returns NULL if the named script was not found, or a pointer to the script
01077  * if it was found.   NOTE: the caller does *not* own the memory returned by
01078  * this function.  Copy it if you need to keep it.
01079  */
01080 char *msiv_getscript(struct sdm_userdata *u, char *script_name) {
01081        struct sdm_script *s;
01082 
01083        for (s=u->first_script; s!=NULL; s=s->next) {
01084               if (!strcasecmp(s->script_name, script_name)) {
01085                      if (s->script_content != NULL) {
01086                             return (s->script_content);
01087                      }
01088               }
01089        }
01090 
01091        return(NULL);
01092 }
01093 
01094 
01095 /*
01096  * Delete a script by name.
01097  *
01098  * Returns 0 if the script was deleted.
01099  *      1 if the script was not found.
01100  *      2 if the script cannot be deleted because it is active.
01101  */
01102 int msiv_deletescript(struct sdm_userdata *u, char *script_name) {
01103        struct sdm_script *s = NULL;
01104        struct sdm_script *script_to_delete = NULL;
01105 
01106        for (s=u->first_script; s!=NULL; s=s->next) {
01107               if (!strcasecmp(s->script_name, script_name)) {
01108                      script_to_delete = s;
01109                      if (s->script_active) {
01110                             return(2);
01111                      }
01112               }
01113        }
01114 
01115        if (script_to_delete == NULL) return(1);
01116 
01117        if (u->first_script == script_to_delete) {
01118               u->first_script = u->first_script->next;
01119        }
01120        else for (s=u->first_script; s!=NULL; s=s->next) {
01121               if (s->next == script_to_delete) {
01122                      s->next = s->next->next;
01123               }
01124        }
01125 
01126        free(script_to_delete->script_content);
01127        free(script_to_delete);
01128        return(0);
01129 }
01130 
01131 
01132 /*
01133  * Add or replace a new script.  
01134  * NOTE: after this function returns, "u" owns the memory that "script_content"
01135  * was pointing to.
01136  */
01137 void msiv_putscript(struct sdm_userdata *u, char *script_name, char *script_content) {
01138        int replaced = 0;
01139        struct sdm_script *s, *sptr;
01140 
01141        for (s=u->first_script; s!=NULL; s=s->next) {
01142               if (!strcasecmp(s->script_name, script_name)) {
01143                      if (s->script_content != NULL) {
01144                             free(s->script_content);
01145                      }
01146                      s->script_content = script_content;
01147                      replaced = 1;
01148               }
01149        }
01150 
01151        if (replaced == 0) {
01152               sptr = malloc(sizeof(struct sdm_script));
01153               safestrncpy(sptr->script_name, script_name, sizeof sptr->script_name);
01154               sptr->script_content = script_content;
01155               sptr->script_active = 0;
01156               sptr->next = u->first_script;
01157               u->first_script = sptr;
01158        }
01159 }
01160 
01161 
01162 
01163 /*
01164  * Citadel protocol to manage sieve scripts.
01165  * This is basically a simplified (read: doesn't resemble IMAP) version
01166  * of the 'managesieve' protocol.
01167  */
01168 void cmd_msiv(char *argbuf) {
01169        char subcmd[256];
01170        struct sdm_userdata u;
01171        char script_name[256];
01172        char *script_content = NULL;
01173        struct sdm_script *s;
01174        int i;
01175        int changes_made = 0;
01176 
01177        memset(&u, 0, sizeof(struct sdm_userdata));
01178 
01179        if (CtdlAccessCheck(ac_logged_in)) return;
01180        extract_token(subcmd, argbuf, 0, '|', sizeof subcmd);
01181        msiv_load(&u);
01182 
01183        if (!strcasecmp(subcmd, "putscript")) {
01184               extract_token(script_name, argbuf, 1, '|', sizeof script_name);
01185               if (!IsEmptyStr(script_name)) {
01186                      cprintf("%d Transmit script now\n", SEND_LISTING);
01187                      script_content = CtdlReadMessageBody(HKEY("000"), config.c_maxmsglen, NULL, 0, 0);
01188                      msiv_putscript(&u, script_name, script_content);
01189                      changes_made = 1;
01190               }
01191               else {
01192                      cprintf("%d Invalid script name.\n", ERROR + ILLEGAL_VALUE);
01193               }
01194        }      
01195        
01196        else if (!strcasecmp(subcmd, "listscripts")) {
01197               cprintf("%d Scripts:\n", LISTING_FOLLOWS);
01198               for (s=u.first_script; s!=NULL; s=s->next) {
01199                      if (s->script_content != NULL) {
01200                             cprintf("%s|%d|\n", s->script_name, s->script_active);
01201                      }
01202               }
01203               cprintf("000\n");
01204        }
01205 
01206        else if (!strcasecmp(subcmd, "setactive")) {
01207               extract_token(script_name, argbuf, 1, '|', sizeof script_name);
01208               if (msiv_setactive(&u, script_name) == 0) {
01209                      cprintf("%d ok\n", CIT_OK);
01210                      changes_made = 1;
01211               }
01212               else {
01213                      cprintf("%d Script '%s' does not exist.\n",
01214                             ERROR + ILLEGAL_VALUE,
01215                             script_name
01216                      );
01217               }
01218        }
01219 
01220        else if (!strcasecmp(subcmd, "getscript")) {
01221               extract_token(script_name, argbuf, 1, '|', sizeof script_name);
01222               script_content = msiv_getscript(&u, script_name);
01223               if (script_content != NULL) {
01224                      int script_len;
01225 
01226                      cprintf("%d Script:\n", LISTING_FOLLOWS);
01227                      script_len = strlen(script_content);
01228                      client_write(script_content, script_len);
01229                      if (script_content[script_len-1] != '\n') {
01230                             cprintf("\n");
01231                      }
01232                      cprintf("000\n");
01233               }
01234               else {
01235                      cprintf("%d Invalid script name.\n", ERROR + ILLEGAL_VALUE);
01236               }
01237        }
01238 
01239        else if (!strcasecmp(subcmd, "deletescript")) {
01240               extract_token(script_name, argbuf, 1, '|', sizeof script_name);
01241               i = msiv_deletescript(&u, script_name);
01242               if (i == 0) {
01243                      cprintf("%d ok\n", CIT_OK);
01244                      changes_made = 1;
01245               }
01246               else if (i == 1) {
01247                      cprintf("%d Script '%s' does not exist.\n",
01248                             ERROR + ILLEGAL_VALUE,
01249                             script_name
01250                      );
01251               }
01252               else if (i == 2) {
01253                      cprintf("%d Script '%s' is active and cannot be deleted.\n",
01254                             ERROR + ILLEGAL_VALUE,
01255                             script_name
01256                      );
01257               }
01258               else {
01259                      cprintf("%d unknown error\n", ERROR);
01260               }
01261        }
01262 
01263        else {
01264               cprintf("%d Invalid subcommand\n", ERROR + CMD_NOT_SUPPORTED);
01265        }
01266 
01267        msiv_store(&u, changes_made);
01268 }
01269 
01270 
01271 
01272 void ctdl_sieve_init(void) {
01273        char *cred = NULL;
01274        sieve2_context_t *sieve2_context = NULL;
01275        int res;
01276 
01277        /*
01278         *     We don't really care about dumping the entire credits to the log
01279         *     every time the server is initialized.  The documentation will suffice
01280         *     for that purpose.  We are making a call to sieve2_credits() in order
01281         *     to demonstrate that we have successfully linked in to libsieve.
01282         */
01283        cred = strdup(sieve2_credits());
01284        if (cred == NULL) return;
01285 
01286        if (strlen(cred) > 60) {
01287               strcpy(&cred[55], "...");
01288        }
01289 
01290        SV_syslog(LOG_INFO, "%s",cred);
01291        free(cred);
01292 
01293        /* Briefly initialize a Sieve parser instance just so we can list the
01294         * extensions that are available.
01295         */
01296        res = sieve2_alloc(&sieve2_context);
01297        if (res != SIEVE2_OK) {
01298               SV_syslog(LOG_CRIT, "sieve2_alloc() returned %d: %s", res, sieve2_errstr(res));
01299               return;
01300        }
01301 
01302        res = sieve2_callbacks(sieve2_context, ctdl_sieve_callbacks);
01303        if (res != SIEVE2_OK) {
01304               SV_syslog(LOG_CRIT, "sieve2_callbacks() returned %d: %s", res, sieve2_errstr(res));
01305               goto BAIL;
01306        }
01307 
01308        msiv_extensions = strdup(sieve2_listextensions(sieve2_context));
01309        SV_syslog(LOG_INFO, "Extensions: %s", msiv_extensions);
01310 
01311 BAIL:  res = sieve2_free(&sieve2_context);
01312        if (res != SIEVE2_OK) {
01313               SV_syslog(LOG_CRIT, "sieve2_free() returned %d: %s", res, sieve2_errstr(res));
01314        }
01315 
01316 }
01317 
01318 void cleanup_sieve(void)
01319 {
01320         struct RoomProcList *ptr, *ptr2;
01321 
01322        if (msiv_extensions != NULL)
01323               free(msiv_extensions);
01324        msiv_extensions = NULL;
01325 
01326         begin_critical_section(S_SIEVELIST);
01327        ptr=sieve_list;
01328        while (ptr != NULL) {
01329               ptr2 = ptr->next;
01330               free(ptr);
01331               ptr = ptr2;
01332        }
01333         sieve_list = NULL;
01334         end_critical_section(S_SIEVELIST);
01335 }
01336 
01337 int serv_sieve_room(struct ctdlroom *room)
01338 {
01339        if (!strcasecmp(&room->QRname[11], MAILROOM)) {
01340               sieve_queue_room(room);
01341        }
01342        return 0;
01343 }
01344 
01345 void LogSieveDebugEnable(const int n)
01346 {
01347        SieveDebugEnable = n;
01348 }
01349 CTDL_MODULE_INIT(sieve)
01350 {
01351        if (!threading)
01352        {
01353               CtdlRegisterDebugFlagHook(HKEY("sieve"), LogSieveDebugEnable, &SieveDebugEnable);
01354               ctdl_sieve_init();
01355               CtdlRegisterProtoHook(cmd_msiv, "MSIV", "Manage Sieve scripts");
01356                CtdlRegisterRoomHook(serv_sieve_room);
01357               CtdlRegisterSessionHook(perform_sieve_processing, EVT_HOUSE, PRIO_HOUSE + 10);
01358               CtdlRegisterCleanupHook(cleanup_sieve);
01359        }
01360        
01361         /* return our module name for the log */
01362        return "sieve";
01363 }
01364