1 /* $Id$ */
2
3 /*
4 * Copyright (c) 2006 Nicholas Marriott <nicholas.marriott@gmail.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /* Declarations */
20
21 %{
22 #include <sys/types.h>
23 #include <sys/stat.h>
24
25 #include <ctype.h>
26 #include <grp.h>
27 #include <pwd.h>
28 #include <string.h>
29 #include <syslog.h>
30
31 #include "fdm.h"
32 #include "deliver.h"
33 #include "fetch.h"
34 #include "match.h"
35
36 struct strb *parse_tags;
37 struct macros parse_macros;
38 struct macro *parse_last; /* last command-line argument macro */
39
40 u_int parse_ruleidx;
41 u_int parse_actionidx;
42
43 ARRAY_DECL(, struct rule *) parse_rulestack;
44 struct rule *parse_rule;
45
46 struct files parse_filestack;
47 struct file *parse_file;
48
49 int yyparse(void);
50
51 int
parse_conf(const char * path,struct strings * macros)52 parse_conf(const char *path, struct strings *macros)
53 {
54 struct macro *macro;
55 FILE *f;
56 u_int i;
57
58 if ((f = fopen(path, "r")) == NULL)
59 return (-1);
60
61 ARRAY_INIT(&parse_rulestack);
62 parse_rule = NULL;
63
64 ARRAY_INIT(&parse_filestack);
65 parse_file = xmalloc(sizeof *parse_file);
66
67 parse_file->f = f;
68 parse_file->line = 0;
69 parse_file->path = path;
70
71 strb_create(&parse_tags);
72 default_tags(&parse_tags, NULL);
73 add_tag(&parse_tags, "home", "%s", conf.user_home);
74
75 TAILQ_INIT(&parse_macros);
76 parse_last = NULL;
77 for (i = 0; i < ARRAY_LENGTH(macros); i++) {
78 parse_last = extract_macro(ARRAY_ITEM(macros, i));
79 TAILQ_INSERT_TAIL(&parse_macros, parse_last, entry);
80 }
81
82 parse_file->line++;
83 yyparse();
84
85 if (!ARRAY_EMPTY(&parse_rulestack))
86 yyerror("missing }");
87 ARRAY_FREE(&parse_rulestack);
88
89 ARRAY_FREE(&parse_filestack);
90 xfree(parse_file);
91
92 while (!TAILQ_EMPTY(&parse_macros)) {
93 macro = TAILQ_FIRST(&parse_macros);
94 TAILQ_REMOVE(&parse_macros, macro, entry);
95
96 if (macro->type == MACRO_STRING)
97 xfree(macro->value.str);
98 xfree(macro);
99 }
100
101 strb_destroy(&parse_tags);
102
103 fclose(f);
104 return (0);
105 }
106
107 __dead printflike1 void
yyerror(const char * fmt,...)108 yyerror(const char *fmt, ...)
109 {
110 va_list ap;
111 char *s;
112
113 xasprintf(&s,
114 "%s: %s at line %d", parse_file->path, fmt, parse_file->line);
115
116 va_start(ap, fmt);
117 log_vwrite(LOG_CRIT, s, ap);
118 va_end(ap);
119
120 exit(1);
121 }
122 %}
123
124 %token TOKACCOUNT
125 %token TOKACCOUNTS
126 %token TOKACTION
127 %token TOKACTIONS
128 %token TOKADDHEADER
129 %token TOKADDTOCACHE
130 %token TOKAGE
131 %token TOKALL
132 %token TOKALLOWMANY
133 %token TOKAND
134 %token TOKANYNAME
135 %token TOKANYSIZE
136 %token TOKANYTYPE
137 %token TOKAPPEND
138 %token TOKATTACHMENT
139 %token TOKBODY
140 %token TOKBYTES
141 %token TOKCACHE
142 %token TOKCASE
143 %token TOKCMDUSER
144 %token TOKCOMPRESS
145 %token TOKCONTINUE
146 %token TOKCOUNT
147 %token TOKDAYS
148 %token TOKDEFUSER
149 %token TOKDELTOOBIG
150 %token TOKDISABLED
151 %token TOKDOMAIN
152 %token TOKDOTLOCK
153 %token TOKDROP
154 %token TOKEQ
155 %token TOKEXEC
156 %token TOKEXPIRE
157 %token TOKFCNTL
158 %token TOKFILEGROUP
159 %token TOKFILEUMASK
160 %token TOKFLOCK
161 %token TOKFOLDER
162 %token TOKFOLDERS
163 %token TOKFROM
164 %token TOKGIGABYTES
165 %token TOKGROUP
166 %token TOKGROUPS
167 %token TOKHEADER
168 %token TOKHEADERS
169 %token TOKHOURS
170 %token TOKIGNOREERRORS
171 %token TOKIMAP
172 %token TOKIMAPS
173 %token TOKIMPLACT
174 %token TOKIN
175 %token TOKINCACHE
176 %token TOKINSECURE
177 %token TOKINVALID
178 %token TOKKEEP
179 %token TOKKEY
180 %token TOKKILOBYTES
181 %token TOKLOCKFILE
182 %token TOKLOCKTIMEOUT
183 %token TOKLOCKTYPES
184 %token TOKLOCKWAIT
185 %token TOKLOOKUPORDER
186 %token TOKMAILDIR
187 %token TOKMAILDIRS
188 %token TOKMATCH
189 %token TOKMATCHED
190 %token TOKMAXSIZE
191 %token TOKMBOX
192 %token TOKMBOXES
193 %token TOKMEGABYTES
194 %token TOKMINUTES
195 %token TOKMONTHS
196 %token TOKNE
197 %token TOKNEWONLY
198 %token TOKNNTP
199 %token TOKNNTPS
200 %token TOKNOAPOP
201 %token TOKNOCRAMMD5
202 %token TOKNOCREATE
203 %token TOKNOLOGIN
204 %token TOKNONE
205 %token TOKNORECEIVED
206 %token TOKNOT
207 %token TOKNOUIDL
208 %token TOKNOVERIFY
209 %token TOKOLDONLY
210 %token TOKOR
211 %token TOKPARALLELACCOUNTS
212 %token TOKPASS
213 %token TOKPASSWD
214 %token TOKPIPE
215 %token TOKPOP3
216 %token TOKPOP3S
217 %token TOKPORT
218 %token TOKPROXY
219 %token TOKPURGEAFTER
220 %token TOKQUEUEHIGH
221 %token TOKQUEUELOW
222 %token TOKREMOVEFROMCACHE
223 %token TOKREMOVEHEADER
224 %token TOKREMOVEHEADERS
225 %token TOKRETURNS
226 %token TOKREWRITE
227 %token TOKSECONDS
228 %token TOKSERVER
229 %token TOKSET
230 %token TOKSIZE
231 %token TOKSMTP
232 %token TOKSTARTTLS
233 %token TOKSTDIN
234 %token TOKSTDOUT
235 %token TOKSTRING
236 %token TOKSTRIPCHARACTERS
237 %token TOKTAG
238 %token TOKTAGGED
239 %token TOKTIMEOUT
240 %token TOKTO
241 %token TOKTOTALSIZE
242 %token TOKUNMATCHED
243 %token TOKUSER
244 %token TOKUSERS
245 %token TOKVALUE
246 %token TOKVERIFYCERTS
247 %token TOKWEEKS
248 %token TOKWRITE
249 %token TOKYEARS
250
251 %union
252 {
253 long long number;
254 char *string;
255 int flag;
256 u_int locks;
257 struct {
258 struct fetch *fetch;
259 void *data;
260 } fetch;
261 struct {
262 char *host;
263 char *port;
264 } server;
265 enum area area;
266 enum exprop exprop;
267 struct actitem *actitem;
268 struct actlist *actlist;
269 struct expr *expr;
270 struct expritem *expritem;
271 struct strings *strings;
272 struct replstrs *replstrs;
273 enum fetch_only only;
274 struct {
275 char *path;
276 enum fetch_only only;
277 } poponly;
278 struct {
279 int flags;
280 char *str;
281 } re;
282 gid_t localgid;
283 enum cmp cmp;
284 struct rule *rule;
285 struct {
286 char *user;
287 int user_netrc;
288 char *pass;
289 int pass_netrc;
290 } userpass;
291 userfunction ufn;
292 struct userfunctions *ufns;
293 }
294
295 %token NONE
296 %token <number> NUMBER
297 %token <string> STRING STRMACRO NUMMACRO
298 %token <string> STRCOMMAND NUMCOMMAND
299
300 %type <actitem> actitem
301 %type <actlist> actlist
302 %type <area> area
303 %type <cmp> cmp ltgt eqne
304 %type <expr> expr exprlist
305 %type <expritem> expritem
306 %type <exprop> exprop
307 %type <fetch> fetchtype
308 %type <flag> cont not disabled keep execpipe writeappend compress verify
309 %type <flag> apop poptype imaptype nntptype nocrammd5 nologin uidl starttls
310 %type <flag> insecure
311 %type <localgid> localgid
312 %type <locks> lock locklist
313 %type <number> size time numv retrc expire
314 %type <only> only imaponly
315 %type <poponly> poponly
316 %type <replstrs> replstrslist actions rmheaders accounts users
317 %type <re> casere retre
318 %type <rule> perform
319 %type <server> server
320 %type <string> port to from xstrv strv replstrv replpathv val optval folder1
321 %type <string> user
322 %type <strings> stringslist pathslist maildirs mboxes groups folders folderlist
323 %type <userpass> userpass userpassreqd userpassnetrc
324 %type <ufn> ufn
325 %type <ufns> ufnlist
326
327 %%
328
329 /* Rules */
330
331 cmds: /* empty */
332 | cmds account
333 | cmds defaction
334 | cmds defmacro
335 | cmds rule
336 | cmds set
337 | cmds close
338 | cmds cache
339 | cmds NONE
340
341 /* Plural/singular combinations. */
342 actionp: TOKACTION
343 | TOKACTIONS
344 userp: TOKUSER
345 | TOKUSERS
346 accountp: TOKACCOUNT
347 | TOKACCOUNTS
348 groupp: TOKGROUP
349 | TOKGROUPS
350 folderp: TOKFOLDER
351 | TOKFOLDERS
352 maildirp: TOKMAILDIR
353 | TOKMAILDIRS
354 mboxp: TOKMBOX
355 | TOKMBOXES
356 rmheaderp: TOKREMOVEHEADER
357 | TOKREMOVEHEADERS
358
359 val: TOKVALUE strv
360 {
361 $$ = $2;
362 }
363 | strv
364 {
365 $$ = $1;
366 }
367
368 optval: TOKVALUE strv
369 {
370 $$ = $2;
371 }
372 | /* empty */
373 {
374 $$ = NULL;
375 }
376
377 xstrv: STRCOMMAND
378 {
379 $$ = run_command($1, parse_file->path);
380 xfree($1);
381 }
382 | STRING
383 {
384 $$ = $1;
385 }
386 | STRMACRO
387 {
388 struct macro *macro;
389
390 if (strlen($1) > MAXNAMESIZE)
391 yyerror("macro name too long: %s", $1);
392
393 if ((macro = find_macro($1)) == NULL)
394 yyerror("undefined macro: %s", $1);
395 if (macro->type != MACRO_STRING)
396 yyerror("string macro expected: %s", $1);
397
398 $$ = xstrdup(macro->value.str);
399
400 xfree($1);
401 }
402
403 strv: xstrv
404 {
405 $$ = $1;
406 }
407 | strv '+' xstrv
408 {
409 size_t size;
410
411 size = strlen($1) + strlen($3) + 1;
412 $$ = xrealloc($1, 1, size);
413 strlcat($$, $3, size);
414 xfree($3);
415 }
416
417 numv: NUMCOMMAND
418 {
419 const char *errstr;
420 char *s;
421
422 s = run_command($1, parse_file->path);
423
424 $$ = strtonum(s, 0, LLONG_MAX, &errstr);
425 if (errstr != NULL)
426 yyerror("number is %s", errstr);
427
428 xfree(s);
429
430 xfree($1);
431 }
432 | NUMBER
433 {
434 $$ = $1;
435 }
436 | NUMMACRO
437 {
438 struct macro *macro;
439
440 if (strlen($1) > MAXNAMESIZE)
441 yyerror("macro name too long: %s", $1);
442
443 if ((macro = find_macro($1)) == NULL)
444 yyerror("undefined macro: %s", $1);
445 if (macro->type != MACRO_NUMBER)
446 yyerror("number macro expected: %s", $1);
447
448 $$ = macro->value.num;
449
450 xfree($1);
451 }
452
453 replstrv: strv
454 {
455 struct replstr rs;
456
457 rs.str = $1;
458 $$ = replacestr(&rs, parse_tags, NULL, NULL);
459 xfree($1);
460 }
461
462 replpathv: strv
463 {
464 struct replpath rp;
465
466 rp.str = $1;
467 $$ = replacepath(&rp, parse_tags, NULL, NULL, conf.user_home);
468 xfree($1);
469 }
470
471 size: numv
472 {
473 $$ = $1;
474 }
475 | numv TOKBYTES
476 {
477 $$ = $1;
478 }
479 | numv TOKKILOBYTES
480 {
481 if ($1 > LLONG_MAX / 1024)
482 yyerror("size is too big");
483 $$ = $1 * 1024;
484 }
485 | numv TOKMEGABYTES
486 {
487 if ($1 > LLONG_MAX / (1024 * 1024))
488 yyerror("size is too big");
489 $$ = $1 * (1024 * 1024);
490 }
491 | numv TOKGIGABYTES
492 {
493 if ($1 > LLONG_MAX / (1024 * 1024 * 1024))
494 yyerror("size is too big");
495 $$ = $1 * (1024 * 1024 * 1024);
496 }
497
498 time: numv
499 {
500 $$ = $1;
501 }
502 | numv TOKHOURS
503 {
504 if ($1 > LLONG_MAX / TIME_HOUR)
505 yyerror("time is too long");
506 $$ = $1 * TIME_HOUR;
507 }
508 | numv TOKMINUTES
509 {
510 if ($1 > LLONG_MAX / TIME_MINUTE)
511 yyerror("time is too long");
512 $$ = $1 * TIME_MINUTE;
513 }
514 | numv TOKSECONDS
515 {
516 $$ = $1;
517 }
518 | numv TOKDAYS
519 {
520 if ($1 > LLONG_MAX / TIME_DAY)
521 yyerror("time is too long");
522 $$ = $1 * TIME_DAY;
523 }
524 | numv TOKWEEKS
525 {
526 if ($1 > LLONG_MAX / TIME_WEEK)
527 yyerror("time is too long");
528 $$ = $1 * TIME_WEEK;
529 }
530 | numv TOKMONTHS
531 {
532 if ($1 > LLONG_MAX / TIME_MONTH)
533 yyerror("time is too long");
534 $$ = $1 * TIME_MONTH;
535 }
536 | numv TOKYEARS
537 {
538 if ($1 > LLONG_MAX / TIME_YEAR)
539 yyerror("time is too long");
540 $$ = $1 * TIME_YEAR;
541 }
542
543 expire: TOKEXPIRE time
544 {
545 #if UINT64_MAX < LLONG_MAX
546 if ($2 > UINT64_MAX)
547 yyerror("time too long");
548 #endif
549
550 $$ = $2;
551 }
552 | /* empty */
553 {
554 $$ = -1;
555 }
556
557 cache: TOKCACHE replpathv expire
558 {
559 struct cache *cache;
560
561 TAILQ_FOREACH(cache, &conf.caches, entry) {
562 if (strcmp(cache->path, $2) == 0)
563 yyerror("duplicate cache path");
564 }
565
566 cache = xcalloc(1, sizeof *cache);
567 cache->path = $2;
568 cache->expire = $3;
569
570 TAILQ_INSERT_TAIL(&conf.caches, cache, entry);
571
572 log_debug2("added cache \"%s\": expire %lld", cache->path, $3);
573 }
574
575 set: TOKSET TOKMAXSIZE size
576 {
577 if ($3 == 0)
578 yyerror("zero maximum size");
579 if ($3 > MAXMAILSIZE)
580 yyerror("maximum size too large: %lld", $3);
581 conf.max_size = $3;
582 }
583 | TOKSET TOKLOCKTYPES locklist
584 {
585 if ($3 & LOCK_FCNTL && $3 & LOCK_FLOCK)
586 yyerror("fcntl and flock locking cannot be used together");
587 conf.lock_types = $3;
588 }
589 | TOKSET TOKLOCKFILE replpathv
590 {
591 if (conf.lock_file != NULL)
592 xfree(conf.lock_file);
593 conf.lock_file = $3;
594 }
595 | TOKSET TOKLOCKWAIT
596 {
597 conf.lock_wait = 1;
598 }
599 | TOKSET TOKLOCKTIMEOUT time
600 {
601 conf.lock_timeout = $3;
602 }
603 | TOKSET TOKDELTOOBIG
604 {
605 conf.del_big = 1;
606 }
607 | TOKSET TOKIGNOREERRORS
608 {
609 conf.ignore_errors = 1;
610 }
611 | TOKSET TOKALLOWMANY
612 {
613 conf.allow_many = 1;
614 }
615 | TOKSET TOKDEFUSER strv
616 {
617 if (conf.def_user == NULL)
618 conf.def_user = $3;
619 }
620 | TOKSET TOKCMDUSER strv
621 {
622 if (conf.cmd_user == NULL)
623 conf.cmd_user = $3;
624 }
625 | TOKSET TOKSTRIPCHARACTERS strv
626 {
627 xfree(conf.strip_chars);
628 conf.strip_chars = $3;
629 }
630 | TOKSET TOKTIMEOUT time
631 {
632 if ($3 == 0)
633 yyerror("zero timeout");
634 if ($3 > INT_MAX / 1000)
635 yyerror("timeout too long: %lld", $3);
636 conf.timeout = $3 * 1000;
637 }
638 | TOKSET TOKQUEUEHIGH numv
639 {
640 if ($3 == 0)
641 yyerror("zero queue-high");
642 if ($3 > MAXQUEUEVALUE)
643 yyerror("queue-high too big: %lld", $3);
644 if (conf.queue_low != -1 && $3 <= conf.queue_low)
645 yyerror("queue-high must be larger than queue-low");
646 conf.queue_high = $3;
647 }
648 | TOKSET TOKQUEUELOW numv
649 {
650 if ($3 > MAXQUEUEVALUE)
651 yyerror("queue-low too big: %lld", $3);
652 if (conf.queue_high == -1)
653 yyerror("queue-high not specified");
654 if ($3 >= conf.queue_high)
655 yyerror("queue-low must be smaller than queue-high");
656 conf.queue_low = $3;
657 }
658 | TOKSET TOKPARALLELACCOUNTS numv
659 {
660 if ($3 > INT_MAX)
661 yyerror("parallel-accounts too big: %lld", $3);
662 if ($3 == 0)
663 yyerror("parallel-accounts cannot be zero");
664 conf.max_accts = $3;
665 }
666 | TOKSET TOKPROXY replstrv
667 {
668 if (conf.proxy != NULL) {
669 xfree(conf.proxy->server.host);
670 xfree(conf.proxy->server.port);
671 if (conf.proxy->user != NULL)
672 xfree(conf.proxy->user);
673 if (conf.proxy->pass != NULL)
674 xfree(conf.proxy->pass);
675 }
676 if ((conf.proxy = getproxy($3)) == NULL)
677 yyerror("invalid proxy");
678 xfree($3);
679 }
680 | TOKSET TOKVERIFYCERTS
681 {
682 conf.verify_certs = 1;
683 }
684 | TOKSET TOKIMPLACT TOKKEEP
685 {
686 conf.impl_act = DECISION_KEEP;
687 }
688 | TOKSET TOKIMPLACT TOKDROP
689 {
690 conf.impl_act = DECISION_DROP;
691 }
692 | TOKSET TOKPURGEAFTER numv
693 {
694 if ($3 == 0)
695 yyerror("invalid purge-after value: 0");
696 if ($3 > UINT_MAX)
697 yyerror("purge-after value too large: %lld", $3);
698
699 conf.purge_after = $3;
700 }
701 | TOKSET TOKPURGEAFTER TOKNONE
702 {
703 conf.purge_after = 0;
704 }
705 | TOKSET TOKNORECEIVED
706 {
707 conf.no_received = 1;
708 }
709 | TOKSET TOKNOCREATE
710 {
711 conf.no_create = 1;
712 }
713 | TOKSET TOKFILEGROUP TOKUSER
714 {
715 conf.file_group = -1;
716 }
717 | TOKSET TOKFILEGROUP localgid
718 {
719 conf.file_group = $3;
720 }
721 | TOKSET TOKFILEUMASK TOKUSER
722 {
723 conf.file_umask = umask(0);
724 umask(conf.file_umask);
725 }
726 | TOKSET TOKLOOKUPORDER ufnlist
727 {
728 ARRAY_FREEALL(conf.user_order);
729 conf.user_order = $3;
730 }
731 | TOKSET TOKFILEUMASK numv
732 {
733 char s[8];
734 u_int n;
735
736 /*
737 * We can't differentiate umasks in octal from normal numbers
738 * (requiring a leading zero a la C would be nice, but it would
739 * potentially break existing configs), so we need to fiddle to
740 * convert.
741 */
742 memset(s, 0, sizeof s);
743 xsnprintf(s, sizeof s, "%03lld", $3);
744 if (s[3] != '\0' || s[0] < '0' || s[0] > '7' ||
745 s[1] < 0 || s[1] > '7' || s[2] < '0' || s[2] > '7')
746 yyerror("invalid umask: %s", s);
747 if (sscanf(s, "%o", &n) != 1)
748 yyerror("invalid umask: %s", s);
749 conf.file_umask = n;
750 }
751
752 defmacro: STRMACRO '=' strv
753 {
754 struct macro *macro;
755
756 if (strlen($1) > MAXNAMESIZE)
757 yyerror("macro name too long: %s", $1);
758
759 macro = xmalloc(sizeof *macro);
760 strlcpy(macro->name, $1, sizeof macro->name);
761 macro->type = MACRO_STRING;
762 macro->value.str = $3;
763
764 if (parse_last == NULL)
765 TAILQ_INSERT_HEAD(&parse_macros, macro, entry);
766 else {
767 TAILQ_INSERT_AFTER(
768 &parse_macros, parse_last, macro, entry);
769 }
770
771 log_debug3("added macro \"%s\": \"%s\"", macro->name,
772 macro->value.str);
773 xfree($1);
774 }
775 | NUMMACRO '=' numv
776 {
777 struct macro *macro;
778
779 if (strlen($1) > MAXNAMESIZE)
780 yyerror("macro name too long: %s", $1);
781
782 macro = xmalloc(sizeof *macro);
783 strlcpy(macro->name, $1, sizeof macro->name);
784 macro->type = MACRO_NUMBER;
785 macro->value.num = $3;
786
787 if (parse_last == NULL)
788 TAILQ_INSERT_HEAD(&parse_macros, macro, entry);
789 else {
790 TAILQ_INSERT_AFTER(
791 &parse_macros, parse_last, macro, entry);
792 }
793
794 log_debug3("added macro \"%s\": %lld", macro->name,
795 macro->value.num);
796 xfree($1);
797 }
798
799 replstrslist: replstrslist strv
800 {
801 if (*$2 == '\0')
802 yyerror("empty string in list");
803
804 $$ = $1;
805 ARRAY_EXPAND($$, 1);
806 ARRAY_LAST($$).str = $2;
807 }
808 | strv
809 {
810 if (*$1 == '\0')
811 yyerror("empty string in list");
812
813 $$ = xmalloc(sizeof *$$);
814 ARRAY_INIT($$);
815 ARRAY_EXPAND($$, 1);
816 ARRAY_LAST($$).str = $1;
817 }
818
819 stringslist: stringslist replstrv
820 {
821 if (*$2 == '\0')
822 yyerror("empty string in list");
823
824 $$ = $1;
825 ARRAY_ADD($$, $2);
826 }
827 | replstrv
828 {
829 if (*$1 == '\0')
830 yyerror("empty string in list");
831
832 $$ = xmalloc(sizeof *$$);
833 ARRAY_INIT($$);
834 ARRAY_ADD($$, $1);
835 }
836
837 pathslist: pathslist replpathv
838 {
839 if (*$2 == '\0')
840 yyerror("invalid path");
841
842 $$ = $1;
843 ARRAY_ADD($$, $2);
844 }
845 | replpathv
846 {
847 if (*$1 == '\0')
848 yyerror("invalid path");
849
850 $$ = xmalloc(sizeof *$$);
851 ARRAY_INIT($$);
852 ARRAY_ADD($$, $1);
853 }
854
855 ufn: TOKPASSWD
856 {
857 $$ = &passwd_lookup;
858 }
859
860 ufnlist: ufnlist ufn
861 {
862 $$ = $1;
863 ARRAY_ADD($$, $2);
864 }
865 | ufn
866 {
867 $$ = xmalloc(sizeof *$$);
868 ARRAY_INIT($$);
869 ARRAY_ADD($$, $1);
870 }
871
872 rmheaders: rmheaderp strv
873 {
874 if (*$2 == '\0')
875 yyerror("invalid header");
876
877 $$ = xmalloc(sizeof *$$);
878 ARRAY_INIT($$);
879 ARRAY_EXPAND($$, 1);
880 ARRAY_LAST($$).str = $2;
881 }
882 | rmheaderp '{' replstrslist '}'
883 {
884 $$ = $3;
885 }
886
887 maildirs: maildirp replpathv
888 {
889 if (*$2 == '\0')
890 yyerror("invalid path");
891
892 $$ = xmalloc(sizeof *$$);
893 ARRAY_INIT($$);
894 ARRAY_ADD($$, $2);
895 }
896 | maildirp '{' pathslist '}'
897 {
898 $$ = $3;
899 }
900
901 mboxes: mboxp replpathv
902 {
903 if (*$2 == '\0')
904 yyerror("invalid path");
905
906 $$ = xmalloc(sizeof *$$);
907 ARRAY_INIT($$);
908 ARRAY_ADD($$, $2);
909 }
910 | mboxp '{' pathslist '}'
911 {
912 $$ = $3;
913 }
914
915 folders: folderp replstrv
916 {
917 if (*$2 == '\0')
918 yyerror("invalid folder");
919
920 $$ = xmalloc(sizeof *$$);
921 ARRAY_INIT($$);
922 ARRAY_ADD($$, $2);
923 }
924 | folderp '{' stringslist '}'
925 {
926 $$ = $3;
927 }
928
929 lock: TOKFCNTL
930 {
931 $$ = LOCK_FCNTL;
932 }
933 | TOKFLOCK
934 {
935 $$ = LOCK_FLOCK;
936 }
937 | TOKDOTLOCK
938 {
939 $$ = LOCK_DOTLOCK;
940 }
941
942 locklist: locklist lock
943 {
944 $$ = $1 | $2;
945 }
946 | lock
947 {
948 $$ = $1;
949 }
950 | TOKNONE
951 {
952 $$ = 0;
953 }
954
955 localgid: replstrv
956 {
957 struct group *gr;
958
959 if (*$1 == '\0')
960 yyerror("invalid group");
961
962 gr = getgrnam($1);
963 if (gr == NULL)
964 yyerror("unknown group: %s", $1);
965 $$ = gr->gr_gid;
966 endgrent();
967
968 xfree($1);
969 }
970 | numv
971 {
972 struct group *gr;
973
974 if ($1 > GID_MAX)
975 yyerror("invalid gid: %llu", $1);
976 gr = getgrgid($1);
977 if (gr == NULL)
978 yyerror("unknown gid: %llu", $1);
979 $$ = gr->gr_gid;
980 endgrent();
981 }
982
983 user: /* empty */
984 {
985 $$ = NULL;
986 }
987 | TOKUSER strv
988 {
989 $$ = $2;
990 }
991
992 users: /* empty */
993 {
994 $$ = NULL;
995 }
996 | userp strv
997 {
998 $$ = xmalloc(sizeof *$$);
999 ARRAY_INIT($$);
1000 ARRAY_EXPAND($$, 1);
1001 ARRAY_LAST($$).str = $2;
1002 }
1003 | userp '{' replstrslist '}'
1004 {
1005 $$ = $3;
1006 }
1007
1008 casere: TOKCASE replstrv
1009 {
1010 /* match case */
1011 $$.flags = 0;
1012 $$.str = $2;
1013 }
1014 | replstrv
1015 {
1016 /* ignore case */
1017 $$.flags = RE_IGNCASE;
1018 $$.str = $1;
1019 }
1020
1021 not: TOKNOT
1022 {
1023 $$ = 1;
1024 }
1025 | /* empty */
1026 {
1027 $$ = 0;
1028 }
1029
1030 keep: TOKKEEP
1031 {
1032 $$ = 1;
1033 }
1034 | /* empty */
1035 {
1036 $$ = 0;
1037 }
1038
1039 disabled: TOKDISABLED
1040 {
1041 $$ = 1;
1042 }
1043 | /* empty */
1044 {
1045 $$ = 0;
1046 }
1047
1048 port: TOKPORT replstrv
1049 {
1050 if (*$2 == '\0')
1051 yyerror("invalid port");
1052
1053 $$ = $2;
1054 }
1055 | TOKPORT numv
1056 {
1057 if ($2 == 0 || $2 > 65535)
1058 yyerror("invalid port");
1059
1060 xasprintf(&$$, "%lld", $2);
1061 }
1062
1063 server: TOKSERVER replstrv port
1064 {
1065 if (*$2 == '\0')
1066 yyerror("invalid host");
1067
1068 $$.host = $2;
1069 $$.port = $3;
1070 }
1071 | TOKSERVER replstrv
1072 {
1073 if (*$2 == '\0')
1074 yyerror("invalid host");
1075
1076 $$.host = $2;
1077 $$.port = NULL;
1078 }
1079
1080 to: /* empty */
1081 {
1082 $$ = NULL;
1083 }
1084 | TOKTO strv
1085 {
1086 $$ = $2;
1087 }
1088
1089 from: /* empty */
1090 {
1091 $$ = NULL;
1092 }
1093 | TOKFROM strv
1094 {
1095 $$ = $2;
1096 }
1097
1098 compress: TOKCOMPRESS
1099 {
1100 $$ = 1;
1101 }
1102 | /* empty */
1103 {
1104 $$ = 0;
1105 }
1106
1107 actitem: execpipe strv
1108 {
1109 struct deliver_pipe_data *data;
1110
1111 if (*$2 == '\0')
1112 yyerror("invalid command");
1113
1114 $$ = xcalloc(1, sizeof *$$);
1115 $$->deliver = &deliver_pipe;
1116
1117 data = xcalloc(1, sizeof *data);
1118 $$->data = data;
1119
1120 data->pipe = $1;
1121 data->cmd.str = $2;
1122 }
1123 | TOKREWRITE strv
1124 {
1125 struct deliver_rewrite_data *data;
1126
1127 if (*$2 == '\0')
1128 yyerror("invalid command");
1129
1130 $$ = xcalloc(1, sizeof *$$);
1131 $$->deliver = &deliver_rewrite;
1132
1133 data = xcalloc(1, sizeof *data);
1134 $$->data = data;
1135
1136 data->cmd.str = $2;
1137 }
1138 | writeappend strv
1139 {
1140 struct deliver_write_data *data;
1141
1142 if (*$2 == '\0')
1143 yyerror("invalid path");
1144
1145 $$ = xcalloc(1, sizeof *$$);
1146 $$->deliver = &deliver_write;
1147
1148 data = xcalloc(1, sizeof *data);
1149 $$->data = data;
1150
1151 data->append = $1;
1152 data->path.str = $2;
1153 }
1154 | TOKMAILDIR strv
1155 {
1156 struct deliver_maildir_data *data;
1157
1158 if (*$2 == '\0')
1159 yyerror("invalid path");
1160
1161 $$ = xcalloc(1, sizeof *$$);
1162 $$->deliver = &deliver_maildir;
1163
1164 data = xcalloc(1, sizeof *data);
1165 $$->data = data;
1166
1167 data->path.str = $2;
1168 }
1169 | rmheaders
1170 {
1171 struct deliver_remove_header_data *data;
1172
1173 $$ = xcalloc(1, sizeof *$$);
1174 $$->deliver = &deliver_remove_header;
1175
1176 data = xcalloc(1, sizeof *data);
1177 $$->data = data;
1178
1179 data->hdrs = $1;
1180 }
1181 | TOKADDHEADER strv val
1182 {
1183 struct deliver_add_header_data *data;
1184
1185 if (*$2 == '\0')
1186 yyerror("invalid header");
1187
1188 $$ = xcalloc(1, sizeof *$$);
1189 $$->deliver = &deliver_add_header;
1190
1191 data = xcalloc(1, sizeof *data);
1192 $$->data = data;
1193
1194 data->hdr.str = $2;
1195 data->value.str = $3;
1196 }
1197 | TOKMBOX strv compress
1198 {
1199 struct deliver_mbox_data *data;
1200
1201 if (*$2 == '\0')
1202 yyerror("invalid path");
1203
1204 $$ = xcalloc(1, sizeof *$$);
1205 $$->deliver = &deliver_mbox;
1206
1207 data = xcalloc(1, sizeof *data);
1208 $$->data = data;
1209
1210 data->path.str = $2;
1211 data->compress = $3;
1212 }
1213 | imaptype server userpassnetrc folder1 verify nocrammd5 nologin
1214 starttls insecure
1215 {
1216 struct deliver_imap_data *data;
1217
1218 if ($1 && $8)
1219 yyerror("use either imaps or set starttls");
1220
1221 $$ = xcalloc(1, sizeof *$$);
1222 $$->deliver = &deliver_imap;
1223
1224 data = xcalloc(1, sizeof *data);
1225 $$->data = data;
1226
1227 if ($3.user_netrc && $3.pass_netrc)
1228 find_netrc($2.host, &data->user, &data->pass);
1229 else {
1230 if ($3.user_netrc)
1231 find_netrc($2.host, &data->user, NULL);
1232 else
1233 data->user = $3.user;
1234 if ($3.pass_netrc)
1235 find_netrc($2.host, NULL, &data->pass);
1236 else
1237 data->pass = $3.pass;
1238 }
1239
1240 data->folder.str = $4;
1241 data->server.ssl = $1;
1242 data->server.verify = $5;
1243 data->server.host = $2.host;
1244 if ($2.port != NULL)
1245 data->server.port = $2.port;
1246 else if ($1)
1247 data->server.port = xstrdup("imaps");
1248 else
1249 data->server.port = xstrdup("imap");
1250 data->server.ai = NULL;
1251 data->nocrammd5 = $6;
1252 data->nologin = $7;
1253 data->starttls = $8;
1254 data->server.insecure = $9;
1255 }
1256 | TOKSMTP server from to
1257 {
1258 struct deliver_smtp_data *data;
1259
1260 $$ = xcalloc(1, sizeof *$$);
1261 $$->deliver = &deliver_smtp;
1262
1263 data = xcalloc(1, sizeof *data);
1264 $$->data = data;
1265
1266 data->server.host = $2.host;
1267 if ($2.port != NULL)
1268 data->server.port = $2.port;
1269 else
1270 data->server.port = xstrdup("smtp");
1271 data->server.ai = NULL;
1272 data->from.str = $3;
1273 data->to.str = $4;
1274 }
1275 | TOKSTDOUT
1276 {
1277 $$ = xcalloc(1, sizeof *$$);
1278 $$->deliver = &deliver_stdout;
1279 }
1280 | TOKTAG strv optval
1281 {
1282 struct deliver_tag_data *data;
1283
1284 if (*$2 == '\0')
1285 yyerror("invalid tag");
1286
1287 $$ = xcalloc(1, sizeof *$$);
1288 $$->deliver = &deliver_tag;
1289
1290 data = xcalloc(1, sizeof *data);
1291 $$->data = data;
1292
1293 data->key.str = $2;
1294 data->value.str = $3;
1295 }
1296 | TOKADDTOCACHE replpathv TOKKEY strv
1297 {
1298 struct deliver_add_to_cache_data *data;
1299
1300 if (*$2 == '\0')
1301 yyerror("invalid path");
1302 if (*$4 == '\0')
1303 yyerror("invalid key");
1304
1305 $$ = xcalloc(1, sizeof *$$);
1306 $$->deliver = &deliver_add_to_cache;
1307
1308 data = xcalloc(1, sizeof *data);
1309 $$->data = data;
1310
1311 data->key.str = $4;
1312 data->path = $2;
1313 }
1314 | TOKREMOVEFROMCACHE replpathv TOKKEY strv
1315 {
1316 struct deliver_remove_from_cache_data *data;
1317
1318 if (*$2 == '\0')
1319 yyerror("invalid path");
1320 if (*$4 == '\0')
1321 yyerror("invalid key");
1322
1323 $$ = xcalloc(1, sizeof *$$);
1324 $$->deliver = &deliver_remove_from_cache;
1325
1326 data = xcalloc(1, sizeof *data);
1327 $$->data = data;
1328
1329 data->key.str = $4;
1330 data->path = $2;
1331 }
1332 | actions
1333 {
1334 struct deliver_action_data *data;
1335
1336 /*
1337 * This is a special-case, handled when the list of delivery
1338 * targets is resolved rather than by calling a deliver
1339 * function, so the deliver pointer is NULL.
1340 */
1341 $$ = xcalloc(1, sizeof *$$);
1342 $$->deliver = NULL;
1343
1344 data = xcalloc(1, sizeof *data);
1345 $$->data = data;
1346
1347 data->actions = $1;
1348 }
1349 | TOKDROP
1350 {
1351 $$ = xcalloc(1, sizeof *$$);
1352 $$->deliver = &deliver_drop;
1353 }
1354 | TOKKEEP
1355 {
1356 $$ = xcalloc(1, sizeof *$$);
1357 $$->deliver = &deliver_keep;
1358 }
1359
1360 actlist: actlist actitem
1361 {
1362 $$ = $1;
1363
1364 TAILQ_INSERT_TAIL($$, $2, entry);
1365 $2->idx = parse_actionidx++;
1366 }
1367 | actitem
1368 {
1369 $$ = xmalloc(sizeof *$$);
1370 TAILQ_INIT($$);
1371
1372 TAILQ_INSERT_HEAD($$, $1, entry);
1373 $1->idx = 0;
1374
1375 parse_actionidx = 1;
1376 }
1377
1378 defaction: TOKACTION replstrv users actitem
1379 {
1380 struct action *t;
1381
1382 if (strlen($2) >= MAXNAMESIZE)
1383 yyerror("action name too long: %s", $2);
1384 if (*$2 == '\0')
1385 yyerror("invalid action name");
1386 if (find_action($2) != NULL)
1387 yyerror("duplicate action: %s", $2);
1388
1389 t = xmalloc(sizeof *t);
1390 strlcpy(t->name, $2, sizeof t->name);
1391
1392 t->list = xmalloc(sizeof *t->list);
1393 TAILQ_INIT(t->list);
1394 TAILQ_INSERT_HEAD(t->list, $4, entry);
1395 $4->idx = 0;
1396
1397 t->users = $3;
1398 TAILQ_INSERT_TAIL(&conf.actions, t, entry);
1399
1400 print_action(t);
1401
1402 xfree($2);
1403 }
1404 | TOKACTION replstrv users '{' actlist '}'
1405 {
1406 struct action *t;
1407
1408 if (strlen($2) >= MAXNAMESIZE)
1409 yyerror("action name too long: %s", $2);
1410 if (*$2 == '\0')
1411 yyerror("invalid action name");
1412 if (find_action($2) != NULL)
1413 yyerror("duplicate action: %s", $2);
1414
1415 t = xmalloc(sizeof *t);
1416 strlcpy(t->name, $2, sizeof t->name);
1417
1418 t->list = $5;
1419
1420 t->users = $3;
1421 TAILQ_INSERT_TAIL(&conf.actions, t, entry);
1422
1423 print_action(t);
1424
1425 xfree($2);
1426 }
1427
1428 accounts: accountp strv
1429 {
1430 if (*$2 == '\0')
1431 yyerror("invalid account name");
1432 if (!have_accounts($2))
1433 yyerror("no matching accounts: %s", $2);
1434
1435 $$ = xmalloc(sizeof *$$);
1436 ARRAY_INIT($$);
1437 ARRAY_EXPAND($$, 1);
1438 ARRAY_LAST($$).str = $2;
1439 }
1440 | accountp '{' replstrslist '}'
1441 {
1442 $$ = $3;
1443 }
1444
1445 actions: actionp strv
1446 {
1447 if (*$2 == '\0')
1448 yyerror("invalid action name");
1449
1450 $$ = xmalloc(sizeof *$$);
1451 ARRAY_INIT($$);
1452 ARRAY_EXPAND($$, 1);
1453 ARRAY_LAST($$).str = $2;
1454 }
1455 | actionp '{' replstrslist '}'
1456 {
1457 $$ = $3;
1458 }
1459
1460 cont: /* empty */
1461 {
1462 $$ = 0;
1463 }
1464 | TOKCONTINUE
1465 {
1466 $$ = 1;
1467 }
1468
1469 area: /* empty */
1470 {
1471 $$ = AREA_ANY;
1472 }
1473 | TOKIN TOKALL
1474 {
1475 $$ = AREA_ANY;
1476 }
1477 | TOKIN TOKHEADERS
1478 {
1479 $$ = AREA_HEADERS;
1480 }
1481 | TOKIN TOKBODY
1482 {
1483 $$ = AREA_BODY;
1484 }
1485
1486 retrc: numv
1487 {
1488 if ($1 < 0 || $1 > 255)
1489 yyerror("invalid return code");
1490
1491 $$ = $1;
1492 }
1493 | /* empty */
1494 {
1495 $$ = -1;
1496 }
1497
1498 retre: casere
1499 {
1500 $$ = $1;
1501 }
1502 | /* empty */
1503 {
1504 $$.str = NULL;
1505 }
1506
1507 ltgt: '<'
1508 {
1509 $$ = CMP_LT;
1510 }
1511 | '>'
1512 {
1513 $$ = CMP_GT;
1514 }
1515
1516 eqne: TOKEQ
1517 {
1518 $$ = CMP_EQ;
1519 }
1520 | TOKNE
1521 {
1522 $$ = CMP_NE;
1523 }
1524
1525 cmp: ltgt
1526 {
1527 $$ = $1;
1528 }
1529 | eqne
1530 {
1531 $$ = $1;
1532 }
1533
1534 execpipe: TOKEXEC
1535 {
1536 $$ = 0;
1537 }
1538 | TOKPIPE
1539 {
1540 $$ = 1;
1541 }
1542
1543 writeappend: TOKWRITE
1544 {
1545 $$ = 0;
1546 }
1547 | TOKAPPEND
1548 {
1549 $$ = 1;
1550 }
1551
1552 exprop: TOKAND
1553 {
1554 $$ = OP_AND;
1555 }
1556 | TOKOR
1557 {
1558 $$ = OP_OR;
1559 }
1560
1561 expritem: not TOKALL
1562 {
1563 $$ = xcalloc(1, sizeof *$$);
1564 $$->match = &match_all;
1565 $$->inverted = $1;
1566 }
1567 | not casere area
1568 {
1569 struct match_regexp_data *data;
1570 char *cause;
1571
1572 $$ = xcalloc(1, sizeof *$$);
1573 $$->match = &match_regexp;
1574 $$->inverted = $1;
1575
1576 data = xcalloc(1, sizeof *data);
1577 $$->data = data;
1578
1579 data->area = $3;
1580
1581 if (re_compile(&data->re, $2.str, $2.flags, &cause) != 0)
1582 yyerror("%s", cause);
1583 xfree($2.str);
1584 }
1585 | not accounts
1586 {
1587 struct match_account_data *data;
1588
1589 $$ = xcalloc(1, sizeof *$$);
1590 $$->match = &match_account;
1591 $$->inverted = $1;
1592
1593 data = xcalloc(1, sizeof *data);
1594 $$->data = data;
1595
1596 data->accounts = $2;
1597 }
1598 | not execpipe strv user TOKRETURNS '(' retrc ',' retre ')'
1599 {
1600 struct match_command_data *data;
1601 char *cause;
1602
1603 if (*$3 == '\0' || ($3[0] == '|' && $3[1] == '\0'))
1604 yyerror("invalid command");
1605 if ($7 == -1 && $9.str == NULL)
1606 yyerror("return code or regexp must be specified");
1607
1608 $$ = xcalloc(1, sizeof *$$);
1609 $$->match = &match_command;
1610 $$->inverted = $1;
1611
1612 data = xcalloc(1, sizeof *data);
1613 $$->data = data;
1614
1615 data->user.str = $4;
1616 data->pipe = $2;
1617 data->cmd.str = $3;
1618
1619 data->ret = $7;
1620
1621 if ($9.str != NULL) {
1622 if (re_compile(
1623 &data->re, $9.str, $9.flags, &cause) != 0)
1624 yyerror("%s", cause);
1625 xfree($9.str);
1626 }
1627
1628 }
1629 | not TOKTAGGED strv
1630 {
1631 struct match_tagged_data *data;
1632
1633 if (*$3 == '\0')
1634 yyerror("invalid tag");
1635
1636 $$ = xcalloc(1, sizeof *$$);
1637
1638 $$->match = &match_tagged;
1639 $$->inverted = $1;
1640
1641 data = xcalloc(1, sizeof *data);
1642 $$->data = data;
1643
1644 data->tag.str = $3;
1645 }
1646 | not TOKSIZE ltgt size
1647 {
1648 struct match_size_data *data;
1649
1650 #if SIZE_MAX < LLONG_MAX
1651 if ($4 > SIZE_MAX)
1652 yyerror("size too large");
1653 #endif
1654
1655 $$ = xcalloc(1, sizeof *$$);
1656
1657 $$->match = &match_size;
1658 $$->inverted = $1;
1659
1660 data = xcalloc(1, sizeof *data);
1661 $$->data = data;
1662
1663 data->size = $4;
1664 data->cmp = $3;
1665 }
1666 | not TOKSTRING strv TOKTO casere
1667 {
1668 struct match_string_data *data;
1669 char *cause;
1670
1671 if (*$3 == '\0')
1672 yyerror("invalid string");
1673
1674 $$ = xcalloc(1, sizeof *$$);
1675
1676 $$->match = &match_string;
1677 $$->inverted = $1;
1678
1679 data = xcalloc(1, sizeof *data);
1680 $$->data = data;
1681
1682 data->str.str = $3;
1683 if (re_compile(
1684 &data->re, $5.str, $5.flags|RE_NOSUBST, &cause) != 0)
1685 yyerror("%s", cause);
1686 xfree($5.str);
1687 }
1688 | not TOKINCACHE replpathv TOKKEY strv
1689 {
1690 struct match_in_cache_data *data;
1691
1692 if (*$3 == '\0')
1693 yyerror("invalid path");
1694 if (*$5 == '\0')
1695 yyerror("invalid key");
1696
1697 $$ = xcalloc(1, sizeof *$$);
1698
1699 $$->match = &match_in_cache;
1700 $$->inverted = $1;
1701
1702 data = xcalloc(1, sizeof *data);
1703 $$->data = data;
1704
1705 data->key.str = $5;
1706 data->path = $3;
1707 }
1708 | not TOKMATCHED
1709 {
1710 $$ = xcalloc(1, sizeof *$$);
1711
1712 $$->match = &match_matched;
1713 $$->inverted = $1;
1714 }
1715 | not TOKUNMATCHED
1716 {
1717 $$ = xcalloc(1, sizeof *$$);
1718
1719 $$->match = &match_unmatched;
1720 $$->inverted = $1;
1721 }
1722 | not TOKAGE ltgt time
1723 {
1724 struct match_age_data *data;
1725
1726 if ($4 == 0)
1727 yyerror("invalid time");
1728
1729 $$ = xcalloc(1, sizeof *$$);
1730
1731 $$->match = &match_age;
1732 $$->inverted = $1;
1733
1734 data = xcalloc(1, sizeof *data);
1735 $$->data = data;
1736
1737 data->time = $4;
1738 data->cmp = $3;
1739 }
1740 | not TOKAGE TOKINVALID
1741 {
1742 struct match_age_data *data;
1743
1744 $$ = xcalloc(1, sizeof *$$);
1745
1746 $$->match = &match_age;
1747 $$->inverted = $1;
1748
1749 data = xcalloc(1, sizeof *data);
1750 $$->data = data;
1751
1752 data->time = -1;
1753 }
1754 | not TOKATTACHMENT TOKCOUNT cmp numv
1755 {
1756 struct match_attachment_data *data;
1757
1758 $$ = xcalloc(1, sizeof *$$);
1759
1760 $$->match = &match_attachment;
1761 $$->inverted = $1;
1762
1763 data = xcalloc(1, sizeof *data);
1764 $$->data = data;
1765
1766 data->op = ATTACHOP_COUNT;
1767 data->cmp = $4;
1768 data->value.num = $5;
1769 }
1770 | not TOKATTACHMENT TOKTOTALSIZE ltgt size
1771 {
1772 struct match_attachment_data *data;
1773
1774 #if SIZE_MAX < LLONG_MAX
1775 if ($5 > SIZE_MAX)
1776 yyerror("size too large");
1777 #endif
1778
1779 $$ = xcalloc(1, sizeof *$$);
1780
1781 $$->match = &match_attachment;
1782 $$->inverted = $1;
1783
1784 data = xcalloc(1, sizeof *data);
1785 $$->data = data;
1786
1787 data->op = ATTACHOP_TOTALSIZE;
1788 data->cmp = $4;
1789 data->value.size = $5;
1790 }
1791 | not TOKATTACHMENT TOKANYSIZE ltgt size
1792 {
1793 struct match_attachment_data *data;
1794
1795 #if SIZE_MAX < LLONG_MAX
1796 if ($5 > SIZE_MAX)
1797 yyerror("size too large");
1798 #endif
1799
1800 $$ = xcalloc(1, sizeof *$$);
1801
1802 $$->match = &match_attachment;
1803 $$->inverted = $1;
1804
1805 data = xcalloc(1, sizeof *data);
1806 $$->data = data;
1807
1808 data->op = ATTACHOP_ANYSIZE;
1809 data->cmp = $4;
1810 data->value.size = $5;
1811 }
1812 | not TOKATTACHMENT TOKANYTYPE strv
1813 {
1814 struct match_attachment_data *data;
1815
1816 if (*$4 == '\0')
1817 yyerror("invalid string");
1818
1819 $$ = xcalloc(1, sizeof *$$);
1820
1821 $$->match = &match_attachment;
1822 $$->inverted = $1;
1823
1824 data = xcalloc(1, sizeof *data);
1825 $$->data = data;
1826
1827 data->op = ATTACHOP_ANYTYPE;
1828 data->value.str.str = $4;
1829 }
1830 | not TOKATTACHMENT TOKANYNAME strv
1831 {
1832 struct match_attachment_data *data;
1833
1834 if (*$4 == '\0')
1835 yyerror("invalid string");
1836
1837 $$ = xcalloc(1, sizeof *$$);
1838
1839 $$->match = &match_attachment;
1840 $$->inverted = $1;
1841
1842 data = xcalloc(1, sizeof *data);
1843 $$->data = data;
1844
1845 data->op = ATTACHOP_ANYNAME;
1846 data->value.str.str = $4;
1847 }
1848
1849 exprlist: exprlist exprop expritem
1850 {
1851 $$ = $1;
1852
1853 $3->op = $2;
1854 TAILQ_INSERT_TAIL($$, $3, entry);
1855 }
1856 | exprop expritem
1857 {
1858 $$ = xmalloc(sizeof *$$);
1859 TAILQ_INIT($$);
1860
1861 $2->op = $1;
1862 TAILQ_INSERT_HEAD($$, $2, entry);
1863 }
1864
1865 expr: expritem
1866 {
1867 $$ = xmalloc(sizeof *$$);
1868 TAILQ_INIT($$);
1869
1870 TAILQ_INSERT_HEAD($$, $1, entry);
1871 }
1872 | expritem exprlist
1873 {
1874 $$ = $2;
1875
1876 TAILQ_INSERT_HEAD($$, $1, entry);
1877 }
1878
1879 perform: users actionp actitem cont
1880 {
1881 struct action *t;
1882
1883 $$ = xcalloc(1, sizeof *$$);
1884 $$->idx = parse_ruleidx++;
1885 $$->actions = NULL;
1886 TAILQ_INIT(&$$->rules);
1887 $$->stop = !$4;
1888 $$->users = $1;
1889
1890 t = $$->lambda = xcalloc(1, sizeof *$$->lambda);
1891 xsnprintf(t->name, sizeof t->name, "<rule %u>", $$->idx);
1892 t->users = NULL;
1893 t->list = xmalloc(sizeof *t->list);
1894 TAILQ_INIT(t->list);
1895 TAILQ_INSERT_HEAD(t->list, $3, entry);
1896 $3->idx = 0;
1897
1898 if (parse_rule == NULL)
1899 TAILQ_INSERT_TAIL(&conf.rules, $$, entry);
1900 else
1901 TAILQ_INSERT_TAIL(&parse_rule->rules, $$, entry);
1902 }
1903 | users actionp '{' actlist '}' cont
1904 {
1905 struct action *t;
1906
1907 $$ = xcalloc(1, sizeof *$$);
1908 $$->idx = parse_ruleidx++;
1909 $$->actions = NULL;
1910 TAILQ_INIT(&$$->rules);
1911 $$->stop = !$6;
1912 $$->users = $1;
1913
1914 t = $$->lambda = xcalloc(1, sizeof *$$->lambda);
1915 xsnprintf(t->name, sizeof t->name, "<rule %u>", $$->idx);
1916 t->users = NULL;
1917 t->list = $4;
1918
1919 if (parse_rule == NULL)
1920 TAILQ_INSERT_TAIL(&conf.rules, $$, entry);
1921 else
1922 TAILQ_INSERT_TAIL(&parse_rule->rules, $$, entry);
1923 }
1924 | users actions cont
1925 {
1926 $$ = xcalloc(1, sizeof *$$);
1927 $$->idx = parse_ruleidx++;
1928 $$->lambda = NULL;
1929 $$->actions = $2;
1930 TAILQ_INIT(&$$->rules);
1931 $$->stop = !$3;
1932 $$->users = $1;
1933
1934 if (parse_rule == NULL)
1935 TAILQ_INSERT_TAIL(&conf.rules, $$, entry);
1936 else
1937 TAILQ_INSERT_TAIL(&parse_rule->rules, $$, entry);
1938 }
1939 | '{'
1940 {
1941 $$ = xcalloc(1, sizeof *$$);
1942 $$->idx = parse_ruleidx++;
1943 $$->lambda = NULL;
1944 $$->actions = NULL;
1945 TAILQ_INIT(&$$->rules);
1946 $$->stop = 0;
1947 $$->users = NULL;
1948
1949 if (parse_rule == NULL)
1950 TAILQ_INSERT_TAIL(&conf.rules, $$, entry);
1951 else
1952 TAILQ_INSERT_TAIL(&parse_rule->rules, $$, entry);
1953
1954 ARRAY_ADD(&parse_rulestack, parse_rule);
1955 parse_rule = $$;
1956 }
1957
1958 close: '}'
1959 {
1960 if (parse_rule == NULL)
1961 yyerror("missing {");
1962
1963 parse_rule = ARRAY_LAST(&parse_rulestack);
1964 ARRAY_TRUNC(&parse_rulestack, 1);
1965 }
1966
1967 rule: TOKMATCH expr perform
1968 {
1969 $3->expr = $2;
1970 print_rule($3);
1971 }
1972
1973 folderlist: /* empty */
1974 {
1975 $$ = xmalloc(sizeof *$$);
1976 ARRAY_INIT($$);
1977 ARRAY_ADD($$, xstrdup("INBOX"));
1978 }
1979 | folders
1980 {
1981 $$ = $1;
1982 }
1983
1984 folder1: /* empty */
1985 {
1986 $$ = xstrdup("INBOX");
1987 }
1988 | folderp strv
1989 {
1990 $$ = $2;
1991 }
1992
1993
1994 groups: groupp replstrv
1995 {
1996 if (*$2 == '\0')
1997 yyerror("invalid group");
1998
1999 $$ = xmalloc(sizeof *$$);
2000 ARRAY_INIT($$);
2001 ARRAY_ADD($$, $2);
2002 }
2003 | groupp '{' stringslist '}'
2004 {
2005 $$ = $3;
2006 }
2007
2008 nocrammd5: TOKNOCRAMMD5
2009 {
2010 $$ = 1;
2011 }
2012 | /* empty */
2013 {
2014 $$ = 0;
2015 }
2016
2017 nologin: TOKNOLOGIN
2018 {
2019 $$ = 1;
2020 }
2021 | /* empty */
2022 {
2023 $$ = 0;
2024 }
2025
2026 starttls: TOKSTARTTLS
2027 {
2028 $$ = 1;
2029 }
2030 | /* empty */
2031 {
2032 $$ = 0;
2033 }
2034
2035
2036 uidl: TOKNOUIDL
2037 {
2038 $$ = 0;
2039 }
2040 | /* empty */
2041 {
2042 $$ = 1;
2043 }
2044
2045 insecure: TOKINSECURE
2046 {
2047 $$ = 1;
2048 }
2049 | /* empty */
2050 {
2051 $$ = 0;
2052 }
2053
2054 verify: TOKNOVERIFY
2055 {
2056 $$ = 0;
2057 }
2058 | /* empty */
2059 {
2060 $$ = 1;
2061 }
2062
2063 apop: TOKNOAPOP
2064 {
2065 $$ = 0;
2066 }
2067 | /* empty */
2068 {
2069 $$ = 1;
2070 }
2071
2072 only: TOKNEWONLY
2073 {
2074 $$ = FETCH_ONLY_NEW;
2075 }
2076 | TOKOLDONLY
2077 {
2078 $$ = FETCH_ONLY_OLD;
2079 }
2080
2081 poptype: TOKPOP3
2082 {
2083 $$ = 0;
2084 }
2085 | TOKPOP3S
2086 {
2087 $$ = 1;
2088 }
2089
2090 imaptype: TOKIMAP
2091 {
2092 $$ = 0;
2093 }
2094 | TOKIMAPS
2095 {
2096 $$ = 1;
2097 }
2098
2099 nntptype: TOKNNTP
2100 {
2101 $$ = 0;
2102 }
2103 | TOKNNTPS
2104 {
2105 $$ = 1;
2106 }
2107
2108 userpassnetrc: TOKUSER replstrv TOKPASS replstrv
2109 {
2110 if (*$2 == '\0')
2111 yyerror("invalid user");
2112 if (*$4 == '\0')
2113 yyerror("invalid pass");
2114
2115 $$.user = $2;
2116 $$.user_netrc = 0;
2117 $$.pass = $4;
2118 $$.pass_netrc = 0;
2119 }
2120 | /* empty */
2121 {
2122 $$.user = NULL;
2123 $$.user_netrc = 1;
2124 $$.pass = NULL;
2125 $$.pass_netrc = 1;
2126 }
2127 | TOKUSER replstrv
2128 {
2129 if (*$2 == '\0')
2130 yyerror("invalid user");
2131
2132 $$.user = $2;
2133 $$.user_netrc = 0;
2134 $$.pass = NULL;
2135 $$.pass_netrc = 1;
2136 }
2137 | TOKPASS replstrv
2138 {
2139 if (*$2 == '\0')
2140 yyerror("invalid pass");
2141
2142 $$.user = NULL;
2143 $$.user_netrc = 1;
2144 $$.pass = $2;
2145 $$.pass_netrc = 0;
2146 }
2147
2148 userpassreqd: TOKUSER replstrv TOKPASS replstrv
2149 {
2150 if (*$2 == '\0')
2151 yyerror("invalid user");
2152 if (*$4 == '\0')
2153 yyerror("invalid pass");
2154
2155 $$.user = $2;
2156 $$.user_netrc = 0;
2157 $$.pass = $4;
2158 $$.pass_netrc = 0;
2159 }
2160
2161 userpass: userpassreqd
2162 {
2163 $$.user = $1.user;
2164 $$.user_netrc = $1.user_netrc;
2165 $$.pass = $1.pass;
2166 $$.pass_netrc = $1.pass_netrc;
2167 }
2168 | /* empty */
2169 {
2170 $$.user = NULL;
2171 $$.user_netrc = 0;
2172 $$.pass = NULL;
2173 $$.pass_netrc = 0;
2174 }
2175
2176 poponly: only TOKCACHE replpathv
2177 {
2178 $$.path = $3;
2179 $$.only = $1;
2180 }
2181 | /* empty */
2182 {
2183 $$.path = NULL;
2184 $$.only = FETCH_ONLY_ALL;
2185 }
2186
2187 imaponly: only
2188 {
2189 $$ = $1;
2190 }
2191 | /* empty */
2192 {
2193 $$ = FETCH_ONLY_ALL;
2194 }
2195
2196 fetchtype: poptype server userpassnetrc poponly apop verify uidl starttls
2197 insecure
2198 {
2199 struct fetch_pop3_data *data;
2200
2201 if ($1 && $8)
2202 yyerror("use either pop3s or set starttls");
2203
2204 $$.fetch = &fetch_pop3;
2205 data = xcalloc(1, sizeof *data);
2206 $$.data = data;
2207
2208 if ($3.user_netrc && $3.pass_netrc)
2209 find_netrc($2.host, &data->user, &data->pass);
2210 else {
2211 if ($3.user_netrc)
2212 find_netrc($2.host, &data->user, NULL);
2213 else
2214 data->user = $3.user;
2215 if ($3.pass_netrc)
2216 find_netrc($2.host, NULL, &data->pass);
2217 else
2218 data->pass = $3.pass;
2219 }
2220
2221 data->server.ssl = $1;
2222 data->server.verify = $6;
2223 data->server.host = $2.host;
2224 if ($2.port != NULL)
2225 data->server.port = $2.port;
2226 else if ($1)
2227 data->server.port = xstrdup("pop3s");
2228 else
2229 data->server.port = xstrdup("pop3");
2230 data->server.ai = NULL;
2231 data->apop = $5;
2232 data->uidl = $7;
2233 data->starttls = $8;
2234 data->server.insecure = $9;
2235
2236 data->path = $4.path;
2237 data->only = $4.only;
2238 }
2239 | TOKPOP3 TOKPIPE replstrv userpassreqd poponly apop
2240 {
2241 struct fetch_pop3_data *data;
2242
2243 $$.fetch = &fetch_pop3pipe;
2244 data = xcalloc(1, sizeof *data);
2245 $$.data = data;
2246 data->user = $4.user;
2247 data->pass = $4.pass;
2248 data->pipecmd = $3;
2249 if (data->pipecmd == NULL || *data->pipecmd == '\0')
2250 yyerror("invalid pipe command");
2251 data->apop = $6;
2252 data->path = $5.path;
2253 data->only = $5.only;
2254 }
2255 | imaptype server userpassnetrc folderlist imaponly verify nocrammd5
2256 nologin starttls insecure
2257 {
2258 struct fetch_imap_data *data;
2259
2260 if ($1 && $9)
2261 yyerror("use either imaps or set starttls");
2262
2263 $$.fetch = &fetch_imap;
2264 data = xcalloc(1, sizeof *data);
2265 $$.data = data;
2266
2267 if ($3.user_netrc && $3.pass_netrc)
2268 find_netrc($2.host, &data->user, &data->pass);
2269 else {
2270 if ($3.user_netrc)
2271 find_netrc($2.host, &data->user, NULL);
2272 else
2273 data->user = $3.user;
2274 if ($3.pass_netrc)
2275 find_netrc($2.host, NULL, &data->pass);
2276 else
2277 data->pass = $3.pass;
2278 }
2279
2280 data->folders = $4;
2281 data->server.ssl = $1;
2282 data->server.verify = $6;
2283 data->server.host = $2.host;
2284 if ($2.port != NULL)
2285 data->server.port = $2.port;
2286 else if ($1)
2287 data->server.port = xstrdup("imaps");
2288 else
2289 data->server.port = xstrdup("imap");
2290 data->server.ai = NULL;
2291 data->only = $5;
2292 data->nocrammd5 = $7;
2293 data->nologin = $8;
2294 data->starttls = $9;
2295 data->server.insecure = $10;
2296 }
2297 | TOKIMAP TOKPIPE replstrv userpass folderlist imaponly
2298 {
2299 struct fetch_imap_data *data;
2300
2301 $$.fetch = &fetch_imappipe;
2302 data = xcalloc(1, sizeof *data);
2303 $$.data = data;
2304 data->user = $4.user;
2305 data->pass = $4.pass;
2306 data->folders = $5;
2307 data->pipecmd = $3;
2308 if (data->pipecmd == NULL || *data->pipecmd == '\0')
2309 yyerror("invalid pipe command");
2310 data->only = $6;
2311 }
2312 | TOKSTDIN
2313 {
2314 $$.fetch = &fetch_stdin;
2315 }
2316 | maildirs
2317 {
2318 struct fetch_maildir_data *data;
2319
2320 $$.fetch = &fetch_maildir;
2321 data = xcalloc(1, sizeof *data);
2322 $$.data = data;
2323 data->maildirs = $1;
2324 }
2325 | mboxes
2326 {
2327 struct fetch_mbox_data *data;
2328
2329 $$.fetch = &fetch_mbox;
2330 data = xcalloc(1, sizeof *data);
2331 $$.data = data;
2332 data->mboxes = $1;
2333 }
2334 | nntptype server userpassnetrc groups TOKCACHE replpathv verify
2335 insecure
2336 {
2337 struct fetch_nntp_data *data;
2338 char *cause;
2339
2340 if (*$6 == '\0')
2341 yyerror("invalid cache");
2342
2343 $$.fetch = &fetch_nntp;
2344 data = xcalloc(1, sizeof *data);
2345 $$.data = data;
2346
2347 if ($3.user_netrc && $3.pass_netrc) {
2348 if (find_netrc1($2.host,
2349 &data->user, &data->pass, &cause) != 0) {
2350 log_debug2("%s", cause);
2351 xfree(cause);
2352 data->user = NULL;
2353 data->pass = NULL;
2354 }
2355
2356 } else {
2357 if ($3.user_netrc)
2358 find_netrc($2.host, &data->user, NULL);
2359 else
2360 data->user = $3.user;
2361 if ($3.pass_netrc)
2362 find_netrc($2.host, NULL, &data->pass);
2363 else
2364 data->pass = $3.pass;
2365 }
2366
2367 data->names = $4;
2368 data->path = $6;
2369 if (data->path == NULL || *data->path == '\0')
2370 yyerror("invalid cache");
2371
2372 data->server.ssl = $1;
2373 data->server.verify = $7;
2374 data->server.insecure = $8;
2375 data->server.host = $2.host;
2376 if ($2.port != NULL)
2377 data->server.port = $2.port;
2378 else if ($1)
2379 data->server.port = xstrdup("nntps");
2380 else
2381 data->server.port = xstrdup("nntp");
2382 data->server.ai = NULL;
2383 }
2384
2385 account: TOKACCOUNT replstrv disabled users fetchtype keep
2386 {
2387 struct account *a;
2388 char *su, desc[DESCBUFSIZE];
2389
2390 if (strlen($2) >= MAXNAMESIZE)
2391 yyerror("account name too long: %s", $2);
2392 if (*$2 == '\0')
2393 yyerror("invalid account name");
2394 if (find_account($2) != NULL)
2395 yyerror("duplicate account: %s", $2);
2396
2397 a = xcalloc(1, sizeof *a);
2398 strlcpy(a->name, $2, sizeof a->name);
2399 a->keep = $6;
2400 a->disabled = $3;
2401 a->users = $4;
2402 a->fetch = $5.fetch;
2403 a->data = $5.data;
2404 TAILQ_INSERT_TAIL(&conf.accounts, a, entry);
2405
2406 if (a->users != NULL)
2407 su = fmt_replstrs(" users=", a->users);
2408 else
2409 su = xstrdup("");
2410 a->fetch->desc(a, desc, sizeof desc);
2411 log_debug2("added account \"%s\":%s fetch=%s", a->name, su,
2412 desc);
2413 xfree(su);
2414
2415 xfree($2);
2416 }
2417
2418 %%
2419
2420 /* Programs */
2421