Merge branch 'master' of ssh://git.citadel.org/appl/gitroot/citadel
[citadel.git] / webcit / sieve.c
1 /*
2  * Copyright (c) 1996-2011 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 as
6  * published by the Free Software Foundation; either version 3 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */
18
19 #include "webcit.h"
20
21 #define MAX_SCRIPTS     100
22 #define MAX_RULES       50
23 #define RULES_SCRIPT    "__WebCit_Generated_Script__"
24 #define FOO 1
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 void display_add_remove_scripts(char *message);
44 void display_rules_editor_inner_div(void);
45
46
47
48
49
50
51 /*
52  * Output parseable Sieve script code based on rules input
53  */
54 void output_sieve_rule(char *hfield, char *compare, char *htext, char *sizecomp, int sizeval,
55                         char *action, char *fileinto, char *redirect, char *automsg, char *final,
56                         char *my_addresses)
57 {
58         char *comp1 = "";
59         char *comp2 = "";
60
61         osr_sanitize(htext);
62         osr_sanitize(fileinto);
63         osr_sanitize(redirect);
64         osr_sanitize(automsg);
65
66         /* Prepare negation and match operators that will be used iff we apply a conditional */
67
68         if (!strcasecmp(compare, "contains")) {
69                 comp1 = "";
70                 comp2 = ":contains";
71         }
72         else if (!strcasecmp(compare, "notcontains")) {
73                 comp1 = "not";
74                 comp2 = ":contains";
75         }
76         else if (!strcasecmp(compare, "is")) {
77                 comp1 = "";
78                 comp2 = ":is";
79         }
80         else if (!strcasecmp(compare, "isnot")) {
81                 comp1 = "not";
82                 comp2 = ":is";
83         }
84         else if (!strcasecmp(compare, "matches")) {
85                 comp1 = "";
86                 comp2 = ":matches";
87         }
88         else if (!strcasecmp(compare, "notmatches")) {
89                 comp1 = "not";
90                 comp2 = ":matches";
91         }
92
93         /* Now do the conditional */
94
95         if (!strcasecmp(hfield, "from")) {
96                 serv_printf("if%s header %s \"From\" \"%s\"",
97                         comp1, comp2,
98                         htext
99                 );
100         }
101
102         else if (!strcasecmp(hfield, "tocc")) {
103                 serv_printf("if%s header %s [\"To\", \"Cc\"] \"%s\"",
104                         comp1, comp2,
105                         htext
106                 );
107         }
108
109         else if (!strcasecmp(hfield, "subject")) {
110                 serv_printf("if%s header %s \"Subject\" \"%s\"",
111                         comp1, comp2,
112                         htext
113                 );
114         }
115
116         else if (!strcasecmp(hfield, "replyto")) {
117                 serv_printf("if%s header %s \"Reply-to\" \"%s\"",
118                         comp1, comp2,
119                         htext
120                 );
121         }
122
123         else if (!strcasecmp(hfield, "sender")) {
124                 serv_printf("if%s header %s \"Sender\" \"%s\"",
125                         comp1, comp2,
126                         htext
127                 );
128         }
129
130         else if (!strcasecmp(hfield, "resentfrom")) {
131                 serv_printf("if%s header %s \"Resent-from\" \"%s\"",
132                         comp1, comp2,
133                         htext
134                 );
135         }
136
137         else if (!strcasecmp(hfield, "resentto")) {
138                 serv_printf("if%s header %s \"Resent-to\" \"%s\"",
139                         comp1, comp2,
140                         htext
141                 );
142         }
143
144         else if (!strcasecmp(hfield, "xmailer")) {
145                 serv_printf("if%s header %s \"X-Mailer\" \"%s\"",
146                         comp1, comp2,
147                         htext
148                 );
149         }
150
151         else if (!strcasecmp(hfield, "xspamflag")) {
152                 serv_printf("if%s header %s \"X-Spam-Flag\" \"%s\"",
153                         comp1, comp2,
154                         htext
155                 );
156         }
157
158         else if (!strcasecmp(hfield, "xspamstatus")) {
159                 serv_printf("if%s header %s \"X-Spam-Status\" \"%s\"",
160                         comp1, comp2,
161                         htext
162                 );
163         }
164
165         else if (!strcasecmp(hfield, "listid")) {
166                 serv_printf("if%s header %s \"List-ID\" \"%s\"",
167                         comp1, comp2,
168                         htext
169                 );
170         }
171
172         else if (!strcasecmp(hfield, "envfrom")) {
173                 serv_printf("if%s envelope %s \"From\" \"%s\"",
174                         comp1, comp2,
175                         htext
176                 );
177         }
178
179         else if (!strcasecmp(hfield, "envto")) {
180                 serv_printf("if%s envelope %s \"To\" \"%s\"",
181                         comp1, comp2,
182                         htext
183                 );
184         }
185
186         else if (!strcasecmp(hfield, "size")) {
187                 if (!strcasecmp(sizecomp, "larger")) {
188                         serv_printf("if size :over %d", sizeval);
189                 }
190                 else if (!strcasecmp(sizecomp, "smaller")) {
191                         serv_printf("if size :under %d", sizeval);
192                 }
193                 else {  /* failsafe - should never get here, but just in case... */
194                         serv_printf("if size :over 1");
195                 }
196         }
197
198         /* Open braces if we're in a conditional loop */
199
200         if (strcasecmp(hfield, "all")) {
201                 serv_printf("{");
202         }
203
204
205         /* Do action */
206
207         if (!strcasecmp(action, "keep")) {
208                 serv_printf("keep;");
209         }
210
211         else if (!strcasecmp(action, "discard")) {
212                 serv_printf("discard;");
213         }
214
215         else if (!strcasecmp(action, "reject")) {
216                 serv_printf("reject \"%s\";", automsg);
217         }
218
219         else if (!strcasecmp(action, "fileinto")) {
220                 serv_printf("fileinto \"%s\";", fileinto);
221         }
222
223         else if (!strcasecmp(action, "redirect")) {
224                 serv_printf("redirect \"%s\";", redirect);
225         }
226
227         else if (!strcasecmp(action, "vacation")) {
228                 serv_printf("vacation :addresses [%s]\n\"%s\";", my_addresses, automsg);
229         }
230
231
232         /* Do 'final' action */
233
234         if (!strcasecmp(final, "stop")) {
235                 serv_printf("stop;");
236         }
237
238
239         /* Close the braces if we're in a conditional loop */
240
241         if (strcasecmp(hfield, "all")) {
242                 serv_printf("}");
243         }
244
245
246         /* End of rule. */
247 }
248
249
250
251 /*
252  * Translate the fields from the rule editor into something we can save...
253  */
254 void parse_fields_from_rule_editor(void) {
255
256         int active;
257         char hfield[256];
258         char compare[32];
259         char htext[256];
260         char sizecomp[32];
261         int sizeval;
262         char action[32];
263         char fileinto[128];
264         char redirect[256];
265         char automsg[1024];
266         char final[32];
267         int i;
268         char buf[256];
269         char fname[256];
270         char rule[2048];
271         char encoded_rule[4096];
272         char my_addresses[4096];
273         
274         /* Enumerate my email addresses in case they are needed for a vacation rule */
275         my_addresses[0] = 0;
276         serv_puts("GVEA");
277         serv_getln(buf, sizeof buf);
278         if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
279                 if (!IsEmptyStr(my_addresses)) {
280                         strcat(my_addresses, ",\n");
281                 }
282                 strcat(my_addresses, "\"");
283                 strcat(my_addresses, buf);
284                 strcat(my_addresses, "\"");
285         }
286
287         /* Now generate the script and write it to the Citadel server */
288         serv_printf("MSIV putscript|%s|", RULES_SCRIPT);
289         serv_getln(buf, sizeof buf);
290         if (buf[0] != '4') {
291                 return;
292         }
293
294         serv_puts("# THIS SCRIPT WAS AUTOMATICALLY GENERATED BY WEBCIT.");
295         serv_puts("# ");
296         serv_puts("# Do not attempt to manually edit it.  If you do so,");
297         serv_puts("# your changes will be overwritten the next time WebCit");
298         serv_puts("# saves its mail filtering rule set.  If you really want");
299         serv_puts("# to use these rules as the basis for another script,");
300         serv_puts("# copy them to another script and save that instead.");
301         serv_puts("");
302         serv_puts("require \"fileinto\";");
303         serv_puts("require \"reject\";");
304         serv_puts("require \"vacation\";");
305         serv_puts("require \"envelope\";");
306         serv_puts("");
307
308         for (i=0; i<MAX_RULES; ++i) {
309                 
310                 strcpy(rule, "");
311
312                 sprintf(fname, "active%d", i);
313                 active = !strcasecmp(BSTR(fname), "on") ;
314
315                 if (active) {
316
317                         sprintf(fname, "hfield%d", i);
318                         safestrncpy(hfield, BSTR(fname), sizeof hfield);
319         
320                         sprintf(fname, "compare%d", i);
321                         safestrncpy(compare, BSTR(fname), sizeof compare);
322         
323                         sprintf(fname, "htext%d", i);
324                         safestrncpy(htext, BSTR(fname), sizeof htext);
325         
326                         sprintf(fname, "sizecomp%d", i);
327                         safestrncpy(sizecomp, BSTR(fname), sizeof sizecomp);
328         
329                         sprintf(fname, "sizeval%d", i);
330                         sizeval = IBSTR(fname);
331         
332                         sprintf(fname, "action%d", i);
333                         safestrncpy(action, BSTR(fname), sizeof action);
334         
335                         sprintf(fname, "fileinto%d", i);
336                         safestrncpy(fileinto, BSTR(fname), sizeof fileinto);
337         
338                         sprintf(fname, "redirect%d", i);
339                         safestrncpy(redirect, BSTR(fname), sizeof redirect);
340         
341                         sprintf(fname, "automsg%d", i);
342                         safestrncpy(automsg, BSTR(fname), sizeof automsg);
343         
344                         sprintf(fname, "final%d", i);
345                         safestrncpy(final, BSTR(fname), sizeof final);
346         
347                         snprintf(rule, sizeof rule, "%d|%s|%s|%s|%s|%d|%s|%s|%s|%s|%s",
348                                 active, hfield, compare, htext, sizecomp, sizeval, action, fileinto,
349                                 redirect, automsg, final
350                         );
351         
352                         CtdlEncodeBase64(encoded_rule, rule, strlen(rule)+1, 0);
353                         serv_printf("# WEBCIT_RULE|%d|%s|", i, encoded_rule);
354                         output_sieve_rule(hfield, compare, htext, sizecomp, sizeval,
355                                         action, fileinto, redirect, automsg, final, my_addresses);
356                         serv_puts("");
357                 }
358
359
360         }
361
362         serv_puts("stop;");
363         serv_puts("000");
364 }
365
366
367
368 /*
369  * save sieve config
370  */
371 void save_sieve(void) {
372         int bigaction;
373         char script_names[MAX_SCRIPTS][64];
374         int num_scripts = 0;
375         int active_script = (-1);
376         int i;
377         char this_name[64];
378         char buf[256];
379
380         if (!havebstr("save_button")) {
381                 strcpy(WC->ImportantMessage,
382                         _("Cancelled.  Changes were not saved."));
383                 display_main_menu();
384                 return;
385         }
386
387         parse_fields_from_rule_editor();
388
389         serv_puts("MSIV listscripts");
390         serv_getln(buf, sizeof(buf));
391         if (buf[0] == '1') while (serv_getln(buf, sizeof(buf)), strcmp(buf, "000")) {
392                 if (num_scripts < MAX_SCRIPTS) {
393                         extract_token(script_names[num_scripts], buf, 0, '|', 64);
394                         if (extract_int(buf, 1) > 0) {
395                                 active_script = num_scripts;
396                         }
397                         ++num_scripts;
398                 }
399         }
400
401         bigaction = ibstr("bigaction");
402
403         if (bigaction == 0) {
404                 serv_puts("MSIV setactive||");
405                 serv_getln(buf, sizeof buf);
406         }
407
408         else if (bigaction == 1) {
409                 serv_printf("MSIV setactive|%s|", RULES_SCRIPT);
410                 serv_getln(buf, sizeof buf);
411         }
412
413         else if (bigaction == 2) {
414                 serv_printf("MSIV setactive|%s|", bstr("active_script"));
415                 serv_getln(buf, sizeof buf);
416         }
417
418         if (num_scripts > 0) {
419                 for (i=0; i<num_scripts; ++i) {
420                         /*
421                          * We only want to save the scripts from the "manually edited scripts"
422                          * screen.  The script that WebCit generates from its ruleset will be
423                          * auto-generated by parse_fields_from_rule_editor() and saved there.
424                          */
425                         if (strcasecmp(script_names[i], RULES_SCRIPT)) {
426                                 serv_printf("MSIV putscript|%s|", script_names[i]);
427                                 serv_getln(buf, sizeof buf);
428                                 if (buf[0] == '4') {
429                                         snprintf(this_name, sizeof this_name, "text_%s", script_names[i]);
430                                         striplt((char *)BSTR(this_name)); /* TODO: get rid of typecast*/
431                                         serv_write(BSTR(this_name), strlen(BSTR(this_name)));
432                                         serv_puts("\n000");
433                                 }
434                         }
435                 }
436         }
437
438         strcpy(WC->ImportantMessage, _("Your changes have been saved."));
439         display_main_menu();
440         return;
441 }
442
443 /*
444  * create a new script
445  * take the web environment script name and create it on the citadel server
446  */
447 void create_script(void) {
448         char buf[256];
449
450         serv_printf("MSIV getscript|%s", bstr("script_name"));
451         serv_getln(buf, sizeof buf);
452         if (buf[0] == '1') {
453                 while (serv_getln(buf, sizeof(buf)), strcmp(buf, "000")) {
454                         /* flush */
455                 }
456 #if FOO
457                 display_add_remove_scripts(_("A script by that name already exists."));
458 #endif
459                 return;
460         }
461         
462         serv_printf("MSIV putscript|%s", bstr("script_name"));
463         serv_getln(buf, sizeof buf);
464         if (buf[0] == '4') {
465                 serv_puts("keep;");
466                 serv_puts("000");
467 #if FOO
468                 display_add_remove_scripts(_("A new script has been created.  Return to the script editing screen to edit and activate it."));
469 #endif
470                 return;
471         }
472
473 #if FOO
474         display_add_remove_scripts(&buf[4]);
475 #endif
476 }
477
478
479
480
481 /*
482  * delete a script
483  */
484 void delete_script(void) {
485         char buf[256];
486
487         serv_printf("MSIV deletescript|%s", bstr("script_name"));
488         serv_getln(buf, sizeof buf);
489 #if FOO
490         display_add_remove_scripts(&buf[4]);
491 #endif
492 }
493                 
494
495
496 /*
497  * dummy panel indicating to the user that the server doesn't support Sieve
498  */
499 void display_no_sieve(void) {
500
501         output_headers(1, 1, 2, 0, 0, 0);
502         do_template("sieve_none");
503         wDumpContent(1);
504 }
505
506 #if FOO
507 /*
508  * view/edit sieve config
509  */
510 void display_sieve(void)
511 {
512         char script_names[MAX_SCRIPTS][64];
513         int num_scripts = 0;
514         int active_script = (-1);
515         char buf[SIZ];          /* Don't make this buffer smaller or it will restrict line length */
516         int i;
517         int rules_script_is_active = 0;
518
519         if (!WC->serv_info->serv_supports_sieve) {
520                 display_no_sieve();
521                 return;
522         }
523
524         memset(script_names, 0, sizeof script_names);
525
526         serv_puts("MSIV listscripts");
527         serv_getln(buf, sizeof(buf));
528         if (buf[0] == '1') while (serv_getln(buf, sizeof(buf)), strcmp(buf, "000")) {
529                 if (num_scripts < MAX_SCRIPTS) {
530                         extract_token(script_names[num_scripts], buf, 0, '|', 64);
531                         if (extract_int(buf, 1) > 0) {
532                                 active_script = num_scripts;
533                                 if (!strcasecmp(script_names[num_scripts], RULES_SCRIPT)) {
534                                         rules_script_is_active = 1;
535                                 }
536                         }
537                         ++num_scripts;
538                 }
539         }
540
541         output_headers(1, 1, 2, 0, 0, 0);
542
543         wc_printf("<script type=\"text/javascript\">\n"
544                   "\n"
545                   "var previously_active_script;\n"
546                   "\n"
547                   "function ToggleSievePanels() {\n"
548                   " d = ($('sieveform').bigaction.options[$('sieveform').bigaction.selectedIndex].value);\n"
549                   " for (i=0; i<3; ++i) {\n"
550                   "  if (i == d) {\n"
551                   "   $('sievediv' + i).style.display = 'block';\n"
552                   "  }\n"
553                   "  else {\n"
554                   "   $('sievediv' + i).style.display = 'none';\n"
555                   "  }\n"
556                   " }\n"
557                   "}\n"
558                   "\n"
559                   "function ToggleScriptPanels() {\n"
560                   " d = ($('sieveform').active_script.options[$('sieveform').active_script.selectedIndex].value);\n"
561                   " if ($('script_' + previously_active_script)) {\n"
562                   "  $('script_' + previously_active_script).style.display = 'none';\n"
563                   " }\n"
564                   " $('script_' + d).style.display = 'block';\n"
565                   " previously_active_script = d;\n"
566                   "}\n"
567                   "\n"
568                   "</script>\n"
569 );
570
571         wc_printf("<div id=\"banner\">\n");
572         wc_printf("<img src=\"static/webcit_icons/essen/32x32/config.png\">");
573         wc_printf("<h1>");
574         wc_printf(_("View/edit server-side mail filters"));
575         wc_printf("</h1>\n");
576         wc_printf("</div>\n");
577
578         wc_printf("<div id=\"content\" class=\"service\">\n");
579
580         wc_printf("<table class=\"sieve_background\">\n"
581                 "<tr>\n<td valign=top>\n");
582
583
584         wc_printf("<form id=\"sieveform\" method=\"post\" action=\"save_sieve\">\n");
585         wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
586
587         wc_printf(_("When new mail arrives: "));
588         wc_printf("<select name=\"bigaction\" size=1 onChange=\"ToggleSievePanels();\">\n");
589
590         wc_printf("<option %s value=\"0\">", ((active_script < 0) ? "selected" : ""));
591         wc_printf(_("Leave it in my inbox without filtering"));
592         wc_printf("</option>\n");
593
594         wc_printf("<option %s value=\"1\">", ((rules_script_is_active) ? "selected" : ""));
595         wc_printf(_("Filter it according to rules selected below"));
596         wc_printf("</option>\n");
597
598         wc_printf("<option %s value=\"2\">",
599                         (((active_script >= 0) && (!rules_script_is_active)) ? "selected" : ""));
600         wc_printf(_("Filter it through a manually edited script (advanced users only)"));
601         wc_printf("</option>\n");
602
603         wc_printf("</select>\n\n");
604
605
606
607         /* The "no filtering" div */
608
609         wc_printf("<div id=\"sievediv0\" style=\"display:none\">\n");
610         wc_printf("<div align=\"center\"><br><br>");
611         wc_printf(_("Your incoming mail will not be filtered through any scripts."));
612         wc_printf("<br><br></div>\n");
613         wc_printf("</div>\n");
614
615         /* The "webcit managed scripts" div */
616
617         wc_printf("<div id=\"sievediv1\" style=\"display:none\">\n");
618         display_rules_editor_inner_div();
619         wc_printf("</div>\n");
620
621         /* The "I'm smart and can write my own Sieve scripts" div */
622
623         wc_printf("<div id=\"sievediv2\" style=\"display:none\">\n");
624
625         if (num_scripts > 0) {
626                 wc_printf(_("The currently active script is: "));
627                 wc_printf("<select name=\"active_script\" size=1 onChange=\"ToggleScriptPanels();\">\n");
628                 for (i=0; i<num_scripts; ++i) {
629                         if (strcasecmp(script_names[i], RULES_SCRIPT)) {
630                                 wc_printf("<option %s value=\"%s\">%s</option>\n",
631                                         ((active_script == i) ? "selected" : ""),
632                                         script_names[i],
633                                         script_names[i]
634                                 );
635                         }
636                 }
637                 wc_printf("</select>\n\n");
638         }
639
640         wc_printf("&nbsp;&nbsp;&nbsp;");
641         wc_printf("<a href=\"display_add_remove_scripts\">%s</a>\n", _("Add or delete scripts"));
642
643         wc_printf("<br>\n");
644
645         if (num_scripts > 0) {
646                 for (i=0; i<num_scripts; ++i) {
647                         if (strcasecmp(script_names[i], RULES_SCRIPT)) {
648                                 wc_printf("<div id=\"script_%s\" style=\"display:none\">\n", script_names[i]);
649                                 wc_printf("<textarea name=\"text_%s\" wrap=soft rows=20 cols=80 width=80>\n",
650                                         script_names[i]);
651                                 serv_printf("MSIV getscript|%s", script_names[i]);
652                                 serv_getln(buf, sizeof buf);
653                                 if (buf[0] == '1') while(serv_getln(buf, sizeof (buf)), strcmp(buf, "000")) {
654                                         wc_printf("%s\n", buf);
655                                 }
656                                 wc_printf("</textarea>\n");
657                                 wc_printf("</div>\n");
658                         }
659                 }
660         }
661
662         wc_printf("<script type=\"text/javascript\">    \n"
663                 "ToggleScriptPanels();                  \n"
664                 "</script>                              \n"
665         );
666
667         wc_printf("</div>\n");
668
669
670         /* The rest of this is common for all panels... */
671
672         wc_printf("<div align=\"center\"><br>");
673         wc_printf("<input type=\"submit\" name=\"save_button\" value=\"%s\">", _("Save changes"));
674         wc_printf("&nbsp;");
675         wc_printf("<input type=\"submit\" name=\"cancel_button\" value=\"%s\">\n", _("Cancel"));
676         wc_printf("</div></form>\n");
677
678         wc_printf("</td></tr></table>\n");
679
680         wc_printf("<script type=\"text/javascript\">    \n"
681                 "ToggleSievePanels();                   \n"
682                 "</script>                              \n"
683         );
684
685         wDumpContent(1);
686
687 }
688
689
690
691
692 /*
693  * show a list of available scripts to add/remove them
694  */
695 void display_add_remove_scripts(char *message)
696 {
697         char buf[256];
698         char script_name[256];
699
700         output_headers(1, 1, 2, 0, 0, 0);
701         wc_printf("<div id=\"banner\">\n");
702         wc_printf("<img src=\"static/webcit_icons/essen/32x32/config.png\">");
703         wc_printf(_("Add or delete scripts"));
704         wc_printf("</h1>\n");
705         wc_printf("</div>\n");
706         
707         wc_printf("<div id=\"content\" class=\"service\">\n");
708
709         if (message != NULL) {
710                 wc_printf("%s", message);
711         }
712
713         wc_printf("<table border=0 cellspacing=10><tr valign=top><td>\n");
714
715         do_template("box_begin_1");
716         StrBufAppendBufPlain(WC->WBuf, _("Add a new script"), -1, 0);
717         do_template("box_begin_2");
718
719         wc_printf(_("To create a new script, enter the desired "
720                 "script name in the box below and click 'Create'."));
721         wc_printf("<br><br>");
722
723         wc_printf("<center><form method=\"POST\" action=\"create_script\">\n");
724         wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
725         wc_printf(_("Script name: "));
726         wc_printf("<input type=\"text\" name=\"script_name\"><br>\n"
727                 "<input type=\"submit\" name=\"create_button\" value=\"%s\">"
728                 "</form></center>\n", _("Create"));
729
730         do_template("box_end");
731
732         do_template("box_begin_1");
733         StrBufAppendBufPlain(WC->WBuf, _("Edit scripts"), -1, 0);
734         do_template("box_begin_2");
735         wc_printf("<br><div align=center><a href=\"display_sieve\">%s</a><br><br>\n",
736                 _("Return to the script editing screen")
737         );
738         do_template("box_end");
739
740         wc_printf("</td><td>");
741
742         do_template("box_begin_1");
743         StrBufAppendBufPlain(WC->WBuf, _("Delete scripts"), -1, 0);
744         do_template("box_begin_2");
745
746         wc_printf(_("To delete an existing script, select the script "
747                 "name from the list and click 'Delete'."));
748         wc_printf("<br><br>");
749         
750         wc_printf("<center>"
751                 "<form method=\"POST\" action=\"delete_script\">\n");
752         wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
753         wc_printf("<select name=\"script_name\" size=10 style=\"width:100%%\">\n");
754
755         serv_puts("MSIV listscripts");
756         serv_getln(buf, sizeof buf);
757         if (buf[0] == '1') {
758                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
759                         extract_token(script_name, buf, 0, '|', sizeof script_name);
760                         if ( (extract_int(buf, 1) == 0) && (strcasecmp(script_name, RULES_SCRIPT)) ) {
761                                 wc_printf("<option>");
762                                 escputs(script_name);
763                                 wc_printf("</option>\n");
764                         }
765                 }
766         }
767         wc_printf("</select>\n\n<br>\n");
768
769         wc_printf("<input type=\"submit\" name=\"delete_button\" value=\"%s\" "
770                 "onClick=\"return confirm('%s');\">", _("Delete script"), _("Delete this script?"));
771         wc_printf("</form></center>\n");
772         do_template("box_end");
773
774         wc_printf("</td></tr></table>\n");
775
776         wDumpContent(1);
777 }
778
779
780
781
782
783
784 void display_rules_editor_inner_div(void) {
785         int i, j;
786         char buf[4096];
787         char rules[MAX_RULES][2048];
788
789         struct {
790                 char name[128];
791         } *rooms = NULL;
792         int num_roomnames = 0;
793         int num_roomnames_alloc = 0;
794
795         int active;
796         char hfield[256];
797         char compare[32];
798         char htext[256];
799         char sizecomp[32];
800         int sizeval;
801         char action[32];
802         char fileinto[128];
803         char redirect[256];
804         char automsg[1024];
805         char final[32];
806
807         /* load the rules */
808         memset(rules, 0, sizeof rules);
809         serv_printf("MSIV getscript|%s", RULES_SCRIPT);
810         serv_getln(buf, sizeof buf);
811         if (buf[0] == '1') while(serv_getln(buf, sizeof (buf)), strcmp(buf, "000")) {
812                 if (!strncasecmp(buf, "# WEBCIT_RULE|", 14)) {
813                         j = extract_int(buf, 1);
814                         remove_token(buf, 0, '|');
815                         remove_token(buf, 0, '|');
816                         CtdlDecodeBase64(rules[j], buf, strlen(buf));
817                 }
818         }
819
820         /* load the roomnames */
821         serv_puts("LKRA");
822         serv_getln(buf, sizeof buf);
823         if (buf[0] == '1') {
824                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
825                         ++num_roomnames;
826                         if (num_roomnames > num_roomnames_alloc) {
827                                 num_roomnames_alloc += 250;
828                                 rooms = realloc(rooms, (num_roomnames_alloc * 128));
829                         }
830                         extract_token(rooms[num_roomnames-1].name, buf, 0, '|', 128);
831                 }
832         }
833
834
835 /*
836  * This script should get called by every onChange event...
837  *
838  */
839         wc_printf("<script type=\"text/javascript\">\n"
840                   "\n"
841                   "var highest_active_rule = (-1);\n"
842                   "\n"
843                   "function UpdateRules() {\n");
844 /*
845  * Show only the active rows...
846  */
847         wc_printf("  highest_active_rule = (-1);\n");
848         wc_printf("  for (i=0; i<%d; ++i) {\n", MAX_RULES);
849         wc_printf("   if ($('active'+i).checked) {\n"
850                   "     $('rule' + i).style.display = 'block';\n"
851                   "     highest_active_rule = i;\n"
852                   "   }\n"
853                   "   else {\n"
854                   "     $('rule' + i).style.display = 'none';\n"
855                   "   }\n"
856                   "  }\n");
857 /*
858  * Show only the fields relevant to the rules...
859  */
860         wc_printf("  for (i=0; i<=highest_active_rule; ++i) {\n"
861                   "    d = ($('movedown'+i));\n"
862                   "    if (i < highest_active_rule) {\n"
863                   "      d.style.display = 'block';\n"
864                   "    }\n"
865                   "    else {\n"
866                   "      d.style.display = 'none';\n"
867                   "    }\n"
868                   "    d = ($('hfield'+i).options[$('hfield'+i).selectedIndex].value);\n"
869                   "    if (d == 'all') {\n"
870                   "      $('div_size'+i).style.display = 'none'; \n"
871                   "      $('div_compare'+i).style.display = 'none';\n"
872                   "      $('div_nocompare'+i).style.display = 'block';\n"
873                   "    }\n"
874                   "    else if (d == 'size') {\n"
875                   "      $('div_size'+i).style.display = 'block'; \n"
876                   "      $('div_compare'+i).style.display = 'none';\n"
877                   "      $('div_nocompare'+i).style.display = 'none';\n"
878                   "    }\n"
879                   "    else {\n"
880                   "      $('div_size'+i).style.display = 'none'; \n"
881                   "      $('div_compare'+i).style.display = 'block';\n"
882                   "      $('div_nocompare'+i).style.display = 'none';\n"
883                   "    }\n"
884                   "    d = ($('action'+i).options[$('action'+i).selectedIndex].value);\n"
885                   "    if (d == 'fileinto') {\n"
886                   "      $('div_fileinto'+i).style.display = 'block';\n"
887                   "      $('div_redirect'+i).style.display = 'none';\n"
888                   "      $('div_automsg'+i).style.display = 'none';\n"
889                   "    } else if (d == 'redirect') {\n"
890                   "      $('div_fileinto'+i).style.display = 'none';\n"
891                   "      $('div_redirect'+i).style.display = 'block';\n"
892                   "      $('div_automsg'+i).style.display = 'none';\n"
893                   "    } else if ((d == 'reject') || (d == 'vacation'))  {\n"
894                   "      $('div_fileinto'+i).style.display = 'none';\n"
895                   "      $('div_redirect'+i).style.display = 'none';\n"
896                   "      $('div_automsg'+i).style.display = 'block';\n"
897                   "    } else {\n"
898                   "      $('div_fileinto'+i).style.display = 'none';\n"
899                   "      $('div_redirect'+i).style.display = 'none';\n"
900                   "      $('div_automsg'+i).style.display = 'none';\n"
901                   "    }\n"
902                   "    if (highest_active_rule < %d) {\n", MAX_RULES-1 );
903         wc_printf("      $('div_addrule').style.display = 'block';\n"
904                   "    } else {\n"
905                   "      $('div_addrule').style.display = 'none';\n"
906                   "    }\n"
907                   "  }\n"
908                   "}\n"
909 /*
910  * Add a rule (really, just un-hide it)
911  */
912                   "function AddRule() {\n"
913                   "  highest_active_rule = highest_active_rule + 1;\n"
914                   "  $('active'+highest_active_rule).checked = true;\n"
915                   "  UpdateRules();\n"
916                   "}\n"
917 /*
918  * Swap two rules
919  */
920                   "function SwapRules(ra, rb) {\n"
921                   "\n"
922                   "  var things = new Array();\n"
923                   "  things[0] = 'hfield';\n"
924                   "  things[1] = 'compare';\n"
925                   "  things[2] = 'htext';\n"
926                   "  things[3] = 'action';\n"
927                   "  things[4] = 'fileinto';\n"
928                   "  things[5] = 'redirect';\n"
929                   "  things[6] = 'final';\n"
930                   "  things[7] = 'sizecomp';\n"
931                   "  things[8] = 'sizeval';\n"
932                   "  things[9] = 'automsg';\n"
933                   "\n"
934                   "  for (i=0; i<=9; ++i) {\n"
935                   "    tempval=$(things[i]+ra).value;\n"
936                   "    $(things[i]+ra).value = $(things[i]+rb).value;\n"
937                   "    $(things[i]+rb).value = tempval;\n"
938                   "  }\n"
939                   "}\n"
940 /*
941  * Delete a rule (percolate the deleted rule out to the end, then deactivate it)
942  */
943                   "function DeleteRule(rd) {\n"
944                   "  for (j=rd; j<=highest_active_rule; ++j) {\n"
945                   "    SwapRules(j, (j+1));\n"
946                   "  }\n"
947                   "  $('active'+highest_active_rule).checked = false;\n"
948                   "}\n"
949                   "</script>\n"
950                 );
951
952
953         wc_printf("<br>");
954
955         wc_printf("<table cellpadding=2 width=100%%>");
956
957         for (i=0; i<MAX_RULES; ++i) {
958
959                 /* Grab our existing values to populate */
960                 active = extract_int(rules[i], 0);
961                 extract_token(hfield, rules[i], 1, '|', sizeof hfield);
962                 extract_token(compare, rules[i], 2, '|', sizeof compare);
963                 extract_token(htext, rules[i], 3, '|', sizeof htext);
964                 extract_token(sizecomp, rules[i], 4, '|', sizeof sizecomp);
965                 sizeval = extract_int(rules[i], 5);
966                 extract_token(action, rules[i], 6, '|', sizeof action);
967                 extract_token(fileinto, rules[i], 7, '|', sizeof fileinto);
968                 extract_token(redirect, rules[i], 8, '|', sizeof redirect);
969                 extract_token(automsg, rules[i], 9, '|', sizeof automsg);
970                 extract_token(final, rules[i], 10, '|', sizeof final);
971                 
972                 /* now generate the table row */
973
974                 wc_printf("<tr id=\"rule%d\" class=\"%s\">",
975                         i,
976                         ((i%2) ? "odd" : "even")
977                 );
978
979                 wc_printf("<td width=5%% align=\"center\">\n");
980
981                 wc_printf("<div style=\"display:none\">\n");
982                 wc_printf("<input type=\"checkbox\" name=\"active%d\" id=\"active%d\" %s>\n",
983                         i, i,
984                         (active ? "checked" : "")
985                 );
986                 wc_printf("</div>\n");
987
988                 if (i>0) wc_printf("<a href=\"javascript:SwapRules(%d,%d);UpdateRules();\">"
989                         "<img border=\"0\" src=\"static/webcit_icons/up_pointer.gif\" "
990                         "title=\"%s\"/></a>\n",
991                         i-1, i, _("Move rule up") );
992
993                 wc_printf("<a href=\"javascript:SwapRules(%d,%d);UpdateRules();\">"
994                         "<img id=\"movedown%d\" border=\"0\" src=\"static/webcit_icons/down_pointer.gif\" "
995                         "title=\"%s\"/></a>\n",
996                         i, i+1, i, _("Move rule down") );
997
998                 wc_printf("<a href=\"javascript:DeleteRule(%d);UpdateRules();\">"
999                         "<img id=\"delete%d\" border=\"0\" src=\"static/webcit_icons/delete.gif\" "
1000                         "title=\"%s\"/></a>\n",
1001                         i, i, _("Delete rule") );
1002
1003                 wc_printf("</td>\n\n\n");
1004
1005                 wc_printf("<td width=5%% align=\"center\">\n");
1006                 wc_printf("<font size=+2>%d</font>\n", i+1);
1007                 wc_printf("</td>\n");
1008
1009                 wc_printf("<td width=20%%>%s ", _("If") );
1010
1011                 char *hfield_values[15][2] = {
1012                         {       "from",         _("From")               },
1013                         {       "tocc",         _("To or Cc")           },
1014                         {       "subject",      _("Subject")            },
1015                         {       "replyto",      _("Reply-to")           },
1016                         {       "sender",       _("Sender")             },
1017                         {       "resentfrom",   _("Resent-From")        },
1018                         {       "resentto",     _("Resent-To")          },
1019                         {       "envfrom",      _("Envelope From")      },
1020                         {       "envto",        _("Envelope To")        },
1021                         {       "xmailer",      _("X-Mailer")           },
1022                         {       "xspamflag",    _("X-Spam-Flag")        },
1023                         {       "xspamstatus",  _("X-Spam-Status")      },
1024                         {       "listid",       _("List-ID")            },
1025                         {       "size",         _("Message size")       },
1026                         {       "all",          _("All")                }
1027                 };
1028
1029                 wc_printf("<select id=\"hfield%d\" name=\"hfield%d\" size=1 onChange=\"UpdateRules();\">",
1030                         i, i);
1031                 for (j=0; j<15; ++j) {
1032                         wc_printf("<option %s value=\"%s\">%s</option>\n",
1033                                 ( (!strcasecmp(hfield, hfield_values[j][0])) ? "selected" : ""),
1034                                 hfield_values[j][0],
1035                                 hfield_values[j][1]
1036                         );
1037                 }
1038
1039                 wc_printf("</select>\n\n");
1040                 wc_printf("</td>");
1041
1042                 wc_printf("<td width=20%%>");
1043
1044                 char *compare_values[6][2] = {
1045                         {       "contains",     _("contains")           },
1046                         {       "notcontains",  _("does not contain")   },
1047                         {       "is",           _("is")                 },
1048                         {       "isnot",        _("is not")             },
1049                         {       "matches",      _("matches")            },
1050                         {       "notmatches",   _("does not match")     }
1051                 };
1052
1053                 wc_printf("<div id=\"div_compare%d\">\n", i);
1054                 wc_printf("<select id=\"compare%d\" name=\"compare%d\" size=1 onChange=\"UpdateRules();\">\n",
1055                         i, i);
1056                 for (j=0; j<6; ++j) {
1057                         wc_printf("<option %s value=\"%s\">%s</option>\n",
1058                                 ( (!strcasecmp(compare, compare_values[j][0])) ? "selected" : ""),
1059                                 compare_values[j][0],
1060                                 compare_values[j][1]
1061                         );
1062                 }
1063                 wc_printf("</select>\n\n");
1064
1065                 wc_printf("<input type=\"text\" id=\"htext%d\" name=\"htext%d\" value=\"", i, i);
1066                 escputs(htext);
1067                 wc_printf("\">\n</div>\n");
1068
1069                 wc_printf("<div id=\"div_nocompare%d\">", i);
1070                 wc_printf("%s", _("(All messages)"));
1071                 wc_printf("</div>\n");
1072
1073                 char *sizecomp_values[2][2] = {
1074                         {       "larger",       _("is larger than")     },
1075                         {       "smaller",      _("is smaller than")    }
1076                 };
1077
1078                 wc_printf("<div id=\"div_size%d\">\n", i);
1079                 wc_printf("<select id=\"sizecomp%d\" name=\"sizecomp%d\" size=1 onChange=\"UpdateRules();\">\n",
1080                         i, i);
1081                 for (j=0; j<2; ++j) {
1082                         wc_printf("<option %s value=\"%s\">%s</option>\n",
1083                                 ( (!strcasecmp(sizecomp, sizecomp_values[j][0])) ? "selected" : ""),
1084                                 sizecomp_values[j][0],
1085                                 sizecomp_values[j][1]
1086                         );
1087                 }
1088                 wc_printf("</select>\n\n");
1089
1090                 wc_printf("<input type=\"text\" id=\"sizeval%d\" name=\"sizeval%d\" value=\"%d\">",
1091                         i, i, sizeval);
1092                 wc_printf("bytes");
1093                 wc_printf("</div>");
1094
1095                 wc_printf("</td>");
1096
1097                 char *action_values[6][2] = {
1098                         {       "keep",         _("Keep")               },
1099                         {       "discard",      _("Discard silently")   },
1100                         {       "reject",       _("Reject")             },
1101                         {       "fileinto",     _("Move message to")    },
1102                         {       "redirect",     _("Forward to")         },
1103                         {       "vacation",     _("Vacation")           }
1104                 };
1105
1106                 wc_printf("<td width=20%%>\n");
1107                 wc_printf("<select id=\"action%d\" name=\"action%d\" size=1 onChange=\"UpdateRules();\">\n",
1108                         i, i);
1109                 for (j=0; j<6; ++j) {
1110                         wc_printf("<option %s value=\"%s\">%s</option>\n",
1111                                 ( (!strcasecmp(action, action_values[j][0])) ? "selected" : ""),
1112                                 action_values[j][0],
1113                                 action_values[j][1]
1114                         );
1115                 }
1116                 wc_printf("</select>\n\n");
1117
1118                 wc_printf("<div id=\"div_fileinto%d\">\n", i);
1119                 wc_printf("<select name=\"fileinto%d\" id=\"fileinto%d\">\n", i, i);
1120                 for (j=0; j<num_roomnames; ++j) {
1121                         wc_printf("<option ");
1122                         if (!strcasecmp(rooms[j].name, fileinto)) {
1123                                 wc_printf("selected ");
1124                         }
1125                         wc_printf("value=\"");
1126                         escputs(rooms[j].name);
1127                         wc_printf("\">");
1128                         escputs(rooms[j].name);
1129                         wc_printf("</option>\n");
1130                 }
1131                 wc_printf("</select>\n\n");
1132                 wc_printf("</div>");
1133
1134                 wc_printf("<div id=\"div_redirect%d\">\n", i);
1135                 wc_printf("<input type=\"text\" id=\"redirect%d\" name=\"redirect%d\" value=\"", i, i);
1136                 escputs(redirect);
1137                 wc_printf("\">\n</div>\n");
1138
1139                 wc_printf("<div id=\"div_automsg%d\">\n", i);
1140                 wc_printf(_("Message:"));
1141                 wc_printf("<br>\n");
1142                 wc_printf("<textarea name=\"automsg%d\" id=\"automsg%d\" wrap=soft rows=5>\n", i, i);
1143                 escputs(automsg);
1144                 wc_printf("</textarea>");
1145                 wc_printf("</div>\n");
1146
1147                 wc_printf("</td>\n");
1148
1149                 char *final_values[2][2] = {
1150                         {       "continue",     _("continue processing")        },
1151                         {       "stop",         _("stop")                       }
1152                 };
1153
1154                 wc_printf("<td width=10%% align=\"center\">%s</td>\n", _("and then") );
1155
1156                 wc_printf("<td width=20%%>\n");
1157                 wc_printf("<select name=\"final%d\" id=\"final%d\" size=1 onChange=\"UpdateRules();\">\n",
1158                         i, i);
1159                 for (j=0; j<2; ++j) {
1160                         wc_printf("<option %s value=\"%s\">%s</option>\n",
1161                                 ( (!strcasecmp(final, final_values[j][0])) ? "selected" : ""),
1162                                 final_values[j][0],
1163                                 final_values[j][1]
1164                         );
1165                 }
1166                 wc_printf("</select>\n\n");
1167                 wc_printf("</td>\n");
1168
1169                 wc_printf("</tr>\n");
1170
1171         }
1172
1173         wc_printf("</table>\n");
1174         wc_printf("<div id=\"div_addrule\"><a href=\"javascript:AddRule();\">%s</a><br></div>\n",
1175                 _("Add rule")
1176         );
1177
1178         wc_printf("<script type=\"text/javascript\">\n");
1179         wc_printf("UpdateRules();\n");
1180         wc_printf("</script>\n");
1181
1182         free(rooms);
1183 }
1184 void _display_add_remove_scripts(void) {display_add_remove_scripts(NULL);}
1185 #endif
1186
1187
1188 typedef struct __SieveListing {
1189         int IsActive;
1190         int IsRulesScript;
1191         StrBuf *Name;
1192         StrBuf *Content;
1193 } SieveListing;
1194
1195 int ConditionalSieveScriptIsActive(StrBuf *Target, WCTemplputParams *TP)
1196 {
1197         SieveListing     *SieveList = (SieveListing *)CTX;
1198         return SieveList->IsActive;
1199 }
1200 int ConditionalSieveScriptIsRulesScript(StrBuf *Target, WCTemplputParams *TP)
1201 {
1202         SieveListing     *SieveList = (SieveListing *)CTX;
1203         return SieveList->IsActive;
1204 }
1205 void tmplput_SieveScriptName(StrBuf *Target, WCTemplputParams *TP) 
1206 {
1207         SieveListing     *SieveList = (SieveListing *)CTX;
1208         StrBufAppendTemplate(Target, TP, SieveList->Name, 0);
1209 }
1210 void tmplput_SieveScriptContent(StrBuf *Target, WCTemplputParams *TP) 
1211 {
1212         SieveListing     *SieveList = (SieveListing *)CTX;
1213         StrBufAppendTemplate(Target, TP, SieveList->Content, 0);
1214 }
1215 void FreeSieveListing(void *vSieveListing)
1216 {
1217         SieveListing *List = (SieveListing*) vSieveListing;
1218
1219         FreeStrBuf(&List->Name);
1220         free(List);
1221 }
1222
1223 HashList *GetSieveScriptListing(StrBuf *Target, WCTemplputParams *TP)
1224 {
1225         wcsession *WCC = WC;
1226         StrBuf *Line;
1227         int num_scripts = 0;
1228         int rules_script_active = 0;
1229         int have_rules_script = 0;
1230         const char *pch;
1231         HashPos  *it;
1232         int Done = 0;
1233         SieveListing *Ruleset;
1234
1235         if (WCC->KnownSieveScripts != NULL)
1236                 return WCC->KnownSieveScripts;
1237
1238         serv_puts("MSIV listscripts");
1239         Line = NewStrBuf();
1240         StrBuf_ServGetln(Line);
1241         if (GetServerStatus(Line, NULL) == 1) 
1242         {
1243                 WCC->KnownSieveScripts = NewHash(1, Flathash);
1244
1245                 while(!Done && (StrBuf_ServGetln(Line) >= 0) )
1246                         if ( (StrLength(Line)==3) && 
1247                              !strcmp(ChrPtr(Line), "000")) 
1248                         {
1249                                 Done = 1;
1250                         }
1251                         else
1252                         {
1253                                 pch = NULL;
1254                                 Ruleset = (SieveListing *) malloc(sizeof(SieveListing));
1255                                 Ruleset->Name = NewStrBufPlain(NULL, StrLength(Line));
1256                                 StrBufExtract_NextToken(Ruleset->Name, Line, &pch, '|');
1257                                 Ruleset->IsActive = StrBufExtractNext_int(Line, &pch, '|'); 
1258                                 Ruleset->Content = NULL;
1259
1260                                 if (!strcasecmp(ChrPtr(Ruleset->Name), RULES_SCRIPT))
1261                                 {
1262                                         Ruleset->IsRulesScript = 1;
1263                                         have_rules_script = 1;
1264                                         if (Ruleset->IsActive)
1265                                         {
1266                                                 rules_script_active = 1;
1267                                                 PutBstr(HKEY("__SIEVE:RULESSCRIPT"), NewStrBufPlain(HKEY("1")));
1268                                         }
1269                                 }
1270                                 Put(WCC->KnownSieveScripts, IKEY(num_scripts), Ruleset, FreeSieveListing);
1271
1272                                 ++num_scripts;
1273                         }
1274         }
1275         if ((num_scripts > 0) && (rules_script_active == 0))
1276                 PutBstr(HKEY("__SIEVE:EXTERNAL_SCRIPT"), NewStrBufPlain(HKEY("1")));
1277
1278         if (num_scripts > have_rules_script)
1279         {
1280                 long rc = 0;
1281                 long len;
1282                 const char *Key;
1283                 void *vRuleset;
1284
1285                 /* 
1286                  * ok; we have custom scripts, expose that via bstr, and load the payload.
1287                  */
1288                 PutBstr(HKEY("__SIEVE:HAVE_EXTERNAL_SCRIPT"), NewStrBufPlain(HKEY("1")));
1289
1290                 it = GetNewHashPos(WCC->KnownSieveScripts, 0);
1291                 while (GetNextHashPos(WCC->KnownSieveScripts, it, &len, &Key, &vRuleset) && 
1292                        (vRuleset != NULL))
1293                 {
1294                         Ruleset = (SieveListing *) vRuleset;
1295
1296                         /*
1297                          * its the webcit rule? we don't need to load that here.
1298                          */
1299                         if (Ruleset->IsRulesScript)
1300                                 continue;
1301
1302                         if (!serv_printf("MSIV getscript|%s", ChrPtr(Ruleset->Name)))
1303                                 break;
1304                         StrBuf_ServGetln(Line);
1305                         if (GetServerStatus(Line, NULL) == 1) 
1306                         {
1307                                 Ruleset->Content = NewStrBuf();
1308                                 while(!Done && (rc = StrBuf_ServGetln(Line), rc >= 0) )
1309                                         if ( (StrLength(Line)==3) && 
1310                                              !strcmp(ChrPtr(Line), "000")) 
1311                                         {
1312                                                 Done = 1;
1313                                         }
1314                                         else
1315                                         {
1316                                                 if (StrLength(Ruleset->Content)>0)
1317                                                         StrBufAppendBufPlain(Ruleset->Content, HKEY("\n"), 0);
1318                                                 StrBufAppendBuf(Ruleset->Content, Line, 0);
1319                                         }
1320                                 if (rc < 0) break;
1321                         }
1322                 }
1323         }
1324         FreeStrBuf(&Line);
1325         return WCC->KnownSieveScripts;
1326 }
1327
1328
1329 typedef enum __eSieveHfield 
1330 {
1331         from,           
1332         tocc,           
1333         subject,        
1334         replyto,        
1335         sender, 
1336         resentfrom,     
1337         resentto,       
1338         envfrom,        
1339         envto,  
1340         xmailer,        
1341         xspamflag,      
1342         xspamstatus,    
1343         listid, 
1344         size,           
1345         all
1346 } eSieveHfield;
1347
1348 typedef enum __eSieveCompare {
1349         contains,
1350         notcontains,
1351         is,
1352         isnot,
1353         matches,
1354         notmatches
1355 } eSieveCompare;
1356
1357 typedef enum __eSieveAction {
1358         keep,
1359         discard,
1360         reject,
1361         fileinto,
1362         redirect,
1363         vacation
1364 } eSieveAction;
1365
1366
1367 typedef enum __eSieveSizeComp {
1368         larger,
1369         smaller
1370 } eSieveSizeComp;
1371
1372 typedef enum __eSieveFinal {
1373         econtinue,
1374         estop
1375 } eSieveFinal;
1376
1377
1378 typedef struct __SieveRule {
1379         int active;
1380         int sizeval;
1381         eSieveHfield hfield;
1382         eSieveCompare compare;
1383         StrBuf *htext;
1384         eSieveSizeComp sizecomp;
1385         eSieveAction Action;
1386         StrBuf *fileinto;
1387         StrBuf *redirect;
1388         StrBuf *automsg;
1389         eSieveFinal final;
1390 }SieveRule;
1391
1392
1393
1394 int ConditionalSieveRule_hfield(StrBuf *Target, WCTemplputParams *TP)
1395 {
1396         SieveRule     *Rule = (SieveRule *)CTX;
1397         
1398         return GetTemplateTokenNumber(Target, 
1399                                       TP, 
1400                                       3, 
1401                                       from)
1402                 ==
1403                 Rule->hfield;
1404 }
1405 int ConditionalSieveRule_compare(StrBuf *Target, WCTemplputParams *TP)
1406 {
1407         SieveRule     *Rule = (SieveRule *)CTX;
1408         return GetTemplateTokenNumber(Target, 
1409                                       TP, 
1410                                       3, 
1411                                       contains)
1412                 ==
1413                 Rule->compare;
1414 }
1415 int ConditionalSieveRule_action(StrBuf *Target, WCTemplputParams *TP)
1416 {
1417         SieveRule     *Rule = (SieveRule *)CTX;
1418         return GetTemplateTokenNumber(Target, 
1419                                       TP, 
1420                                       3, 
1421                                       keep)
1422                 ==
1423                 Rule->Action; 
1424 }
1425 int ConditionalSieveRule_sizecomp(StrBuf *Target, WCTemplputParams *TP)
1426 {
1427         SieveRule     *Rule = (SieveRule *)CTX;
1428         return GetTemplateTokenNumber(Target, 
1429                                       TP, 
1430                                       3, 
1431                                       larger)
1432                 ==
1433                 Rule->sizecomp;
1434 }
1435 int ConditionalSieveRule_final(StrBuf *Target, WCTemplputParams *TP)
1436 {
1437         SieveRule     *Rule = (SieveRule *)CTX;
1438         return GetTemplateTokenNumber(Target, 
1439                                       TP, 
1440                                       3, 
1441                                       econtinue)
1442                 ==
1443                 Rule->final;
1444 }
1445 int ConditionalSieveRule_ThisRoom(StrBuf *Target, WCTemplputParams *TP)
1446 {
1447         SieveRule     *Rule = (SieveRule *)CTX;
1448         return GetTemplateTokenNumber(Target, 
1449                                       TP, 
1450                                       3, 
1451                                       econtinue)
1452                 ==
1453                 Rule->final;
1454 }
1455 int ConditionalSieveRule_Active(StrBuf *Target, WCTemplputParams *TP)
1456 {
1457         SieveRule     *Rule = (SieveRule *)CTX;
1458         return Rule->active;
1459 }
1460
1461
1462 /*
1463 void tmplput_SieveRule_hfield(StrBuf *Target, WCTemplputParams *TP) 
1464 {
1465         SieveRule     *Rule = (SieveRule *)CTX;
1466         StrBufAppendTemplate(Target, TP, Rule->hfield, 0);
1467 }
1468 void tmplput_SieveRule_compare(StrBuf *Target, WCTemplputParams *TP) 
1469 {
1470         SieveRule     *Rule = (SieveRule *)CTX;
1471         StrBufAppendTemplate(Target, TP, Rule->compare, 0);
1472 }
1473 */
1474 void tmplput_SieveRule_htext(StrBuf *Target, WCTemplputParams *TP) 
1475 {
1476         SieveRule     *Rule = (SieveRule *)CTX;
1477         StrBufAppendTemplate(Target, TP, Rule->htext, 0);
1478 }
1479 /*
1480 void tmplput_SieveRule_sizecomp(StrBuf *Target, WCTemplputParams *TP) 
1481 {
1482         SieveRule     *Rule = (SieveRule *)CTX;
1483         StrBufAppendTemplate(Target, TP, Rule->sizecomp, 0);
1484 }
1485 void tmplput_SieveRule_action(StrBuf *Target, WCTemplputParams *TP) 
1486 {
1487         SieveRule     *Rule = (SieveRule *)CTX;
1488         StrBufAppendTemplate(Target, TP, Rule->action, 0);
1489         }*/
1490 void tmplput_SieveRule_fileinto(StrBuf *Target, WCTemplputParams *TP) 
1491 {
1492         SieveRule     *Rule = (SieveRule *)CTX;
1493         StrBufAppendTemplate(Target, TP, Rule->fileinto, 0);
1494 }
1495 void tmplput_SieveRule_redirect(StrBuf *Target, WCTemplputParams *TP) 
1496 {
1497         SieveRule     *Rule = (SieveRule *)CTX;
1498         StrBufAppendTemplate(Target, TP, Rule->redirect, 0);
1499 }
1500 void tmplput_SieveRule_automsg(StrBuf *Target, WCTemplputParams *TP) 
1501 {
1502         SieveRule     *Rule = (SieveRule *)CTX;
1503         StrBufAppendTemplate(Target, TP, Rule->automsg, 0);
1504 }
1505 /*
1506 void tmplput_SieveRule_final(StrBuf *Target, WCTemplputParams *TP) 
1507 {
1508         SieveRule     *Rule = (SieveRule *)CTX;
1509         StrBufAppendTemplate(Target, TP, Rule->final, 0);
1510 }
1511 */
1512 void tmplput_SieveRule_sizeval(StrBuf *Target, WCTemplputParams *TP) 
1513 {
1514         SieveRule     *Rule = (SieveRule *)CTX;
1515         StrBufAppendPrintf(Target, "%d", Rule->sizeval);
1516 }
1517
1518 void tmplput_SieveRule_lookup_FileIntoRoom(StrBuf *Target, WCTemplputParams *TP) 
1519 {
1520         void *vRoom;
1521         SieveRule     *Rule = (SieveRule *)CTX;
1522         wcsession *WCC = WC;
1523         HashList *Rooms = GetRoomListHashLKRA(Target, TP);
1524
1525         GetHash(Rooms, SKEY(Rule->fileinto), &vRoom);
1526         WCC->ThisRoom = (folder*) vRoom;
1527 }
1528
1529 void FreeSieveRule(void *vRule)
1530 {
1531         SieveRule *Rule = (SieveRule*) vRule;
1532
1533         FreeStrBuf(&Rule->htext);
1534         FreeStrBuf(&Rule->fileinto);
1535         FreeStrBuf(&Rule->redirect);
1536         FreeStrBuf(&Rule->automsg);
1537         
1538         free(Rule);
1539 }
1540
1541 #define WC_RULE_HEADER "# WEBCIT_RULE|"
1542 HashList *GetSieveRules(StrBuf *Target, WCTemplputParams *TP)
1543 {
1544         StrBuf *Line;
1545         StrBuf *EncodedRule;
1546         int n;
1547         const char *pch;
1548         HashList *SieveRules = NULL;
1549         int Done = 0;
1550         SieveRule *Rule;
1551
1552         serv_printf("MSIV getscript|"RULES_SCRIPT);
1553         Line = NewStrBuf();
1554         EncodedRule = NewStrBuf();
1555         StrBuf_ServGetln(Line);
1556         if (GetServerStatus(Line, NULL) == 1) 
1557         {
1558                 SieveRules = NewHash(1, Flathash);
1559
1560                 while(!Done && (StrBuf_ServGetln(Line) >= 0) )
1561                         if ( (StrLength(Line)==3) && 
1562                              !strcmp(ChrPtr(Line), "000")) 
1563                         {
1564                                 Done = 1;
1565                         }
1566                         else
1567                         {
1568                                 pch = NULL;
1569                                 /* We just care for our encoded header and skip everything else */
1570                                 if ((StrLength(Line) > sizeof(WC_RULE_HEADER) - 1) &&
1571                                     (!strncasecmp(ChrPtr(Line), HKEY(WC_RULE_HEADER))))
1572                                 {
1573                                         StrBufSkip_NTokenS(Line, &pch, '|', 1);
1574                                         n = StrBufExtractNext_int(Line, &pch, '|'); 
1575                                         StrBufExtract_NextToken(EncodedRule, Line, &pch, '|');
1576                                         StrBufDecodeBase64(EncodedRule);
1577
1578                                         Rule = (SieveRule*) malloc(sizeof(SieveRule));
1579
1580                                         Rule->htext = NewStrBufPlain (NULL, StrLength(EncodedRule));
1581
1582                                         Rule->fileinto = NewStrBufPlain (NULL, StrLength(EncodedRule));
1583                                         Rule->redirect = NewStrBufPlain (NULL, StrLength(EncodedRule));
1584                                         Rule->automsg = NewStrBufPlain (NULL, StrLength(EncodedRule));
1585
1586                                         /* Grab our existing values to populate */
1587                                         pch = NULL;
1588                                         Rule->active = StrBufExtractNext_int(EncodedRule, &pch, '|');
1589                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
1590                                         
1591                                         Rule->hfield = (eSieveHfield) GetTokenDefine(SKEY(Line), tocc);
1592                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
1593                                         Rule->compare = (eSieveCompare) GetTokenDefine(SKEY(Line), contains);
1594                                         StrBufExtract_NextToken(Rule->htext, EncodedRule, &pch, '|');
1595                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
1596                                         Rule->sizecomp = (eSieveSizeComp) GetTokenDefine(SKEY(Line), larger);
1597                                         Rule->sizeval = StrBufExtractNext_int(EncodedRule, &pch, '|');
1598                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
1599                                         Rule->Action = (eSieveAction) GetTokenDefine(SKEY(Line), keep);
1600                                         StrBufExtract_NextToken(Rule->fileinto, EncodedRule, &pch, '|');
1601                                         StrBufExtract_NextToken(Rule->redirect, EncodedRule, &pch, '|');
1602                                         StrBufExtract_NextToken(Rule->automsg, EncodedRule, &pch, '|');
1603                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
1604                                         Rule->final = (eSieveFinal) GetTokenDefine(SKEY(Line), econtinue);
1605                                         Put(SieveRules, IKEY(n), Rule, FreeSieveRule);
1606                                         n++;
1607                                 }
1608                         }
1609         }
1610
1611         while (n < MAX_RULES) {
1612                 Rule = (SieveRule*) malloc(sizeof(SieveRule));
1613                 memset(Rule, 0, sizeof(SieveRule));
1614                 Put(SieveRules, IKEY(n), Rule, FreeSieveRule);
1615             
1616                 n++;
1617         }
1618
1619
1620         FreeStrBuf(&EncodedRule);
1621         FreeStrBuf(&Line);
1622         return SieveRules;
1623 }
1624
1625
1626 HashList *GetEmptySieveRule(StrBuf *Target, WCTemplputParams *TP)
1627 {
1628         int n = 0;
1629         HashList *OneSieveRule;
1630         SieveRule *Rule;
1631
1632         OneSieveRule = NewHash(1, Flathash);
1633
1634         /* We just care for our encoded header and skip everything else */
1635         Rule = (SieveRule*) malloc(sizeof(SieveRule));
1636         memset(Rule, 0, sizeof(SieveRule));
1637         Put(OneSieveRule, IKEY(n), Rule, FreeSieveRule);
1638
1639         return OneSieveRule;
1640 }
1641
1642 void
1643 SessionDetachModule_SIEVE
1644 (wcsession *sess)
1645 {
1646         DeleteHash(&sess->KnownSieveScripts);
1647 }
1648
1649 void 
1650 InitModule_SIEVE
1651 (void)
1652 {
1653         REGISTERTokenParamDefine(from);         
1654         REGISTERTokenParamDefine(tocc);         
1655         REGISTERTokenParamDefine(subject);      
1656         REGISTERTokenParamDefine(replyto);      
1657         REGISTERTokenParamDefine(sender);       
1658         REGISTERTokenParamDefine(resentfrom);   
1659         REGISTERTokenParamDefine(resentto);     
1660         REGISTERTokenParamDefine(envfrom);      
1661         REGISTERTokenParamDefine(envto);        
1662         REGISTERTokenParamDefine(xmailer);      
1663         REGISTERTokenParamDefine(xspamflag);    
1664         REGISTERTokenParamDefine(xspamstatus);  
1665         REGISTERTokenParamDefine(listid);       
1666         REGISTERTokenParamDefine(size);         
1667         REGISTERTokenParamDefine(all);
1668
1669         REGISTERTokenParamDefine(contains);
1670         REGISTERTokenParamDefine(notcontains);
1671         REGISTERTokenParamDefine(is);
1672         REGISTERTokenParamDefine(isnot);
1673         REGISTERTokenParamDefine(matches);
1674         REGISTERTokenParamDefine(notmatches);
1675
1676         REGISTERTokenParamDefine(keep);
1677         REGISTERTokenParamDefine(discard);
1678         REGISTERTokenParamDefine(reject);
1679         REGISTERTokenParamDefine(fileinto);
1680         REGISTERTokenParamDefine(redirect);
1681         REGISTERTokenParamDefine(vacation);
1682
1683         REGISTERTokenParamDefine(larger);
1684         REGISTERTokenParamDefine(smaller);
1685
1686         /* these are c-keyworads, so do it by hand. */
1687         RegisterTokenParamDefine(HKEY("continue"), econtinue);
1688         RegisterTokenParamDefine(HKEY("stop"), estop);
1689
1690         RegisterIterator("SIEVE:SCRIPTS", 0, NULL, GetSieveScriptListing, NULL, NULL, CTX_SIEVELIST, CTX_NONE, IT_NOFLAG);
1691
1692         RegisterConditional(HKEY("COND:SIEVE:SCRIPT:ACTIVE"), 0, ConditionalSieveScriptIsActive, CTX_SIEVELIST);
1693         RegisterConditional(HKEY("COND:SIEVE:SCRIPT:ISRULES"), 0, ConditionalSieveScriptIsRulesScript, CTX_SIEVELIST);
1694         RegisterNamespace("SIEVE:SCRIPT:NAME", 0, 1, tmplput_SieveScriptName, NULL, CTX_SIEVELIST);
1695         RegisterNamespace("SIEVE:SCRIPT:CONTENT", 0, 1, tmplput_SieveScriptContent, NULL, CTX_SIEVELIST);
1696
1697  
1698         RegisterIterator("SIEVE:RULES", 0, NULL, GetSieveRules, NULL, DeleteHash, CTX_SIEVESCRIPT, CTX_NONE, IT_NOFLAG);
1699         RegisterIterator("SIEVE:RULE:EMPTY", 0, NULL, GetEmptySieveRule, NULL, DeleteHash, CTX_SIEVESCRIPT, CTX_NONE, IT_NOFLAG);
1700 /*
1701 <?ITERATE("SIEVE:RULE:EMPTY", ="sieve_display_one")>
1702 */
1703
1704
1705         RegisterConditional(HKEY("COND:SIEVE:ACTIVE"), 1, ConditionalSieveRule_Active, CTX_SIEVESCRIPT);
1706         RegisterConditional(HKEY("COND:SIEVE:HFIELD"), 1, ConditionalSieveRule_hfield, CTX_SIEVESCRIPT);
1707         RegisterConditional(HKEY("COND:SIEVE:COMPARE"), 1, ConditionalSieveRule_compare, CTX_SIEVESCRIPT);
1708         RegisterConditional(HKEY("COND:SIEVE:ACTION"), 1, ConditionalSieveRule_action, CTX_SIEVESCRIPT);
1709         RegisterConditional(HKEY("COND:SIEVE:SIZECOMP"), 1, ConditionalSieveRule_sizecomp, CTX_SIEVESCRIPT);
1710         RegisterConditional(HKEY("COND:SIEVE:FINAL"), 1, ConditionalSieveRule_final, CTX_SIEVESCRIPT);
1711         RegisterConditional(HKEY("COND:SIEVE:THISROOM"), 1, ConditionalSieveRule_ThisRoom, CTX_SIEVESCRIPT);
1712
1713         //RegisterNamespace("SIEVE:SCRIPT:HFIELD", 0, 1, tmplput_SieveRule_hfield, NULL, CTX_SIEVESCRIPT);
1714         //RegisterNamespace("SIEVE:SCRIPT:COMPARE", 0, 1, tmplput_SieveRule_compare, NULL, CTX_SIEVESCRIPT);
1715         RegisterNamespace("SIEVE:SCRIPT:HTEXT", 0, 1, tmplput_SieveRule_htext, NULL, CTX_SIEVESCRIPT);
1716         RegisterNamespace("SIEVE:SCRIPT:SIZE", 0, 1, tmplput_SieveRule_sizeval, NULL, CTX_SIEVESCRIPT);
1717         ///RegisterNamespace("SIEVE:SCRIPT:ACTION", 0, 1, tmplput_SieveRule_action, NULL, CTX_SIEVESCRIPT);
1718         RegisterNamespace("SIEVE:SCRIPT:FILEINTO", 0, 1, tmplput_SieveRule_fileinto, NULL, CTX_SIEVESCRIPT);
1719         RegisterNamespace("SIEVE:SCRIPT:REDIRECT", 0, 1, tmplput_SieveRule_redirect, NULL, CTX_SIEVESCRIPT);
1720         RegisterNamespace("SIEVE:SCRIPT:AUTOMSG", 0, 1, tmplput_SieveRule_automsg, NULL, CTX_SIEVESCRIPT);
1721         ///RegisterNamespace("SIEVE:SCRIPT:FINAL", 0, 1, tmplput_SieveRule_final, NULL, CTX_SIEVESCRIPT);
1722         /* fetch our room into WCC->ThisRoom, to evaluate while iterating over rooms with COND:THIS:THAT:ROOM */
1723         RegisterNamespace("SIEVE:SCRIPT:LOOKUP_FILEINTO", 0, 1, tmplput_SieveRule_lookup_FileIntoRoom, NULL, CTX_SIEVESCRIPT);
1724
1725 #if FOO
1726         WebcitAddUrlHandler(HKEY("display_sieve"), "", 0, display_sieve, 0);
1727         WebcitAddUrlHandler(HKEY("display_add_remove_scripts"), "", 0, _display_add_remove_scripts, 0);
1728 #endif
1729         WebcitAddUrlHandler(HKEY("save_sieve"), "", 0, save_sieve, 0);
1730
1731         WebcitAddUrlHandler(HKEY("create_script"), "", 0, create_script, 0);
1732         WebcitAddUrlHandler(HKEY("delete_script"), "", 0, delete_script, 0);
1733 }