Reworked some suboptimal logic found in create_script()
[citadel.git] / webcit / sieve.c
1 /*
2  * Copyright (c) 1996-2012 by the citadel.org team
3  *
4  * This program is open source software.  You can redistribute it and/or
5  * modify it under the terms of the GNU General Public License version 3.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * FIXME: add logic to exclude the webcit-generated script from the manual script selection
13  */
14
15 #include "webcit.h"
16
17 #define MAX_SCRIPTS     100
18 #define MAX_RULES       50
19 #define RULES_SCRIPT    "__WebCit_Generated_Script__"
20
21
22 /*
23  * Helper function for output_sieve_rule() to output strings with quotes escaped
24  */
25 void osr_sanitize(char *str) {
26         int i, len;
27
28         if (str == NULL) return;
29         len = strlen(str);
30         for (i=0; i<len; ++i) {
31                 if (str[i]=='\"') {
32                         str[i] = '\'' ;
33                 }
34                 else if (isspace(str[i])) {
35                         str[i] = ' ';
36                 }
37         }
38 }
39
40
41 /*
42  * Output parseable Sieve script code based on rules input
43  */
44 void output_sieve_rule(char *hfield, char *compare, char *htext, char *sizecomp, int sizeval,
45                         char *action, char *fileinto, char *redirect, char *automsg, char *final,
46                         char *my_addresses)
47 {
48         char *comp1 = "";
49         char *comp2 = "";
50
51         osr_sanitize(htext);
52         osr_sanitize(fileinto);
53         osr_sanitize(redirect);
54         osr_sanitize(automsg);
55
56         /* Prepare negation and match operators that will be used iff we apply a conditional */
57
58         if (!strcasecmp(compare, "contains")) {
59                 comp1 = "";
60                 comp2 = ":contains";
61         }
62         else if (!strcasecmp(compare, "notcontains")) {
63                 comp1 = "not";
64                 comp2 = ":contains";
65         }
66         else if (!strcasecmp(compare, "is")) {
67                 comp1 = "";
68                 comp2 = ":is";
69         }
70         else if (!strcasecmp(compare, "isnot")) {
71                 comp1 = "not";
72                 comp2 = ":is";
73         }
74         else if (!strcasecmp(compare, "matches")) {
75                 comp1 = "";
76                 comp2 = ":matches";
77         }
78         else if (!strcasecmp(compare, "notmatches")) {
79                 comp1 = "not";
80                 comp2 = ":matches";
81         }
82
83         /* Now do the conditional */
84
85         if (!strcasecmp(hfield, "from")) {
86                 serv_printf("if%s header %s \"From\" \"%s\"",
87                         comp1, comp2,
88                         htext
89                 );
90         }
91
92         else if (!strcasecmp(hfield, "tocc")) {
93                 serv_printf("if%s header %s [\"To\", \"Cc\"] \"%s\"",
94                         comp1, comp2,
95                         htext
96                 );
97         }
98
99         else if (!strcasecmp(hfield, "subject")) {
100                 serv_printf("if%s header %s \"Subject\" \"%s\"",
101                         comp1, comp2,
102                         htext
103                 );
104         }
105
106         else if (!strcasecmp(hfield, "replyto")) {
107                 serv_printf("if%s header %s \"Reply-to\" \"%s\"",
108                         comp1, comp2,
109                         htext
110                 );
111         }
112
113         else if (!strcasecmp(hfield, "sender")) {
114                 serv_printf("if%s header %s \"Sender\" \"%s\"",
115                         comp1, comp2,
116                         htext
117                 );
118         }
119
120         else if (!strcasecmp(hfield, "resentfrom")) {
121                 serv_printf("if%s header %s \"Resent-from\" \"%s\"",
122                         comp1, comp2,
123                         htext
124                 );
125         }
126
127         else if (!strcasecmp(hfield, "resentto")) {
128                 serv_printf("if%s header %s \"Resent-to\" \"%s\"",
129                         comp1, comp2,
130                         htext
131                 );
132         }
133
134         else if (!strcasecmp(hfield, "xmailer")) {
135                 serv_printf("if%s header %s \"X-Mailer\" \"%s\"",
136                         comp1, comp2,
137                         htext
138                 );
139         }
140
141         else if (!strcasecmp(hfield, "xspamflag")) {
142                 serv_printf("if%s header %s \"X-Spam-Flag\" \"%s\"",
143                         comp1, comp2,
144                         htext
145                 );
146         }
147
148         else if (!strcasecmp(hfield, "xspamstatus")) {
149                 serv_printf("if%s header %s \"X-Spam-Status\" \"%s\"",
150                         comp1, comp2,
151                         htext
152                 );
153         }
154
155         else if (!strcasecmp(hfield, "listid")) {
156                 serv_printf("if%s header %s \"List-ID\" \"%s\"",
157                         comp1, comp2,
158                         htext
159                 );
160         }
161
162         else if (!strcasecmp(hfield, "envfrom")) {
163                 serv_printf("if%s envelope %s \"From\" \"%s\"",
164                         comp1, comp2,
165                         htext
166                 );
167         }
168
169         else if (!strcasecmp(hfield, "envto")) {
170                 serv_printf("if%s envelope %s \"To\" \"%s\"",
171                         comp1, comp2,
172                         htext
173                 );
174         }
175
176         else if (!strcasecmp(hfield, "size")) {
177                 if (!strcasecmp(sizecomp, "larger")) {
178                         serv_printf("if size :over %d", sizeval);
179                 }
180                 else if (!strcasecmp(sizecomp, "smaller")) {
181                         serv_printf("if size :under %d", sizeval);
182                 }
183                 else {  /* failsafe - should never get here, but just in case... */
184                         serv_printf("if size :over 1");
185                 }
186         }
187
188         /* Open braces if we're in a conditional loop */
189
190         if (strcasecmp(hfield, "all")) {
191                 serv_printf("{");
192         }
193
194         /* Do action */
195
196         if (!strcasecmp(action, "keep")) {
197                 serv_printf("keep;");
198         }
199
200         else if (!strcasecmp(action, "discard")) {
201                 serv_printf("discard;");
202         }
203
204         else if (!strcasecmp(action, "reject")) {
205                 serv_printf("reject \"%s\";", automsg);
206         }
207
208         else if (!strcasecmp(action, "fileinto")) {
209                 serv_printf("fileinto \"%s\";", fileinto);
210         }
211
212         else if (!strcasecmp(action, "redirect")) {
213                 serv_printf("redirect \"%s\";", redirect);
214         }
215
216         else if (!strcasecmp(action, "vacation")) {
217                 serv_printf("vacation :addresses [%s]\n\"%s\";", my_addresses, automsg);
218         }
219
220         /* Do 'final' action */
221
222         if (!strcasecmp(final, "stop")) {
223                 serv_printf("stop;");
224         }
225
226         /* Close the braces if we're in a conditional loop */
227
228         if (strcasecmp(hfield, "all")) {
229                 serv_printf("}");
230         }
231
232         /* End of rule. */
233 }
234
235
236 /*
237  * Translate the fields from the rule editor into something we can save...
238  */
239 void parse_fields_from_rule_editor(void) {
240
241         int active;
242         char hfield[256];
243         char compare[32];
244         char htext[256];
245         char sizecomp[32];
246         int sizeval;
247         char action[32];
248         char fileinto[128];
249         char redirect[256];
250         char automsg[1024];
251         char final[32];
252         int i;
253         char buf[256];
254         char fname[256];
255         char rule[2048];
256         char encoded_rule[4096];
257         char my_addresses[4096];
258         
259         /* Enumerate my email addresses in case they are needed for a vacation rule */
260         my_addresses[0] = 0;
261         serv_puts("GVEA");
262         serv_getln(buf, sizeof buf);
263         if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
264                 if (!IsEmptyStr(my_addresses)) {
265                         strcat(my_addresses, ",\n");
266                 }
267                 strcat(my_addresses, "\"");
268                 strcat(my_addresses, buf);
269                 strcat(my_addresses, "\"");
270         }
271
272         /* Now generate the script and write it to the Citadel server */
273         serv_printf("MSIV putscript|%s|", RULES_SCRIPT);
274         serv_getln(buf, sizeof buf);
275         if (buf[0] != '4') {
276                 return;
277         }
278
279         serv_puts("# THIS SCRIPT WAS AUTOMATICALLY GENERATED BY WEBCIT.");
280         serv_puts("# ");
281         serv_puts("# Do not attempt to manually edit it.  If you do so,");
282         serv_puts("# your changes will be overwritten the next time WebCit");
283         serv_puts("# saves its mail filtering rule set.  If you really want");
284         serv_puts("# to use these rules as the basis for another script,");
285         serv_puts("# copy them to another script and save that instead.");
286         serv_puts("");
287         serv_puts("require \"fileinto\";");
288         serv_puts("require \"reject\";");
289         serv_puts("require \"vacation\";");
290         serv_puts("require \"envelope\";");
291         serv_puts("");
292
293         for (i=0; i<MAX_RULES; ++i) {
294                 
295                 strcpy(rule, "");
296
297                 sprintf(fname, "active%d", i);
298                 active = !strcasecmp(BSTR(fname), "on") ;
299
300                 if (active) {
301
302                         sprintf(fname, "hfield%d", i);
303                         safestrncpy(hfield, BSTR(fname), sizeof hfield);
304         
305                         sprintf(fname, "compare%d", i);
306                         safestrncpy(compare, BSTR(fname), sizeof compare);
307         
308                         sprintf(fname, "htext%d", i);
309                         safestrncpy(htext, BSTR(fname), sizeof htext);
310         
311                         sprintf(fname, "sizecomp%d", i);
312                         safestrncpy(sizecomp, BSTR(fname), sizeof sizecomp);
313         
314                         sprintf(fname, "sizeval%d", i);
315                         sizeval = IBSTR(fname);
316         
317                         sprintf(fname, "action%d", i);
318                         safestrncpy(action, BSTR(fname), sizeof action);
319         
320                         sprintf(fname, "fileinto%d", i);
321                         safestrncpy(fileinto, BSTR(fname), sizeof fileinto);
322         
323                         sprintf(fname, "redirect%d", i);
324                         safestrncpy(redirect, BSTR(fname), sizeof redirect);
325         
326                         sprintf(fname, "automsg%d", i);
327                         safestrncpy(automsg, BSTR(fname), sizeof automsg);
328         
329                         sprintf(fname, "final%d", i);
330                         safestrncpy(final, BSTR(fname), sizeof final);
331         
332                         snprintf(rule, sizeof rule, "%d|%s|%s|%s|%s|%d|%s|%s|%s|%s|%s",
333                                 active, hfield, compare, htext, sizecomp, sizeval, action, fileinto,
334                                 redirect, automsg, final
335                         );
336         
337                         CtdlEncodeBase64(encoded_rule, rule, strlen(rule)+1, 0);
338                         serv_printf("# WEBCIT_RULE|%d|%s|", i, encoded_rule);
339                         output_sieve_rule(hfield, compare, htext, sizecomp, sizeval,
340                                         action, fileinto, redirect, automsg, final, my_addresses);
341                         serv_puts("");
342                 }
343
344
345         }
346
347         serv_puts("stop;");
348         serv_puts("000");
349 }
350
351
352 /*
353  * save sieve config
354  */
355 void save_sieve(void) {
356         int bigaction;
357         char script_names[MAX_SCRIPTS][64];
358         int num_scripts = 0;
359         int i;
360         char this_name[64];
361         char buf[256];
362
363         if (!havebstr("save_button")) {
364                 AppendImportantMessage(_("Cancelled.  Changes were not saved."), -1);
365                 display_main_menu();
366                 return;
367         }
368
369         parse_fields_from_rule_editor();
370
371         serv_puts("MSIV listscripts");
372         serv_getln(buf, sizeof(buf));
373         if (buf[0] == '1') while (serv_getln(buf, sizeof(buf)), strcmp(buf, "000")) {
374                 if (num_scripts < MAX_SCRIPTS) {
375                         extract_token(script_names[num_scripts], buf, 0, '|', 64);
376                         ++num_scripts;
377                 }
378         }
379
380         bigaction = ibstr("bigaction");
381
382         if (bigaction == 0) {
383                 serv_puts("MSIV setactive||");
384                 serv_getln(buf, sizeof buf);
385         }
386
387         else if (bigaction == 1) {
388                 serv_printf("MSIV setactive|%s|", RULES_SCRIPT);
389                 serv_getln(buf, sizeof buf);
390         }
391
392         else if (bigaction == 2) {
393                 serv_printf("MSIV setactive|%s|", bstr("active_script"));
394                 serv_getln(buf, sizeof buf);
395         }
396
397         if (num_scripts > 0) {
398                 for (i=0; i<num_scripts; ++i) {
399                         /*
400                          * We only want to save the scripts from the "manually edited scripts"
401                          * screen.  The script that WebCit generates from its ruleset will be
402                          * auto-generated by parse_fields_from_rule_editor() and saved there.
403                          */
404                         if (strcasecmp(script_names[i], RULES_SCRIPT)) {
405                                 serv_printf("MSIV putscript|%s|", script_names[i]);
406                                 serv_getln(buf, sizeof buf);
407                                 if (buf[0] == '4') {
408                                         snprintf(this_name, sizeof this_name, "text_%s", script_names[i]);
409                                         striplt((char *)BSTR(this_name)); /* TODO: get rid of typecast*/
410                                         serv_write(BSTR(this_name), strlen(BSTR(this_name)));
411                                         serv_puts("\n000");
412                                 }
413                         }
414                 }
415         }
416
417         AppendImportantMessage(_("Your changes have been saved."), -1);
418         display_main_menu();
419         return;
420 }
421
422
423 /*
424  * create a new script
425  * take the web environment script name and create it on the citadel server
426  */
427 void create_script(void) {
428         char buf[256];
429
430         serv_printf("MSIV getscript|%s", bstr("script_name"));
431         serv_getln(buf, sizeof buf);
432         if (buf[0] == '1') {            // does script exist already?
433                 while (serv_getln(buf, sizeof(buf)), strcmp(buf, "000")) {
434                                         // yes -- flush the output
435                 }
436         }
437         else {
438                                         // no -- safe to create a new one by this name
439                 serv_printf("MSIV putscript|%s", bstr("script_name"));
440                 serv_getln(buf, sizeof buf);
441                 if (buf[0] == '4') {
442                         serv_puts("keep;");
443                         serv_puts("000");
444                 }
445         }
446
447         output_headers(1, 1, 2, 0, 0, 0);
448         do_template("sieve_add");
449         wDumpContent(1);
450 }
451
452
453 /*
454  * delete a script
455  */
456 void delete_script(void) {
457         char buf[256];
458
459         serv_printf("MSIV deletescript|%s", bstr("script_name"));
460         serv_getln(buf, sizeof buf);
461         output_headers(1, 1, 2, 0, 0, 0);
462         do_template("sieve_add");
463         wDumpContent(1);
464 }
465
466
467 /*
468  * dummy panel indicating to the user that the server doesn't support Sieve
469  */
470 void display_no_sieve(void) {
471
472         output_headers(1, 1, 2, 0, 0, 0);
473         do_template("sieve_none");
474         wDumpContent(1);
475 }
476
477
478 typedef struct __SieveListing {
479         int IsActive;
480         int IsRulesScript;
481         StrBuf *Name;
482         StrBuf *Content;
483 } SieveListing;
484
485 int ConditionalSieveScriptIsActive(StrBuf *Target, WCTemplputParams *TP)
486 {
487         SieveListing     *SieveList = (SieveListing *)CTX;
488         return SieveList->IsActive;
489 }
490 int ConditionalSieveScriptIsRulesScript(StrBuf *Target, WCTemplputParams *TP)
491 {
492         SieveListing     *SieveList = (SieveListing *)CTX;
493         return SieveList->IsActive;
494 }
495 void tmplput_SieveScriptName(StrBuf *Target, WCTemplputParams *TP) 
496 {
497         SieveListing     *SieveList = (SieveListing *)CTX;
498         StrBufAppendTemplate(Target, TP, SieveList->Name, 0);
499 }
500 void tmplput_SieveScriptContent(StrBuf *Target, WCTemplputParams *TP) 
501 {
502         SieveListing     *SieveList = (SieveListing *)CTX;
503         StrBufAppendTemplate(Target, TP, SieveList->Content, 0);
504 }
505 void FreeSieveListing(void *vSieveListing)
506 {
507         SieveListing *List = (SieveListing*) vSieveListing;
508
509         FreeStrBuf(&List->Name);
510         free(List);
511 }
512
513 HashList *GetSieveScriptListing(StrBuf *Target, WCTemplputParams *TP)
514 {
515         wcsession *WCC = WC;
516         StrBuf *Line;
517         int num_scripts = 0;
518         int rules_script_active = 0;
519         int have_rules_script = 0;
520         const char *pch;
521         HashPos  *it;
522         int Done = 0;
523         SieveListing *Ruleset;
524
525         if (WCC->KnownSieveScripts != NULL) {
526                 return WCC->KnownSieveScripts;
527         }
528
529         serv_puts("MSIV listscripts");
530         Line = NewStrBuf();
531         StrBuf_ServGetln(Line);
532         if (GetServerStatus(Line, NULL) == 1) 
533         {
534                 WCC->KnownSieveScripts = NewHash(1, Flathash);
535
536                 while(!Done && (StrBuf_ServGetln(Line) >= 0) )
537                         if ( (StrLength(Line)==3) && 
538                              !strcmp(ChrPtr(Line), "000")) 
539                         {
540                                 Done = 1;
541                         }
542                         else
543                         {
544                                 pch = NULL;
545                                 Ruleset = (SieveListing *) malloc(sizeof(SieveListing));
546                                 Ruleset->Name = NewStrBufPlain(NULL, StrLength(Line));
547                                 StrBufExtract_NextToken(Ruleset->Name, Line, &pch, '|');
548                                 Ruleset->IsActive = StrBufExtractNext_int(Line, &pch, '|'); 
549                                 Ruleset->Content = NULL;
550
551                                 if (!strcasecmp(ChrPtr(Ruleset->Name), RULES_SCRIPT))
552                                 {
553                                         Ruleset->IsRulesScript = 1;
554                                         have_rules_script = 1;
555                                         if (Ruleset->IsActive)
556                                         {
557                                                 rules_script_active = 1;
558                                                 PutBstr(HKEY("__SIEVE:RULESSCRIPT"), NewStrBufPlain(HKEY("1")));
559                                         }
560                                 }
561                                 Put(WCC->KnownSieveScripts, IKEY(num_scripts), Ruleset, FreeSieveListing);
562
563                                 ++num_scripts;
564                         }
565         }
566
567         if ((num_scripts > 0) && (rules_script_active == 0)) {
568                 PutBstr(HKEY("__SIEVE:EXTERNAL_SCRIPT"), NewStrBufPlain(HKEY("1")));
569         }
570
571         if (num_scripts > have_rules_script)
572         {
573                 long rc = 0;
574                 long len;
575                 const char *Key;
576                 void *vRuleset;
577
578                 /* 
579                  * ok; we have custom scripts, expose that via bstr, and load the payload.
580                  */
581                 PutBstr(HKEY("__SIEVE:HAVE_EXTERNAL_SCRIPT"), NewStrBufPlain(HKEY("1")));
582
583                 it = GetNewHashPos(WCC->KnownSieveScripts, 0);
584                 while (GetNextHashPos(WCC->KnownSieveScripts, it, &len, &Key, &vRuleset) && 
585                        (vRuleset != NULL))
586                 {
587                         Ruleset = (SieveListing *) vRuleset;
588                         serv_printf("MSIV getscript|%s", ChrPtr(Ruleset->Name));
589                         StrBuf_ServGetln(Line);
590                         if (GetServerStatus(Line, NULL) == 1) 
591                         {
592                                 Ruleset->Content = NewStrBuf();
593                                 Done = 0;
594                                 while(!Done && (rc = StrBuf_ServGetln(Line), rc >= 0) )
595                                         if ( (StrLength(Line)==3) && 
596                                              !strcmp(ChrPtr(Line), "000")) 
597                                         {
598                                                 Done = 1;
599                                         }
600                                         else
601                                         {
602                                                 if (StrLength(Ruleset->Content)>0)
603                                                         StrBufAppendBufPlain(Ruleset->Content, HKEY("\n"), 0);
604                                                 StrBufAppendBuf(Ruleset->Content, Line, 0);
605                                         }
606                                 if (rc < 0) break;
607                         }
608                 }
609         }
610         FreeStrBuf(&Line);
611         return WCC->KnownSieveScripts;
612 }
613
614
615 typedef enum __eSieveHfield 
616 {
617         from,           
618         tocc,           
619         subject,        
620         replyto,        
621         sender, 
622         resentfrom,     
623         resentto,       
624         envfrom,        
625         envto,  
626         xmailer,        
627         xspamflag,      
628         xspamstatus,    
629         listid, 
630         size,           
631         all
632 } eSieveHfield;
633
634 typedef enum __eSieveCompare {
635         contains,
636         notcontains,
637         is,
638         isnot,
639         matches,
640         notmatches
641 } eSieveCompare;
642
643 typedef enum __eSieveAction {
644         keep,
645         discard,
646         reject,
647         fileinto,
648         redirect,
649         vacation
650 } eSieveAction;
651
652
653 typedef enum __eSieveSizeComp {
654         larger,
655         smaller
656 } eSieveSizeComp;
657
658 typedef enum __eSieveFinal {
659         econtinue,
660         estop
661 } eSieveFinal;
662
663
664 typedef struct __SieveRule {
665         int active;
666         int sizeval;
667         eSieveHfield hfield;
668         eSieveCompare compare;
669         StrBuf *htext;
670         eSieveSizeComp sizecomp;
671         eSieveAction Action;
672         StrBuf *fileinto;
673         StrBuf *redirect;
674         StrBuf *automsg;
675         eSieveFinal final;
676 }SieveRule;
677
678
679
680 int ConditionalSieveRule_hfield(StrBuf *Target, WCTemplputParams *TP)
681 {
682         SieveRule     *Rule = (SieveRule *)CTX;
683         
684         return GetTemplateTokenNumber(Target, 
685                                       TP, 
686                                       3, 
687                                       from)
688                 ==
689                 Rule->hfield;
690 }
691 int ConditionalSieveRule_compare(StrBuf *Target, WCTemplputParams *TP)
692 {
693         SieveRule     *Rule = (SieveRule *)CTX;
694         return GetTemplateTokenNumber(Target, 
695                                       TP, 
696                                       3, 
697                                       contains)
698                 ==
699                 Rule->compare;
700 }
701 int ConditionalSieveRule_action(StrBuf *Target, WCTemplputParams *TP)
702 {
703         SieveRule     *Rule = (SieveRule *)CTX;
704         return GetTemplateTokenNumber(Target, 
705                                       TP, 
706                                       3, 
707                                       keep)
708                 ==
709                 Rule->Action; 
710 }
711 int ConditionalSieveRule_sizecomp(StrBuf *Target, WCTemplputParams *TP)
712 {
713         SieveRule     *Rule = (SieveRule *)CTX;
714         return GetTemplateTokenNumber(Target, 
715                                       TP, 
716                                       3, 
717                                       larger)
718                 ==
719                 Rule->sizecomp;
720 }
721 int ConditionalSieveRule_final(StrBuf *Target, WCTemplputParams *TP)
722 {
723         SieveRule     *Rule = (SieveRule *)CTX;
724         return GetTemplateTokenNumber(Target, 
725                                       TP, 
726                                       3, 
727                                       econtinue)
728                 ==
729                 Rule->final;
730 }
731 int ConditionalSieveRule_ThisRoom(StrBuf *Target, WCTemplputParams *TP)
732 {
733         SieveRule     *Rule = (SieveRule *)CTX;
734         return GetTemplateTokenNumber(Target, 
735                                       TP, 
736                                       3, 
737                                       econtinue)
738                 ==
739                 Rule->final;
740 }
741 int ConditionalSieveRule_Active(StrBuf *Target, WCTemplputParams *TP)
742 {
743         SieveRule     *Rule = (SieveRule *)CTX;
744         return Rule->active;
745 }
746 void tmplput_SieveRule_htext(StrBuf *Target, WCTemplputParams *TP) 
747 {
748         SieveRule     *Rule = (SieveRule *)CTX;
749         StrBufAppendTemplate(Target, TP, Rule->htext, 0);
750 }
751 void tmplput_SieveRule_fileinto(StrBuf *Target, WCTemplputParams *TP) 
752 {
753         SieveRule     *Rule = (SieveRule *)CTX;
754         StrBufAppendTemplate(Target, TP, Rule->fileinto, 0);
755 }
756 void tmplput_SieveRule_redirect(StrBuf *Target, WCTemplputParams *TP) 
757 {
758         SieveRule     *Rule = (SieveRule *)CTX;
759         StrBufAppendTemplate(Target, TP, Rule->redirect, 0);
760 }
761 void tmplput_SieveRule_automsg(StrBuf *Target, WCTemplputParams *TP) 
762 {
763         SieveRule     *Rule = (SieveRule *)CTX;
764         StrBufAppendTemplate(Target, TP, Rule->automsg, 0);
765 }
766 void tmplput_SieveRule_sizeval(StrBuf *Target, WCTemplputParams *TP) 
767 {
768         SieveRule     *Rule = (SieveRule *)CTX;
769         StrBufAppendPrintf(Target, "%d", Rule->sizeval);
770 }
771
772 void tmplput_SieveRule_lookup_FileIntoRoom(StrBuf *Target, WCTemplputParams *TP) 
773 {
774         void *vRoom;
775         SieveRule     *Rule = (SieveRule *)CTX;
776         wcsession *WCC = WC;
777         HashList *Rooms = GetRoomListHashLKRA(Target, TP);
778
779         GetHash(Rooms, SKEY(Rule->fileinto), &vRoom);
780         WCC->ThisRoom = (folder*) vRoom;
781 }
782
783 void FreeSieveRule(void *vRule)
784 {
785         SieveRule *Rule = (SieveRule*) vRule;
786
787         FreeStrBuf(&Rule->htext);
788         FreeStrBuf(&Rule->fileinto);
789         FreeStrBuf(&Rule->redirect);
790         FreeStrBuf(&Rule->automsg);
791         
792         free(Rule);
793 }
794
795 #define WC_RULE_HEADER "# WEBCIT_RULE|"
796 HashList *GetSieveRules(StrBuf *Target, WCTemplputParams *TP)
797 {
798         StrBuf *Line = NULL;
799         StrBuf *EncodedRule = NULL;
800         int n = 0;
801         const char *pch = NULL;
802         HashList *SieveRules = NULL;
803         int Done = 0;
804         SieveRule *Rule = NULL;
805
806         SieveRules = NewHash(1, Flathash);
807         serv_printf("MSIV getscript|"RULES_SCRIPT);
808         Line = NewStrBuf();
809         EncodedRule = NewStrBuf();
810         StrBuf_ServGetln(Line);
811         if (GetServerStatus(Line, NULL) == 1) 
812         {
813                 while(!Done && (StrBuf_ServGetln(Line) >= 0) )
814                         if ( (StrLength(Line)==3) && 
815                              !strcmp(ChrPtr(Line), "000")) 
816                         {
817                                 Done = 1;
818                         }
819                         else
820                         {
821                                 pch = NULL;
822                                 /* We just care for our encoded header and skip everything else */
823                                 if ((StrLength(Line) > sizeof(WC_RULE_HEADER) - 1) &&
824                                     (!strncasecmp(ChrPtr(Line), HKEY(WC_RULE_HEADER))))
825                                 {
826                                         StrBufSkip_NTokenS(Line, &pch, '|', 1);
827                                         n = StrBufExtractNext_int(Line, &pch, '|'); 
828                                         StrBufExtract_NextToken(EncodedRule, Line, &pch, '|');
829                                         StrBufDecodeBase64(EncodedRule);
830
831                                         Rule = (SieveRule*) malloc(sizeof(SieveRule));
832
833                                         Rule->htext = NewStrBufPlain (NULL, StrLength(EncodedRule));
834
835                                         Rule->fileinto = NewStrBufPlain (NULL, StrLength(EncodedRule));
836                                         Rule->redirect = NewStrBufPlain (NULL, StrLength(EncodedRule));
837                                         Rule->automsg = NewStrBufPlain (NULL, StrLength(EncodedRule));
838
839                                         /* Grab our existing values to populate */
840                                         pch = NULL;
841                                         Rule->active = StrBufExtractNext_int(EncodedRule, &pch, '|');
842                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
843                                         
844                                         Rule->hfield = (eSieveHfield) GetTokenDefine(SKEY(Line), tocc);
845                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
846                                         Rule->compare = (eSieveCompare) GetTokenDefine(SKEY(Line), contains);
847                                         StrBufExtract_NextToken(Rule->htext, EncodedRule, &pch, '|');
848                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
849                                         Rule->sizecomp = (eSieveSizeComp) GetTokenDefine(SKEY(Line), larger);
850                                         Rule->sizeval = StrBufExtractNext_int(EncodedRule, &pch, '|');
851                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
852                                         Rule->Action = (eSieveAction) GetTokenDefine(SKEY(Line), keep);
853                                         StrBufExtract_NextToken(Rule->fileinto, EncodedRule, &pch, '|');
854                                         StrBufExtract_NextToken(Rule->redirect, EncodedRule, &pch, '|');
855                                         StrBufExtract_NextToken(Rule->automsg, EncodedRule, &pch, '|');
856                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
857                                         Rule->final = (eSieveFinal) GetTokenDefine(SKEY(Line), econtinue);
858                                         Put(SieveRules, IKEY(n), Rule, FreeSieveRule);
859                                         n++;
860                                 }
861                         }
862         }
863
864         while (n < MAX_RULES) {
865                 Rule = (SieveRule*) malloc(sizeof(SieveRule));
866                 memset(Rule, 0, sizeof(SieveRule));
867                 Put(SieveRules, IKEY(n), Rule, FreeSieveRule);
868             
869                 n++;
870         }
871
872
873         FreeStrBuf(&EncodedRule);
874         FreeStrBuf(&Line);
875         return SieveRules;
876 }
877
878 void
879 SessionDetachModule_SIEVE
880 (wcsession *sess)
881 {
882         DeleteHash(&sess->KnownSieveScripts);
883 }
884
885 void 
886 InitModule_SIEVE
887 (void)
888 {
889         REGISTERTokenParamDefine(from);         
890         REGISTERTokenParamDefine(tocc);         
891         REGISTERTokenParamDefine(subject);      
892         REGISTERTokenParamDefine(replyto);      
893         REGISTERTokenParamDefine(sender);       
894         REGISTERTokenParamDefine(resentfrom);   
895         REGISTERTokenParamDefine(resentto);     
896         REGISTERTokenParamDefine(envfrom);      
897         REGISTERTokenParamDefine(envto);        
898         REGISTERTokenParamDefine(xmailer);      
899         REGISTERTokenParamDefine(xspamflag);    
900         REGISTERTokenParamDefine(xspamstatus);  
901         REGISTERTokenParamDefine(listid);       
902         REGISTERTokenParamDefine(size);         
903         REGISTERTokenParamDefine(all);
904
905         REGISTERTokenParamDefine(contains);
906         REGISTERTokenParamDefine(notcontains);
907         REGISTERTokenParamDefine(is);
908         REGISTERTokenParamDefine(isnot);
909         REGISTERTokenParamDefine(matches);
910         REGISTERTokenParamDefine(notmatches);
911
912         REGISTERTokenParamDefine(keep);
913         REGISTERTokenParamDefine(discard);
914         REGISTERTokenParamDefine(reject);
915         REGISTERTokenParamDefine(fileinto);
916         REGISTERTokenParamDefine(redirect);
917         REGISTERTokenParamDefine(vacation);
918
919         REGISTERTokenParamDefine(larger);
920         REGISTERTokenParamDefine(smaller);
921
922         /* these are c-keyworads, so do it by hand. */
923         RegisterTokenParamDefine(HKEY("continue"), econtinue);
924         RegisterTokenParamDefine(HKEY("stop"), estop);
925
926         RegisterIterator("SIEVE:SCRIPTS", 0, NULL, GetSieveScriptListing, NULL, NULL, CTX_SIEVELIST, CTX_NONE, IT_NOFLAG);
927
928         RegisterConditional(HKEY("COND:SIEVE:SCRIPT:ACTIVE"), 0, ConditionalSieveScriptIsActive, CTX_SIEVELIST);
929         RegisterConditional(HKEY("COND:SIEVE:SCRIPT:ISRULES"), 0, ConditionalSieveScriptIsRulesScript, CTX_SIEVELIST);
930         RegisterNamespace("SIEVE:SCRIPT:NAME", 0, 1, tmplput_SieveScriptName, NULL, CTX_SIEVELIST);
931         RegisterNamespace("SIEVE:SCRIPT:CONTENT", 0, 1, tmplput_SieveScriptContent, NULL, CTX_SIEVELIST);
932
933  
934         RegisterIterator("SIEVE:RULES", 0, NULL, GetSieveRules, NULL, DeleteHash, CTX_SIEVESCRIPT, CTX_NONE, IT_NOFLAG);
935
936         RegisterConditional(HKEY("COND:SIEVE:ACTIVE"), 1, ConditionalSieveRule_Active, CTX_SIEVESCRIPT);
937         RegisterConditional(HKEY("COND:SIEVE:HFIELD"), 1, ConditionalSieveRule_hfield, CTX_SIEVESCRIPT);
938         RegisterConditional(HKEY("COND:SIEVE:COMPARE"), 1, ConditionalSieveRule_compare, CTX_SIEVESCRIPT);
939         RegisterConditional(HKEY("COND:SIEVE:ACTION"), 1, ConditionalSieveRule_action, CTX_SIEVESCRIPT);
940         RegisterConditional(HKEY("COND:SIEVE:SIZECOMP"), 1, ConditionalSieveRule_sizecomp, CTX_SIEVESCRIPT);
941         RegisterConditional(HKEY("COND:SIEVE:FINAL"), 1, ConditionalSieveRule_final, CTX_SIEVESCRIPT);
942         RegisterConditional(HKEY("COND:SIEVE:THISROOM"), 1, ConditionalSieveRule_ThisRoom, CTX_SIEVESCRIPT);
943
944         RegisterNamespace("SIEVE:SCRIPT:HTEXT", 0, 1, tmplput_SieveRule_htext, NULL, CTX_SIEVESCRIPT);
945         RegisterNamespace("SIEVE:SCRIPT:SIZE", 0, 1, tmplput_SieveRule_sizeval, NULL, CTX_SIEVESCRIPT);
946         RegisterNamespace("SIEVE:SCRIPT:FILEINTO", 0, 1, tmplput_SieveRule_fileinto, NULL, CTX_SIEVESCRIPT);
947         RegisterNamespace("SIEVE:SCRIPT:REDIRECT", 0, 1, tmplput_SieveRule_redirect, NULL, CTX_SIEVESCRIPT);
948         RegisterNamespace("SIEVE:SCRIPT:AUTOMSG", 0, 1, tmplput_SieveRule_automsg, NULL, CTX_SIEVESCRIPT);
949
950         /* fetch our room into WCC->ThisRoom, to evaluate while iterating over rooms with COND:THIS:THAT:ROOM */
951         RegisterNamespace("SIEVE:SCRIPT:LOOKUP_FILEINTO", 0, 1, tmplput_SieveRule_lookup_FileIntoRoom, NULL, CTX_SIEVESCRIPT);
952         WebcitAddUrlHandler(HKEY("save_sieve"), "", 0, save_sieve, 0);
953         WebcitAddUrlHandler(HKEY("create_script"), "", 0, create_script, 0);
954         WebcitAddUrlHandler(HKEY("delete_script"), "", 0, delete_script, 0);
955 }