X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fserv_sieve.c;h=e8277d697324ff5dc7061fc13bab3a066dd82b5e;hb=d9f0c753cde3bdc98f536eed77308be9434b3ed5;hp=9d13989aa990da9901a3f404ac63e101141e8044;hpb=e5272a148c5ccda9af5e3e5c696ba719de23810a;p=citadel.git diff --git a/citadel/serv_sieve.c b/citadel/serv_sieve.c index 9d13989aa..e8277d697 100644 --- a/citadel/serv_sieve.c +++ b/citadel/serv_sieve.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -62,12 +62,14 @@ int ctdl_debug(sieve2_context_t *s, void *my) static int ctdl_libsieve_debug = 0; if (ctdl_libsieve_debug) { +/* lprintf(CTDL_DEBUG, "Sieve: level [%d] module [%s] file [%s] function [%s]\n", sieve2_getvalue_int(s, "level"), sieve2_getvalue_string(s, "module"), sieve2_getvalue_string(s, "file"), sieve2_getvalue_string(s, "function")); - lprintf(CTDL_DEBUG, " message [%s]\n", + */ + lprintf(CTDL_DEBUG, "Sieve: %s\n", sieve2_getvalue_string(s, "message")); } return SIEVE2_OK; @@ -120,20 +122,20 @@ int ctdl_redirect(sieve2_context_t *s, void *my) } if (valid->num_error > 0) { lprintf(CTDL_WARNING, "REDIRECT failed: bad recipient <%s>\n", recp); - free(valid); + free_recipients(valid); return SIEVE2_ERROR_BADARGS; } msg = CtdlFetchMessage(cs->msgnum, 1); if (msg == NULL) { lprintf(CTDL_WARNING, "REDIRECT failed: unable to fetch msg %ld\n", cs->msgnum); - free(valid); + free_recipients(valid); return SIEVE2_ERROR_BADARGS; } CtdlSubmitMsg(msg, valid, NULL); - cs->actiontaken = 1; - free(valid); + cs->cancel_implicit_keep = 1; + free_recipients(valid); CtdlFreeMessage(msg); return SIEVE2_OK; } @@ -149,7 +151,7 @@ int ctdl_keep(sieve2_context_t *s, void *my) lprintf(CTDL_DEBUG, "Action is KEEP\n"); cs->keep = 1; - cs->actiontaken = 1; + cs->cancel_implicit_keep = 1; return SIEVE2_OK; } @@ -170,7 +172,7 @@ int ctdl_fileinto(sieve2_context_t *s, void *my) /* FILEINTO 'INBOX' is the same thing as KEEP */ if ( (!strcasecmp(dest_folder, "INBOX")) || (!strcasecmp(dest_folder, MAILROOM)) ) { cs->keep = 1; - cs->actiontaken = 1; + cs->cancel_implicit_keep = 1; return SIEVE2_OK; } @@ -182,12 +184,12 @@ int ctdl_fileinto(sieve2_context_t *s, void *my) c = getroom(&CC->room, foldername); /* Then a regular room name match (public and private rooms) */ - if (c < 0) { + if (c != 0) { safestrncpy(foldername, dest_folder, sizeof foldername); c = getroom(&CC->room, foldername); } - if (c < 0) { + if (c != 0) { lprintf(CTDL_WARNING, "FILEINTO failed: target <%s> does not exist\n", dest_folder); return SIEVE2_ERROR_BADARGS; } @@ -203,7 +205,7 @@ int ctdl_fileinto(sieve2_context_t *s, void *my) } if (c == 0) { - cs->actiontaken = 1; + cs->cancel_implicit_keep = 1; return SIEVE2_OK; } else { @@ -221,10 +223,8 @@ int ctdl_discard(sieve2_context_t *s, void *my) lprintf(CTDL_DEBUG, "Action is DISCARD\n"); - /* Yes, this is really all there is to it. Since we are not setting "keep" to 1, - * the message will be discarded because "some other action" was successfully taken. - */ - cs->actiontaken = 1; + /* Cancel the implicit keep. That's all there is to it. */ + cs->cancel_implicit_keep = 1; return SIEVE2_OK; } @@ -265,7 +265,8 @@ int ctdl_reject(sieve2_context_t *s, void *my) ); quickie_message( /* This delivers the message */ - "Citadel", + NULL, + cs->envelope_to, cs->sender, NULL, reject_text, @@ -274,7 +275,7 @@ int ctdl_reject(sieve2_context_t *s, void *my) ); free(reject_text); - cs->actiontaken = 1; + cs->cancel_implicit_keep = 1; return SIEVE2_OK; } @@ -282,12 +283,88 @@ int ctdl_reject(sieve2_context_t *s, void *my) /* * Callback function to indicate that a vacation message should be generated - * FIXME implement this */ int ctdl_vacation(sieve2_context_t *s, void *my) { + struct ctdl_sieve *cs = (struct ctdl_sieve *)my; + struct sdm_vacation *vptr; + int days = 1; + const char *message; + char *vacamsg_text = NULL; + char vacamsg_subject[1024]; + lprintf(CTDL_DEBUG, "Action is VACATION\n"); - return SIEVE2_ERROR_UNSUPPORTED; + + message = sieve2_getvalue_string(s, "message"); + if (message == NULL) return SIEVE2_ERROR_BADARGS; + + if (sieve2_getvalue_string(s, "subject") != NULL) { + safestrncpy(vacamsg_subject, sieve2_getvalue_string(s, "subject"), sizeof vacamsg_subject); + } + else { + snprintf(vacamsg_subject, sizeof vacamsg_subject, "Re: %s", cs->subject); + } + + days = sieve2_getvalue_int(s, "days"); + if (days < 1) days = 1; + if (days > MAX_VACATION) days = MAX_VACATION; + + /* Check to see whether we've already alerted this sender that we're on vacation. */ + for (vptr = cs->u->first_vacation; vptr != NULL; vptr = vptr->next) { + if (!strcasecmp(vptr->fromaddr, cs->sender)) { + if ( (time(NULL) - vptr->timestamp) < (days * 86400) ) { + lprintf(CTDL_DEBUG, "Already alerted <%s> recently.\n", cs->sender); + return SIEVE2_OK; + } + } + } + + /* Assemble the reject message. */ + vacamsg_text = malloc(strlen(message) + 1024); + if (vacamsg_text == NULL) { + return SIEVE2_ERROR_FAIL; + } + + sprintf(vacamsg_text, + "Content-type: text/plain\n" + "\n" + "%s\n" + "\n" + , + message + ); + + quickie_message( /* This delivers the message */ + NULL, + cs->envelope_to, + cs->sender, + NULL, + vacamsg_text, + FMT_RFC822, + vacamsg_subject + ); + + free(vacamsg_text); + + /* Now update the list to reflect the fact that we've alerted this sender. + * If they're already in the list, just update the timestamp. + */ + for (vptr = cs->u->first_vacation; vptr != NULL; vptr = vptr->next) { + if (!strcasecmp(vptr->fromaddr, cs->sender)) { + vptr->timestamp = time(NULL); + return SIEVE2_OK; + } + } + + /* If we get to this point, create a new record. + */ + vptr = malloc(sizeof(struct sdm_vacation)); + vptr->timestamp = time(NULL); + safestrncpy(vptr->fromaddr, cs->sender, sizeof vptr->fromaddr); + vptr->next = cs->u->first_vacation; + cs->u->first_vacation = vptr; + + return SIEVE2_OK; } @@ -315,22 +392,28 @@ int ctdl_getsubaddress(sieve2_context_t *s, void *my) /* * Callback function to parse message envelope - * FIXME implement this */ int ctdl_getenvelope(sieve2_context_t *s, void *my) { - return SIEVE2_ERROR_UNSUPPORTED; + struct ctdl_sieve *cs = (struct ctdl_sieve *)my; + + lprintf(CTDL_DEBUG, "Action is GETENVELOPE\n"); + sieve2_setvalue_string(s, "to", cs->envelope_to); + sieve2_setvalue_string(s, "from", cs->envelope_from); + return SIEVE2_OK; } /* * Callback function to fetch message body - * FIXME implement this - */ + * (Uncomment the code if we implement this extension) + * int ctdl_getbody(sieve2_context_t *s, void *my) { return SIEVE2_ERROR_UNSUPPORTED; } + * + */ /* @@ -379,7 +462,6 @@ int ctdl_getheaders(sieve2_context_t *s, void *my) { struct ctdl_sieve *cs = (struct ctdl_sieve *)my; lprintf(CTDL_DEBUG, "ctdl_getheaders() was called\n"); - sieve2_setvalue_string(s, "allheaders", cs->rfc822headers); return SIEVE2_OK; } @@ -413,26 +495,43 @@ void sieve_do_msg(long msgnum, void *userdata) { struct ctdl_sieve my; int res; struct CtdlMessage *msg; + int i; + size_t headers_len = 0; lprintf(CTDL_DEBUG, "Performing sieve processing on msg <%ld>\n", msgnum); msg = CtdlFetchMessage(msgnum, 0); if (msg == NULL) return; + /* + * Grab the message headers so we can feed them to libSieve. + */ CC->redirect_buffer = malloc(SIZ); CC->redirect_len = 0; CC->redirect_alloc = SIZ; CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ONLY, 0, 1); my.rfc822headers = CC->redirect_buffer; + headers_len = CC->redirect_len; CC->redirect_buffer = NULL; CC->redirect_len = 0; CC->redirect_alloc = 0; - my.keep = 0; /* Don't keep a copy in the inbox unless a callback tells us to do so */ - my.actiontaken = 0; /* Keep track of whether any actions were successfully taken */ + /* + * libSieve clobbers the stack if it encounters badly formed + * headers. Sanitize our headers by stripping nonprintable + * characters. + */ + for (i=0; iroom.QRname); /* Keep track of the owner of the room's namespace */ - my.msgnum = msgnum; /* Keep track of the message number in our local store */ - my.u = u; /* Hand off a pointer to the rest of this info */ + my.msgnum = msgnum; /* Keep track of the message number in our local store */ + my.u = u; /* Hand off a pointer to the rest of this info */ /* Keep track of the recipient so we can do handling based on it later */ process_rfc822_addr(msg->cm_fields['R'], my.recp_user, my.recp_node, my.recp_name); @@ -451,7 +550,34 @@ void sieve_do_msg(long msgnum, void *userdata) { strcpy(my.sender, ""); } - free(msg); + /* Keep track of the subject so we can use it for VACATION responses */ + if (msg->cm_fields['U'] != NULL) { + safestrncpy(my.subject, msg->cm_fields['U'], sizeof my.subject); + } + else { + strcpy(my.subject, ""); + } + + /* Keep track of the envelope-from address (use body-from if not found) */ + if (msg->cm_fields['P'] != NULL) { + safestrncpy(my.envelope_from, msg->cm_fields['P'], sizeof my.envelope_from); + } + else if (msg->cm_fields['F'] != NULL) { + safestrncpy(my.envelope_from, msg->cm_fields['F'], sizeof my.envelope_from); + } + else { + strcpy(my.envelope_from, ""); + } + + /* Keep track of the envelope-to address */ + if (msg->cm_fields['V'] != NULL) { + safestrncpy(my.envelope_to, msg->cm_fields['V'], sizeof my.envelope_to); + } + else { + strcpy(my.envelope_to, ""); + } + + CtdlFreeMessage(msg); sieve2_setvalue_string(sieve2_context, "allheaders", my.rfc822headers); @@ -468,9 +594,9 @@ void sieve_do_msg(long msgnum, void *userdata) { * Delete the message from the inbox unless either we were told not to, or * if no other action was successfully taken. */ - if ( (!my.keep) && (my.actiontaken) ) { + if ( (!my.keep) && (my.cancel_implicit_keep) ) { lprintf(CTDL_DEBUG, "keep is 0 -- deleting message from inbox\n"); - CtdlDeleteMessages(CC->room.QRname, &msgnum, 1, "", 0); + CtdlDeleteMessages(CC->room.QRname, &msgnum, 1, ""); } lprintf(CTDL_DEBUG, "Completed sieve processing on msg <%ld>\n", msgnum); @@ -487,9 +613,10 @@ void sieve_do_msg(long msgnum, void *userdata) { */ void parse_sieve_config(char *conf, struct sdm_userdata *u) { char *ptr; - char *c; + char *c, *vacrec; char keyword[256]; struct sdm_script *sptr; + struct sdm_vacation *vptr; ptr = conf; while (c = ptr, ptr = bmstrcasestr(ptr, CTDLSIEVECONFIGSEPARATOR), ptr != NULL) { @@ -514,6 +641,23 @@ void parse_sieve_config(char *conf, struct sdm_userdata *u) { u->first_script = sptr; } + else if (!strcasecmp(keyword, "vacation")) { + + if (c != NULL) while (vacrec=c, c=strchr(c, '\n'), (c != NULL)) { + + *c = 0; + ++c; + + if (strncasecmp(vacrec, "vacation|", 9)) { + vptr = malloc(sizeof(struct sdm_vacation)); + extract_token(vptr->fromaddr, vacrec, 0, '|', sizeof vptr->fromaddr); + vptr->timestamp = extract_long(vacrec, 1); + vptr->next = u->first_vacation; + u->first_vacation = vptr; + } + } + } + /* ignore unknown keywords */ } } @@ -548,13 +692,18 @@ void get_sieve_config_backend(long msgnum, void *userdata) { /* * Write our citadel sieve config back to disk + * + * (Set yes_write_to_disk to nonzero to make it actually write the config; + * otherwise it just frees the data structures.) */ -void rewrite_ctdl_sieve_config(struct sdm_userdata *u) { +void rewrite_ctdl_sieve_config(struct sdm_userdata *u, int yes_write_to_disk) { char *text; struct sdm_script *sptr; - + struct sdm_vacation *vptr; + size_t tsize; text = malloc(1024); + tsize = 1024; snprintf(text, 1024, "Content-type: application/x-citadel-sieve-config\n" "\n" @@ -566,7 +715,10 @@ void rewrite_ctdl_sieve_config(struct sdm_userdata *u) { ); while (u->first_script != NULL) { - text = realloc(text, strlen(text) + strlen(u->first_script->script_content) + 256); + size_t tlen; + tlen = strlen(text); + tsize = tlen + strlen(u->first_script->script_content) +256; + text = realloc(text, tsize); sprintf(&text[strlen(text)], "script|%s|%d|%s" CTDLSIEVECONFIGSEPARATOR, u->first_script->script_name, u->first_script->script_active, @@ -578,16 +730,40 @@ void rewrite_ctdl_sieve_config(struct sdm_userdata *u) { free(sptr); } + if (u->first_vacation != NULL) { + + tsize = strlen(text) + 256; + for (vptr = u->first_vacation; vptr != NULL; vptr = vptr->next) { + tsize += strlen(vptr->fromaddr + 32); + } + text = realloc(text, tsize); + + sprintf(&text[strlen(text)], "vacation|\n"); + while (u->first_vacation != NULL) { + if ( (time(NULL) - u->first_vacation->timestamp) < (MAX_VACATION * 86400)) { + sprintf(&text[strlen(text)], "%s|%ld\n", + u->first_vacation->fromaddr, + u->first_vacation->timestamp + ); + } + vptr = u->first_vacation; + u->first_vacation = u->first_vacation->next; + free(vptr); + } + sprintf(&text[strlen(text)], CTDLSIEVECONFIGSEPARATOR); + } + /* Save the config */ - quickie_message("Citadel", NULL, u->config_roomname, + quickie_message("Citadel", NULL, NULL, u->config_roomname, text, 4, "Sieve configuration" ); - + + free (text); /* And delete the old one */ if (u->config_msgnum > 0) { - CtdlDeleteMessages(u->config_roomname, &u->config_msgnum, 1, "", 0); + CtdlDeleteMessages(u->config_roomname, &u->config_msgnum, 1, ""); } } @@ -597,22 +773,27 @@ void rewrite_ctdl_sieve_config(struct sdm_userdata *u) { * This is our callback registration table for libSieve. */ sieve2_callback_t ctdl_sieve_callbacks[] = { - { SIEVE2_ACTION_REJECT, ctdl_reject }, - { SIEVE2_ACTION_NOTIFY, NULL /* ctdl_notify */ }, - { SIEVE2_ACTION_VACATION, ctdl_vacation }, - { SIEVE2_ERRCALL_PARSE, ctdl_errparse }, - { SIEVE2_ERRCALL_RUNTIME, ctdl_errexec }, - { SIEVE2_ACTION_FILEINTO, ctdl_fileinto }, - { SIEVE2_ACTION_REDIRECT, ctdl_redirect }, - { SIEVE2_ACTION_DISCARD, ctdl_discard }, - { SIEVE2_ACTION_KEEP, ctdl_keep }, - { SIEVE2_SCRIPT_GETSCRIPT, ctdl_getscript }, - { SIEVE2_DEBUG_TRACE, ctdl_debug }, - { SIEVE2_MESSAGE_GETALLHEADERS, ctdl_getheaders }, - { SIEVE2_MESSAGE_GETSUBADDRESS, NULL /* ctdl_getsubaddress */ }, - { SIEVE2_MESSAGE_GETENVELOPE, ctdl_getenvelope }, - { SIEVE2_MESSAGE_GETBODY, ctdl_getbody }, - { SIEVE2_MESSAGE_GETSIZE, ctdl_getsize }, + { SIEVE2_ACTION_REJECT, ctdl_reject }, + { SIEVE2_ACTION_VACATION, ctdl_vacation }, + { SIEVE2_ERRCALL_PARSE, ctdl_errparse }, + { SIEVE2_ERRCALL_RUNTIME, ctdl_errexec }, + { SIEVE2_ACTION_FILEINTO, ctdl_fileinto }, + { SIEVE2_ACTION_REDIRECT, ctdl_redirect }, + { SIEVE2_ACTION_DISCARD, ctdl_discard }, + { SIEVE2_ACTION_KEEP, ctdl_keep }, + { SIEVE2_SCRIPT_GETSCRIPT, ctdl_getscript }, + { SIEVE2_DEBUG_TRACE, ctdl_debug }, + { SIEVE2_MESSAGE_GETALLHEADERS, ctdl_getheaders }, + { SIEVE2_MESSAGE_GETSIZE, ctdl_getsize }, + { SIEVE2_MESSAGE_GETENVELOPE, ctdl_getenvelope }, +/* + * These actions are unsupported by Citadel so we don't declare them. + * + { SIEVE2_ACTION_NOTIFY, ctdl_notify }, + { SIEVE2_MESSAGE_GETSUBADDRESS, ctdl_getsubaddress }, + { SIEVE2_MESSAGE_GETBODY, ctdl_getbody }, + * + */ { 0 } }; @@ -632,7 +813,7 @@ void sieve_do_room(char *roomname) { /* See if the user who owns this 'mailbox' has any Sieve scripts that * require execution. */ - snprintf(u.config_roomname, sizeof u.config_roomname, "%010ld.%s", atol(roomname), SIEVERULES); + snprintf(u.config_roomname, sizeof u.config_roomname, "%010ld.%s", atol(roomname), USERCONFIGROOM); if (getroom(&CC->room, u.config_roomname) != 0) { lprintf(CTDL_DEBUG, "<%s> does not exist. No processing is required.\n", u.config_roomname); return; @@ -697,9 +878,7 @@ BAIL: } /* Rewrite the config if we have to */ - if (u.lastproc > orig_lastproc) { - rewrite_ctdl_sieve_config(&u); - } + rewrite_ctdl_sieve_config(&u, (u.lastproc > orig_lastproc) ) ; } @@ -743,7 +922,7 @@ void msiv_load(struct sdm_userdata *u) { strcpy(hold_rm, CC->room.QRname); /* save current room */ /* Take a spin through the user's personal address book */ - if (getroom(&CC->room, SIEVERULES) == 0) { + if (getroom(&CC->room, USERCONFIGROOM) == 0) { u->config_msgnum = (-1); strcpy(u->config_roomname, CC->room.QRname); @@ -757,8 +936,8 @@ void msiv_load(struct sdm_userdata *u) { } } -void msiv_store(struct sdm_userdata *u) { - rewrite_ctdl_sieve_config(u); +void msiv_store(struct sdm_userdata *u, int yes_write_to_disk) { + rewrite_ctdl_sieve_config(u, yes_write_to_disk); } @@ -903,6 +1082,7 @@ void cmd_msiv(char *argbuf) { char *script_content = NULL; struct sdm_script *s; int i; + int changes_made = 0; memset(&u, 0, sizeof(struct sdm_userdata)); @@ -916,6 +1096,7 @@ void cmd_msiv(char *argbuf) { cprintf("%d Transmit script now\n", SEND_LISTING); script_content = CtdlReadMessageBody("000", config.c_maxmsglen, NULL, 0); msiv_putscript(&u, script_name, script_content); + changes_made = 1; } else { cprintf("%d Invalid script name.\n", ERROR + ILLEGAL_VALUE); @@ -936,6 +1117,7 @@ void cmd_msiv(char *argbuf) { extract_token(script_name, argbuf, 1, '|', sizeof script_name); if (msiv_setactive(&u, script_name) == 0) { cprintf("%d ok\n", CIT_OK); + changes_made = 1; } else { cprintf("%d Script '%s' does not exist.\n", @@ -949,8 +1131,15 @@ void cmd_msiv(char *argbuf) { extract_token(script_name, argbuf, 1, '|', sizeof script_name); script_content = msiv_getscript(&u, script_name); if (script_content != NULL) { - cprintf("%d Script:\n", SEND_LISTING); - cprintf("%s000\n", script_content); + int script_len; + + cprintf("%d Script:\n", LISTING_FOLLOWS); + script_len = strlen(script_content); + client_write(script_content, script_len); + if (script_content[script_len-1] != '\n') { + cprintf("\n"); + } + cprintf("000\n"); } else { cprintf("%d Invalid script name.\n", ERROR + ILLEGAL_VALUE); @@ -962,6 +1151,7 @@ void cmd_msiv(char *argbuf) { i = msiv_deletescript(&u, script_name); if (i == 0) { cprintf("%d ok\n", CIT_OK); + changes_made = 1; } else if (i == 1) { cprintf("%d Script '%s' does not exist.\n", @@ -984,7 +1174,7 @@ void cmd_msiv(char *argbuf) { cprintf("%d Invalid subcommand\n", ERROR + CMD_NOT_SUPPORTED); } - msiv_store(&u); + msiv_store(&u, changes_made); }