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