3 * Copyright (c) 1996-2020 by the citadel.org team
5 * This program is open source software. You can redistribute it and/or
6 * modify it under the terms of the GNU General Public License version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 * Implementation note: this was kind of hacked up when we switched from Sieve to custom rules.
15 * As a result there's probably some cruft in here...
22 CtxType CTX_SIEVELIST = CTX_NONE;
23 CtxType CTX_SIEVESCRIPT = CTX_NONE;
25 #define MAX_SCRIPTS 100
27 #define RULES_SCRIPT "__WebCit_Generated_Script__"
31 * Translate the fields from the rule editor into something we can save...
33 void parse_fields_from_rule_editor(void) {
50 char encoded_rule[4096];
51 char my_addresses[4096];
53 /* Enumerate my email addresses in case they are needed for a vacation rule */
56 serv_getln(buf, sizeof buf);
58 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
59 if (!IsEmptyStr(my_addresses)) {
60 strcat(my_addresses, ",\n");
62 strcat(my_addresses, "\"");
63 strcat(my_addresses, buf);
64 strcat(my_addresses, "\"");
67 /* Now generate the script and write it to the Citadel server */
69 serv_getln(buf, sizeof buf);
74 for (i = 0; i < MAX_RULES; ++i) {
78 sprintf(fname, "active%d", i);
79 active = !strcasecmp(BSTR(fname), "on");
83 sprintf(fname, "hfield%d", i);
84 safestrncpy(hfield, BSTR(fname), sizeof hfield);
86 sprintf(fname, "compare%d", i);
87 safestrncpy(compare, BSTR(fname), sizeof compare);
89 sprintf(fname, "htext%d", i);
90 safestrncpy(htext, BSTR(fname), sizeof htext);
92 sprintf(fname, "sizecomp%d", i);
93 safestrncpy(sizecomp, BSTR(fname), sizeof sizecomp);
95 sprintf(fname, "sizeval%d", i);
96 sizeval = IBSTR(fname);
98 sprintf(fname, "action%d", i);
99 safestrncpy(action, BSTR(fname), sizeof action);
101 sprintf(fname, "fileinto%d", i);
102 safestrncpy(fileinto, BSTR(fname), sizeof fileinto);
104 sprintf(fname, "redirect%d", i);
105 safestrncpy(redirect, BSTR(fname), sizeof redirect);
107 sprintf(fname, "automsg%d", i);
108 safestrncpy(automsg, BSTR(fname), sizeof automsg);
110 sprintf(fname, "final%d", i);
111 safestrncpy(final, BSTR(fname), sizeof final);
113 snprintf(rule, sizeof rule, "%d|%s|%s|%s|%s|%d|%s|%s|%s|%s|%s",
114 active, hfield, compare, htext, sizecomp, sizeval, action, fileinto, redirect, automsg, final);
116 size_t len = CtdlEncodeBase64(encoded_rule, rule, strlen(rule) + 1, BASE64_NO_LINEBREAKS);
117 if (encoded_rule[len - 1] == '\n') {
118 encoded_rule[len - 1] = '\0';
120 serv_printf("rule|%d|%s|", i, encoded_rule);
132 void save_sieve(void) {
134 if (!havebstr("save_button")) {
135 AppendImportantMessage(_("Cancelled. Changes were not saved."), -1);
140 parse_fields_from_rule_editor();
142 AppendImportantMessage(_("Your changes have been saved."), -1);
148 void display_sieve_add_or_delete(void) {
149 output_headers(1, 1, 2, 0, 0, 0);
150 do_template("sieve_add");
157 * dummy panel indicating to the user that the server doesn't support Sieve
159 void display_no_sieve(void) {
161 output_headers(1, 1, 1, 0, 0, 0);
162 do_template("sieve_none");
167 typedef struct __SieveListing {
174 int ConditionalSieveScriptIsActive(StrBuf * Target, WCTemplputParams * TP) {
175 SieveListing *SieveList = (SieveListing *) CTX(CTX_SIEVELIST);
176 return SieveList->IsActive;
178 int ConditionalSieveScriptIsRulesScript(StrBuf * Target, WCTemplputParams * TP) {
179 SieveListing *SieveList = (SieveListing *) CTX(CTX_SIEVELIST);
180 return SieveList->IsActive;
182 void tmplput_SieveScriptName(StrBuf * Target, WCTemplputParams * TP) {
183 SieveListing *SieveList = (SieveListing *) CTX(CTX_SIEVELIST);
184 StrBufAppendTemplate(Target, TP, SieveList->Name, 0);
186 void tmplput_SieveScriptContent(StrBuf * Target, WCTemplputParams * TP) {
187 SieveListing *SieveList = (SieveListing *) CTX(CTX_SIEVELIST);
188 StrBufAppendTemplate(Target, TP, SieveList->Content, 0);
190 void FreeSieveListing(void *vSieveListing) {
191 SieveListing *List = (SieveListing *) vSieveListing;
193 FreeStrBuf(&List->Name);
197 HashList *GetSieveScriptListing(StrBuf * Target, WCTemplputParams * TP) {
201 int rules_script_active = 0;
202 int have_rules_script = 0;
206 SieveListing *Ruleset;
208 if (WCC->KnownSieveScripts != NULL) {
209 return WCC->KnownSieveScripts;
212 serv_puts("MSIV listscripts");
214 StrBuf_ServGetln(Line);
215 if (GetServerStatus(Line, NULL) == 1) {
216 WCC->KnownSieveScripts = NewHash(1, Flathash);
218 while (!Done && (StrBuf_ServGetln(Line) >= 0))
219 if ((StrLength(Line) == 3) && !strcmp(ChrPtr(Line), "000")) {
224 Ruleset = (SieveListing *) malloc(sizeof(SieveListing));
225 Ruleset->Name = NewStrBufPlain(NULL, StrLength(Line));
226 StrBufExtract_NextToken(Ruleset->Name, Line, &pch, '|');
227 Ruleset->IsActive = StrBufExtractNext_int(Line, &pch, '|');
228 Ruleset->Content = NULL;
230 if (!strcasecmp(ChrPtr(Ruleset->Name), RULES_SCRIPT)) {
231 Ruleset->IsRulesScript = 1;
232 have_rules_script = 1;
233 if (Ruleset->IsActive) {
234 rules_script_active = 1;
235 PutBstr(HKEY("__SIEVE:RULESSCRIPT"), NewStrBufPlain(HKEY("1")));
238 Put(WCC->KnownSieveScripts, IKEY(num_scripts), Ruleset, FreeSieveListing);
244 if ((num_scripts > 0) && (rules_script_active == 0)) {
245 PutBstr(HKEY("__SIEVE:EXTERNAL_SCRIPT"), NewStrBufPlain(HKEY("1")));
248 if (num_scripts > have_rules_script) {
255 * ok; we have custom scripts, expose that via bstr, and load the payload.
257 PutBstr(HKEY("__SIEVE:HAVE_EXTERNAL_SCRIPT"), NewStrBufPlain(HKEY("1")));
259 it = GetNewHashPos(WCC->KnownSieveScripts, 0);
260 while (GetNextHashPos(WCC->KnownSieveScripts, it, &len, &Key, &vRuleset) && (vRuleset != NULL)) {
261 Ruleset = (SieveListing *) vRuleset;
262 serv_printf("MSIV getscript|%s", ChrPtr(Ruleset->Name));
263 StrBuf_ServGetln(Line);
264 if (GetServerStatus(Line, NULL) == 1) {
265 Ruleset->Content = NewStrBuf();
267 while (!Done && (rc = StrBuf_ServGetln(Line), rc >= 0))
268 if ((StrLength(Line) == 3) && !strcmp(ChrPtr(Line), "000")) {
272 if (StrLength(Ruleset->Content) > 0)
273 StrBufAppendBufPlain(Ruleset->Content, HKEY("\n"), 0);
274 StrBufAppendBuf(Ruleset->Content, Line, 0);
282 return WCC->KnownSieveScripts;
286 typedef enum __eSieveHfield {
304 typedef enum __eSieveCompare {
313 typedef enum __eSieveAction {
323 typedef enum __eSieveSizeComp {
328 typedef enum __eSieveFinal {
334 typedef struct __SieveRule {
338 eSieveCompare compare;
340 eSieveSizeComp sizecomp;
350 int ConditionalSieveRule_hfield(StrBuf * Target, WCTemplputParams * TP) {
351 SieveRule *Rule = (SieveRule *) CTX(CTX_SIEVESCRIPT);
353 return GetTemplateTokenNumber(Target, TP, 3, from)
356 int ConditionalSieveRule_compare(StrBuf * Target, WCTemplputParams * TP) {
357 SieveRule *Rule = (SieveRule *) CTX(CTX_SIEVESCRIPT);
358 return GetTemplateTokenNumber(Target, TP, 3, contains)
361 int ConditionalSieveRule_action(StrBuf * Target, WCTemplputParams * TP) {
362 SieveRule *Rule = (SieveRule *) CTX(CTX_SIEVESCRIPT);
363 return GetTemplateTokenNumber(Target, TP, 3, keep)
366 int ConditionalSieveRule_sizecomp(StrBuf * Target, WCTemplputParams * TP) {
367 SieveRule *Rule = (SieveRule *) CTX(CTX_SIEVESCRIPT);
368 return GetTemplateTokenNumber(Target, TP, 3, larger)
371 int ConditionalSieveRule_final(StrBuf * Target, WCTemplputParams * TP) {
372 SieveRule *Rule = (SieveRule *) CTX(CTX_SIEVESCRIPT);
373 return GetTemplateTokenNumber(Target, TP, 3, econtinue)
376 int ConditionalSieveRule_ThisRoom(StrBuf * Target, WCTemplputParams * TP) {
377 SieveRule *Rule = (SieveRule *) CTX(CTX_SIEVESCRIPT);
378 return GetTemplateTokenNumber(Target, TP, 3, econtinue)
381 int ConditionalSieveRule_Active(StrBuf * Target, WCTemplputParams * TP) {
382 SieveRule *Rule = (SieveRule *) CTX(CTX_SIEVESCRIPT);
385 void tmplput_SieveRule_htext(StrBuf * Target, WCTemplputParams * TP) {
386 SieveRule *Rule = (SieveRule *) CTX(CTX_SIEVESCRIPT);
387 StrBufAppendTemplate(Target, TP, Rule->htext, 0);
389 void tmplput_SieveRule_fileinto(StrBuf * Target, WCTemplputParams * TP) {
390 SieveRule *Rule = (SieveRule *) CTX(CTX_SIEVESCRIPT);
391 StrBufAppendTemplate(Target, TP, Rule->fileinto, 0);
393 void tmplput_SieveRule_redirect(StrBuf * Target, WCTemplputParams * TP) {
394 SieveRule *Rule = (SieveRule *) CTX(CTX_SIEVESCRIPT);
395 StrBufAppendTemplate(Target, TP, Rule->redirect, 0);
397 void tmplput_SieveRule_automsg(StrBuf * Target, WCTemplputParams * TP) {
398 SieveRule *Rule = (SieveRule *) CTX(CTX_SIEVESCRIPT);
399 StrBufAppendTemplate(Target, TP, Rule->automsg, 0);
401 void tmplput_SieveRule_sizeval(StrBuf * Target, WCTemplputParams * TP) {
402 SieveRule *Rule = (SieveRule *) CTX(CTX_SIEVESCRIPT);
403 StrBufAppendPrintf(Target, "%d", Rule->sizeval);
406 void tmplput_SieveRule_lookup_FileIntoRoom(StrBuf * Target, WCTemplputParams * TP) {
408 SieveRule *Rule = (SieveRule *) CTX(CTX_SIEVESCRIPT);
410 HashList *Rooms = GetRoomListHashLKRA(Target, TP);
412 GetHash(Rooms, SKEY(Rule->fileinto), &vRoom);
413 WCC->ThisRoom = (folder *) vRoom;
416 void FreeSieveRule(void *vRule) {
417 SieveRule *Rule = (SieveRule *) vRule;
419 FreeStrBuf(&Rule->htext);
420 FreeStrBuf(&Rule->fileinto);
421 FreeStrBuf(&Rule->redirect);
422 FreeStrBuf(&Rule->automsg);
427 #define WC_RULE_HEADER "rule|"
428 HashList *GetSieveRules(StrBuf * Target, WCTemplputParams * TP) {
430 StrBuf *EncodedRule = NULL;
432 const char *pch = NULL;
433 HashList *SieveRules = NULL;
435 SieveRule *Rule = NULL;
437 SieveRules = NewHash(1, Flathash);
440 EncodedRule = NewStrBuf();
441 StrBuf_ServGetln(Line);
442 if (GetServerStatus(Line, NULL) == 1) {
443 while (!Done && (StrBuf_ServGetln(Line) >= 0))
444 if ((StrLength(Line) == 3) && !strcmp(ChrPtr(Line), "000")) {
449 /* We just care for our encoded header and skip everything else */
450 if ((StrLength(Line) > sizeof(WC_RULE_HEADER) - 1)
451 && (!strncasecmp(ChrPtr(Line), HKEY(WC_RULE_HEADER)))) {
452 StrBufSkip_NTokenS(Line, &pch, '|', 1);
453 n = StrBufExtractNext_int(Line, &pch, '|');
454 StrBufExtract_NextToken(EncodedRule, Line, &pch, '|');
455 StrBufDecodeBase64(EncodedRule);
457 Rule = (SieveRule *) malloc(sizeof(SieveRule));
459 Rule->htext = NewStrBufPlain(NULL, StrLength(EncodedRule));
461 Rule->fileinto = NewStrBufPlain(NULL, StrLength(EncodedRule));
462 Rule->redirect = NewStrBufPlain(NULL, StrLength(EncodedRule));
463 Rule->automsg = NewStrBufPlain(NULL, StrLength(EncodedRule));
465 /* Grab our existing values to populate */
467 Rule->active = StrBufExtractNext_int(EncodedRule, &pch, '|');
468 StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
470 Rule->hfield = (eSieveHfield) GetTokenDefine(SKEY(Line), tocc);
471 StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
472 Rule->compare = (eSieveCompare) GetTokenDefine(SKEY(Line), contains);
473 StrBufExtract_NextToken(Rule->htext, EncodedRule, &pch, '|');
474 StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
475 Rule->sizecomp = (eSieveSizeComp) GetTokenDefine(SKEY(Line), larger);
476 Rule->sizeval = StrBufExtractNext_int(EncodedRule, &pch, '|');
477 StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
478 Rule->Action = (eSieveAction) GetTokenDefine(SKEY(Line), keep);
479 StrBufExtract_NextToken(Rule->fileinto, EncodedRule, &pch, '|');
480 StrBufExtract_NextToken(Rule->redirect, EncodedRule, &pch, '|');
481 StrBufExtract_NextToken(Rule->automsg, EncodedRule, &pch, '|');
482 StrBufExtract_NextToken(Line, EncodedRule, &pch, '|');
483 Rule->final = (eSieveFinal) GetTokenDefine(SKEY(Line), econtinue);
484 Put(SieveRules, IKEY(n), Rule, FreeSieveRule);
490 while (n < MAX_RULES) {
491 Rule = (SieveRule *) malloc(sizeof(SieveRule));
492 memset(Rule, 0, sizeof(SieveRule));
493 Put(SieveRules, IKEY(n), Rule, FreeSieveRule);
499 FreeStrBuf(&EncodedRule);
504 void SessionDetachModule_SIEVE(wcsession * sess) {
505 DeleteHash(&sess->KnownSieveScripts);
508 void InitModule_SIEVE(void) {
509 RegisterCTX(CTX_SIEVELIST);
510 RegisterCTX(CTX_SIEVESCRIPT);
511 REGISTERTokenParamDefine(from);
512 REGISTERTokenParamDefine(tocc);
513 REGISTERTokenParamDefine(subject);
514 REGISTERTokenParamDefine(replyto);
515 REGISTERTokenParamDefine(sender);
516 REGISTERTokenParamDefine(resentfrom);
517 REGISTERTokenParamDefine(resentto);
518 REGISTERTokenParamDefine(envfrom);
519 REGISTERTokenParamDefine(envto);
520 REGISTERTokenParamDefine(xmailer);
521 REGISTERTokenParamDefine(xspamflag);
522 REGISTERTokenParamDefine(xspamstatus);
523 REGISTERTokenParamDefine(listid);
524 REGISTERTokenParamDefine(size);
525 REGISTERTokenParamDefine(all);
527 REGISTERTokenParamDefine(contains);
528 REGISTERTokenParamDefine(notcontains);
529 REGISTERTokenParamDefine(is);
530 REGISTERTokenParamDefine(isnot);
531 REGISTERTokenParamDefine(matches);
532 REGISTERTokenParamDefine(notmatches);
534 REGISTERTokenParamDefine(keep);
535 REGISTERTokenParamDefine(discard);
536 REGISTERTokenParamDefine(reject);
537 REGISTERTokenParamDefine(fileinto);
538 REGISTERTokenParamDefine(redirect);
539 REGISTERTokenParamDefine(vacation);
541 REGISTERTokenParamDefine(larger);
542 REGISTERTokenParamDefine(smaller);
544 /* these are c-keyworads, so do it by hand. */
545 RegisterTokenParamDefine(HKEY("continue"), econtinue);
546 RegisterTokenParamDefine(HKEY("stop"), estop);
548 RegisterIterator("SIEVE:SCRIPTS", 0, NULL, GetSieveScriptListing, NULL, NULL, CTX_SIEVELIST, CTX_NONE, IT_NOFLAG);
550 RegisterConditional("COND:SIEVE:SCRIPT:ACTIVE", 0, ConditionalSieveScriptIsActive, CTX_SIEVELIST);
551 RegisterConditional("COND:SIEVE:SCRIPT:ISRULES", 0, ConditionalSieveScriptIsRulesScript, CTX_SIEVELIST);
552 RegisterNamespace("SIEVE:SCRIPT:NAME", 0, 1, tmplput_SieveScriptName, NULL, CTX_SIEVELIST);
553 RegisterNamespace("SIEVE:SCRIPT:CONTENT", 0, 1, tmplput_SieveScriptContent, NULL, CTX_SIEVELIST);
556 RegisterIterator("SIEVE:RULES", 0, NULL, GetSieveRules, NULL, DeleteHash, CTX_SIEVESCRIPT, CTX_NONE, IT_NOFLAG);
558 RegisterConditional("COND:SIEVE:ACTIVE", 1, ConditionalSieveRule_Active, CTX_SIEVESCRIPT);
559 RegisterConditional("COND:SIEVE:HFIELD", 1, ConditionalSieveRule_hfield, CTX_SIEVESCRIPT);
560 RegisterConditional("COND:SIEVE:COMPARE", 1, ConditionalSieveRule_compare, CTX_SIEVESCRIPT);
561 RegisterConditional("COND:SIEVE:ACTION", 1, ConditionalSieveRule_action, CTX_SIEVESCRIPT);
562 RegisterConditional("COND:SIEVE:SIZECOMP", 1, ConditionalSieveRule_sizecomp, CTX_SIEVESCRIPT);
563 RegisterConditional("COND:SIEVE:FINAL", 1, ConditionalSieveRule_final, CTX_SIEVESCRIPT);
564 RegisterConditional("COND:SIEVE:THISROOM", 1, ConditionalSieveRule_ThisRoom, CTX_SIEVESCRIPT);
566 RegisterNamespace("SIEVE:SCRIPT:HTEXT", 0, 1, tmplput_SieveRule_htext, NULL, CTX_SIEVESCRIPT);
567 RegisterNamespace("SIEVE:SCRIPT:SIZE", 0, 1, tmplput_SieveRule_sizeval, NULL, CTX_SIEVESCRIPT);
568 RegisterNamespace("SIEVE:SCRIPT:FILEINTO", 0, 1, tmplput_SieveRule_fileinto, NULL, CTX_SIEVESCRIPT);
569 RegisterNamespace("SIEVE:SCRIPT:REDIRECT", 0, 1, tmplput_SieveRule_redirect, NULL, CTX_SIEVESCRIPT);
570 RegisterNamespace("SIEVE:SCRIPT:AUTOMSG", 0, 1, tmplput_SieveRule_automsg, NULL, CTX_SIEVESCRIPT);
572 /* fetch our room into WCC->ThisRoom, to evaluate while iterating over rooms with COND:THIS:THAT:ROOM */
573 RegisterNamespace("SIEVE:SCRIPT:LOOKUP_FILEINTO", 0, 1, tmplput_SieveRule_lookup_FileIntoRoom, NULL, CTX_SIEVESCRIPT);
574 WebcitAddUrlHandler(HKEY("save_sieve"), "", 0, save_sieve, 0);
575 WebcitAddUrlHandler(HKEY("display_sieve_add_or_delete"), "", 0, display_sieve_add_or_delete, 0);