Please substitute this comment with whatever you feel like reading right now.
[citadel.git] / citadel / modules / inboxrules / serv_inboxrules.c
index 30feea856624410a99285e52999396070bf0acc4..3705c9508e3f44dc0821c10ff9bb081cdf0fd54b 100644 (file)
@@ -628,33 +628,8 @@ void parse_sieve_config(char *conf, struct sdm_userdata *u) {
        }
 }
 
-/*
- * 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);
-       }
-
-}
 
 
 /* 
@@ -841,12 +816,214 @@ BAIL:
        rewrite_ctdl_sieve_config(&u, (u.lastproc > orig_lastproc) ) ;
 }
 
+#endif
 
 
 
-#endif
+/*
+ * A user account is identified as requring inbox processing.
+ * Do it.
+ */
+void do_inbox_processing_for_user(long usernum) {
+       if (CtdlGetUserByNumber(&CC->user, usernum) == 0) {
+               TRACE;
+               if (CC->user.msgnum_inboxrules <= 0) {
+                       return;                                         // this user has no inbox rules
+               }
+
+               struct CtdlMessage *msg;
+               char *conf;
+               long conflen;
+       
+               msg = CtdlFetchMessage(CC->user.msgnum_inboxrules, 1, 1);
+               if (msg == NULL) {
+                       return;                                         // config msgnum is set but that message does not exist
+               }
+       
+               CM_GetAsField(msg, eMesageText, &conf, &conflen);
+               CM_Free(msg);
+       
+               if (conf == NULL) {
+                       return;                                         // config message exists but body is null
+               }
+
+
+               syslog(LOG_DEBUG, "RULEZ for %s", CC->user.fullname);
+               syslog(LOG_DEBUG, "%s", conf);
+
+               // do something now
+
+               free(conf);
+       }
+}
+
+
+/*
+ * 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
+ */
+void perform_inbox_processing(void) {
+       if (num_urip == 0) {
+               return;                                                                                 // no action required
+       }
+
+       for (int i=0; i<num_urip; ++i) {
+               do_inbox_processing_for_user(users_requiring_inbox_processing[i]);
+       }
+
+       free(users_requiring_inbox_processing);
+       users_requiring_inbox_processing = NULL;
+       num_urip = 0;
+       num_urip_alloc = 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
+ */
+int serv_inboxrules_roomhook(struct ctdlroom *room) {
+
+       // Is this someone's inbox?
+       if (!strcasecmp(&room->QRname[11], MAILROOM)) {
+               long usernum = atol(room->QRname);
+               if (usernum > 0) {
+
+                       // first check to see if this user is already on the list
+                       if (num_urip > 0) {
+                               for (int i=0; i<=num_urip; ++i) {
+                                       if (users_requiring_inbox_processing[i] == usernum) {           // already on the list!
+                                               return(0);
+                                       }
+                               }
+                       }
+
+                       // make room if we need to
+                       if (num_urip_alloc == 0) {
+                               num_urip_alloc = 100;
+                               users_requiring_inbox_processing = malloc(sizeof(long) * num_urip_alloc);
+                       }
+                       else if (num_urip >= num_urip_alloc) {
+                               num_urip_alloc += 100;
+                               users_requiring_inbox_processing = realloc(users_requiring_inbox_processing, (sizeof(long) * num_urip_alloc));
+                       }
+                       
+                       // now add the user to the list
+                       users_requiring_inbox_processing[num_urip++] = usernum;
+               }
+       }
+
+       // No errors are possible from this function.
+       return(0);
+}
+
+
+struct irule {
+       int field_compare_op;
+       char *compared_field;
+       char *compared_value;
+       int size_compare_op;
+       long compared_size;
+       int action;
+       char *file_into;
+       char *redirect_to;
+       char *autoreply_message;
+       int final_action;
+};
+
+struct inboxrules {
+       long lastproc;
+       int num_rules;
+       struct irule *rules;
+};
+
+
+void free_inbox_rules(struct inboxrules *ibr) {
+       int i;
+
+       if (ibr->num_rules > 0) {
+               for (i=0; i<ibr->num_rules; ++i) {
+                       if (ibr->rules[i].compared_field)       free(ibr->rules[i].compared_field);
+                       if (ibr->rules[i].compared_value)       free(ibr->rules[i].compared_value);
+                       if (ibr->rules[i].file_into)            free(ibr->rules[i].file_into);
+                       if (ibr->rules[i].redirect_to)          free(ibr->rules[i].autoreply_message);
+                       if (ibr->rules[i].autoreply_message)    free(ibr->rules[i].autoreply_message);
+               }
+       }
+
+       free(ibr->rules);
+       free(ibr);
+}
+
+
+/*
+ * Convert the serialized inbox rules message to a data type.
+ */
+struct inboxrules *deserialize_inbox_rules(char *serialized_rules) {
+
+       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 are 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)) {
+                       syslog(LOG_DEBUG, "rule: %s", &token[5]);
+                       remove_token(&token[5], 0, '|');
+                       char *decoded_rule = malloc(strlen(token));
+                       CtdlDecodeBase64(decoded_rule, &token[5], strlen(&token[5]));
+                       TRACE;
+                       syslog(LOG_DEBUG, "%s", decoded_rule);  
+
+                       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));
+
+                       free(decoded_rule);
+               }
+
+               // "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]);
+                       syslog(LOG_DEBUG, "lastsent: %ld", ibr->lastproc);
+               }
+
+       }
+
+       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.
+}
 
 
 /*
@@ -863,6 +1040,11 @@ void cmd_gibr(char *argbuf) {
        struct CtdlMessage *msg = CtdlFetchMessage(CC->user.msgnum_inboxrules, 1, 1);
        if (msg != NULL) {
                if (!CM_IsEmpty(msg, eMesageText)) {
+
+
+                       struct inboxrules *ii = deserialize_inbox_rules(msg->cm_fields[eMesageText]);
+                       free_inbox_rules(ii);
+
                        char *token; 
                        char *rest = msg->cm_fields[eMesageText];
                        while ((token = strtok_r(rest, "\n", &rest))) {
@@ -946,11 +1128,10 @@ CTDL_MODULE_INIT(sieve)
 {
        if (!threading)
        {
-               // ctdl_sieve_init();
                CtdlRegisterProtoHook(cmd_gibr, "GIBR", "Get InBox Rules");
                CtdlRegisterProtoHook(cmd_pibr, "PIBR", "Put InBox Rules");
-               // CtdlRegisterSessionHook(perform_sieve_processing, EVT_HOUSE, PRIO_HOUSE + 10);
-               // CtdlRegisterCleanupHook(cleanup_sieve);
+               CtdlRegisterRoomHook(serv_inboxrules_roomhook);
+               CtdlRegisterSessionHook(perform_inbox_processing, EVT_HOUSE, PRIO_HOUSE + 10);
        }
        
         /* return our module name for the log */