1 #include "config.h"
2 /*
3 */
4 
5 /*
6 ** Copyright 2000-2010 Double Precision, Inc.  See COPYING for
7 ** distribution information.
8 */
9 
10 #include	"mailfilter.h"
11 #include	"sqwebmail.h"
12 #include	"maildir.h"
13 #include	"auth.h"
14 #include	"maildir/maildirmisc.h"
15 #include	"maildir/maildirfilter.h"
16 #include	"maildir/autoresponse.h"
17 #include	"numlib/numlib.h"
18 #include	"cgi/cgi.h"
19 #include	<string.h>
20 #include	<stdlib.h>
21 #include	<stdio.h>
22 
23 extern void list_folder(const char *);
24 extern void output_attrencoded(const char *);
25 extern const char *sqwebmail_content_charset;
26 
27 static const char *internal_err=0;
28 
clrfields()29 static void clrfields()
30 {
31 	cgi_put("currentfilternum", "");
32 	cgi_put("rulename", "");
33 	cgi_put("filtertype", "");
34 	cgi_put("hasrecipienttype", "");
35 	cgi_put("hasrecipientaddr", "");
36 	cgi_put("headername", "");
37 	cgi_put("headervalue", "");
38 	cgi_put("headermatchtype", "");
39 	cgi_put("action", "");
40 	cgi_put("forwardaddy", "");
41 	cgi_put("bouncemsg", "");
42 	cgi_put("savefolder", "");
43 	cgi_put("sizecompare", "");
44 	cgi_put("bytecount", "");
45 	cgi_put("continuefiltering", "");
46 	cgi_put("autoresponse_choose", "");
47 	cgi_put("autoresponse_dsn", "");
48 	cgi_put("autoresponse_regexp", "");
49 	cgi_put("autoresponse_dupe", "");
50 	cgi_put("autoresponse_days", "");
51 	cgi_put("autoresponse_from", "");
52 	cgi_put("autoresponse_noquote", "");
53 }
54 
mailfilter_list()55 void mailfilter_list()
56 {
57 struct maildirfilter mf;
58 struct maildirfilterrule *r;
59 unsigned cnt;
60 
61 	memset(&mf, 0, sizeof(mf));
62 
63 	if (maildir_filter_loadmaildirfilter(&mf, "."))
64 	{
65 		maildir_filter_freerules(&mf);
66 		return;
67 	}
68 
69 	for (cnt=0, r=mf.first; r; r=r->next, ++cnt)
70 	{
71 		char *p=unicode_convert_fromutf8(r->rulename_utf8,
72 						   sqwebmail_content_charset,
73 						   NULL);
74 
75 		printf("<option value=\"%u\">", cnt);
76 		output_attrencoded(p ? p:r->rulename_utf8);
77 		printf("</option>");
78 		if (p)
79 			free(p);
80 	}
81 	maildir_filter_freerules(&mf);
82 }
83 
mailfilter_init()84 void mailfilter_init()
85 {
86 const char *p;
87 unsigned n;
88 struct maildirfilter mf;
89 struct maildirfilterrule *r;
90 
91 	if (*cgi("import"))
92 	{
93 		if (maildir_filter_importmaildirfilter("."))
94 		{
95 			printf("%s", getarg("BADIMPORT"));
96 			return;
97 		}
98 	}
99 
100 	if (*cgi("internalerr"))
101 	{
102 		const char *p=internal_err;
103 
104 		if (*cgi("currentfilternum"))
105 			printf("<input name=\"currentfilternum\""
106 			       " type=\"hidden\""
107 			       " value=\"%s\" />", cgi("currentfilternum"));
108 		if (p)
109 			printf("%s", getarg(p));
110 	}
111 	internal_err=0;
112 
113 	if (*cgi("do.save"))
114 	{
115 		if (maildir_filter_exportmaildirfilter(".") ||
116 			maildir_filter_importmaildirfilter("."))
117 			printf("%s", getarg("INTERNAL"));
118 		else
119 			printf("%s", getarg("UPDATED"));
120 		clrfields();
121 	}
122 
123 	if (*cgi("do.add"))
124 		clrfields();
125 	memset(&mf, 0, sizeof(mf));
126 
127 	if (maildir_filter_loadmaildirfilter(&mf, "."))
128 	{
129 		maildir_filter_freerules(&mf);
130 		printf("%s", getarg("BADIMPORT"));
131 		return;
132 	}
133 	if (*(p=cgi("currentfilter")) == 0)
134 	{
135 		maildir_filter_freerules(&mf);
136 		return;
137 	}
138 	n=atoi(p);
139 
140 	for (r=mf.first; n && r; r=r->next)
141 		--n;
142 	if (!r)
143 	{
144 		maildir_filter_freerules(&mf);
145 		return;
146 	}
147 
148 	if (*cgi("do.moveup") && r)
149 	{
150 		maildir_filter_ruleup(&mf, r);
151 		maildir_filter_savemaildirfilter(&mf, ".", login_returnaddr());
152 		clrfields();
153 	}
154 	else if (*cgi("do.movedown") && r)
155 	{
156 		maildir_filter_ruledown(&mf, r);
157 		maildir_filter_savemaildirfilter(&mf, ".", login_returnaddr());
158 		clrfields();
159 	}
160 	else if (*cgi("do.delete") && r)
161 	{
162 		maildir_filter_ruledel(&mf, r);
163 		maildir_filter_savemaildirfilter(&mf, ".", login_returnaddr());
164 		clrfields();
165 	}
166 	else if (*cgi("do.edit"))
167 	{
168 	static char *namebuf=0;
169 	static char *headernamebuf=0;
170 	static char *headervaluebuf=0;
171 	static char *actionbuf=0;
172 	char	numbuf[NUMBUFSIZE+1];
173 
174 		printf("<input name=\"currentfilternum\""
175 			" type=\"hidden\""
176 			" value=\"%s\" />", p);
177 
178 		cgi_put("filtertype",
179 			r->type == startswith ||
180 			r->type == endswith ||
181 			r->type == contains ?
182 				r->flags & MFR_BODY ? "body":"header":
183 			r->type == hasrecipient
184 					? "hasrecipient":
185 			r->type == mimemultipart ?
186 				r->flags & MFR_DOESNOT ?
187 					"nothasmultipart":
188 					"hasmultipart":
189 			r->type == islargerthan ? "hassize":
190 			r->type == anymessage
191 					? "anymessage":
192 			r->type == textplain ?
193 				r->flags & MFR_DOESNOT ?
194 					"nothastextplain":
195 					"hastextplain":""
196 				) ;
197 
198 		cgi_put("continuefiltering",
199 				r->flags & MFR_CONTINUE ? "1":"");
200 
201 		cgi_put("headermatch",
202 			r->type == startswith ?
203 				r->flags & MFR_DOESNOT ? "notstartswith":"startswith":
204 			r->type == endswith ?
205 				r->flags & MFR_DOESNOT ? "notendswith":"endswith":
206 			r->type == contains ?
207 				r->flags & MFR_DOESNOT ? "notcontains":"contains":"");
208 
209 		if (namebuf)	free(namebuf);
210 		p=r->rulename_utf8 ? r->rulename_utf8:"";
211 
212 		namebuf=unicode_convert_fromutf8(p, sqwebmail_content_charset,
213 						   NULL);
214 
215 		if (!namebuf)	enomem();
216 		cgi_put("rulename", namebuf);
217 
218 		p=r->fieldname_utf8 ? r->fieldname_utf8:"";
219 		if (r->type != startswith &&
220 			r->type != endswith &&
221 			r->type != contains)	p="";
222 		if (r->flags & MFR_BODY)	p="";
223 
224 		if (headernamebuf)	free(headernamebuf);
225 		headernamebuf=unicode_convert_fromutf8(p,
226 							 sqwebmail_content_charset,
227 							 NULL);
228 		if (!headernamebuf)	enomem();
229 		cgi_put("headername", headernamebuf);
230 
231 		p=r->fieldvalue_utf8 ? r->fieldvalue_utf8:"";
232 		if (r->type != startswith &&
233 			r->type != endswith &&
234 			r->type != contains &&
235 			r->type != hasrecipient &&
236 			r->type != islargerthan)	p="";
237 
238 		if (r->type == islargerthan)
239 			p=libmail_str_size_t( atol(p)+( r->flags & MFR_DOESNOT ? 1:0),
240 				numbuf);
241 
242 		if (headervaluebuf)	free(headervaluebuf);
243 
244 		headervaluebuf=
245 			unicode_convert_fromutf8(p,
246 						   sqwebmail_content_charset,
247 						   NULL);
248 		if (!headervaluebuf)	enomem();
249 
250 		cgi_put("hasrecipientaddr", "");
251 		cgi_put("headervalue", "");
252 		cgi_put("bytecount", "");
253 		cgi_put("sizecompare", "");
254 
255 		cgi_put(r->type == hasrecipient ? "hasrecipientaddr":
256 			r->type == islargerthan ? "bytecount":
257 				"headervalue", headervaluebuf);
258 
259 		if (r->type == hasrecipient)
260 			cgi_put("hasrecipienttype",
261 				r->flags & MFR_DOESNOT ? "nothasrecipient":
262 					"hasrecipient");
263 		if (r->type == islargerthan)
264 			cgi_put("sizecompare",
265 				r->flags & MFR_DOESNOT
266 					? "issmallerthan":"islargerthan");
267 		if (actionbuf)	actionbuf=0;
268 		p=r->tofolder;
269 		if (!p)	p="";
270 		actionbuf=malloc(strlen(p)+1);
271 		if (!actionbuf)	enomem();
272 		strcpy(actionbuf, p);
273 
274 		cgi_put("bouncemsg", "");
275 		cgi_put("forwardaddy", "");
276 		cgi_put("savefolder", "");
277 
278 		cgi_put("autoresponse_regexp",
279 			r->flags & MFR_PLAINSTRING ? "":"1");
280 
281 		if (actionbuf[0] == '!')
282 		{
283 			cgi_put("action", "forwardto");
284 			cgi_put("forwardaddy", actionbuf+1);
285 		}
286 		else if (actionbuf[0] == '*')
287 		{
288 			cgi_put("action", "bounce");
289 			cgi_put("bouncemsg", actionbuf+1);
290 		}
291 		else if (actionbuf[0] == '+')
292 		{
293 			struct maildir_filter_autoresp_info mfai;
294 			static char *autoresp_name_buf=0;
295 			static char days_buf[NUMBUFSIZE];
296 			static char *fromhdr=0;
297 
298 			if (maildir_filter_autoresp_info_init_str(&mfai, actionbuf+1))
299 				enomem();
300 
301 			if (autoresp_name_buf)
302 				free(autoresp_name_buf);
303 
304 			if ((autoresp_name_buf=strdup(mfai.name)) == NULL)
305 				enomem();
306 
307 			cgi_put("action", "autoresponse");
308 			cgi_put("autoresponse_choose", autoresp_name_buf);
309 			cgi_put("autoresponse_dsn",
310 				mfai.dsnflag ? "1":"");
311 
312 			cgi_put("autoresponse_dupe",
313 				mfai.days > 0 ? "1":"");
314 
315 			libmail_str_size_t(mfai.days, days_buf);
316 			cgi_put("autoresponse_days", mfai.days ?
317 				days_buf:"");
318 			maildir_filter_autoresp_info_free(&mfai);
319 
320 			if (fromhdr)
321 				free(fromhdr);
322 			fromhdr=strdup(r->fromhdr ? r->fromhdr:"");
323 			if (!fromhdr)
324 				enomem();
325 			cgi_put("autoresponse_from", fromhdr);
326 
327 			if (mfai.noquote)
328 				cgi_put("autoresponse_noquote", "1");
329 		}
330 		else if (strcmp(actionbuf, "exit") == 0)
331 		{
332 			cgi_put("action", "purge");
333 		}
334 		else
335 		{
336 			cgi_put("action", "savefolder");
337 			cgi_put("savefolder",
338 				strcmp(actionbuf, ".") == 0 ? INBOX:
339 				*actionbuf == '.' ? actionbuf+1:actionbuf);
340 		}
341 	}
342 	else if (*(p=cgi("currentfilternum")) != 0)
343 	{
344 		printf("<input name=\"currentfilternum\""
345 			" type=\"hidden\""
346 			" value=\"%s\" />", p);
347 	}
348 
349 
350 
351 	maildir_filter_freerules(&mf);
352 }
353 
mailfilter_listfolders()354 void mailfilter_listfolders()
355 {
356 char	**folders;
357 int	i;
358 const char *f=cgi("savefolder");
359 
360 	printf("<select name=\"savefolder\">");
361 
362 	maildir_listfolders(INBOX, ".", &folders);
363 
364 	for (i=0; folders[i]; i++)
365 	{
366 		const char *p=folders[i];
367 		int selected=0;
368 
369 		if (strcmp(p, INBOX) &&
370 		    strncmp(p, INBOX ".", sizeof(INBOX)))
371 			continue;
372 
373 		printf("<option value=\"");
374 		output_attrencoded(p);
375 		if (strcmp(p, f) == 0)
376 			selected=1;
377 
378 		if (strcmp(p, INBOX) == 0)
379 		{
380 			p=getarg("INBOX");
381 			selected=0;
382 			if (strcmp(f, ".") == 0)
383 				selected=1;
384 		}
385 		else if (strcmp(p, INBOX "." DRAFTS) == 0)
386 			p=getarg("DRAFTS");
387 		else if (strcmp(p, INBOX "." TRASH) == 0)
388 			p=getarg("TRASH");
389 		else if (strcmp(p, INBOX "." SENT) == 0)
390 			p=getarg("SENT");
391 		else
392 			p=strchr(p, '.')+1;
393 
394 		printf("\"");
395 		if (selected)
396 			printf(" selected='selected'");
397 		printf(">");
398 		list_folder(p);
399 		printf("</option>\n");
400 	}
401 
402 	maildir_freefolders(&folders);
403 	printf("</select>");
404 }
405 
mailfilter_submit()406 void mailfilter_submit()
407 {
408 struct maildirfilter mf;
409 struct maildirfilterrule *r;
410 const char *p;
411 enum maildirfiltertype type;
412 int flags=0;
413 const char *rulename=0;
414 const char *fieldname=0;
415 const char *fieldvalue=0;
416 char *tofolder=0;
417 char *fieldname_cpy;
418 int	err_num;
419 char	numbuf[NUMBUFSIZE];
420 const char *autoreply_from="";
421 
422 	memset(&mf, 0, sizeof(mf));
423 	if (maildir_filter_loadmaildirfilter(&mf, "."))
424 	{
425 		maildir_filter_freerules(&mf);
426 		return;
427 	}
428 
429 	r=0;
430 	p=cgi("currentfilternum");
431 	if (*p)
432 	{
433 	unsigned n=atoi(p);
434 
435 		for (r=mf.first; r && n; r=r->next)
436 			--n;
437 	}
438 
439 	rulename=cgi("rulename");
440 
441 	p=cgi("filtertype");
442 	if (strcmp(p, "hasrecipient") == 0)
443 	{
444 		type=hasrecipient;
445 		if (strcmp(cgi("hasrecipienttype"), "nothasrecipient") == 0)
446 			flags |= MFR_DOESNOT;
447 
448 		fieldvalue=cgi("hasrecipientaddr");
449 	}
450 	else if (strcmp(p, "hastextplain") == 0)
451 	{
452 		type=textplain;
453 	}
454 	else if (strcmp(p, "nothastextplain") == 0)
455 	{
456 		type=textplain;
457 		flags |= MFR_DOESNOT;
458 	}
459 	else if (strcmp(p, "hasmultipart") == 0)
460 	{
461 		type=mimemultipart;
462 	}
463 	else if (strcmp(p, "nothasmultipart") == 0)
464 	{
465 		type=mimemultipart;
466 		flags |= MFR_DOESNOT;
467 	}
468 	else if (strcmp(p, "hassize") == 0)
469 	{
470 	unsigned long n=atol(cgi("bytecount"));
471 
472 		type=islargerthan;
473 		if (strcmp(cgi("sizecompare"), "issmallerthan") == 0)
474 		{
475 			flags |= MFR_DOESNOT;
476 			if (n)	--n;
477 		}
478 		fieldvalue=libmail_str_size_t(n, numbuf);
479 	}
480 	else if (strcmp(p, "anymessage") == 0)
481 	{
482 		type=anymessage;
483 	}
484 	else
485 	{
486 		if (strcmp(p, "body") == 0)
487 			flags |= MFR_BODY;
488 
489 		fieldname=cgi("headername");
490 		p=cgi("headermatchtype");
491 		type=strcmp(p, "startswith") == 0 ||
492 			strcmp(p, "notstartswith") == 0 ? startswith:
493 			strcmp(p, "contains") == 0 ||
494 			strcmp(p, "notcontains") == 0 ? contains:endswith;
495 		if (strncmp(p, "not", 3) == 0)
496 			flags |= MFR_DOESNOT;
497 		fieldvalue=cgi("headervalue");
498 	}
499 
500 	if (*cgi("continuefiltering"))
501 		flags |= MFR_CONTINUE;
502 
503 	if (!*cgi("autoresponse_regexp"))
504 		flags |= MFR_PLAINSTRING;
505 
506 	p=cgi("action");
507 	if (strcmp(p, "forwardto") == 0)
508 	{
509 		p=cgi("forwardaddy");
510 		tofolder=malloc(strlen(p)+2);
511 		if (!tofolder)	enomem();
512 		strcat(strcpy(tofolder, "!"), p);
513 	}
514 	else if (strcmp(p, "bounce") == 0)
515 	{
516 		p=cgi("bouncemsg");
517 		tofolder=malloc(strlen(p)+2);
518 		if (!tofolder)	enomem();
519 		strcat(strcpy(tofolder, "*"), p);
520 	}
521 	else if (strcmp(p, "autoresponse") == 0)
522 	{
523 		struct maildir_filter_autoresp_info mfaii;
524 		char *q;
525 
526 		p=cgi("autoresponse_choose");
527 
528 		if (maildir_filter_autoresp_info_init(&mfaii, p))
529 		{
530 			internal_err="AUTOREPLY";
531 			cgi_put("internal_err", "1");
532 			return;
533 		}
534 
535 		p=cgi("autoresponse_dsn");
536 
537 		if (*p)
538 			mfaii.dsnflag=1;
539 
540 		p=cgi("autoresponse_dupe");
541 		if (*p)
542 		{
543 			p=cgi("autoresponse_days");
544 			mfaii.days=atoi(p);
545 		}
546 
547 		p=cgi("autoresponse_noquote");
548 
549 		if (*p)
550 			mfaii.noquote=1;
551 
552 		q=maildir_filter_autoresp_info_asstr(&mfaii);
553 		maildir_filter_autoresp_info_free(&mfaii);
554 
555 		if (!q)
556 			enomem();
557 
558 		if (!(tofolder=malloc(strlen(q)+2)))
559 		{
560 			free(q);
561 			enomem();
562 		}
563 
564 		tofolder[0]='+';
565 		strcpy(tofolder+1, q);
566 		free(q);
567 		autoreply_from=cgi("autoresponse_from");
568 	}
569 	else if (strcmp(p, "purge") == 0)
570 	{
571 		tofolder = strdup("exit");
572 		if (!tofolder) enomem();
573 	}
574 	else
575 	{
576 		tofolder=strdup(cgi("savefolder"));
577 		if (!tofolder)	enomem();
578 	}
579 
580 	fieldname_cpy=NULL;
581 
582 	if (fieldname)
583 	{
584 		char *p;
585 
586 		fieldname_cpy=strdup(fieldname);
587 
588 		p=strrchr(fieldname_cpy, ':');
589 
590 		if (p && p[1] == 0)
591 			*p=0;
592 	}
593 
594 	if (!r)
595 		r=maildir_filter_appendrule(&mf, rulename, type, flags, fieldname_cpy,
596 					    fieldvalue, tofolder, autoreply_from, sqwebmail_content_charset, &err_num);
597 	else if (maildir_filter_ruleupdate(&mf, r, rulename, type, flags, fieldname_cpy,
598 					   fieldvalue, tofolder, autoreply_from, sqwebmail_content_charset, &err_num))
599 		r=0;
600 	free(tofolder);
601 	if (fieldname_cpy)
602 		free(fieldname_cpy);
603 	if (r)
604 	{
605 		maildir_filter_savemaildirfilter(&mf, ".", login_returnaddr());
606 		maildir_filter_freerules(&mf);
607 		clrfields();
608 		return;
609 	}
610 	maildir_filter_freerules(&mf);
611 
612 	internal_err="INTERNAL";
613 	if (err_num == MF_ERR_BADRULENAME)
614 		internal_err= "BADRULENAME";
615 	if (err_num == MF_ERR_EXISTS)
616 		internal_err= "EXISTS";
617 	if (err_num == MF_ERR_BADRULEHEADER)
618 		internal_err= "BADHEADER";
619 	if (err_num == MF_ERR_BADRULEVALUE)
620 		internal_err= "BADVALUE";
621 	if (err_num == MF_ERR_BADRULEFOLDER)
622 		internal_err= "ERRTOFOLDER";
623 	if (err_num == MF_ERR_BADFROMHDR)
624 		internal_err= "FROMHDR";
625 
626 	cgi_put("internalerr", "1");
627 }
628 
mailfilter_folderused(const char * foldername)629 int mailfilter_folderused(const char *foldername)
630 {
631 struct maildirfilter mf;
632 struct maildirfilterrule *r;
633 int	rc;
634 
635 	memset(&mf, 0, sizeof(mf));
636 	if (maildir_filter_hasmaildirfilter(".") ||
637 		maildir_filter_importmaildirfilter("."))	return (0);
638 
639 	rc=maildir_filter_loadmaildirfilter(&mf, ".");
640 	maildir_filter_endmaildirfilter(".");
641 	if (rc)
642 	{
643 		maildir_filter_freerules(&mf);
644 		return (0);
645 	}
646 
647 	for (r=mf.first; r; r=r->next)
648 	{
649 		if (r->tofolder == 0)	continue;
650 		if (strcmp(foldername, r->tofolder) == 0)
651 		{
652 			maildir_filter_freerules(&mf);
653 			return (-1);
654 		}
655 	}
656 	maildir_filter_freerules(&mf);
657 	return (0);
658 }
659 
mailfilter_autoreplyused(const char * autoreply)660 int mailfilter_autoreplyused(const char *autoreply)
661 {
662 	struct maildirfilter mf;
663 	struct maildirfilterrule *r;
664 	int	rc;
665 
666 	memset(&mf, 0, sizeof(mf));
667 	if (maildir_filter_hasmaildirfilter(".") ||
668 	    maildir_filter_importmaildirfilter("."))	return (0);
669 
670 	rc=maildir_filter_loadmaildirfilter(&mf, ".");
671 	maildir_filter_endmaildirfilter(".");
672 	if (rc)
673 	{
674 		maildir_filter_freerules(&mf);
675 		return (0);
676 	}
677 
678 	for (r=mf.first; r; r=r->next)
679 	{
680 		struct maildir_filter_autoresp_info mfai;
681 
682 		if (r->tofolder == 0)	continue;
683 		if (r->tofolder[0] != '+')
684 			continue;
685 
686 		if (maildir_filter_autoresp_info_init_str(&mfai, r->tofolder+1))
687 			enomem();
688 
689 		if (strcmp(autoreply, mfai.name) == 0)
690 		{
691 			maildir_filter_autoresp_info_free(&mfai);
692 			maildir_filter_freerules(&mf);
693 			return (-1);
694 		}
695 		maildir_filter_autoresp_info_free(&mfai);
696 	}
697 	maildir_filter_freerules(&mf);
698 	return (0);
699 }
700 
mailfilter_cleanup()701 void mailfilter_cleanup()
702 {
703 	internal_err=0;
704 }
705