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