a49d6b30d29a2fe063b33ea4ffb9a83890317dec
[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 void display_sieve_add_or_delete(void) {
424         output_headers(1, 1, 2, 0, 0, 0);
425         do_template("sieve_add");
426         wDumpContent(1);
427 }
428
429
430
431 /*
432  * create a new script
433  * take the web environment script name and create it on the citadel server
434  */
435 void create_script(void) {
436         char buf[256];
437
438         serv_printf("MSIV getscript|%s", bstr("script_name"));
439         serv_getln(buf, sizeof buf);
440         if (buf[0] == '1') {            // does script exist already?
441                 while (serv_getln(buf, sizeof(buf)), strcmp(buf, "000")) {
442                                         // yes -- flush the output
443                 }
444         }
445         else {
446                                         // no -- safe to create a new one by this name
447                 serv_printf("MSIV putscript|%s", bstr("script_name"));
448                 serv_getln(buf, sizeof buf);
449                 if (buf[0] == '4') {
450                         serv_puts("keep;");
451                         serv_puts("000");
452                 }
453         }
454
455         display_sieve_add_or_delete();
456 }
457
458
459 /*
460  * delete a script
461  */
462 void delete_script(void) {
463         char buf[256];
464
465         serv_printf("MSIV deletescript|%s", bstr("script_name"));
466         serv_getln(buf, sizeof buf);
467         display_sieve_add_or_delete();
468 }
469
470
471 /*
472  * dummy panel indicating to the user that the server doesn't support Sieve
473  */
474 void display_no_sieve(void) {
475
476         output_headers(1, 1, 1, 0, 0, 0);
477         do_template("sieve_none");
478         wDumpContent(1);
479 }
480
481
482 typedef struct __SieveListing {
483         int IsActive;
484         int IsRulesScript;
485         StrBuf *Name;
486         StrBuf *Content;
487 } SieveListing;
488
489 int ConditionalSieveScriptIsActive(StrBuf *Target, WCTemplputParams *TP)
490 {
491         SieveListing     *SieveList = (SieveListing *)CTX(CTX_SIEVELIST);
492         return SieveList->IsActive;
493 }
494 int ConditionalSieveScriptIsRulesScript(StrBuf *Target, WCTemplputParams *TP)
495 {
496         SieveListing     *SieveList = (SieveListing *)CTX(CTX_SIEVELIST);
497         return SieveList->IsActive;
498 }
499 void tmplput_SieveScriptName(StrBuf *Target, WCTemplputParams *TP) 
500 {
501         SieveListing     *SieveList = (SieveListing *)CTX(CTX_SIEVELIST);
502         StrBufAppendTemplate(Target, TP, SieveList->Name, 0);
503 }
504 void tmplput_SieveScriptContent(StrBuf *Target, WCTemplputParams *TP) 
505 {
506         SieveListing     *SieveList = (SieveListing *)CTX(CTX_SIEVELIST);
507         StrBufAppendTemplate(Target, TP, SieveList->Content, 0);
508 }
509 void FreeSieveListing(void *vSieveListing)
510 {
511         SieveListing *List = (SieveListing*) vSieveListing;
512
513         FreeStrBuf(&List->Name);
514         free(List);
515 }
516
517 HashList *GetSieveScriptListing(StrBuf *Target, WCTemplputParams *TP)
518 {
519         wcsession *WCC = WC;
520         StrBuf *Line;
521         int num_scripts = 0;
522         int rules_script_active = 0;
523         int have_rules_script = 0;
524         const char *pch;
525         HashPos  *it;
526         int Done = 0;
527         SieveListing *Ruleset;
528
529         if (WCC->KnownSieveScripts != NULL) {
530                 return WCC->KnownSieveScripts;
531         }
532
533         serv_puts("MSIV listscripts");
534         Line = NewStrBuf();
535         StrBuf_ServGetln(Line);
536         if (GetServerStatus(Line, NULL) == 1) 
537         {
538                 WCC->KnownSieveScripts = NewHash(1, Flathash);
539
540                 while(!Done && (StrBuf_ServGetln(Line) >= 0) )
541                         if ( (StrLength(Line)==3) && 
542                              !strcmp(ChrPtr(Line), "000")) 
543                         {
544                                 Done = 1;
545                         }
546                         else
547                         {
548                                 pch = NULL;
549                                 Ruleset = (SieveListing *) malloc(sizeof(SieveListing));
550                                 Ruleset->Name = NewStrBufPlain(NULL, StrLength(Line));
551                                 StrBufExtract_NextToken(Ruleset->Name, Line, &pch, '|');
552                                 Ruleset->IsActive = StrBufExtractNext_int(Line, &pch, '|'); 
553                                 Ruleset->Content = NULL;
554
555                                 if (!strcasecmp(ChrPtr(Ruleset->Name), RULES_SCRIPT))
556                                 {
557                                         Ruleset->IsRulesScript = 1;
558                                         have_rules_script = 1;
559                                         if (Ruleset->IsActive)
560                                         {
561                                                 rules_script_active = 1;
562                                                 PutBstr(HKEY("__SIEVE:RULESSCRIPT"), NewStrBufPlain(HKEY("1")));
563                                         }
564                                 }
565                                 Put(WCC->KnownSieveScripts, IKEY(num_scripts), Ruleset, FreeSieveListing);
566
567                                 ++num_scripts;
568                         }
569         }
570
571         if ((num_scripts > 0) && (rules_script_active == 0)) {
572                 PutBstr(HKEY("__SIEVE:EXTERNAL_SCRIPT"), NewStrBufPlain(HKEY("1")));
573         }
574
575         if (num_scripts > have_rules_script)
576         {
577                 long rc = 0;
578                 long len;
579                 const char *Key;
580                 void *vRuleset;
581
582                 /* 
583                  * ok; we have custom scripts, expose that via bstr, and load the payload.
584                  */
585                 PutBstr(HKEY("__SIEVE:HAVE_EXTERNAL_SCRIPT"), NewStrBufPlain(HKEY("1")));
586
587                 it = GetNewHashPos(WCC->KnownSieveScripts, 0);
588                 while (GetNextHashPos(WCC->KnownSieveScripts, it, &len, &Key, &vRuleset) && 
589                        (vRuleset != NULL))
590                 {
591                         Ruleset = (SieveListing *) vRuleset;
592                         serv_printf("MSIV getscript|%s", ChrPtr(Ruleset->Name));
593                         StrBuf_ServGetln(Line);
594                         if (GetServerStatus(Line, NULL) == 1) 
595                         {
596                                 Ruleset->Content = NewStrBuf();
597                                 Done = 0;
598                                 while(!Done && (rc = StrBuf_ServGetln(Line), rc >= 0) )
599                                         if ( (StrLength(Line)==3) && 
600                                              !strcmp(ChrPtr(Line), "000")) 
601                                         {
602                                                 Done = 1;
603                                         }
604                                         else
605                                         {
606                                                 if (StrLength(Ruleset->Content)>0)
607                                                         StrBufAppendBufPlain(Ruleset->Content, HKEY("\n"), 0);
608                                                 StrBufAppendBuf(Ruleset->Content, Line, 0);
609                                         }
610                                 if (rc < 0) break;
611                         }
612                 }
613         }
614         FreeStrBuf(&Line);
615         return WCC->KnownSieveScripts;
616 }
617
618
619 typedef enum __eSieveHfield 
620 {
621         from,           
622         tocc,           
623         subject,        
624         replyto,        
625         sender, 
626         resentfrom,     
627         resentto,       
628         envfrom,        
629         envto,  
630         xmailer,        
631         xspamflag,      
632         xspamstatus,    
633         listid, 
634         size,           
635         all
636 } eSieveHfield;
637
638 typedef enum __eSieveCompare {
639         contains,
640         notcontains,
641         is,
642         isnot,
643         matches,
644         notmatches
645 } eSieveCompare;
646
647 typedef enum __eSieveAction {
648         keep,
649         discard,
650         reject,
651         fileinto,
652         redirect,
653         vacation
654 } eSieveAction;
655
656
657 typedef enum __eSieveSizeComp {
658         larger,
659         smaller
660 } eSieveSizeComp;
661
662 typedef enum __eSieveFinal {
663         econtinue,
664         estop
665 } eSieveFinal;
666
667
668 typedef struct __SieveRule {
669         int active;
670         int sizeval;
671         eSieveHfield hfield;
672         eSieveCompare compare;
673         StrBuf *htext;
674         eSieveSizeComp sizecomp;
675         eSieveAction Action;
676         StrBuf *fileinto;
677         StrBuf *redirect;
678         StrBuf *automsg;
679         eSieveFinal final;
680 }SieveRule;
681
682
683
684 int ConditionalSieveRule_hfield(StrBuf *Target, WCTemplputParams *TP)
685 {
686         SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
687         
688         return GetTemplateTokenNumber(Target, 
689                                       TP, 
690                                       3, 
691                                       from)
692                 ==
693                 Rule->hfield;
694 }
695 int ConditionalSieveRule_compare(StrBuf *Target, WCTemplputParams *TP)
696 {
697         SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
698         return GetTemplateTokenNumber(Target, 
699                                       TP, 
700                                       3, 
701                                       contains)
702                 ==
703                 Rule->compare;
704 }
705 int ConditionalSieveRule_action(StrBuf *Target, WCTemplputParams *TP)
706 {
707         SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
708         return GetTemplateTokenNumber(Target, 
709                                       TP, 
710                                       3, 
711                                       keep)
712                 ==
713                 Rule->Action; 
714 }
715 int ConditionalSieveRule_sizecomp(StrBuf *Target, WCTemplputParams *TP)
716 {
717         SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
718         return GetTemplateTokenNumber(Target, 
719                                       TP, 
720                                       3, 
721                                       larger)
722                 ==
723                 Rule->sizecomp;
724 }
725 int ConditionalSieveRule_final(StrBuf *Target, WCTemplputParams *TP)
726 {
727         SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
728         return GetTemplateTokenNumber(Target, 
729                                       TP, 
730                                       3, 
731                                       econtinue)
732                 ==
733                 Rule->final;
734 }
735 int ConditionalSieveRule_ThisRoom(StrBuf *Target, WCTemplputParams *TP)
736 {
737         SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
738         return GetTemplateTokenNumber(Target, 
739                                       TP, 
740                                       3, 
741                                       econtinue)
742                 ==
743                 Rule->final;
744 }
745 int ConditionalSieveRule_Active(StrBuf *Target, WCTemplputParams *TP)
746 {
747         SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
748         return Rule->active;
749 }
750 void tmplput_SieveRule_htext(StrBuf *Target, WCTemplputParams *TP) 
751 {
752         SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
753         StrBufAppendTemplate(Target, TP, Rule->htext, 0);
754 }
755 void tmplput_SieveRule_fileinto(StrBuf *Target, WCTemplputParams *TP) 
756 {
757         SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
758         StrBufAppendTemplate(Target, TP, Rule->fileinto, 0);
759 }
760 void tmplput_SieveRule_redirect(StrBuf *Target, WCTemplputParams *TP) 
761 {
762         SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
763         StrBufAppendTemplate(Target, TP, Rule->redirect, 0);
764 }
765 void tmplput_SieveRule_automsg(StrBuf *Target, WCTemplputParams *TP) 
766 {
767         SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
768         StrBufAppendTemplate(Target, TP, Rule->automsg, 0);
769 }
770 void tmplput_SieveRule_sizeval(StrBuf *Target, WCTemplputParams *TP) 
771 {
772         SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
773         StrBufAppendPrintf(Target, "%d", Rule->sizeval);
774 }
775
776 void tmplput_SieveRule_lookup_FileIntoRoom(StrBuf *Target, WCTemplputParams *TP) 
777 {
778         void *vRoom;
779         SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
780         wcsession *WCC = WC;
781         HashList *Rooms = GetRoomListHashLKRA(Target, TP);
782
783         GetHash(Rooms, SKEY(Rule->fileinto), &vRoom);
784         WCC->ThisRoom = (folder*) vRoom;
785 }
786
787 void FreeSieveRule(void *vRule)
788 {
789         SieveRule *Rule = (SieveRule*) vRule;
790
791         FreeStrBuf(&Rule->htext);
792         FreeStrBuf(&Rule->fileinto);
793         FreeStrBuf(&Rule->redirect);
794         FreeStrBuf(&Rule->automsg);
795         
796         free(Rule);
797 }
798
799 #define WC_RULE_HEADER "# WEBCIT_RULE|"
800 HashList *GetSieveRules(StrBuf *Target, WCTemplputParams *TP)
801 {
802         StrBuf *Line = NULL;
803         StrBuf *EncodedRule = NULL;
804         int n = 0;
805         const char *pch = NULL;
806         HashList *SieveRules = NULL;
807         int Done = 0;
808         SieveRule *Rule = NULL;
809
810         SieveRules = NewHash(1, Flathash);
811         serv_printf("MSIV getscript|"RULES_SCRIPT);
812         Line = NewStrBuf();
813         EncodedRule = NewStrBuf();
814         StrBuf_ServGetln(Line);
815         if (GetServerStatus(Line, NULL) == 1) 
816         {
817                 while(!Done && (StrBuf_ServGetln(Line) >= 0) )
818                         if ( (StrLength(Line)==3) && 
819                              !strcmp(ChrPtr(Line), "000")) 
820                         {
821                                 Done = 1;
822                         }
823                         else
824                         {
825                                 pch = NULL;
826                                 /* We just care for our encoded header and skip everything else */
827                                 if ((StrLength(Line) > sizeof(WC_RULE_HEADER) - 1) &&
828                                     (!strncasecmp(ChrPtr(Line), HKEY(WC_RULE_HEADER))))
829                                 {
830                                         StrBufSkip_NTokenS(Line, &pch, '|', 1);
831                                         n = StrBufExtractNext_int(Line, &pch, '|'); 
832                                         StrBufExtract_NextToken(EncodedRule, Line, &pch, '|');
833                                         StrBufDecodeBase64(EncodedRule);
834
835                                         Rule = (SieveRule*) malloc(sizeof(SieveRule));
836
837                                         Rule->htext = NewStrBufPlain (NULL, StrLength(EncodedRule));
838
839                                         Rule->fileinto = NewStrBufPlain (NULL, StrLength(EncodedRule));
840                                         Rule->redirect = NewStrBufPlain (NULL, StrLength(EncodedRule));
841                                         Rule->automsg = NewStrBufPlain (NULL, StrLength(EncodedRule));
842
843                                         /* Grab our existing values to populate */
844                                         pch = NULL;
845                                         Rule->active = StrBufExtractNext_int(EncodedRule, &pch, '|');
846                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
847                                         
848                                         Rule->hfield = (eSieveHfield) GetTokenDefine(SKEY(Line), tocc);
849                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
850                                         Rule->compare = (eSieveCompare) GetTokenDefine(SKEY(Line), contains);
851                                         StrBufExtract_NextToken(Rule->htext, EncodedRule, &pch, '|');
852                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
853                                         Rule->sizecomp = (eSieveSizeComp) GetTokenDefine(SKEY(Line), larger);
854                                         Rule->sizeval = StrBufExtractNext_int(EncodedRule, &pch, '|');
855                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
856                                         Rule->Action = (eSieveAction) GetTokenDefine(SKEY(Line), keep);
857                                         StrBufExtract_NextToken(Rule->fileinto, EncodedRule, &pch, '|');
858                                         StrBufExtract_NextToken(Rule->redirect, EncodedRule, &pch, '|');
859                                         StrBufExtract_NextToken(Rule->automsg, EncodedRule, &pch, '|');
860                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
861                                         Rule->final = (eSieveFinal) GetTokenDefine(SKEY(Line), econtinue);
862                                         Put(SieveRules, IKEY(n), Rule, FreeSieveRule);
863                                         n++;
864                                 }
865                         }
866         }
867
868         while (n < MAX_RULES) {
869                 Rule = (SieveRule*) malloc(sizeof(SieveRule));
870                 memset(Rule, 0, sizeof(SieveRule));
871                 Put(SieveRules, IKEY(n), Rule, FreeSieveRule);
872             
873                 n++;
874         }
875
876
877         FreeStrBuf(&EncodedRule);
878         FreeStrBuf(&Line);
879         return SieveRules;
880 }
881
882 void
883 SessionDetachModule_SIEVE
884 (wcsession *sess)
885 {
886         DeleteHash(&sess->KnownSieveScripts);
887 }
888
889 void 
890 InitModule_SIEVE
891 (void)
892 {
893         REGISTERTokenParamDefine(from);         
894         REGISTERTokenParamDefine(tocc);         
895         REGISTERTokenParamDefine(subject);      
896         REGISTERTokenParamDefine(replyto);      
897         REGISTERTokenParamDefine(sender);       
898         REGISTERTokenParamDefine(resentfrom);   
899         REGISTERTokenParamDefine(resentto);     
900         REGISTERTokenParamDefine(envfrom);      
901         REGISTERTokenParamDefine(envto);        
902         REGISTERTokenParamDefine(xmailer);      
903         REGISTERTokenParamDefine(xspamflag);    
904         REGISTERTokenParamDefine(xspamstatus);  
905         REGISTERTokenParamDefine(listid);       
906         REGISTERTokenParamDefine(size);         
907         REGISTERTokenParamDefine(all);
908
909         REGISTERTokenParamDefine(contains);
910         REGISTERTokenParamDefine(notcontains);
911         REGISTERTokenParamDefine(is);
912         REGISTERTokenParamDefine(isnot);
913         REGISTERTokenParamDefine(matches);
914         REGISTERTokenParamDefine(notmatches);
915
916         REGISTERTokenParamDefine(keep);
917         REGISTERTokenParamDefine(discard);
918         REGISTERTokenParamDefine(reject);
919         REGISTERTokenParamDefine(fileinto);
920         REGISTERTokenParamDefine(redirect);
921         REGISTERTokenParamDefine(vacation);
922
923         REGISTERTokenParamDefine(larger);
924         REGISTERTokenParamDefine(smaller);
925
926         /* these are c-keyworads, so do it by hand. */
927         RegisterTokenParamDefine(HKEY("continue"), econtinue);
928         RegisterTokenParamDefine(HKEY("stop"), estop);
929
930         RegisterIterator("SIEVE:SCRIPTS", 0, NULL, GetSieveScriptListing, NULL, NULL, CTX_SIEVELIST, CTX_NONE, IT_NOFLAG);
931
932         RegisterConditional(HKEY("COND:SIEVE:SCRIPT:ACTIVE"), 0, ConditionalSieveScriptIsActive, CTX_SIEVELIST);
933         RegisterConditional(HKEY("COND:SIEVE:SCRIPT:ISRULES"), 0, ConditionalSieveScriptIsRulesScript, CTX_SIEVELIST);
934         RegisterNamespace("SIEVE:SCRIPT:NAME", 0, 1, tmplput_SieveScriptName, NULL, CTX_SIEVELIST);
935         RegisterNamespace("SIEVE:SCRIPT:CONTENT", 0, 1, tmplput_SieveScriptContent, NULL, CTX_SIEVELIST);
936
937  
938         RegisterIterator("SIEVE:RULES", 0, NULL, GetSieveRules, NULL, DeleteHash, CTX_SIEVESCRIPT, CTX_NONE, IT_NOFLAG);
939
940         RegisterConditional(HKEY("COND:SIEVE:ACTIVE"), 1, ConditionalSieveRule_Active, CTX_SIEVESCRIPT);
941         RegisterConditional(HKEY("COND:SIEVE:HFIELD"), 1, ConditionalSieveRule_hfield, CTX_SIEVESCRIPT);
942         RegisterConditional(HKEY("COND:SIEVE:COMPARE"), 1, ConditionalSieveRule_compare, CTX_SIEVESCRIPT);
943         RegisterConditional(HKEY("COND:SIEVE:ACTION"), 1, ConditionalSieveRule_action, CTX_SIEVESCRIPT);
944         RegisterConditional(HKEY("COND:SIEVE:SIZECOMP"), 1, ConditionalSieveRule_sizecomp, CTX_SIEVESCRIPT);
945         RegisterConditional(HKEY("COND:SIEVE:FINAL"), 1, ConditionalSieveRule_final, CTX_SIEVESCRIPT);
946         RegisterConditional(HKEY("COND:SIEVE:THISROOM"), 1, ConditionalSieveRule_ThisRoom, CTX_SIEVESCRIPT);
947
948         RegisterNamespace("SIEVE:SCRIPT:HTEXT", 0, 1, tmplput_SieveRule_htext, NULL, CTX_SIEVESCRIPT);
949         RegisterNamespace("SIEVE:SCRIPT:SIZE", 0, 1, tmplput_SieveRule_sizeval, NULL, CTX_SIEVESCRIPT);
950         RegisterNamespace("SIEVE:SCRIPT:FILEINTO", 0, 1, tmplput_SieveRule_fileinto, NULL, CTX_SIEVESCRIPT);
951         RegisterNamespace("SIEVE:SCRIPT:REDIRECT", 0, 1, tmplput_SieveRule_redirect, NULL, CTX_SIEVESCRIPT);
952         RegisterNamespace("SIEVE:SCRIPT:AUTOMSG", 0, 1, tmplput_SieveRule_automsg, NULL, CTX_SIEVESCRIPT);
953
954         /* fetch our room into WCC->ThisRoom, to evaluate while iterating over rooms with COND:THIS:THAT:ROOM */
955         RegisterNamespace("SIEVE:SCRIPT:LOOKUP_FILEINTO", 0, 1, tmplput_SieveRule_lookup_FileIntoRoom, NULL, CTX_SIEVESCRIPT);
956         WebcitAddUrlHandler(HKEY("save_sieve"), "", 0, save_sieve, 0);
957         WebcitAddUrlHandler(HKEY("create_script"), "", 0, create_script, 0);
958         WebcitAddUrlHandler(HKEY("delete_script"), "", 0, delete_script, 0);
959         WebcitAddUrlHandler(HKEY("display_sieve_add_or_delete"), "", 0, display_sieve_add_or_delete, 0);
960 }