}
-
/*
* Perform sieve processing for one message (called by sieve_do_room() for each message)
*/
}
}
-/*
- * We found the Sieve configuration for this user.
- * Now do something with it.
- */
-void get_sieve_config_backend(long msgnum, void *userdata) {
- struct sdm_userdata *u = (struct sdm_userdata *) userdata;
- struct CtdlMessage *msg;
- char *conf;
- long conflen;
-
- u->config_msgnum = msgnum;
- msg = CtdlFetchMessage(msgnum, 1, 1);
- if (msg == NULL) {
- u->config_msgnum = (-1) ;
- return;
- }
-
- CM_GetAsField(msg, eMesageText, &conf, &conflen);
- CM_Free(msg);
- if (conf != NULL) {
- parse_sieve_config(conf, u);
- free(conf);
- }
-
-}
/*
#endif
+/*
+ * The next sections are enums and keys that drive the serialize/deserialize functions for the inbox rules/state configuration.
+ */
+
+// Fields to be compared
+enum {
+ field_from,
+ field_tocc,
+ field_subject,
+ field_replyto,
+ field_sender,
+ field_resentfrom,
+ field_resentto,
+ field_envfrom,
+ field_envto,
+ field_xmailer,
+ field_xspamflag,
+ field_xspamstatus,
+ field_listid,
+ field_size,
+ field_all
+};
+char *field_keys[] = {
+ "from",
+ "tocc",
+ "subject",
+ "replyto",
+ "sender",
+ "resentfrom",
+ "resentto",
+ "envfrom",
+ "envto",
+ "xmailer",
+ "xspamflag",
+ "xspamstatus",
+ "listid",
+ "size",
+ "all"
+};
+
+// Field comparison operators
+enum {
+ fcomp_contains,
+ fcomp_notcontains,
+ fcomp_is,
+ fcomp_isnot,
+ fcomp_matches,
+ fcomp_notmatches
+};
+char *fcomp_keys[] = {
+ "contains",
+ "notcontains",
+ "is",
+ "isnot",
+ "matches",
+ "notmatches"
+};
+
+// Actions
+enum {
+ action_keep,
+ action_discard,
+ action_reject,
+ action_fileinto,
+ action_redirect,
+ action_vacation
+};
+char *action_keys[] = {
+ "keep",
+ "discard",
+ "reject",
+ "fileinto",
+ "redirect",
+ "vacation"
+};
+
+// Size comparison operators
+enum {
+ scomp_larger,
+ scomp_smaller
+};
+char *scomp_keys[] = {
+ "larger",
+ "smaller"
+};
+
+// Final actions
+enum {
+ final_continue,
+ final_stop
+};
+char *final_keys[] = {
+ "continue",
+ "stop"
+};
+
+// This data structure represents ONE inbox rule within the configuration.
+struct irule {
+ int field_compare_op;
+ int compared_field;
+ char compared_value[128];
+ int size_compare_op;
+ long compared_size;
+ int action;
+ char file_into[ROOMNAMELEN];
+ char redirect_to[1024];
+ char autoreply_message[SIZ];
+ int final_action;
+};
+
+// This data structure represents the entire inbox rules configuration AND current state for a single user.
+struct inboxrules {
+ long lastproc;
+ int num_rules;
+ struct irule *rules;
+};
+
+
+// Destructor for 'struct inboxrules'
+void free_inbox_rules(struct inboxrules *ibr) {
+ free(ibr->rules);
+ free(ibr);
+}
+
+
+// Constructor for 'struct inboxrules' that deserializes the configuration from text input.
+struct inboxrules *deserialize_inbox_rules(char *serialized_rules) {
+ int i;
+
+ if (!serialized_rules) {
+ return NULL;
+ }
+
+ /* Make a copy of the supplied buffer because we're going to shit all over it with strtok_r() */
+ char *sr = strdup(serialized_rules);
+ if (!sr) {
+ return NULL;
+ }
+
+ struct inboxrules *ibr = malloc(sizeof(struct inboxrules));
+ if (ibr == NULL) {
+ return NULL;
+ }
+ memset(ibr, 0, sizeof(struct inboxrules));
+
+ char *token;
+ char *rest = sr;
+ while ((token = strtok_r(rest, "\n", &rest))) {
+
+ // For backwards compatibility, "# WEBCIT_RULE" is an alias for "rule".
+ // Prior to version 930, WebCit converted its rules to Sieve scripts, but saved the rules as comments for later re-editing.
+ // Now, the rules hidden in the comments become the real rules.
+ if (!strncasecmp(token, "# WEBCIT_RULE|", 14)) {
+ strcpy(token, "rule|");
+ strcpy(&token[5], &token[14]);
+ }
+
+ // Lines containing actual rules are double-serialized with Base64. It seemed like a good idea at the time :(
+ if (!strncasecmp(token, "rule|", 5)) {
+ remove_token(&token[5], 0, '|');
+ char *decoded_rule = malloc(strlen(token));
+ CtdlDecodeBase64(decoded_rule, &token[5], strlen(&token[5]));
+ ibr->num_rules++;
+ ibr->rules = realloc(ibr->rules, (sizeof(struct irule) * ibr->num_rules));
+ struct irule *new_rule = &ibr->rules[ibr->num_rules - 1];
+ memset(new_rule, 0, sizeof(struct irule));
+
+ // We have a rule , now parse it
+ char rtoken[SIZ];
+ int nt = num_tokens(decoded_rule, '|');
+ for (int t=0; t<nt; ++t) {
+ extract_token(rtoken, decoded_rule, t, '|', sizeof(rtoken));
+ striplt(rtoken);
+ switch(t) {
+ case 1: // field to compare
+ for (i=0; i<=field_all; ++i) {
+ if (!strcasecmp(rtoken, field_keys[i])) {
+ new_rule->compared_field = i;
+ }
+ }
+ break;
+ case 2: // field comparison operation
+ for (i=0; i<=fcomp_notmatches; ++i) {
+ if (!strcasecmp(rtoken, fcomp_keys[i])) {
+ new_rule->field_compare_op = i;
+ }
+ }
+ break;
+ case 3: // field comparison value
+ safestrncpy(new_rule->compared_value, rtoken, sizeof(new_rule->compared_value));
+ break;
+ case 4: // size comparison operation
+ for (i=0; i<=scomp_smaller; ++i) {
+ if (!strcasecmp(rtoken, scomp_keys[i])) {
+ new_rule->size_compare_op = i;
+ }
+ }
+ break;
+ case 5: // size comparison value
+ new_rule->compared_size = atol(rtoken);
+ break;
+ case 6: // action
+ for (i=0; i<=action_vacation; ++i) {
+ if (!strcasecmp(rtoken, action_keys[i])) {
+ new_rule->action = i;
+ }
+ }
+ break;
+ case 7: // file into (target room)
+ safestrncpy(new_rule->file_into, rtoken, sizeof(new_rule->file_into));
+ break;
+ case 8: // redirect to (target address)
+ safestrncpy(new_rule->redirect_to, rtoken, sizeof(new_rule->redirect_to));
+ break;
+ case 9: // autoreply message
+ safestrncpy(new_rule->autoreply_message, rtoken, sizeof(new_rule->autoreply_message));
+ break;
+ case 10: // final_action;
+ for (i=0; i<=final_stop; ++i) {
+ if (!strcasecmp(rtoken, final_keys[i])) {
+ new_rule->final_action = i;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ free(decoded_rule);
+
+ // if we re-serialized this now, what would it look like?
+ //syslog(LOG_DEBUG, "test reserialize: 0|%s|%s|%s|%s|%ld|%s|%s|%s|%s|%s",
+ //field_keys[new_rule->compared_field],
+ //fcomp_keys[new_rule->field_compare_op],
+ //new_rule->compared_value,
+ //scomp_keys[new_rule->size_compare_op],
+ //new_rule->compared_size,
+ //action_keys[new_rule->action],
+ //new_rule->file_into,
+ //new_rule->redirect_to,
+ //new_rule->autoreply_message,
+ //final_keys[new_rule->final_action]
+ //);
+ // delete the above after moving it to a reserialize function
+
+ }
+
+ // "lastproc" indicates the newest message number in the inbox that was previously processed by our inbox rules.
+ else if (!strncasecmp(token, "lastproc|", 5)) {
+ ibr->lastproc = atol(&token[9]);
+ }
+
+ }
+
+ free(sr); // free our copy of the source buffer that has now been trashed with null bytes...
+ return(ibr); // and return our complex data type to the caller.
+}
+
/*
* A user account is identified as requring inbox processing.
* Do it.
*/
void do_inbox_processing_for_user(long usernum) {
+ struct CtdlMessage *msg;
+ struct inboxrules *ii;
+
if (CtdlGetUserByNumber(&CC->user, usernum) == 0) {
- TRACE;
if (CC->user.msgnum_inboxrules <= 0) {
- syslog(LOG_DEBUG, "NO RULEZ for %s", CC->user.fullname);
return; // this user has no inbox rules
}
+
+ msg = CtdlFetchMessage(CC->user.msgnum_inboxrules, 1, 1);
+ if (msg == NULL) {
+ return; // config msgnum is set but that message does not exist
+ }
+
+ ii = deserialize_inbox_rules(msg->cm_fields[eMesageText]);
+ CM_Free(msg);
+
+ if (ii == NULL) {
+ return; // config message exists but body is null
+ }
+
+ TRACE;
syslog(LOG_DEBUG, "RULEZ for %s", CC->user.fullname);
+
+ // do something now FIXME actually write this
+ free_inbox_rules(ii);
}
}
}
+
/*
* 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) {