we need to create the rules hash regardles of the server reply; this fixes the broken...
[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                 AppendImportantMessage(_("Cancelled.  Changes were not saved."), -1);
382                 display_main_menu();
383                 return;
384         }
385
386         parse_fields_from_rule_editor();
387
388         serv_puts("MSIV listscripts");
389         serv_getln(buf, sizeof(buf));
390         if (buf[0] == '1') while (serv_getln(buf, sizeof(buf)), strcmp(buf, "000")) {
391                 if (num_scripts < MAX_SCRIPTS) {
392                         extract_token(script_names[num_scripts], buf, 0, '|', 64);
393                         if (extract_int(buf, 1) > 0) {
394                                 active_script = num_scripts;
395                         }
396                         ++num_scripts;
397                 }
398         }
399
400         bigaction = ibstr("bigaction");
401
402         if (bigaction == 0) {
403                 serv_puts("MSIV setactive||");
404                 serv_getln(buf, sizeof buf);
405         }
406
407         else if (bigaction == 1) {
408                 serv_printf("MSIV setactive|%s|", RULES_SCRIPT);
409                 serv_getln(buf, sizeof buf);
410         }
411
412         else if (bigaction == 2) {
413                 serv_printf("MSIV setactive|%s|", bstr("active_script"));
414                 serv_getln(buf, sizeof buf);
415         }
416
417         if (num_scripts > 0) {
418                 for (i=0; i<num_scripts; ++i) {
419                         /*
420                          * We only want to save the scripts from the "manually edited scripts"
421                          * screen.  The script that WebCit generates from its ruleset will be
422                          * auto-generated by parse_fields_from_rule_editor() and saved there.
423                          */
424                         if (strcasecmp(script_names[i], RULES_SCRIPT)) {
425                                 serv_printf("MSIV putscript|%s|", script_names[i]);
426                                 serv_getln(buf, sizeof buf);
427                                 if (buf[0] == '4') {
428                                         snprintf(this_name, sizeof this_name, "text_%s", script_names[i]);
429                                         striplt((char *)BSTR(this_name)); /* TODO: get rid of typecast*/
430                                         serv_write(BSTR(this_name), strlen(BSTR(this_name)));
431                                         serv_puts("\n000");
432                                 }
433                         }
434                 }
435         }
436
437         AppendImportantMessage(_("Your changes have been saved."), -1);
438         display_main_menu();
439         return;
440 }
441
442 /*
443  * create a new script
444  * take the web environment script name and create it on the citadel server
445  */
446 void create_script(void) {
447         char buf[256];
448
449         serv_printf("MSIV getscript|%s", bstr("script_name"));
450         serv_getln(buf, sizeof buf);
451         if (buf[0] == '1') {
452                 while (serv_getln(buf, sizeof(buf)), strcmp(buf, "000")) {
453                         /* flush */
454                 }
455 #if FOO
456                 display_add_remove_scripts(_("A script by that name already exists."));
457 #endif
458                 return;
459         }
460         
461         serv_printf("MSIV putscript|%s", bstr("script_name"));
462         serv_getln(buf, sizeof buf);
463         if (buf[0] == '4') {
464                 serv_puts("keep;");
465                 serv_puts("000");
466 #if FOO
467                 display_add_remove_scripts(_("A new script has been created.  Return to the script editing screen to edit and activate it."));
468 #else
469         output_headers(1, 1, 2, 0, 0, 0);
470         do_template("sieve_add");
471         wDumpContent(1);
472 #endif
473                 return;
474         }
475
476 #if FOO
477         display_add_remove_scripts(&buf[4]);
478 #else
479         output_headers(1, 1, 2, 0, 0, 0);
480         do_template("sieve_add");
481         wDumpContent(1);
482 #endif
483 }
484
485
486
487
488 /*
489  * delete a script
490  */
491 void delete_script(void) {
492         char buf[256];
493
494         serv_printf("MSIV deletescript|%s", bstr("script_name"));
495         serv_getln(buf, sizeof buf);
496 #if FOO
497         display_add_remove_scripts(&buf[4]);
498 #else
499         output_headers(1, 1, 2, 0, 0, 0);
500         do_template("sieve_add");
501         wDumpContent(1);
502 #endif
503 }
504                 
505
506
507 /*
508  * dummy panel indicating to the user that the server doesn't support Sieve
509  */
510 void display_no_sieve(void) {
511
512         output_headers(1, 1, 2, 0, 0, 0);
513         do_template("sieve_none");
514         wDumpContent(1);
515 }
516
517 #if FOO
518 /*
519  * view/edit sieve config
520  */
521 void display_sieve(void)
522 {
523         char script_names[MAX_SCRIPTS][64];
524         int num_scripts = 0;
525         int active_script = (-1);
526         char buf[SIZ];          /* Don't make this buffer smaller or it will restrict line length */
527         int i;
528         int rules_script_is_active = 0;
529
530         if (!WC->serv_info->serv_supports_sieve) {
531                 display_no_sieve();
532                 return;
533         }
534
535         memset(script_names, 0, sizeof script_names);
536
537         serv_puts("MSIV listscripts");
538         serv_getln(buf, sizeof(buf));
539         if (buf[0] == '1') while (serv_getln(buf, sizeof(buf)), strcmp(buf, "000")) {
540                 if (num_scripts < MAX_SCRIPTS) {
541                         extract_token(script_names[num_scripts], buf, 0, '|', 64);
542                         if (extract_int(buf, 1) > 0) {
543                                 active_script = num_scripts;
544                                 if (!strcasecmp(script_names[num_scripts], RULES_SCRIPT)) {
545                                         rules_script_is_active = 1;
546                                 }
547                         }
548                         ++num_scripts;
549                 }
550         }
551
552         output_headers(1, 1, 2, 0, 0, 0);
553
554         wc_printf("<script type=\"text/javascript\">\n"
555                   "\n"
556                   "var previously_active_script;\n"
557                   "\n"
558                   "function ToggleSievePanels() {\n"
559                   " d = ($('sieveform').bigaction.options[$('sieveform').bigaction.selectedIndex].value);\n"
560                   " for (i=0; i<3; ++i) {\n"
561                   "  if (i == d) {\n"
562                   "   $('sievediv' + i).style.display = 'block';\n"
563                   "  }\n"
564                   "  else {\n"
565                   "   $('sievediv' + i).style.display = 'none';\n"
566                   "  }\n"
567                   " }\n"
568                   "}\n"
569                   "\n"
570                   "function ToggleScriptPanels() {\n"
571                   " d = ($('sieveform').active_script.options[$('sieveform').active_script.selectedIndex].value);\n"
572                   " if ($('script_' + previously_active_script)) {\n"
573                   "  $('script_' + previously_active_script).style.display = 'none';\n"
574                   " }\n"
575                   " $('script_' + d).style.display = 'block';\n"
576                   " previously_active_script = d;\n"
577                   "}\n"
578                   "\n"
579                   "</script>\n"
580 );
581
582         wc_printf("<div id=\"banner\">\n");
583         wc_printf("<img src=\"static/webcit_icons/essen/32x32/config.png\">");
584         wc_printf("<h1>");
585         wc_printf(_("View/edit server-side mail filters"));
586         wc_printf("</h1>\n");
587         wc_printf("</div>\n");
588
589         wc_printf("<div id=\"content\" class=\"service\">\n");
590
591         wc_printf("<table class=\"sieve_background\">\n"
592                 "<tr>\n<td valign=top>\n");
593
594
595         wc_printf("<form id=\"sieveform\" method=\"post\" action=\"save_sieve\">\n");
596         wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
597
598         wc_printf(_("When new mail arrives: "));
599         wc_printf("<select name=\"bigaction\" size=1 onChange=\"ToggleSievePanels();\">\n");
600
601         wc_printf("<option %s value=\"0\">", ((active_script < 0) ? "selected" : ""));
602         wc_printf(_("Leave it in my inbox without filtering"));
603         wc_printf("</option>\n");
604
605         wc_printf("<option %s value=\"1\">", ((rules_script_is_active) ? "selected" : ""));
606         wc_printf(_("Filter it according to rules selected below"));
607         wc_printf("</option>\n");
608
609         wc_printf("<option %s value=\"2\">",
610                         (((active_script >= 0) && (!rules_script_is_active)) ? "selected" : ""));
611         wc_printf(_("Filter it through a manually edited script (advanced users only)"));
612         wc_printf("</option>\n");
613
614         wc_printf("</select>\n\n");
615
616
617
618         /* The "no filtering" div */
619
620         wc_printf("<div id=\"sievediv0\" style=\"display:none\">\n");
621         wc_printf("<div align=\"center\"><br><br>");
622         wc_printf(_("Your incoming mail will not be filtered through any scripts."));
623         wc_printf("<br><br></div>\n");
624         wc_printf("</div>\n");
625
626         /* The "webcit managed scripts" div */
627
628         wc_printf("<div id=\"sievediv1\" style=\"display:none\">\n");
629         display_rules_editor_inner_div();
630         wc_printf("</div>\n");
631
632         /* The "I'm smart and can write my own Sieve scripts" div */
633
634         wc_printf("<div id=\"sievediv2\" style=\"display:none\">\n");
635
636         if (num_scripts > 0) {
637                 wc_printf(_("The currently active script is: "));
638                 wc_printf("<select name=\"active_script\" size=1 onChange=\"ToggleScriptPanels();\">\n");
639                 for (i=0; i<num_scripts; ++i) {
640                         if (strcasecmp(script_names[i], RULES_SCRIPT)) {
641                                 wc_printf("<option %s value=\"%s\">%s</option>\n",
642                                         ((active_script == i) ? "selected" : ""),
643                                         script_names[i],
644                                         script_names[i]
645                                 );
646                         }
647                 }
648                 wc_printf("</select>\n\n");
649         }
650
651         wc_printf("&nbsp;&nbsp;&nbsp;");
652         wc_printf("<a href=\"display_add_remove_scripts\">%s</a>\n", _("Add or delete scripts"));
653
654         wc_printf("<br>\n");
655
656         if (num_scripts > 0) {
657                 for (i=0; i<num_scripts; ++i) {
658                         if (strcasecmp(script_names[i], RULES_SCRIPT)) {
659                                 wc_printf("<div id=\"script_%s\" style=\"display:none\">\n", script_names[i]);
660                                 wc_printf("<textarea name=\"text_%s\" wrap=soft rows=20 cols=80 width=80>\n",
661                                         script_names[i]);
662                                 serv_printf("MSIV getscript|%s", script_names[i]);
663                                 serv_getln(buf, sizeof buf);
664                                 if (buf[0] == '1') while(serv_getln(buf, sizeof (buf)), strcmp(buf, "000")) {
665                                         wc_printf("%s\n", buf);
666                                 }
667                                 wc_printf("</textarea>\n");
668                                 wc_printf("</div>\n");
669                         }
670                 }
671         }
672
673         wc_printf("<script type=\"text/javascript\">    \n"
674                 "ToggleScriptPanels();                  \n"
675                 "</script>                              \n"
676         );
677
678         wc_printf("</div>\n");
679
680
681         /* The rest of this is common for all panels... */
682
683         wc_printf("<div align=\"center\"><br>");
684         wc_printf("<input type=\"submit\" name=\"save_button\" value=\"%s\">", _("Save changes"));
685         wc_printf("&nbsp;");
686         wc_printf("<input type=\"submit\" name=\"cancel_button\" value=\"%s\">\n", _("Cancel"));
687         wc_printf("</div></form>\n");
688
689         wc_printf("</td></tr></table>\n");
690
691         wc_printf("<script type=\"text/javascript\">    \n"
692                 "ToggleSievePanels();                   \n"
693                 "</script>                              \n"
694         );
695
696         wDumpContent(1);
697
698 }
699
700
701
702
703 /*
704  * show a list of available scripts to add/remove them
705  */
706 void display_add_remove_scripts(char *message)
707 {
708         char buf[256];
709         char script_name[256];
710
711         output_headers(1, 1, 2, 0, 0, 0);
712         wc_printf("<div id=\"banner\">\n");
713         wc_printf("<img src=\"static/webcit_icons/essen/32x32/config.png\">");
714         wc_printf(_("Add or delete scripts"));
715         wc_printf("</h1>\n");
716         wc_printf("</div>\n");
717         
718         wc_printf("<div id=\"content\" class=\"service\">\n");
719
720         if (message != NULL) {
721                 wc_printf("%s", message);
722         }
723
724         wc_printf("<table border=0 cellspacing=10><tr valign=top><td>\n");
725
726         do_template("box_begin_1");
727         StrBufAppendBufPlain(WC->WBuf, _("Add a new script"), -1, 0);
728         do_template("box_begin_2");
729
730         wc_printf(_("To create a new script, enter the desired "
731                 "script name in the box below and click 'Create'."));
732         wc_printf("<br><br>");
733
734         wc_printf("<center><form method=\"POST\" action=\"create_script\">\n");
735         wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
736         wc_printf(_("Script name: "));
737         wc_printf("<input type=\"text\" name=\"script_name\"><br>\n"
738                 "<input type=\"submit\" name=\"create_button\" value=\"%s\">"
739                 "</form></center>\n", _("Create"));
740
741         do_template("box_end");
742
743         do_template("box_begin_1");
744         StrBufAppendBufPlain(WC->WBuf, _("Edit scripts"), -1, 0);
745         do_template("box_begin_2");
746         wc_printf("<br><div align=center><a href=\"display_sieve\">%s</a><br><br>\n",
747                 _("Return to the script editing screen")
748         );
749         do_template("box_end");
750
751         wc_printf("</td><td>");
752
753         do_template("box_begin_1");
754         StrBufAppendBufPlain(WC->WBuf, _("Delete scripts"), -1, 0);
755         do_template("box_begin_2");
756
757         wc_printf(_("To delete an existing script, select the script "
758                 "name from the list and click 'Delete'."));
759         wc_printf("<br><br>");
760         
761         wc_printf("<center>"
762                 "<form method=\"POST\" action=\"delete_script\">\n");
763         wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
764         wc_printf("<select name=\"script_name\" size=10 style=\"width:100%%\">\n");
765
766         serv_puts("MSIV listscripts");
767         serv_getln(buf, sizeof buf);
768         if (buf[0] == '1') {
769                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
770                         extract_token(script_name, buf, 0, '|', sizeof script_name);
771                         if ( (extract_int(buf, 1) == 0) && (strcasecmp(script_name, RULES_SCRIPT)) ) {
772                                 wc_printf("<option>");
773                                 escputs(script_name);
774                                 wc_printf("</option>\n");
775                         }
776                 }
777         }
778         wc_printf("</select>\n\n<br>\n");
779
780         wc_printf("<input type=\"submit\" name=\"delete_button\" value=\"%s\" "
781                 "onClick=\"return confirm('%s');\">", _("Delete script"), _("Delete this script?"));
782         wc_printf("</form></center>\n");
783         do_template("box_end");
784
785         wc_printf("</td></tr></table>\n");
786
787         wDumpContent(1);
788 }
789
790
791
792
793
794
795 void display_rules_editor_inner_div(void) {
796         int i, j;
797         char buf[4096];
798         char rules[MAX_RULES][2048];
799
800         struct {
801                 char name[128];
802         } *rooms = NULL;
803         int num_roomnames = 0;
804         int num_roomnames_alloc = 0;
805
806         int active;
807         char hfield[256];
808         char compare[32];
809         char htext[256];
810         char sizecomp[32];
811         int sizeval;
812         char action[32];
813         char fileinto[128];
814         char redirect[256];
815         char automsg[1024];
816         char final[32];
817
818         /* load the rules */
819         memset(rules, 0, sizeof rules);
820         serv_printf("MSIV getscript|%s", RULES_SCRIPT);
821         serv_getln(buf, sizeof buf);
822         if (buf[0] == '1') while(serv_getln(buf, sizeof (buf)), strcmp(buf, "000")) {
823                 if (!strncasecmp(buf, "# WEBCIT_RULE|", 14)) {
824                         j = extract_int(buf, 1);
825                         remove_token(buf, 0, '|');
826                         remove_token(buf, 0, '|');
827                         CtdlDecodeBase64(rules[j], buf, strlen(buf));
828                 }
829         }
830
831         /* load the roomnames */
832         serv_puts("LKRA");
833         serv_getln(buf, sizeof buf);
834         if (buf[0] == '1') {
835                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
836                         ++num_roomnames;
837                         if (num_roomnames > num_roomnames_alloc) {
838                                 num_roomnames_alloc += 250;
839                                 rooms = realloc(rooms, (num_roomnames_alloc * 128));
840                         }
841                         extract_token(rooms[num_roomnames-1].name, buf, 0, '|', 128);
842                 }
843         }
844
845
846 /*
847  * This script should get called by every onChange event...
848  *
849  */
850         wc_printf("<script type=\"text/javascript\">\n"
851                   "\n"
852                   "var highest_active_rule = (-1);\n"
853                   "\n"
854                   "function UpdateRules() {\n");
855 /*
856  * Show only the active rows...
857  */
858         wc_printf("  highest_active_rule = (-1);\n");
859         wc_printf("  for (i=0; i<%d; ++i) {\n", MAX_RULES);
860         wc_printf("   if ($('active'+i).checked) {\n"
861                   "     $('rule' + i).style.display = 'block';\n"
862                   "     highest_active_rule = i;\n"
863                   "   }\n"
864                   "   else {\n"
865                   "     $('rule' + i).style.display = 'none';\n"
866                   "   }\n"
867                   "  }\n");
868 /*
869  * Show only the fields relevant to the rules...
870  */
871         wc_printf("  for (i=0; i<=highest_active_rule; ++i) {\n"
872                   "    d = ($('movedown'+i));\n"
873                   "    if (i < highest_active_rule) {\n"
874                   "      d.style.display = 'block';\n"
875                   "    }\n"
876                   "    else {\n"
877                   "      d.style.display = 'none';\n"
878                   "    }\n"
879                   "    d = ($('hfield'+i).options[$('hfield'+i).selectedIndex].value);\n"
880                   "    if (d == 'all') {\n"
881                   "      $('div_size'+i).style.display = 'none'; \n"
882                   "      $('div_compare'+i).style.display = 'none';\n"
883                   "      $('div_nocompare'+i).style.display = 'block';\n"
884                   "    }\n"
885                   "    else if (d == 'size') {\n"
886                   "      $('div_size'+i).style.display = 'block'; \n"
887                   "      $('div_compare'+i).style.display = 'none';\n"
888                   "      $('div_nocompare'+i).style.display = 'none';\n"
889                   "    }\n"
890                   "    else {\n"
891                   "      $('div_size'+i).style.display = 'none'; \n"
892                   "      $('div_compare'+i).style.display = 'block';\n"
893                   "      $('div_nocompare'+i).style.display = 'none';\n"
894                   "    }\n"
895                   "    d = ($('action'+i).options[$('action'+i).selectedIndex].value);\n"
896                   "    if (d == 'fileinto') {\n"
897                   "      $('div_fileinto'+i).style.display = 'block';\n"
898                   "      $('div_redirect'+i).style.display = 'none';\n"
899                   "      $('div_automsg'+i).style.display = 'none';\n"
900                   "    } else if (d == 'redirect') {\n"
901                   "      $('div_fileinto'+i).style.display = 'none';\n"
902                   "      $('div_redirect'+i).style.display = 'block';\n"
903                   "      $('div_automsg'+i).style.display = 'none';\n"
904                   "    } else if ((d == 'reject') || (d == 'vacation'))  {\n"
905                   "      $('div_fileinto'+i).style.display = 'none';\n"
906                   "      $('div_redirect'+i).style.display = 'none';\n"
907                   "      $('div_automsg'+i).style.display = 'block';\n"
908                   "    } else {\n"
909                   "      $('div_fileinto'+i).style.display = 'none';\n"
910                   "      $('div_redirect'+i).style.display = 'none';\n"
911                   "      $('div_automsg'+i).style.display = 'none';\n"
912                   "    }\n"
913                   "    if (highest_active_rule < %d) {\n", MAX_RULES-1 );
914         wc_printf("      $('div_addrule').style.display = 'block';\n"
915                   "    } else {\n"
916                   "      $('div_addrule').style.display = 'none';\n"
917                   "    }\n"
918                   "  }\n"
919                   "}\n"
920 /*
921  * Add a rule (really, just un-hide it)
922  */
923                   "function AddRule() {\n"
924                   "  highest_active_rule = highest_active_rule + 1;\n"
925                   "  $('active'+highest_active_rule).checked = true;\n"
926                   "  UpdateRules();\n"
927                   "}\n"
928 /*
929  * Swap two rules
930  */
931                   "function SwapRules(ra, rb) {\n"
932                   "\n"
933                   "  var things = new Array();\n"
934                   "  things[0] = 'hfield';\n"
935                   "  things[1] = 'compare';\n"
936                   "  things[2] = 'htext';\n"
937                   "  things[3] = 'action';\n"
938                   "  things[4] = 'fileinto';\n"
939                   "  things[5] = 'redirect';\n"
940                   "  things[6] = 'final';\n"
941                   "  things[7] = 'sizecomp';\n"
942                   "  things[8] = 'sizeval';\n"
943                   "  things[9] = 'automsg';\n"
944                   "\n"
945                   "  for (i=0; i<=9; ++i) {\n"
946                   "    tempval=$(things[i]+ra).value;\n"
947                   "    $(things[i]+ra).value = $(things[i]+rb).value;\n"
948                   "    $(things[i]+rb).value = tempval;\n"
949                   "  }\n"
950                   "}\n"
951 /*
952  * Delete a rule (percolate the deleted rule out to the end, then deactivate it)
953  */
954                   "function DeleteRule(rd) {\n"
955                   "  for (j=rd; j<=highest_active_rule; ++j) {\n"
956                   "    SwapRules(j, (j+1));\n"
957                   "  }\n"
958                   "  $('active'+highest_active_rule).checked = false;\n"
959                   "}\n"
960                   "</script>\n"
961                 );
962
963
964         wc_printf("<br>");
965
966         wc_printf("<table cellpadding=2 width=100%%>");
967
968         for (i=0; i<MAX_RULES; ++i) {
969
970                 /* Grab our existing values to populate */
971                 active = extract_int(rules[i], 0);
972                 extract_token(hfield, rules[i], 1, '|', sizeof hfield);
973                 extract_token(compare, rules[i], 2, '|', sizeof compare);
974                 extract_token(htext, rules[i], 3, '|', sizeof htext);
975                 extract_token(sizecomp, rules[i], 4, '|', sizeof sizecomp);
976                 sizeval = extract_int(rules[i], 5);
977                 extract_token(action, rules[i], 6, '|', sizeof action);
978                 extract_token(fileinto, rules[i], 7, '|', sizeof fileinto);
979                 extract_token(redirect, rules[i], 8, '|', sizeof redirect);
980                 extract_token(automsg, rules[i], 9, '|', sizeof automsg);
981                 extract_token(final, rules[i], 10, '|', sizeof final);
982                 
983                 /* now generate the table row */
984
985                 wc_printf("<tr id=\"rule%d\" class=\"%s\">",
986                         i,
987                         ((i%2) ? "odd" : "even")
988                 );
989
990                 wc_printf("<td width=5%% align=\"center\">\n");
991
992                 wc_printf("<div style=\"display:none\">\n");
993                 wc_printf("<input type=\"checkbox\" name=\"active%d\" id=\"active%d\" %s>\n",
994                         i, i,
995                         (active ? "checked" : "")
996                 );
997                 wc_printf("</div>\n");
998
999                 if (i>0) wc_printf("<a href=\"javascript:SwapRules(%d,%d);UpdateRules();\">"
1000                         "<img border=\"0\" src=\"static/webcit_icons/up_pointer.gif\" "
1001                         "title=\"%s\"/></a>\n",
1002                         i-1, i, _("Move rule up") );
1003
1004                 wc_printf("<a href=\"javascript:SwapRules(%d,%d);UpdateRules();\">"
1005                         "<img id=\"movedown%d\" border=\"0\" src=\"static/webcit_icons/down_pointer.gif\" "
1006                         "title=\"%s\"/></a>\n",
1007                         i, i+1, i, _("Move rule down") );
1008
1009                 wc_printf("<a href=\"javascript:DeleteRule(%d);UpdateRules();\">"
1010                         "<img id=\"delete%d\" border=\"0\" src=\"static/webcit_icons/delete.gif\" "
1011                         "title=\"%s\"/></a>\n",
1012                         i, i, _("Delete rule") );
1013
1014                 wc_printf("</td>\n\n\n");
1015
1016                 wc_printf("<td width=5%% align=\"center\">\n");
1017                 wc_printf("<font size=+2>%d</font>\n", i+1);
1018                 wc_printf("</td>\n");
1019
1020                 wc_printf("<td width=20%%>%s ", _("If") );
1021
1022                 char *hfield_values[15][2] = {
1023                         {       "from",         _("From")               },
1024                         {       "tocc",         _("To or Cc")           },
1025                         {       "subject",      _("Subject")            },
1026                         {       "replyto",      _("Reply-to")           },
1027                         {       "sender",       _("Sender")             },
1028                         {       "resentfrom",   _("Resent-From")        },
1029                         {       "resentto",     _("Resent-To")          },
1030                         {       "envfrom",      _("Envelope From")      },
1031                         {       "envto",        _("Envelope To")        },
1032                         {       "xmailer",      _("X-Mailer")           },
1033                         {       "xspamflag",    _("X-Spam-Flag")        },
1034                         {       "xspamstatus",  _("X-Spam-Status")      },
1035                         {       "listid",       _("List-ID")            },
1036                         {       "size",         _("Message size")       },
1037                         {       "all",          _("All")                }
1038                 };
1039
1040                 wc_printf("<select id=\"hfield%d\" name=\"hfield%d\" size=1 onChange=\"UpdateRules();\">",
1041                         i, i);
1042                 for (j=0; j<15; ++j) {
1043                         wc_printf("<option %s value=\"%s\">%s</option>\n",
1044                                 ( (!strcasecmp(hfield, hfield_values[j][0])) ? "selected" : ""),
1045                                 hfield_values[j][0],
1046                                 hfield_values[j][1]
1047                         );
1048                 }
1049
1050                 wc_printf("</select>\n\n");
1051                 wc_printf("</td>");
1052
1053                 wc_printf("<td width=20%%>");
1054
1055                 char *compare_values[6][2] = {
1056                         {       "contains",     _("contains")           },
1057                         {       "notcontains",  _("does not contain")   },
1058                         {       "is",           _("is")                 },
1059                         {       "isnot",        _("is not")             },
1060                         {       "matches",      _("matches")            },
1061                         {       "notmatches",   _("does not match")     }
1062                 };
1063
1064                 wc_printf("<div id=\"div_compare%d\">\n", i);
1065                 wc_printf("<select id=\"compare%d\" name=\"compare%d\" size=1 onChange=\"UpdateRules();\">\n",
1066                         i, i);
1067                 for (j=0; j<6; ++j) {
1068                         wc_printf("<option %s value=\"%s\">%s</option>\n",
1069                                 ( (!strcasecmp(compare, compare_values[j][0])) ? "selected" : ""),
1070                                 compare_values[j][0],
1071                                 compare_values[j][1]
1072                         );
1073                 }
1074                 wc_printf("</select>\n\n");
1075
1076                 wc_printf("<input type=\"text\" id=\"htext%d\" name=\"htext%d\" value=\"", i, i);
1077                 escputs(htext);
1078                 wc_printf("\">\n</div>\n");
1079
1080                 wc_printf("<div id=\"div_nocompare%d\">", i);
1081                 wc_printf("%s", _("(All messages)"));
1082                 wc_printf("</div>\n");
1083
1084                 char *sizecomp_values[2][2] = {
1085                         {       "larger",       _("is larger than")     },
1086                         {       "smaller",      _("is smaller than")    }
1087                 };
1088
1089                 wc_printf("<div id=\"div_size%d\">\n", i);
1090                 wc_printf("<select id=\"sizecomp%d\" name=\"sizecomp%d\" size=1 onChange=\"UpdateRules();\">\n",
1091                         i, i);
1092                 for (j=0; j<2; ++j) {
1093                         wc_printf("<option %s value=\"%s\">%s</option>\n",
1094                                 ( (!strcasecmp(sizecomp, sizecomp_values[j][0])) ? "selected" : ""),
1095                                 sizecomp_values[j][0],
1096                                 sizecomp_values[j][1]
1097                         );
1098                 }
1099                 wc_printf("</select>\n\n");
1100
1101                 wc_printf("<input type=\"text\" id=\"sizeval%d\" name=\"sizeval%d\" value=\"%d\">",
1102                         i, i, sizeval);
1103                 wc_printf("bytes");
1104                 wc_printf("</div>");
1105
1106                 wc_printf("</td>");
1107
1108                 char *action_values[6][2] = {
1109                         {       "keep",         _("Keep")               },
1110                         {       "discard",      _("Discard silently")   },
1111                         {       "reject",       _("Reject")             },
1112                         {       "fileinto",     _("Move message to")    },
1113                         {       "redirect",     _("Forward to")         },
1114                         {       "vacation",     _("Vacation")           }
1115                 };
1116
1117                 wc_printf("<td width=20%%>\n");
1118                 wc_printf("<select id=\"action%d\" name=\"action%d\" size=1 onChange=\"UpdateRules();\">\n",
1119                         i, i);
1120                 for (j=0; j<6; ++j) {
1121                         wc_printf("<option %s value=\"%s\">%s</option>\n",
1122                                 ( (!strcasecmp(action, action_values[j][0])) ? "selected" : ""),
1123                                 action_values[j][0],
1124                                 action_values[j][1]
1125                         );
1126                 }
1127                 wc_printf("</select>\n\n");
1128
1129                 wc_printf("<div id=\"div_fileinto%d\">\n", i);
1130                 wc_printf("<select name=\"fileinto%d\" id=\"fileinto%d\">\n", i, i);
1131                 for (j=0; j<num_roomnames; ++j) {
1132                         wc_printf("<option ");
1133                         if (!strcasecmp(rooms[j].name, fileinto)) {
1134                                 wc_printf("selected ");
1135                         }
1136                         wc_printf("value=\"");
1137                         escputs(rooms[j].name);
1138                         wc_printf("\">");
1139                         escputs(rooms[j].name);
1140                         wc_printf("</option>\n");
1141                 }
1142                 wc_printf("</select>\n\n");
1143                 wc_printf("</div>");
1144
1145                 wc_printf("<div id=\"div_redirect%d\">\n", i);
1146                 wc_printf("<input type=\"text\" id=\"redirect%d\" name=\"redirect%d\" value=\"", i, i);
1147                 escputs(redirect);
1148                 wc_printf("\">\n</div>\n");
1149
1150                 wc_printf("<div id=\"div_automsg%d\">\n", i);
1151                 wc_printf(_("Message:"));
1152                 wc_printf("<br>\n");
1153                 wc_printf("<textarea name=\"automsg%d\" id=\"automsg%d\" wrap=soft rows=5>\n", i, i);
1154                 escputs(automsg);
1155                 wc_printf("</textarea>");
1156                 wc_printf("</div>\n");
1157
1158                 wc_printf("</td>\n");
1159
1160                 char *final_values[2][2] = {
1161                         {       "continue",     _("continue processing")        },
1162                         {       "stop",         _("stop")                       }
1163                 };
1164
1165                 wc_printf("<td width=10%% align=\"center\">%s</td>\n", _("and then") );
1166
1167                 wc_printf("<td width=20%%>\n");
1168                 wc_printf("<select name=\"final%d\" id=\"final%d\" size=1 onChange=\"UpdateRules();\">\n",
1169                         i, i);
1170                 for (j=0; j<2; ++j) {
1171                         wc_printf("<option %s value=\"%s\">%s</option>\n",
1172                                 ( (!strcasecmp(final, final_values[j][0])) ? "selected" : ""),
1173                                 final_values[j][0],
1174                                 final_values[j][1]
1175                         );
1176                 }
1177                 wc_printf("</select>\n\n");
1178                 wc_printf("</td>\n");
1179
1180                 wc_printf("</tr>\n");
1181
1182         }
1183
1184         wc_printf("</table>\n");
1185         wc_printf("<div id=\"div_addrule\"><a href=\"javascript:AddRule();\">%s</a><br></div>\n",
1186                 _("Add rule")
1187         );
1188
1189         wc_printf("<script type=\"text/javascript\">\n");
1190         wc_printf("UpdateRules();\n");
1191         wc_printf("</script>\n");
1192
1193         free(rooms);
1194 }
1195 void _display_add_remove_scripts(void) {display_add_remove_scripts(NULL);}
1196 #endif
1197
1198
1199 typedef struct __SieveListing {
1200         int IsActive;
1201         int IsRulesScript;
1202         StrBuf *Name;
1203         StrBuf *Content;
1204 } SieveListing;
1205
1206 int ConditionalSieveScriptIsActive(StrBuf *Target, WCTemplputParams *TP)
1207 {
1208         SieveListing     *SieveList = (SieveListing *)CTX;
1209         return SieveList->IsActive;
1210 }
1211 int ConditionalSieveScriptIsRulesScript(StrBuf *Target, WCTemplputParams *TP)
1212 {
1213         SieveListing     *SieveList = (SieveListing *)CTX;
1214         return SieveList->IsActive;
1215 }
1216 void tmplput_SieveScriptName(StrBuf *Target, WCTemplputParams *TP) 
1217 {
1218         SieveListing     *SieveList = (SieveListing *)CTX;
1219         StrBufAppendTemplate(Target, TP, SieveList->Name, 0);
1220 }
1221 void tmplput_SieveScriptContent(StrBuf *Target, WCTemplputParams *TP) 
1222 {
1223         SieveListing     *SieveList = (SieveListing *)CTX;
1224         StrBufAppendTemplate(Target, TP, SieveList->Content, 0);
1225 }
1226 void FreeSieveListing(void *vSieveListing)
1227 {
1228         SieveListing *List = (SieveListing*) vSieveListing;
1229
1230         FreeStrBuf(&List->Name);
1231         free(List);
1232 }
1233
1234 HashList *GetSieveScriptListing(StrBuf *Target, WCTemplputParams *TP)
1235 {
1236         wcsession *WCC = WC;
1237         StrBuf *Line;
1238         int num_scripts = 0;
1239         int rules_script_active = 0;
1240         int have_rules_script = 0;
1241         const char *pch;
1242         HashPos  *it;
1243         int Done = 0;
1244         SieveListing *Ruleset;
1245
1246         if (WCC->KnownSieveScripts != NULL)
1247                 return WCC->KnownSieveScripts;
1248
1249         serv_puts("MSIV listscripts");
1250         Line = NewStrBuf();
1251         StrBuf_ServGetln(Line);
1252         if (GetServerStatus(Line, NULL) == 1) 
1253         {
1254                 WCC->KnownSieveScripts = NewHash(1, Flathash);
1255
1256                 while(!Done && (StrBuf_ServGetln(Line) >= 0) )
1257                         if ( (StrLength(Line)==3) && 
1258                              !strcmp(ChrPtr(Line), "000")) 
1259                         {
1260                                 Done = 1;
1261                         }
1262                         else
1263                         {
1264                                 pch = NULL;
1265                                 Ruleset = (SieveListing *) malloc(sizeof(SieveListing));
1266                                 Ruleset->Name = NewStrBufPlain(NULL, StrLength(Line));
1267                                 StrBufExtract_NextToken(Ruleset->Name, Line, &pch, '|');
1268                                 Ruleset->IsActive = StrBufExtractNext_int(Line, &pch, '|'); 
1269                                 Ruleset->Content = NULL;
1270
1271                                 if (!strcasecmp(ChrPtr(Ruleset->Name), RULES_SCRIPT))
1272                                 {
1273                                         Ruleset->IsRulesScript = 1;
1274                                         have_rules_script = 1;
1275                                         if (Ruleset->IsActive)
1276                                         {
1277                                                 rules_script_active = 1;
1278                                                 PutBstr(HKEY("__SIEVE:RULESSCRIPT"), NewStrBufPlain(HKEY("1")));
1279                                         }
1280                                 }
1281                                 Put(WCC->KnownSieveScripts, IKEY(num_scripts), Ruleset, FreeSieveListing);
1282
1283                                 ++num_scripts;
1284                         }
1285         }
1286         if ((num_scripts > 0) && (rules_script_active == 0))
1287                 PutBstr(HKEY("__SIEVE:EXTERNAL_SCRIPT"), NewStrBufPlain(HKEY("1")));
1288
1289         if (num_scripts > have_rules_script)
1290         {
1291                 long rc = 0;
1292                 long len;
1293                 const char *Key;
1294                 void *vRuleset;
1295
1296                 /* 
1297                  * ok; we have custom scripts, expose that via bstr, and load the payload.
1298                  */
1299                 PutBstr(HKEY("__SIEVE:HAVE_EXTERNAL_SCRIPT"), NewStrBufPlain(HKEY("1")));
1300
1301                 it = GetNewHashPos(WCC->KnownSieveScripts, 0);
1302                 while (GetNextHashPos(WCC->KnownSieveScripts, it, &len, &Key, &vRuleset) && 
1303                        (vRuleset != NULL))
1304                 {
1305                         Ruleset = (SieveListing *) vRuleset;
1306
1307                         /*
1308                          * its the webcit rule? we don't need to load that here.
1309                          */
1310                         if (Ruleset->IsRulesScript)
1311                                 continue;
1312
1313                         if (!serv_printf("MSIV getscript|%s", ChrPtr(Ruleset->Name)))
1314                                 break;
1315                         StrBuf_ServGetln(Line);
1316                         if (GetServerStatus(Line, NULL) == 1) 
1317                         {
1318                                 Ruleset->Content = NewStrBuf();
1319                                 while(!Done && (rc = StrBuf_ServGetln(Line), rc >= 0) )
1320                                         if ( (StrLength(Line)==3) && 
1321                                              !strcmp(ChrPtr(Line), "000")) 
1322                                         {
1323                                                 Done = 1;
1324                                         }
1325                                         else
1326                                         {
1327                                                 if (StrLength(Ruleset->Content)>0)
1328                                                         StrBufAppendBufPlain(Ruleset->Content, HKEY("\n"), 0);
1329                                                 StrBufAppendBuf(Ruleset->Content, Line, 0);
1330                                         }
1331                                 if (rc < 0) break;
1332                         }
1333                 }
1334         }
1335         FreeStrBuf(&Line);
1336         return WCC->KnownSieveScripts;
1337 }
1338
1339
1340 typedef enum __eSieveHfield 
1341 {
1342         from,           
1343         tocc,           
1344         subject,        
1345         replyto,        
1346         sender, 
1347         resentfrom,     
1348         resentto,       
1349         envfrom,        
1350         envto,  
1351         xmailer,        
1352         xspamflag,      
1353         xspamstatus,    
1354         listid, 
1355         size,           
1356         all
1357 } eSieveHfield;
1358
1359 typedef enum __eSieveCompare {
1360         contains,
1361         notcontains,
1362         is,
1363         isnot,
1364         matches,
1365         notmatches
1366 } eSieveCompare;
1367
1368 typedef enum __eSieveAction {
1369         keep,
1370         discard,
1371         reject,
1372         fileinto,
1373         redirect,
1374         vacation
1375 } eSieveAction;
1376
1377
1378 typedef enum __eSieveSizeComp {
1379         larger,
1380         smaller
1381 } eSieveSizeComp;
1382
1383 typedef enum __eSieveFinal {
1384         econtinue,
1385         estop
1386 } eSieveFinal;
1387
1388
1389 typedef struct __SieveRule {
1390         int active;
1391         int sizeval;
1392         eSieveHfield hfield;
1393         eSieveCompare compare;
1394         StrBuf *htext;
1395         eSieveSizeComp sizecomp;
1396         eSieveAction Action;
1397         StrBuf *fileinto;
1398         StrBuf *redirect;
1399         StrBuf *automsg;
1400         eSieveFinal final;
1401 }SieveRule;
1402
1403
1404
1405 int ConditionalSieveRule_hfield(StrBuf *Target, WCTemplputParams *TP)
1406 {
1407         SieveRule     *Rule = (SieveRule *)CTX;
1408         
1409         return GetTemplateTokenNumber(Target, 
1410                                       TP, 
1411                                       3, 
1412                                       from)
1413                 ==
1414                 Rule->hfield;
1415 }
1416 int ConditionalSieveRule_compare(StrBuf *Target, WCTemplputParams *TP)
1417 {
1418         SieveRule     *Rule = (SieveRule *)CTX;
1419         return GetTemplateTokenNumber(Target, 
1420                                       TP, 
1421                                       3, 
1422                                       contains)
1423                 ==
1424                 Rule->compare;
1425 }
1426 int ConditionalSieveRule_action(StrBuf *Target, WCTemplputParams *TP)
1427 {
1428         SieveRule     *Rule = (SieveRule *)CTX;
1429         return GetTemplateTokenNumber(Target, 
1430                                       TP, 
1431                                       3, 
1432                                       keep)
1433                 ==
1434                 Rule->Action; 
1435 }
1436 int ConditionalSieveRule_sizecomp(StrBuf *Target, WCTemplputParams *TP)
1437 {
1438         SieveRule     *Rule = (SieveRule *)CTX;
1439         return GetTemplateTokenNumber(Target, 
1440                                       TP, 
1441                                       3, 
1442                                       larger)
1443                 ==
1444                 Rule->sizecomp;
1445 }
1446 int ConditionalSieveRule_final(StrBuf *Target, WCTemplputParams *TP)
1447 {
1448         SieveRule     *Rule = (SieveRule *)CTX;
1449         return GetTemplateTokenNumber(Target, 
1450                                       TP, 
1451                                       3, 
1452                                       econtinue)
1453                 ==
1454                 Rule->final;
1455 }
1456 int ConditionalSieveRule_ThisRoom(StrBuf *Target, WCTemplputParams *TP)
1457 {
1458         SieveRule     *Rule = (SieveRule *)CTX;
1459         return GetTemplateTokenNumber(Target, 
1460                                       TP, 
1461                                       3, 
1462                                       econtinue)
1463                 ==
1464                 Rule->final;
1465 }
1466 int ConditionalSieveRule_Active(StrBuf *Target, WCTemplputParams *TP)
1467 {
1468         SieveRule     *Rule = (SieveRule *)CTX;
1469         return Rule->active;
1470 }
1471 void tmplput_SieveRule_htext(StrBuf *Target, WCTemplputParams *TP) 
1472 {
1473         SieveRule     *Rule = (SieveRule *)CTX;
1474         StrBufAppendTemplate(Target, TP, Rule->htext, 0);
1475 }
1476 void tmplput_SieveRule_fileinto(StrBuf *Target, WCTemplputParams *TP) 
1477 {
1478         SieveRule     *Rule = (SieveRule *)CTX;
1479         StrBufAppendTemplate(Target, TP, Rule->fileinto, 0);
1480 }
1481 void tmplput_SieveRule_redirect(StrBuf *Target, WCTemplputParams *TP) 
1482 {
1483         SieveRule     *Rule = (SieveRule *)CTX;
1484         StrBufAppendTemplate(Target, TP, Rule->redirect, 0);
1485 }
1486 void tmplput_SieveRule_automsg(StrBuf *Target, WCTemplputParams *TP) 
1487 {
1488         SieveRule     *Rule = (SieveRule *)CTX;
1489         StrBufAppendTemplate(Target, TP, Rule->automsg, 0);
1490 }
1491 void tmplput_SieveRule_sizeval(StrBuf *Target, WCTemplputParams *TP) 
1492 {
1493         SieveRule     *Rule = (SieveRule *)CTX;
1494         StrBufAppendPrintf(Target, "%d", Rule->sizeval);
1495 }
1496
1497 void tmplput_SieveRule_lookup_FileIntoRoom(StrBuf *Target, WCTemplputParams *TP) 
1498 {
1499         void *vRoom;
1500         SieveRule     *Rule = (SieveRule *)CTX;
1501         wcsession *WCC = WC;
1502         HashList *Rooms = GetRoomListHashLKRA(Target, TP);
1503
1504         GetHash(Rooms, SKEY(Rule->fileinto), &vRoom);
1505         WCC->ThisRoom = (folder*) vRoom;
1506 }
1507
1508 void FreeSieveRule(void *vRule)
1509 {
1510         SieveRule *Rule = (SieveRule*) vRule;
1511
1512         FreeStrBuf(&Rule->htext);
1513         FreeStrBuf(&Rule->fileinto);
1514         FreeStrBuf(&Rule->redirect);
1515         FreeStrBuf(&Rule->automsg);
1516         
1517         free(Rule);
1518 }
1519
1520 #define WC_RULE_HEADER "# WEBCIT_RULE|"
1521 HashList *GetSieveRules(StrBuf *Target, WCTemplputParams *TP)
1522 {
1523         StrBuf *Line;
1524         StrBuf *EncodedRule;
1525         int n;
1526         const char *pch;
1527         HashList *SieveRules = NULL;
1528         int Done = 0;
1529         SieveRule *Rule;
1530
1531         SieveRules = NewHash(1, Flathash);
1532         serv_printf("MSIV getscript|"RULES_SCRIPT);
1533         Line = NewStrBuf();
1534         EncodedRule = NewStrBuf();
1535         StrBuf_ServGetln(Line);
1536         if (GetServerStatus(Line, NULL) == 1) 
1537         {
1538                 while(!Done && (StrBuf_ServGetln(Line) >= 0) )
1539                         if ( (StrLength(Line)==3) && 
1540                              !strcmp(ChrPtr(Line), "000")) 
1541                         {
1542                                 Done = 1;
1543                         }
1544                         else
1545                         {
1546                                 pch = NULL;
1547                                 /* We just care for our encoded header and skip everything else */
1548                                 if ((StrLength(Line) > sizeof(WC_RULE_HEADER) - 1) &&
1549                                     (!strncasecmp(ChrPtr(Line), HKEY(WC_RULE_HEADER))))
1550                                 {
1551                                         StrBufSkip_NTokenS(Line, &pch, '|', 1);
1552                                         n = StrBufExtractNext_int(Line, &pch, '|'); 
1553                                         StrBufExtract_NextToken(EncodedRule, Line, &pch, '|');
1554                                         StrBufDecodeBase64(EncodedRule);
1555
1556                                         Rule = (SieveRule*) malloc(sizeof(SieveRule));
1557
1558                                         Rule->htext = NewStrBufPlain (NULL, StrLength(EncodedRule));
1559
1560                                         Rule->fileinto = NewStrBufPlain (NULL, StrLength(EncodedRule));
1561                                         Rule->redirect = NewStrBufPlain (NULL, StrLength(EncodedRule));
1562                                         Rule->automsg = NewStrBufPlain (NULL, StrLength(EncodedRule));
1563
1564                                         /* Grab our existing values to populate */
1565                                         pch = NULL;
1566                                         Rule->active = StrBufExtractNext_int(EncodedRule, &pch, '|');
1567                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
1568                                         
1569                                         Rule->hfield = (eSieveHfield) GetTokenDefine(SKEY(Line), tocc);
1570                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
1571                                         Rule->compare = (eSieveCompare) GetTokenDefine(SKEY(Line), contains);
1572                                         StrBufExtract_NextToken(Rule->htext, EncodedRule, &pch, '|');
1573                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
1574                                         Rule->sizecomp = (eSieveSizeComp) GetTokenDefine(SKEY(Line), larger);
1575                                         Rule->sizeval = StrBufExtractNext_int(EncodedRule, &pch, '|');
1576                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
1577                                         Rule->Action = (eSieveAction) GetTokenDefine(SKEY(Line), keep);
1578                                         StrBufExtract_NextToken(Rule->fileinto, EncodedRule, &pch, '|');
1579                                         StrBufExtract_NextToken(Rule->redirect, EncodedRule, &pch, '|');
1580                                         StrBufExtract_NextToken(Rule->automsg, EncodedRule, &pch, '|');
1581                                         StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
1582                                         Rule->final = (eSieveFinal) GetTokenDefine(SKEY(Line), econtinue);
1583                                         Put(SieveRules, IKEY(n), Rule, FreeSieveRule);
1584                                         n++;
1585                                 }
1586                         }
1587         }
1588
1589         while (n < MAX_RULES) {
1590                 Rule = (SieveRule*) malloc(sizeof(SieveRule));
1591                 memset(Rule, 0, sizeof(SieveRule));
1592                 Put(SieveRules, IKEY(n), Rule, FreeSieveRule);
1593             
1594                 n++;
1595         }
1596
1597
1598         FreeStrBuf(&EncodedRule);
1599         FreeStrBuf(&Line);
1600         return SieveRules;
1601 }
1602
1603 void
1604 SessionDetachModule_SIEVE
1605 (wcsession *sess)
1606 {
1607         DeleteHash(&sess->KnownSieveScripts);
1608 }
1609
1610 void 
1611 InitModule_SIEVE
1612 (void)
1613 {
1614         REGISTERTokenParamDefine(from);         
1615         REGISTERTokenParamDefine(tocc);         
1616         REGISTERTokenParamDefine(subject);      
1617         REGISTERTokenParamDefine(replyto);      
1618         REGISTERTokenParamDefine(sender);       
1619         REGISTERTokenParamDefine(resentfrom);   
1620         REGISTERTokenParamDefine(resentto);     
1621         REGISTERTokenParamDefine(envfrom);      
1622         REGISTERTokenParamDefine(envto);        
1623         REGISTERTokenParamDefine(xmailer);      
1624         REGISTERTokenParamDefine(xspamflag);    
1625         REGISTERTokenParamDefine(xspamstatus);  
1626         REGISTERTokenParamDefine(listid);       
1627         REGISTERTokenParamDefine(size);         
1628         REGISTERTokenParamDefine(all);
1629
1630         REGISTERTokenParamDefine(contains);
1631         REGISTERTokenParamDefine(notcontains);
1632         REGISTERTokenParamDefine(is);
1633         REGISTERTokenParamDefine(isnot);
1634         REGISTERTokenParamDefine(matches);
1635         REGISTERTokenParamDefine(notmatches);
1636
1637         REGISTERTokenParamDefine(keep);
1638         REGISTERTokenParamDefine(discard);
1639         REGISTERTokenParamDefine(reject);
1640         REGISTERTokenParamDefine(fileinto);
1641         REGISTERTokenParamDefine(redirect);
1642         REGISTERTokenParamDefine(vacation);
1643
1644         REGISTERTokenParamDefine(larger);
1645         REGISTERTokenParamDefine(smaller);
1646
1647         /* these are c-keyworads, so do it by hand. */
1648         RegisterTokenParamDefine(HKEY("continue"), econtinue);
1649         RegisterTokenParamDefine(HKEY("stop"), estop);
1650
1651         RegisterIterator("SIEVE:SCRIPTS", 0, NULL, GetSieveScriptListing, NULL, NULL, CTX_SIEVELIST, CTX_NONE, IT_NOFLAG);
1652
1653         RegisterConditional(HKEY("COND:SIEVE:SCRIPT:ACTIVE"), 0, ConditionalSieveScriptIsActive, CTX_SIEVELIST);
1654         RegisterConditional(HKEY("COND:SIEVE:SCRIPT:ISRULES"), 0, ConditionalSieveScriptIsRulesScript, CTX_SIEVELIST);
1655         RegisterNamespace("SIEVE:SCRIPT:NAME", 0, 1, tmplput_SieveScriptName, NULL, CTX_SIEVELIST);
1656         RegisterNamespace("SIEVE:SCRIPT:CONTENT", 0, 1, tmplput_SieveScriptContent, NULL, CTX_SIEVELIST);
1657
1658  
1659         RegisterIterator("SIEVE:RULES", 0, NULL, GetSieveRules, NULL, DeleteHash, CTX_SIEVESCRIPT, CTX_NONE, IT_NOFLAG);
1660
1661         RegisterConditional(HKEY("COND:SIEVE:ACTIVE"), 1, ConditionalSieveRule_Active, CTX_SIEVESCRIPT);
1662         RegisterConditional(HKEY("COND:SIEVE:HFIELD"), 1, ConditionalSieveRule_hfield, CTX_SIEVESCRIPT);
1663         RegisterConditional(HKEY("COND:SIEVE:COMPARE"), 1, ConditionalSieveRule_compare, CTX_SIEVESCRIPT);
1664         RegisterConditional(HKEY("COND:SIEVE:ACTION"), 1, ConditionalSieveRule_action, CTX_SIEVESCRIPT);
1665         RegisterConditional(HKEY("COND:SIEVE:SIZECOMP"), 1, ConditionalSieveRule_sizecomp, CTX_SIEVESCRIPT);
1666         RegisterConditional(HKEY("COND:SIEVE:FINAL"), 1, ConditionalSieveRule_final, CTX_SIEVESCRIPT);
1667         RegisterConditional(HKEY("COND:SIEVE:THISROOM"), 1, ConditionalSieveRule_ThisRoom, CTX_SIEVESCRIPT);
1668
1669         RegisterNamespace("SIEVE:SCRIPT:HTEXT", 0, 1, tmplput_SieveRule_htext, NULL, CTX_SIEVESCRIPT);
1670         RegisterNamespace("SIEVE:SCRIPT:SIZE", 0, 1, tmplput_SieveRule_sizeval, NULL, CTX_SIEVESCRIPT);
1671         RegisterNamespace("SIEVE:SCRIPT:FILEINTO", 0, 1, tmplput_SieveRule_fileinto, NULL, CTX_SIEVESCRIPT);
1672         RegisterNamespace("SIEVE:SCRIPT:REDIRECT", 0, 1, tmplput_SieveRule_redirect, NULL, CTX_SIEVESCRIPT);
1673         RegisterNamespace("SIEVE:SCRIPT:AUTOMSG", 0, 1, tmplput_SieveRule_automsg, NULL, CTX_SIEVESCRIPT);
1674
1675         /* fetch our room into WCC->ThisRoom, to evaluate while iterating over rooms with COND:THIS:THAT:ROOM */
1676         RegisterNamespace("SIEVE:SCRIPT:LOOKUP_FILEINTO", 0, 1, tmplput_SieveRule_lookup_FileIntoRoom, NULL, CTX_SIEVESCRIPT);
1677
1678 #if FOO
1679         WebcitAddUrlHandler(HKEY("display_sieve"), "", 0, display_sieve, 0);
1680         WebcitAddUrlHandler(HKEY("display_add_remove_scripts"), "", 0, _display_add_remove_scripts, 0);
1681 #endif
1682         WebcitAddUrlHandler(HKEY("save_sieve"), "", 0, save_sieve, 0);
1683
1684         WebcitAddUrlHandler(HKEY("create_script"), "", 0, create_script, 0);
1685         WebcitAddUrlHandler(HKEY("delete_script"), "", 0, delete_script, 0);
1686 }