// 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.
#include <sys/wait.h>
#include <string.h>
#include <limits.h>
+#include <regex.h>
#include <libcitadel.h>
#include "../../citadel_defs.h"
#include "../../server.h"
return(1); // don't delete the inbox copy if this failed
}
- struct recptypes *valid = validate_recipients(rule->redirect_to, NULL, 0);
+ struct recptypes *valid = validate_recipients(rule->redirect_to, 0);
if (valid == NULL) {
syslog(LOG_WARNING, "inboxrules: inbox_do_redirect() invalid recipient <%s>", rule->redirect_to);
return(1); // don't delete the inbox copy if this failed
}
-/*
- * 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.
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);
}
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.
}
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;
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(®ex, 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(®ex, compare_me, 0, 0, 0) ? 0 : 1)
+ + (regexec(®ex, 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(®ex, compare_me, 0, 0, 0) ? 0 : 1);
}
- syslog(LOG_DEBUG, "substring match: %d", substring_match);
- syslog(LOG_DEBUG, "exact match: %d", exact_match);
+
+ regfree(®ex);
+ 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;
}
-/*
- * 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;
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) {
}
-/*
- * 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;
}
-/*
- * 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;
-/*
- * 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;
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"
}
-/*
- * 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];
}
-/*
- * 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;
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";
}