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