]> code.citadel.org Git - citadel.git/blobdiff - citadel/server/modules/inboxrules/serv_inboxrules.c
dammit, learn to spell
[citadel.git] / citadel / server / modules / inboxrules / serv_inboxrules.c
index 72eabb635432f6aa9cca566864eb9ead27e95950..5c01625af9bb447f11ecf41c438f4f4fa590e55f 100644 (file)
@@ -1,6 +1,6 @@
 // Inbox handling rules
 //
-// Copyright (c) 1987-2022 by the citadel.org team
+// Copyright (c) 1987-2023 by the citadel.org team
 //
 // This program is open source software; you can redistribute it and/or modify
 // it under the terms of the GNU General Public License version 3.
@@ -23,6 +23,7 @@
 #include <sys/wait.h>
 #include <string.h>
 #include <limits.h>
+#include <regex.h>
 #include <libcitadel.h>
 #include "../../citadel_defs.h"
 #include "../../server.h"
@@ -471,9 +472,7 @@ void inbox_do_vacation(struct irule *rule, struct CtdlMessage *msg) {
 }
 
 
-/*
- * Process a single message.  We know the room, the user, the rules, the message number, etc.
- */
+// Process a single message.  We know the room, the user, the rules, the message number, etc.
 void inbox_do_msg(long msgnum, void *userdata) {
        struct inboxrules *ii = (struct inboxrules *) userdata;
        struct CtdlMessage *msg = NULL;         // If we are loading a message to process, put it here.
@@ -486,6 +485,8 @@ void inbox_do_msg(long msgnum, void *userdata) {
        int compare_compound = 0;               // Set to 1 when we are comparing both display name and email address
        int keep_message = 1;                   // Nonzero to keep the message in the inbox after processing, 0 to delete it.
        int i;
+       const char *ptr,*ptr1;                  // Temporary variables to get X-Spam-Status
+       int len;
 
        syslog(LOG_DEBUG, "inboxrules: processing message #%ld which is higher than %ld, we are in %s", msgnum, ii->lastproc, CC->room.QRname);
 
@@ -495,7 +496,7 @@ void inbox_do_msg(long msgnum, void *userdata) {
        }
 
        for (i=0; i<ii->num_rules; ++i) {
-               syslog(LOG_DEBUG, "inboxrules: processing rule %d is %s", i, field_keys[ ii->rules[i].compared_field ]);
+               syslog(LOG_DEBUG, "inboxrules: processing rule: %d , field: %s", i, field_keys[ ii->rules[i].compared_field ]);
                rule_activated = 0;
 
                // Before doing a field compare, check to see if we have the correct parts of the message in memory.
@@ -608,12 +609,28 @@ void inbox_do_msg(long msgnum, void *userdata) {
                                }
                                break;
 
+                       case field_xspamstatus:
+                               if (!IsEmptyStr(msg->cm_fields[eMessageText])) {
+                                       if ((ptr=strstr(msg->cm_fields[eMessageText],"X-Spam-Status"))!=NULL) {
+                                               len=0;
+                                               ptr1=ptr;
+                                               while (*ptr1 && (*ptr1!='\r') && (*ptr1!='\n')) {
+                                                       ptr1++;
+                                                       len++;
+                                               }
+                                               if (len && (len<SIZ)) {
+                                                       memcpy(compare_me, ptr, len);
+                                                       compare_me[len]='\0';
+                                               }
+                                       }
+                               }
+                       break;
+
                        case field_sender:
                        case field_resentfrom:
                        case field_resentto:
                        case field_xmailer:
                        case field_xspamflag:
-                       case field_xspamstatus:
 
                        default:
                                break;
@@ -636,33 +653,66 @@ void inbox_do_msg(long msgnum, void *userdata) {
                        case field_xspamstatus:
 
                                // For all of the above fields, we can compare the field we've loaded into the buffer.
-                               syslog(LOG_DEBUG, "Value of field to compare is: <%s>", compare_me);
-                               int substring_match = (bmstrcasestr(compare_me, ii->rules[i].compared_value) ? 1 : 0);
+                               syslog(LOG_DEBUG, "inboxrules: rule %d: field \"%s\" is \"%s\", looking for \"%s\"",
+                                       i,
+                                       field_keys[ii->rules[i].compared_field],
+                                       compare_me,
+                                       ii->rules[i].compared_value
+                               );
+                               int substring_match = 0;
+                               int regex_match = 0;
                                int exact_match = 0;
-                               if (compare_compound) {
+                               int rc = 0;
+
+                               regex_t regex;
+                               
+                               rc = regcomp(&regex, ii->rules[i].compared_value, (REG_EXTENDED | REG_ICASE | REG_NOSUB | REG_NEWLINE));
+                               if (rc) {
+                                       syslog(LOG_ERR, "inboxrules: regcomp: error %d trying to compile \"%s\"", rc, ii->rules[i].compared_value);
+                               }
+
+                               if (compare_compound) {                                 // comparing a compound field such as name+address
                                        char *sep = strchr(compare_me, '|');
                                        if (sep) {
                                                *sep = 0;
+                                               ++sep;
                                                exact_match =
                                                        (strcasecmp(compare_me, ii->rules[i].compared_value) ? 0 : 1)
-                                                       + (strcasecmp(++sep, ii->rules[i].compared_value) ? 0 : 1)
+                                                       + (strcasecmp(sep, ii->rules[i].compared_value) ? 0 : 1)
+                                               ;
+                                               substring_match =
+                                                       (bmstrcasestr(compare_me, ii->rules[i].compared_value) ? 1 : 0)
+                                                       + (bmstrcasestr(sep, ii->rules[i].compared_value) ? 1 : 0)
+                                               ;
+                                               if (!rc) regex_match =
+                                                       (regexec(&regex, compare_me, 0, 0, 0) ? 0 : 1)
+                                                       + (regexec(&regex, sep, 0, 0, 0) ? 0 : 1)
                                                ;
                                        }
                                }
-                               else {
+                               else {                                                  // comparing a single field only
                                        exact_match = (strcasecmp(compare_me, ii->rules[i].compared_value) ? 0 : 1);
+                                       substring_match = (bmstrcasestr(compare_me, ii->rules[i].compared_value) ? 1 : 0);
+                                       if (!rc) regex_match = (regexec(&regex, compare_me, 0, 0, 0) ? 0 : 1);
                                }
-                               syslog(LOG_DEBUG, "substring match: %d", substring_match);
-                               syslog(LOG_DEBUG, "exact match: %d", exact_match);
+
+                               regfree(&regex);
+                               syslog(LOG_DEBUG, "inboxrules: substring match: %d", substring_match);
+                               syslog(LOG_DEBUG, "inboxrules:     regex match: %d", regex_match);
+                               syslog(LOG_DEBUG, "inboxrules:     exact match: %d", exact_match);
                                switch(ii->rules[i].field_compare_op) {
                                        case fcomp_contains:
-                                       case fcomp_matches:
                                                rule_activated = substring_match;
                                                break;
+                                       case fcomp_matches:
+                                               rule_activated = regex_match;
+                                               break;
                                        case fcomp_notcontains:
-                                       case fcomp_notmatches:
                                                rule_activated = !substring_match;
                                                break;
+                                       case fcomp_notmatches:
+                                               rule_activated = !regex_match;
+                                               break;
                                        case fcomp_is:
                                                rule_activated = exact_match;
                                                break;
@@ -746,10 +796,7 @@ void inbox_do_msg(long msgnum, void *userdata) {
 }
 
 
-/*
- * A user account is identified as requring inbox processing.
- * Do it.
- */
+// A user account is identified as requring inbox processing.  Go ahead and do it.
 void do_inbox_processing_for_user(long usernum) {
        struct CtdlMessage *msg;
        struct inboxrules *ii;
@@ -771,7 +818,7 @@ void do_inbox_processing_for_user(long usernum) {
                return;                                         // config msgnum is set but that message does not exist
        }
 
-       ii = deserialize_inbox_rules(msg->cm_fields[eMesageText]);
+       ii = deserialize_inbox_rules(msg->cm_fields[eMessageText]);
        CM_Free(msg);
 
        if (ii == NULL) {
@@ -806,17 +853,13 @@ void do_inbox_processing_for_user(long usernum) {
 }
 
 
-/*
- * Here is an array of users (by number) who have received messages in their inbox and may require processing.
- */
+// Here is an array of users (by number) who have received messages in their inbox and may require processing.
 long *users_requiring_inbox_processing = NULL;
 int num_urip = 0;
 int num_urip_alloc = 0;
 
 
-/*
- * Perform inbox processing for all rooms which require it
- */
+// Perform inbox processing for all rooms which require it
 void perform_inbox_processing(void) {
        int i = 0;
 
@@ -835,10 +878,7 @@ void perform_inbox_processing(void) {
 }
 
 
-/*
- * This function is called after a message is saved to a room.
- * If it's someone's inbox, we have to check for inbox rules
- */
+// This function is called after a message is saved to a room.  If it's someone's inbox, we have to check for inbox rules
 int serv_inboxrules_roomhook(struct ctdlroom *room) {
        int i = 0;
 
@@ -877,13 +917,11 @@ int serv_inboxrules_roomhook(struct ctdlroom *room) {
 
 
 
-/*
- * Get InBox Rules
- *
- * This is a client-facing function which fetches the user's inbox rules -- it omits all lines containing anything other than a rule.
- * 
- * hmmmmm ... should we try to rebuild this in terms of deserialize_inbox_rules() instread?
- */
+// Get InBox Rules
+//
+// This is a client-facing function which fetches the user's inbox rules -- it omits all lines containing anything other than a rule.
+// 
+// hmmmmm ... should we try to rebuild this in terms of deserialize_inbox_rules() instread?
 void cmd_gibr(char *argbuf) {
 
        if (CtdlAccessCheck(ac_logged_in)) return;
@@ -892,9 +930,9 @@ void cmd_gibr(char *argbuf) {
 
        struct CtdlMessage *msg = CtdlFetchMessage(CC->user.msgnum_inboxrules, 1);
        if (msg != NULL) {
-               if (!CM_IsEmpty(msg, eMesageText)) {
+               if (!CM_IsEmpty(msg, eMessageText)) {
                        char *token; 
-                       char *rest = msg->cm_fields[eMesageText];
+                       char *rest = msg->cm_fields[eMessageText];
                        while ((token = strtok_r(rest, "\n", &rest))) {
 
                                // for backwards compatibility, "# WEBCIT_RULE" is an alias for "rule" 
@@ -918,11 +956,9 @@ void cmd_gibr(char *argbuf) {
 }
 
 
-/*
- * Rewrite the rule set to disk after it has been modified
- * Called by cmd_pibr()
- * Returns the msgnum of the new rules message
- */
+// Rewrite the rule set to disk after it has been modified
+// Called by cmd_pibr()
+// Returns the msgnum of the new rules message
 void rewrite_rules_to_disk(const char *new_config) {
        long old_msgnum = CC->user.msgnum_inboxrules;
        char userconfigroomname[ROOMNAMELEN];
@@ -939,11 +975,8 @@ void rewrite_rules_to_disk(const char *new_config) {
 }
 
 
-/*
- * Put InBox Rules
- *
- * User transmits the new inbox rules for the account.  They are inserted into the account, replacing the ones already there.
- */
+// Put InBox Rules
+// User transmits the new inbox rules for the account.  They are inserted into the account, replacing the ones already there.
 void cmd_pibr(char *argbuf) {
        if (CtdlAccessCheck(ac_logged_in)) return;
 
@@ -976,6 +1009,6 @@ char *ctdl_module_init_sieve(void) {
                CtdlRegisterSessionHook(perform_inbox_processing, EVT_HOUSE, PRIO_HOUSE + 10);
        }
        
-        /* return our module name for the log */
+        // return our module name for the log
        return "inboxrules";
 }