1 %{
2 /* sieve.y -- sieve parser
3  * Larry Greenfield
4  *
5  * Copyright (c) 1994-2008 Carnegie Mellon University.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The name "Carnegie Mellon University" must not be used to
20  *    endorse or promote products derived from this software without
21  *    prior written permission. For permission or any legal
22  *    details, please contact
23  *      Carnegie Mellon University
24  *      Center for Technology Transfer and Enterprise Creation
25  *      4615 Forbes Avenue
26  *      Suite 302
27  *      Pittsburgh, PA  15213
28  *      (412) 268-7393, fax: (412) 268-7395
29  *      innovation@andrew.cmu.edu
30  *
31  * 4. Redistributions of any form whatsoever must retain the following
32  *    acknowledgment:
33  *    "This product includes software developed by Computing Services
34  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
35  *
36  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
37  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
38  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
39  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
41  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
42  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43  */
44 
45 #ifdef HAVE_CONFIG_H
46 #include <config.h>
47 #endif
48 
49 #include <stdlib.h>
50 #include <assert.h>
51 #include <string.h>
52 #include "xmalloc.h"
53 #include "sieve/comparator.h"
54 #include "sieve/interp.h"
55 #include "sieve/script.h"
56 #include "sieve/tree.h"
57 #include "sieve/flags.h"
58 #include "sieve/grammar.h"
59 #include "sieve/sieve_err.h"
60 
61 #include "imapurl.h"
62 #include "lib/gmtoff.h"
63 #include "util.h"
64 #include "imparse.h"
65 #include "libconfig.h"
66 #include "times.h"
67 
68 #define ERR_BUF_SIZE 1024
69 
70 /*
71  * Definitions
72  */
73 
74 extern int addrparse(sieve_script_t*);
75 typedef struct yy_buffer_state *YY_BUFFER_STATE;
76 extern YY_BUFFER_STATE addr_scan_string(const char*);
77 extern void addr_delete_buffer(YY_BUFFER_STATE);
78 
79 extern int sievelineno;
80 
81 struct vtags {
82     int seconds;
83     strarray_t *addresses;
84     char *subject;
85     char *from;
86     char *handle;
87     int mime;
88 };
89 
90 struct comptags {
91     int match;
92     int relation;
93     char *comparator;  /* only used where comparator can be defined */
94     int index;         /* only used where index extension is defined */
95 };
96 
97 struct aetags {
98     struct comptags comptags;  /* MUST be first so we can typecast */
99     int addrtag;
100 };
101 
102 struct btags {
103     struct comptags comptags;  /* MUST be first so we can typecast */
104     int transform;
105     int offset;
106     strarray_t *content_types;
107 };
108 
109 struct ntags {
110     char *method;
111     char *id;
112     strarray_t *options;
113     int priority;
114     char *message;
115 };
116 
117 struct dtags {
118     struct comptags comptags;  /* MUST be first so we can typecast */
119     void *pattern;
120     int priority;
121 };
122 
123 struct itags {
124     int location;
125     int once;
126     int optional;
127 };
128 
129 struct dttags {
130     struct comptags comptags;  /* MUST be first so we can typecast */
131     int zonetag;
132     char *zone;
133 };
134 
135 struct ftags {
136     int copy;
137     int create;
138     strarray_t *flags;
139 };
140 
141 struct stags {
142     int mod40; /* :lower or :upper */
143     int mod30; /* :lowerfirst or :upperfirst */
144     int mod20; /* :quotewildcard */
145     int mod10; /* :length */
146 };
147 
148 static char *check_reqs(sieve_script_t *script, strarray_t *sl);
149 
150 static test_t *build_address(int t, struct aetags *ae,
151                              strarray_t *sl, strarray_t *pl);
152 static test_t *build_header(int t, struct comptags *c,
153                             strarray_t *sl, strarray_t *pl);
154 static test_t *build_body(int t, struct btags *b, strarray_t *pl);
155 static test_t *build_date(int t, struct dttags *dt,
156                           char *hn, int part, strarray_t *kl);
157 static test_t *build_mailboxtest(int t, struct comptags *c, const char *extname,
158                                  const char *keyname, strarray_t *keylist);
159 
160 static commandlist_t *build_vacation(int t, struct vtags *h, char *s);
161 static commandlist_t *build_notify(int t, struct ntags *n);
162 static commandlist_t *build_denotify(int t, struct dtags *n);
163 static commandlist_t *build_keep(int t, struct ftags *f);
164 static commandlist_t *build_fileinto(int t, struct ftags *f, char *folder);
165 static commandlist_t *build_redirect(int t, int c, char *a);
166 static commandlist_t *build_include(int, struct itags *, char*);
167 static commandlist_t *build_set(int t, struct stags *s,
168                                 char *variable, char *value);
169 static commandlist_t *build_flag(int t, char *variable, strarray_t *flags);
170 
171 static struct aetags *new_aetags(void);
172 static struct aetags *canon_aetags(struct aetags *ae);
173 static void free_aetags(struct aetags *ae);
174 
175 static struct comptags *new_comptags(void);
176 static struct comptags *init_comptags(struct comptags *c);
177 static struct comptags *canon_comptags(struct comptags *c);
178 static void free_comptags(struct comptags *c, int destroy);
179 
180 static struct btags *new_btags(void);
181 static struct btags *canon_btags(struct btags *b);
182 static void free_btags(struct btags *b);
183 
184 static struct vtags *new_vtags(void);
185 static struct vtags *canon_vtags(sieve_script_t *script, struct vtags *v);
186 static void free_vtags(struct vtags *v);
187 
188 static struct ntags *new_ntags(void);
189 static struct ntags *canon_ntags(struct ntags *n);
190 static void free_ntags(struct ntags *n);
191 
192 static struct dtags *new_dtags(void);
193 static struct dtags *canon_dtags(struct dtags *d);
194 static void free_dtags(struct dtags *d);
195 
196 static struct itags *new_itags(void);
197 
198 static struct dttags *new_dttags(void);
199 static struct dttags *canon_dttags(struct dttags *dt);
200 static void free_dttags(struct dttags *b);
201 
202 static struct ftags *new_ftags(void);
203 static struct ftags *canon_ftags(struct ftags *f);
204 static void free_ftags(struct ftags *f);
205 
206 static struct stags *new_stags(void);
207 static struct stags *canon_stags(struct stags *s);
208 static void free_stags(struct stags *s);
209 
210 static int verify_stringlist(sieve_script_t*, strarray_t *sl,
211                              int (*verify)(sieve_script_t*, char *));
212 static int verify_patternlist(sieve_script_t *parse_script,
213                               strarray_t *sl, struct comptags *c,
214                               int (*verify)(sieve_script_t*, char *));
215 static int verify_mailbox(sieve_script_t*, char *s);
216 static int verify_address(sieve_script_t*, char *s);
217 static int verify_header(sieve_script_t*, char *s);
218 static int verify_addrheader(sieve_script_t*, char *s);
219 static int verify_envelope(sieve_script_t*, char *s);
220 static int verify_relat(sieve_script_t*, char *s);
221 static int verify_zone(sieve_script_t*, char *s);
222 static int verify_date_part(sieve_script_t *parse_script, char *dp);
223 static int verify_utf8(sieve_script_t*, char *s);
224 static int verify_identifier(sieve_script_t*, char *s);
225 
226 static void parse_error(sieve_script_t *parse_script, int err, ...);
227 void yyerror(sieve_script_t*, const char *msg);
228 extern int yylex(void*, sieve_script_t*);
229 extern void sieverestart(FILE *f);
230 
231 #define YYERROR_VERBOSE /* i want better error messages! */
232 
233 /* byacc default is 500, bison default is 10000 - go with the
234    larger to support big sieve scripts (see Bug #3461) */
235 #define YYSTACKSIZE 10000
236 %}
237 
238 %union {
239     int nval;
240     char *sval;
241     strarray_t *sl;
242     test_t *test;
243     testlist_t *testl;
244     commandlist_t *cl;
245     struct vtags *vtag;
246     struct aetags *aetag;
247     struct comptags *ctag;
248     struct btags *btag;
249     struct ntags *ntag;
250     struct dtags *dtag;
251     struct itags *itag;
252     struct dttags *dttag;
253     struct ftags *ftag;
254     struct stags *stag;
255 }
256 
257 %token <nval> NUMBER
258 %token <sval> STRING
259 %token IF ELSIF ELSE
260 %token REJCT FILEINTO REDIRECT KEEP STOP DISCARD VACATION REQUIRE
261 %token MARK UNMARK FLAGS
262 %token NOTIFY DENOTIFY
263 %token ANYOF ALLOF EXISTS SFALSE STRUE HEADER NOT SIZE ADDRESS ENVELOPE BODY
264 %token COMPARATOR IS CONTAINS MATCHES REGEX COUNT VALUE OVER UNDER
265 %token GT GE LT LE EQ NE
266 %token ALL LOCALPART DOMAIN USER DETAIL
267 %token RAW TEXT CONTENT
268 %token DAYS ADDRESSES SUBJECT FROM HANDLE MIME SECONDS
269 %token METHOD ID OPTIONS LOW NORMAL HIGH ANY MESSAGE
270 %token INCLUDE PERSONAL GLOBAL RETURN OPTIONAL ONCE
271 %token COPY
272 %token DATE CURRENTDATE INDEX LAST ZONE ORIGINALZONE
273 %token MAILBOXEXISTS CREATE
274 %token METADATA METADATAEXISTS
275 %token SERVERMETADATA SERVERMETADATAEXISTS
276 %token YEAR MONTH DAY JULIAN HOUR MINUTE SECOND TIME ISO8601 STD11 WEEKDAY
277 %token <nval> STRINGT SET LOWER UPPER LOWERFIRST UPPERFIRST QUOTEWILDCARD LENGTH
278 %token <nval> SETFLAG ADDFLAG REMOVEFLAG HASFLAG
279 
280 %type <cl> commands command action elsif block
281 %type <sl> stringlist strings
282 %type <test> test
283 %type <nval> match relmatch sizetag addrparttag copy rtags creat datepart
284 %type <testl> testlist tests
285 %type <ctag> htags strtags hftags mtags
286 %type <aetag> atags etags
287 %type <btag> btags
288 %type <vtag> vtags
289 %type <ntag> ntags
290 %type <dtag> dtags
291 %type <itag> itags
292 %type <dttag> dttags cdtags
293 %type <nval> priority
294 %type <ftag> ftags
295 %type <stag> stags
296 %type <nval> mod40 mod30 mod20 mod10
297 %type <sval> flagtags
298 %type <nval> flagaction
299 
300 %name-prefix "sieve"
301 %defines
302 %destructor { free_tree($$); } commands command action elsif block
303 
304 %param { sieve_script_t *parse_script }
305 %pure-parser
306 
307 
308 /*
309  * Rules
310  */
311 
312 %%
313 
314 start: reqs                     { parse_script->cmds = NULL; }
315         | reqs commands         { parse_script->cmds = $2; }
316         ;
317 
318 reqs: /* empty */
319         | require reqs
320         ;
321 
322 require: REQUIRE stringlist ';'
323                                 {
324                                     char *err = check_reqs(parse_script, $2);
325                                     if (err) {
326                                         yyerror(parse_script, err);
327                                         free(err);
328                                         YYERROR;
329                                     }
330                                 }
331         ;
332 
333 commands: command               { $$ = $1; }
334         | command commands      { $1->next = $2; $$ = $1; }
335         ;
336 
337 command: action ';'             { $$ = $1; }
338         | IF test block elsif   { $$ = new_if($2, $3, $4); }
339         | error ';'             { $$ = new_command(STOP); }
340         ;
341 
342 elsif: /* empty */               { $$ = NULL; }
343         | ELSIF test block elsif { $$ = new_if($2, $3, $4); }
344         | ELSE block             { $$ = $2; }
345         ;
346 
347 action: REJCT STRING
348                                  {
349                                      if (!parse_script->support.reject) {
350                                          parse_error(parse_script,
351                                                      SIEVE_MISSING_REQUIRE,
352                                                      "reject");
353                                          YYERROR; /* pe should call yyerror() */
354                                      }
355                                      if (!verify_utf8(parse_script, $2)) {
356                                          YYERROR; /* vu should call yyerror() */
357                                      }
358                                      $$ = new_command(REJCT);
359                                      $$->u.reject = $2;
360                                  }
361 
362         | FILEINTO ftags STRING
363                                  {
364                                      if (!parse_script->support.fileinto) {
365                                          parse_error(parse_script,
366                                                      SIEVE_MISSING_REQUIRE,
367                                                      "fileinto");
368                                          YYERROR; /* pe should call yyerror() */
369                                      }
370                                      if (!verify_mailbox(parse_script, $3)) {
371                                          YYERROR; /* vm should call yyerror() */
372                                      }
373                                      $$ = build_fileinto(FILEINTO,
374                                                          canon_ftags($2), $3);
375                                  }
376 
377         | REDIRECT rtags STRING
378                                  {
379                                      if (!verify_address(parse_script, $3)) {
380                                          YYERROR; /* va should call yyerror() */
381                                      }
382                                      $$ = build_redirect(REDIRECT, $2, $3);
383                                  }
384 
385         | KEEP ftags             { $$ = build_keep(KEEP,canon_ftags($2)); }
386         | STOP                   { $$ = new_command(STOP); }
387         | DISCARD                { $$ = new_command(DISCARD); }
388 
389         | VACATION vtags STRING
390                                  {
391                                      if (!parse_script->support.vacation) {
392                                          parse_error(parse_script,
393                                                      SIEVE_MISSING_REQUIRE,
394                                                      "vacation");
395                                          YYERROR; /* pe should call yyerror() */
396                                      }
397                                      if (($2->mime == -1) &&
398                                          !verify_utf8(parse_script, $3)) {
399                                          YYERROR; /* vu should call yyerror() */
400                                      }
401                                      $$ = build_vacation(VACATION,
402                                                          canon_vtags(parse_script, $2),
403                                                          $3);
404                                  }
405 
406         | flagaction flagtags stringlist
407                                  {
408                                      if (!(parse_script->support.imapflags ||
409                                            parse_script->support.imap4flags)) {
410                                          parse_error(parse_script,
411                                                      SIEVE_MISSING_REQUIRE,
412                                                      "imap[4]flags");
413                                          YYERROR; /* pe should call yyerror() */
414                                      }
415                                      if (!parse_script->support.variables) {
416                                          verify_flaglist($3);
417                                      }
418                                      if (!$3->count) strarray_add($3, "");
419                                      $$ = build_flag($1, $2, $3);
420                                  }
421 
422          | MARK
423                                  {
424                                      if (!parse_script->support.imapflags) {
425                                          parse_error(parse_script,
426                                                      SIEVE_MISSING_REQUIRE,
427                                                      "imapflags");
428                                          YYERROR; /* pe should call yyerror() */
429                                      }
430                                      $$ = new_command(MARK);
431                                  }
432 
433          | UNMARK
434                                  {
435                                      if (!parse_script->support.imapflags) {
436                                          parse_error(parse_script,
437                                                      SIEVE_MISSING_REQUIRE,
438                                                      "imapflags");
439                                          YYERROR; /* pe should call yyerror() */
440                                      }
441                                      $$ = new_command(UNMARK);
442                                  }
443 
444          | NOTIFY ntags
445                                  {
446                                      if (!parse_script->support.notify) {
447                                          parse_error(parse_script,
448                                                      SIEVE_MISSING_REQUIRE,
449                                                      "notify");
450                                          $$ = new_command(NOTIFY);
451                                          YYERROR; /* pe should call yyerror() */
452                                      }
453                                      $$ = build_notify(NOTIFY, canon_ntags($2));
454                                  }
455 
456          | DENOTIFY dtags
457                                  {
458                                      if (!parse_script->support.notify) {
459                                          parse_error(parse_script,
460                                                      SIEVE_MISSING_REQUIRE,
461                                                      "notify");
462                                          $$ = new_command(DENOTIFY);
463                                          YYERROR; /* pe should call yyerror() */
464                                      }
465                                      $$ = build_denotify(DENOTIFY,
466                                                          canon_dtags($2));
467                                      if ($$ == NULL) {
468                                          parse_error(parse_script,
469                                                      SIEVE_BUILD_FAILURE,
470                                                      "denotify action");
471                                          YYERROR; /* pe should call yyerror() */
472                                      }
473                                  }
474 
475          | INCLUDE itags STRING
476                                  {
477                                      if (!parse_script->support.include) {
478                                          parse_error(parse_script,
479                                                      SIEVE_MISSING_REQUIRE,
480                                                      "include");
481                                          YYERROR; /* pe should call yyerror() */
482                                      }
483                                      int i;
484                                      for (i = 0; $3[i] != '\0'; i++) {
485                                          if ($3[i] == '/') {
486                                              parse_error(parse_script,
487                                                          SIEVE_INVALID_VALUE,
488                                                          "script-name");
489                                              YYERROR; /* pe should call yyerror() */
490                                              break;
491                                          }
492                                      }
493                                      $$ = build_include(INCLUDE, $2, $3);
494                                  }
495 
496          | RETURN
497                                  {
498                                      if (!parse_script->support.include) {
499                                          parse_error(parse_script,
500                                                      SIEVE_MISSING_REQUIRE,
501                                                      "include");
502                                          YYERROR; /* pe should call yyerror() */
503                                      }
504                                      $$ = new_command(RETURN);
505                                  }
506 
507          | SET stags STRING STRING
508                                  {
509                                      if (!parse_script->support.variables) {
510                                          parse_error(parse_script,
511                                                      SIEVE_MISSING_REQUIRE,
512                                                      "variables");
513                                          YYERROR; /* pe should call yyerror() */
514                                      }
515                                      if (!verify_identifier(parse_script, $3)) {
516                                          YYERROR; /* vi should call yyerror() */
517                                      }
518                                      if (!verify_utf8(parse_script, $4)) {
519                                          YYERROR; /* vu should call yyerror() */
520                                      }
521                                      $$ = build_set(SET, canon_stags($2), $3, $4);
522                                  }
523         ;
524 
525 flagaction: ADDFLAG
526         | SETFLAG
527         | REMOVEFLAG
528         ;
529 
530 flagtags: /* empty */            { $$ = NULL; }
531         | flagtags STRING
532                                  {
533                                      if (!(parse_script->support.imap4flags)) {
534                                          parse_error(parse_script,
535                                                      SIEVE_MISSING_REQUIRE,
536                                                      "imap4flags");
537                                          YYERROR; /* pe should call yyerror() */
538                                      }
539                                      if ($1) {
540                                          parse_error(parse_script,
541                                                      SIEVE_DUPLICATE_ARG,
542                                                      "variablename");
543                                          YYERROR; /* pe should call yyerror() */
544                                      }
545                                      if (!is_identifier($2)) {
546                                          YYERROR; /* id should call yyerror() */
547                                      }
548                                      $$ = $2;
549                                  }
550         ;
551 
552 stags: /* empty */               { $$ = new_stags(); }
553         | stags mod40
554                                  {
555                                      if ($$->mod40) {
556                                          parse_error(parse_script,
557                                                      SIEVE_DUPLICATE_TAG,
558                                                      "precedence 40 modifier");
559                                          YYERROR; /* pe should call yyerror() */
560                                      }
561                                      else $$->mod40 = $2;
562                                  }
563         | stags mod30
564                                  {
565                                      if ($$->mod30) {
566                                          parse_error(parse_script,
567                                                      SIEVE_DUPLICATE_TAG,
568                                                      "precedence 30 modifier");
569                                          YYERROR; /* pe should call yyerror() */
570                                      }
571                                      else $$->mod30 = $2;
572                                  }
573         | stags mod20
574                                  {
575                                      if ($$->mod20) {
576                                          parse_error(parse_script,
577                                                      SIEVE_DUPLICATE_TAG,
578                                                      "precedence 20 modifier");
579                                          YYERROR; /* pe should call yyerror() */
580                                      }
581                                      else $$->mod20 = $2;
582                                  }
583 /* TODO: :encodeurl
584    Requires "enotify" extension, which has not been implemented yet.
585 
586    RFC 5435 (Sieve Extension: Notifications)
587    6.  Modifier encodeurl to the 'set' Action
588 
589    Usage:  ":encodeurl"
590 
591    When the Sieve script specifies both "variables" [Variables] and
592    "enotify" capabilities in the "require", a new "set" action modifier
593    (see [Variables]) ":encodeurl" becomes available to Sieve scripts.
594    This modifier performs percent-encoding of any octet in the string
595    that doesn't belong to the "unreserved" set (see [URI]).  The
596    percent-encoding procedure is described in [URI].
597 
598    The ":encodeurl" modifier has precedence 15.
599 
600    Example 6:
601    require ["enotify", "variables"];
602 
603    set :encodeurl "body_param" "Safe body&evil=evilbody";
604 
605    notify "mailto:tim@example.com?body=${body_param}";
606 
607 */
608         | stags mod10
609                                  {
610                                      if ($$->mod10) {
611                                          parse_error(parse_script,
612                                                      SIEVE_DUPLICATE_TAG,
613                                                      "precedence 10 modifier");
614                                          YYERROR; /* pe should call yyerror() */
615                                      }
616                                      else $$->mod10 = $2;
617                                  }
618 ;
619 
620 mod40:  LOWER
621         | UPPER
622         ;
623 mod30:  LOWERFIRST
624         | UPPERFIRST
625         ;
626 mod20:  QUOTEWILDCARD
627         ;
628 mod10:  LENGTH
629         ;
630 
631 itags: /* empty */               { $$ = new_itags(); }
632         | itags PERSONAL
633                                  {
634                                      if ($$->location != -1) {
635                                          parse_error(parse_script,
636                                                      SIEVE_DUPLICATE_TAG,
637                                                      "location");
638                                          YYERROR; /* pe should call yyerror() */
639                                      }
640                                      else $$->location = PERSONAL;
641                                  }
642         | itags GLOBAL
643                                  {
644                                      if ($$->location != -1) {
645                                          parse_error(parse_script,
646                                                      SIEVE_DUPLICATE_TAG,
647                                                      "location");
648                                          YYERROR; /* pe should call yyerror() */
649                                      }
650                                      else $$->location = GLOBAL;
651                                  }
652         | itags ONCE
653                                  {
654                                      if ($$->once != -1) {
655                                          parse_error(parse_script,
656                                                      SIEVE_DUPLICATE_TAG,
657                                                      ":once");
658                                          YYERROR; /* pe should call yyerror() */
659                                      }
660                                      else $$->once = 1;
661                                  }
662         | itags OPTIONAL
663                                  { if ($$->optional != -1) {
664                                          parse_error(parse_script,
665                                                      SIEVE_DUPLICATE_TAG,
666                                                      ":optional");
667                                          YYERROR; /* pe should call yyerror() */
668                                      }
669                                      else $$->optional = 1;
670                                  }
671         ;
672 
673 ntags: /* empty */               { $$ = new_ntags(); }
674         | ntags ID STRING
675                                  {
676                                      if ($$->id != NULL) {
677                                          parse_error(parse_script,
678                                                      SIEVE_DUPLICATE_TAG,
679                                                      ":id");
680                                          YYERROR; /* pe should call yyerror() */
681                                      }
682                                      else $$->id = $3;
683                                  }
684         | ntags METHOD STRING
685                                  {
686                                      if ($$->method != NULL) {
687                                          parse_error(parse_script,
688                                                      SIEVE_DUPLICATE_TAG,
689                                                      ":method");
690                                          YYERROR; /* pe should call yyerror() */
691                                      }
692                                      else $$->method = $3;
693                                  }
694         | ntags OPTIONS stringlist
695                                  {
696                                      if ($$->options != NULL) {
697                                          parse_error(parse_script,
698                                                      SIEVE_DUPLICATE_TAG,
699                                                      ":options");
700                                          YYERROR; /* pe should call yyerror() */
701                                      }
702                                      else $$->options = $3;
703                                  }
704         | ntags priority
705                                  {
706                                      if ($$->priority != -1) {
707                                          parse_error(parse_script,
708                                                      SIEVE_DUPLICATE_TAG,
709                                                      "priority");
710                                          YYERROR; /* pe should call yyerror() */
711                                      }
712                                      else $$->priority = $2;
713                                  }
714         | ntags MESSAGE STRING
715                                  {
716                                      if ($$->message != NULL) {
717                                          parse_error(parse_script,
718                                                      SIEVE_DUPLICATE_TAG,
719                                                      ":message");
720                                          YYERROR; /* pe should call yyerror() */
721                                      }
722                                      else $$->message = $3;
723                                  }
724         ;
725 
726 dtags: /* empty */               { $$ = new_dtags(); }
727         | dtags priority
728                                  {
729                                      if ($$->priority != -1) {
730                                          parse_error(parse_script,
731                                                      SIEVE_DUPLICATE_TAG,
732                                                      "priority");
733                                          YYERROR; /* pe should call yyerror() */
734                                      }
735                                      else $$->priority = $2;
736                                  }
737         | dtags matchtags STRING
738                                  {
739                                      $$->pattern = $3;
740 
741                                      strarray_t sa = STRARRAY_INITIALIZER;
742                                      strarray_appendm(&sa, $3);
743                                      if (!verify_patternlist(parse_script, &sa,
744                                                              &($$->comptags),
745                                                              NULL)) {
746                                          YYERROR; /* vp should call yyerror() */
747                                      }
748                                      strarray_fini(&sa);
749                                  }
750         ;
751 
752 priority: LOW                   { $$ = LOW; }
753         | NORMAL                { $$ = NORMAL; }
754         | HIGH                  { $$ = HIGH; }
755         ;
756 
757 vtags: /* empty */               { $$ = new_vtags(); }
758         | vtags DAYS NUMBER
759                                  {
760                                      if ($$->seconds != -1) {
761                                          parse_error(parse_script,
762                                                      SIEVE_DUPLICATE_TAG,
763                                                      "period");
764                                          YYERROR; /* pe should call yyerror() */
765                                      }
766                                      else $$->seconds = $3 * DAY2SEC;
767                                  }
768         | vtags SECONDS NUMBER
769                                  {
770                                      if (!parse_script->support.vacation_seconds) {
771                                          parse_error(parse_script,
772                                                      SIEVE_MISSING_REQUIRE,
773                                                      "vacation-seconds");
774                                          YYERROR; /* pe should call yyerror() */
775                                      }
776                                      if ($$->seconds != -1) {
777                                          parse_error(parse_script,
778                                                      SIEVE_DUPLICATE_TAG,
779                                                      "period");
780                                          YYERROR; /* pe should call yyerror() */
781                                      }
782                                      $$->seconds = $3;
783                                  }
784         | vtags ADDRESSES stringlist
785                                  {
786                                      if ($$->addresses != NULL) {
787                                          parse_error(parse_script,
788                                                      SIEVE_DUPLICATE_TAG,
789                                                      ":addresses");
790                                          YYERROR; /* pe should call yyerror() */
791                                      }
792                                      if (!verify_stringlist(parse_script, $3,
793                                                             verify_address)) {
794                                          YYERROR;
795                                      }
796                                      $$->addresses = $3;
797                                  }
798         | vtags SUBJECT STRING
799                                  {
800                                      if ($$->subject != NULL) {
801                                          parse_error(parse_script,
802                                                      SIEVE_DUPLICATE_TAG,
803                                                      ":subject");
804                                          YYERROR; /* pe should call yyerror() */
805                                      }
806                                      if (!verify_utf8(parse_script, $3)) {
807                                          YYERROR; /* vu should call yyerror() */
808                                      }
809                                      $$->subject = $3;
810                                  }
811         | vtags FROM STRING
812                                  {
813                                      if ($$->from != NULL) {
814                                          parse_error(parse_script,
815                                                      SIEVE_DUPLICATE_TAG,
816                                                      ":from");
817                                          YYERROR; /* pe should call yyerror() */
818                                      }
819                                      if (!verify_address(parse_script, $3)) {
820                                          YYERROR; /* va should call yyerror() */
821                                      }
822                                      $$->from = $3;
823                                  }
824         | vtags HANDLE STRING
825                                  {
826                                      if ($$->handle != NULL) {
827                                          parse_error(parse_script,
828                                                      SIEVE_DUPLICATE_TAG,
829                                                      ":handle");
830                                          YYERROR; /* pe should call yyerror() */
831                                      }
832                                      if (!verify_utf8(parse_script, $3)) {
833                                          YYERROR; /* vu should call yyerror() */
834                                      }
835                                      $$->handle = $3;
836                                  }
837         | vtags MIME
838                                  {
839                                      if ($$->mime != -1) {
840                                          parse_error(parse_script,
841                                                      SIEVE_DUPLICATE_TAG,
842                                                      ":mime");
843                                          YYERROR; /* pe should call yyerror() */
844                                      }
845                                      $$->mime = MIME;
846                                  }
847         ;
848 
849 stringlist: '[' strings ']'      { $$ = $2; }
850         | STRING                 {
851                                     $$ = strarray_new();
852                                     strarray_appendm($$, $1);
853                                  }
854         ;
855 
856 strings: STRING                  {
857                                     $$ = strarray_new();
858                                     strarray_appendm($$, $1);
859                                  }
860         | strings ',' STRING     {
861                                     $$ = $1;
862                                     strarray_appendm($$, $3);
863                                  }
864         ;
865 
866 block: '{' commands '}'          { $$ = $2; }
867         | '{' '}'                { $$ = NULL; }
868         ;
869 
870 test:     ANYOF testlist         { $$ = new_test(ANYOF); $$->u.tl = $2; }
871         | ALLOF testlist         { $$ = new_test(ALLOF); $$->u.tl = $2; }
872         | EXISTS stringlist      { $$ = new_test(EXISTS); $$->u.sl = $2; }
873         | SFALSE                 { $$ = new_test(SFALSE); }
874         | STRUE                  { $$ = new_test(STRUE); }
875 
876         | HEADER htags stringlist stringlist
877                                  {
878                                      if (!verify_stringlist(parse_script,
879                                                             $3, verify_header)) {
880                                          YYERROR; /* vh should call yyerror() */
881                                      }
882                                      $2 = canon_comptags($2);
883 
884                                      if (!verify_patternlist(parse_script,
885                                                              $4, $2,
886                                                              verify_utf8)) {
887                                          YYERROR; /* vp should call yyerror() */
888                                      }
889 
890                                      $$ = build_header(HEADER, $2, $3, $4);
891                                      if ($$ == NULL) {
892                                          parse_error(parse_script,
893                                                      SIEVE_BUILD_FAILURE,
894                                                      "header test");
895                                          YYERROR; /* pe should call yyerror() */
896                                      }
897                                  }
898 
899         | STRINGT strtags stringlist stringlist
900                                  {
901                                      if (!parse_script->support.variables) {
902                                          parse_error(parse_script,
903                                                      SIEVE_MISSING_REQUIRE,
904                                                      "variables");
905                                          YYERROR; /* pe should call yyerror() */
906                                      }
907                                      if (!verify_stringlist(parse_script,
908                                                             $3, verify_utf8)) {
909                                          YYERROR; /* vu should call yyerror() */
910                                      }
911                                      $2 = canon_comptags($2);
912 
913                                      if (!verify_patternlist(parse_script,
914                                                              $4, $2,
915                                                              verify_utf8)) {
916                                          YYERROR; /* vp should call yyerror() */
917                                      }
918 
919                                      $$ = build_header(STRINGT, $2, $3, $4);
920                                      if ($$ == NULL) {
921                                          parse_error(parse_script,
922                                                      SIEVE_BUILD_FAILURE,
923                                                      "string test");
924                                          YYERROR; /* pe should call yyerror() */
925                                      }
926                                  }
927 
928 /* Per RFC 5232, the variables list (penultimate argument) is optional,
929    but defining the grammar this way results in a shift/reduce conflict.
930    Therefore, we have to flatten the grammar into two rules.
931 */
932         | HASFLAG hftags stringlist stringlist
933                                  {
934                                      if (!parse_script->support.imap4flags) {
935                                          parse_error(parse_script,
936                                                      SIEVE_MISSING_REQUIRE,
937                                                      "imap4flags");
938                                          YYERROR; /* pe should call yyerror() */
939                                      }
940                                      if (!parse_script->support.variables) {
941                                          parse_error(parse_script,
942                                                      SIEVE_MISSING_REQUIRE,
943                                                      "variables");
944                                          YYERROR; /* pe should call yyerror() */
945                                      }
946                                      if (!verify_stringlist(parse_script, $3,
947                                                             verify_identifier)) {
948                                          YYERROR; /* vi should call yyerror() */
949                                      }
950                                      $2 = canon_comptags($2);
951 
952                                      if (!verify_patternlist(parse_script,
953                                                              $4, $2,
954                                                              verify_utf8)) {
955                                          YYERROR; /* vp should call yyerror() */
956                                      }
957 
958                                      $$ = build_header(HASFLAG, $2, $3, $4);
959                                      if ($$ == NULL) {
960                                          parse_error(parse_script,
961                                                      SIEVE_BUILD_FAILURE,
962                                                      "hasflag test");
963                                          YYERROR; /* pe should call yyerror() */
964                                      }
965                                  }
966 
967         | HASFLAG hftags stringlist
968                                  {
969                                      if (!parse_script->support.imap4flags) {
970                                          parse_error(parse_script,
971                                                      SIEVE_MISSING_REQUIRE,
972                                                      "imap4flags");
973                                          YYERROR; /* pe should call yyerror() */
974                                      }
975                                      $2 = canon_comptags($2);
976 
977                                      if (!verify_patternlist(parse_script,
978                                                              $3, $2,
979                                                              verify_utf8)) {
980                                          YYERROR; /* vp should call yyerror() */
981                                      }
982 
983                                      $$ = build_header(HASFLAG, $2, NULL, $3);
984                                      if ($$ == NULL) {
985                                          parse_error(parse_script,
986                                                      SIEVE_BUILD_FAILURE,
987                                                      "hasflag test");
988                                          YYERROR; /* pe should call yyerror() */
989                                      }
990                                  }
991 
992         | ADDRESS atags stringlist stringlist
993                                  {
994                                      if (!verify_stringlist(parse_script, $3,
995                                                             verify_addrheader)) {
996                                          YYERROR; /* vah should call yyerror() */
997                                      }
998                                      $2 = canon_aetags($2);
999 
1000                                      if (!verify_patternlist(parse_script, $4,
1001                                                              &($2->comptags),
1002                                                              NULL)) {
1003                                          YYERROR; /* vp should call yyerror() */
1004                                      }
1005 
1006                                      $$ = build_address(ADDRESS, $2, $3, $4);
1007                                      if ($$ == NULL) {
1008                                          parse_error(parse_script,
1009                                                      SIEVE_BUILD_FAILURE,
1010                                                      "address test");
1011                                          YYERROR; /* pe should call yyerror() */
1012                                      }
1013                                  }
1014 
1015         | ENVELOPE etags stringlist stringlist
1016                                  {
1017                                      if (!parse_script->support.envelope) {
1018                                          parse_error(parse_script,
1019                                                      SIEVE_MISSING_REQUIRE,
1020                                                      "envelope");
1021                                          YYERROR; /* pe should call yyerror() */
1022                                      }
1023                                      if (!verify_stringlist(parse_script, $3,
1024                                                             verify_envelope)) {
1025                                          YYERROR;
1026                                      }
1027                                      $2 = canon_aetags($2);
1028 
1029                                      if (!verify_patternlist(parse_script, $4,
1030                                                              &($2->comptags),
1031                                                              NULL)) {
1032                                          YYERROR; /* vp should call yyerror() */
1033                                      }
1034 
1035                                      $$ = build_address(ENVELOPE, $2, $3, $4);
1036                                      if ($$ == NULL) {
1037                                          parse_error(parse_script,
1038                                                      SIEVE_BUILD_FAILURE,
1039                                                      "envelope test");
1040                                          YYERROR; /* pe should call yyerror() */
1041                                      }
1042                                  }
1043 
1044         | BODY btags stringlist
1045                                  {
1046                                      if (!parse_script->support.body) {
1047                                          parse_error(parse_script,
1048                                                      SIEVE_MISSING_REQUIRE,
1049                                                      "body");
1050                                          YYERROR; /* pe should call yyerror() */
1051                                      }
1052                                      $2 = canon_btags($2);
1053 
1054                                      if (!verify_patternlist(parse_script, $3,
1055                                                              &($2->comptags),
1056                                                              verify_utf8)) {
1057                                          YYERROR; /* vp should call yyerror() */
1058                                      }
1059 
1060                                      $$ = build_body(BODY, $2, $3);
1061                                      if ($$ == NULL) {
1062                                          parse_error(parse_script,
1063                                                      SIEVE_BUILD_FAILURE,
1064                                                      "body test");
1065                                          YYERROR; /* pe should call yyerror() */
1066                                      }
1067                                  }
1068 
1069         | NOT test               { $$ = new_test(NOT); $$->u.t = $2; }
1070         | SIZE sizetag NUMBER    { $$ = new_test(SIZE); $$->u.sz.t = $2;
1071                                    $$->u.sz.n = $3; }
1072 
1073         | DATE dttags STRING datepart stringlist
1074                                  {
1075                                      if (!parse_script->support.date) {
1076                                          parse_error(parse_script,
1077                                                      SIEVE_MISSING_REQUIRE,
1078                                                      "date");
1079                                          YYERROR; /* pe should call yyerror() */
1080                                      }
1081                                      if (!verify_header(parse_script, $3)) {
1082                                          YYERROR; /* vh should call yyerror() */
1083                                      }
1084                                      $2 = canon_dttags($2);
1085 
1086                                      if (!verify_patternlist(parse_script, $5,
1087                                                              &($2->comptags),
1088                                                              NULL)) {
1089                                          YYERROR; /* vp should call yyerror() */
1090                                      }
1091 
1092                                      $$ = build_date(DATE, $2, $3, $4, $5);
1093                                      if ($$ == NULL) {
1094                                          parse_error(parse_script,
1095                                                      SIEVE_BUILD_FAILURE,
1096                                                      "date test");
1097                                          YYERROR; /* pe should call yyerror() */
1098                                      }
1099                                  }
1100 
1101         | CURRENTDATE cdtags datepart stringlist
1102                                  {
1103                                      if (!parse_script->support.date) {
1104                                          parse_error(parse_script,
1105                                                      SIEVE_MISSING_REQUIRE,
1106                                                      "date");
1107                                          YYERROR; /* pe should call yyerror() */
1108                                      }
1109                                      $2 = canon_dttags($2);
1110 
1111                                      if (!verify_patternlist(parse_script, $4,
1112                                                              &($2->comptags),
1113                                                              NULL)) {
1114                                          YYERROR; /* vp should call yyerror() */
1115                                      }
1116 
1117                                      $$ = build_date(CURRENTDATE,
1118                                                      $2, NULL, $3, $4);
1119                                      if ($$ == NULL) {
1120                                          parse_error(parse_script,
1121                                                      SIEVE_BUILD_FAILURE,
1122                                                      "currentdate test");
1123                                          YYERROR; /* pe should call yyerror() */
1124                                      }
1125                                  }
1126 
1127         | MAILBOXEXISTS stringlist
1128                                  {
1129                                      if (!parse_script->support.mailbox) {
1130                                          parse_error(parse_script,
1131                                                      SIEVE_MISSING_REQUIRE,
1132                                                      "mailbox");
1133                                          YYERROR; /* pe should call yyerror() */
1134                                      }
1135 
1136                                      $$ = build_mailboxtest(MAILBOXEXISTS, NULL,
1137                                                             NULL, NULL, $2);
1138                                      if ($$ == NULL) {
1139                                          parse_error(parse_script,
1140                                                      SIEVE_BUILD_FAILURE,
1141                                                      "mailboxexists test");
1142                                          YYERROR; /* pe should call yyerror() */
1143                                      }
1144                                  }
1145 
1146         | METADATA mtags STRING STRING stringlist
1147                                  {
1148                                      if (!parse_script->support.mboxmetadata) {
1149                                          parse_error(parse_script,
1150                                                      SIEVE_MISSING_REQUIRE,
1151                                                      "mboxmetadata");
1152                                          YYERROR; /* pe should call yyerror() */
1153                                      }
1154 
1155                                      $$ = build_mailboxtest(METADATA,
1156                                                             $2, $3, $4, $5);
1157                                      if ($$ == NULL) {
1158                                          parse_error(parse_script,
1159                                                      SIEVE_BUILD_FAILURE,
1160                                                      "metadata test");
1161                                          YYERROR; /* pe should call yyerror() */
1162                                      }
1163                                  }
1164 
1165         | METADATAEXISTS STRING stringlist
1166                                  {
1167                                      if (!parse_script->support.mboxmetadata) {
1168                                          parse_error(parse_script,
1169                                                      SIEVE_MISSING_REQUIRE,
1170                                                      "mboxmetadata");
1171                                          YYERROR; /* pe should call yyerror() */
1172                                      }
1173 
1174                                      $$ = build_mailboxtest(METADATAEXISTS,
1175                                                             NULL, $2, NULL, $3);
1176                                      if ($$ == NULL) {
1177                                          parse_error(parse_script,
1178                                                      SIEVE_BUILD_FAILURE,
1179                                                      "metadataexists test");
1180                                          YYERROR; /* pe should call yyerror() */
1181                                      }
1182                                  }
1183 
1184         | SERVERMETADATA mtags STRING stringlist
1185                                  {
1186                                      if (!parse_script->support.servermetadata) {
1187                                          parse_error(parse_script,
1188                                                      SIEVE_MISSING_REQUIRE,
1189                                                      "servermetadata");
1190                                          YYERROR; /* pe should call yyerror() */
1191                                      }
1192 
1193                                      $$ = build_mailboxtest(SERVERMETADATA,
1194                                                             $2, NULL, $3, $4);
1195                                      if ($$ == NULL) {
1196                                          parse_error(parse_script,
1197                                                      SIEVE_BUILD_FAILURE,
1198                                                      "servermetadata test");
1199                                          YYERROR; /* pe should call yyerror() */
1200                                      }
1201                                  }
1202 
1203         | SERVERMETADATAEXISTS stringlist
1204                                  {
1205                                      if (!parse_script->support.servermetadata) {
1206                                          parse_error(parse_script,
1207                                                      SIEVE_MISSING_REQUIRE,
1208                                                      "servermetadata");
1209                                          YYERROR; /* pe should call yyerror() */
1210                                      }
1211 
1212                                      $$ = build_mailboxtest(SERVERMETADATAEXISTS,
1213                                                             NULL, NULL, NULL, $2);
1214                                      if ($$ == NULL) {
1215                                          parse_error(parse_script,
1216                                                      SIEVE_BUILD_FAILURE,
1217                                                      "servermetadataexists test");
1218                                          YYERROR; /* pe should call yyerror() */
1219                                      }
1220                                  }
1221 
1222         | error                  { $$ = NULL; }
1223         ;
1224 
1225 atags: /* empty */               { $$ = new_aetags(); }
1226         | atags addrparttag
1227                                  {
1228                                      $$ = $1;
1229                                      if ($$->addrtag != -1) {
1230                                          parse_error(parse_script,
1231                                                      SIEVE_DUPLICATE_TAG,
1232                                                      "address-part");
1233                                          YYERROR; /* pe should call yyerror() */
1234                                      }
1235                                      else $$->addrtag = $2;
1236                                  }
1237         | atags matchtags
1238         | atags comparator
1239         | atags idxtags
1240         ;
1241 
1242 etags: /* empty */               { $$ = new_aetags(); }
1243         | etags addrparttag
1244                                  {
1245                                      $$ = $1;
1246                                      if ($$->addrtag != -1) {
1247                                          parse_error(parse_script,
1248                                                      SIEVE_DUPLICATE_TAG,
1249                                                      "address-part");
1250                                          YYERROR; /* pe should call yyerror() */
1251                                      }
1252                                      else $$->addrtag = $2;
1253                                  }
1254         | etags matchtags
1255         | etags comparator
1256         ;
1257 
1258 /* $0 is the symbol which precedes comptags (e.g. aetags).
1259    We typecast this pointer into struct comptags *
1260 */
1261 matchtags: match
1262                                  {
1263                                      struct comptags *ctags = $<ctag>0;
1264                                      if (ctags->match != -1) {
1265                                          parse_error(parse_script,
1266                                                      SIEVE_DUPLICATE_TAG,
1267                                                      "match-type");
1268                                          YYERROR; /* pe should call yyerror() */
1269                                      }
1270                                      else ctags->match = $1;
1271                                  }
1272         | relmatch STRING
1273                                  {
1274                                      struct comptags *ctags = $<ctag>0;
1275                                      if (ctags->match != -1) {
1276                                          parse_error(parse_script,
1277                                                      SIEVE_DUPLICATE_TAG,
1278                                                      "match-type");
1279                                          YYERROR; /* pe should call yyerror() */
1280                                      }
1281                                      else {
1282                                          ctags->match = $1;
1283                                          ctags->relation =
1284                                              verify_relat(parse_script, $2);
1285                                          if (ctags->relation == -1) {
1286                                              YYERROR; /*vr called yyerror()*/
1287                                          }
1288                                      }
1289                                  }
1290         ;
1291 
1292 /* $0 is the symbol which precedes comparator (e.g. aetags).
1293    We typecast this pointer into struct comptags *
1294 */
1295 comparator: COMPARATOR STRING
1296                                  {
1297                                      struct comptags *ctags = $<ctag>0;
1298                                      if (ctags->comparator != NULL) {
1299                                          parse_error(parse_script,
1300                                                      SIEVE_DUPLICATE_TAG,
1301                                                      ":comparator");
1302                                          YYERROR; /* pe should call yyerror() */
1303                                      }
1304                                      else if (!strcmp($2, "i;ascii-numeric") &&
1305                                               !parse_script->support.i_ascii_numeric) {
1306                                          parse_error(parse_script,
1307                                                      SIEVE_MISSING_REQUIRE,
1308                                                      "comparator-i;ascii-numeric");
1309                                          YYERROR; /* pe should call yyerror() */
1310                                      }
1311                                      else ctags->comparator = $2;
1312                                  }
1313         ;
1314 
1315 /* $0 is the symbol which precedes idxtags (e.g. aetags).
1316    We typecast this pointer into struct comptags *
1317 
1318    Note: This rule forces :index to occur before :last
1319    even though RFC 5228 states that tagged arguments can appear in any order.
1320 */
1321 idxtags: INDEX NUMBER
1322                                  {
1323                                      struct comptags *ctags = $<ctag>0;
1324                                      if (!parse_script->support.index) {
1325                                          parse_error(parse_script,
1326                                                      SIEVE_MISSING_REQUIRE,
1327                                                      "index");
1328                                          YYERROR; /* pe should call yyerror() */
1329                                      }
1330                                      if (ctags->index != 0) {
1331                                          parse_error(parse_script,
1332                                                      SIEVE_DUPLICATE_TAG,
1333                                                      ":index");
1334                                          YYERROR; /* pe should call yyerror() */
1335                                      }
1336                                      if ($2 <= 0) {
1337                                          parse_error(parse_script,
1338                                                      SIEVE_INVALID_VALUE,
1339                                                      ":index");
1340                                          YYERROR; /* pe should call yyerror() */
1341                                      }
1342                                      else ctags->index = $2;
1343                                  }
1344         | LAST
1345                                  { struct comptags *ctags = $<ctag>0;
1346                                      if (!parse_script->support.index) {
1347                                          parse_error(parse_script,
1348                                                      SIEVE_MISSING_REQUIRE,
1349                                                      "index");
1350                                          YYERROR; /* pe should call yyerror() */
1351                                      }
1352                                      if (ctags->index == 0) {
1353                                          parse_error(parse_script,
1354                                                      SIEVE_MISSING_TAG,
1355                                                      ":index");
1356                                          YYERROR; /* pe should call yyerror() */
1357                                      }
1358                                      else if (ctags->index < 0) {
1359                                          parse_error(parse_script,
1360                                                      SIEVE_DUPLICATE_TAG,
1361                                                      ":last");
1362                                          YYERROR; /* pe should call yyerror() */
1363                                      }
1364                                      else ctags->index *= -1;
1365                                  }
1366         ;
1367 
1368 htags: /* empty */               { $$ = new_comptags(); }
1369         | htags matchtags
1370         | htags comparator
1371         | htags idxtags
1372         ;
1373 
1374 strtags:/* empty */              { $$ = new_comptags(); }
1375         | strtags matchtags
1376         | strtags comparator
1377         ;
1378 
1379 hftags:/* empty */               { $$ = new_comptags(); }
1380         | hftags matchtags
1381         | hftags comparator
1382         ;
1383 
1384 mtags: /* empty */               { $$ = new_comptags(); }
1385         | mtags matchtags
1386         | mtags comparator
1387         ;
1388 
1389 btags: /* empty */               { $$ = new_btags(); }
1390         | btags RAW
1391                                  {
1392                                      $$ = $1;
1393                                      if ($$->transform != -1) {
1394                                          parse_error(parse_script,
1395                                                      SIEVE_DUPLICATE_TAG,
1396                                                      "transform");
1397                                          YYERROR; /* pe should call yyerror() */
1398                                      }
1399                                      else $$->transform = RAW;
1400                                  }
1401         | btags TEXT
1402                                  {
1403                                      $$ = $1;
1404                                      if ($$->transform != -1) {
1405                                          parse_error(parse_script,
1406                                                      SIEVE_DUPLICATE_TAG,
1407                                                      "transform");
1408                                          YYERROR; /* pe should call yyerror() */
1409                                      }
1410                                      else $$->transform = TEXT;
1411                                  }
1412         | btags CONTENT stringlist
1413                                  {
1414                                      $$ = $1;
1415                                      if ($$->transform != -1) {
1416                                          parse_error(parse_script,
1417                                                      SIEVE_DUPLICATE_TAG,
1418                                                      "transform");
1419                                          YYERROR; /* pe should call yyerror() */
1420                                      }
1421                                      else {
1422                                          $$->transform = CONTENT;
1423                                          $$->content_types = $3;
1424                                      }
1425                                  }
1426         | btags matchtags
1427         | btags comparator
1428         ;
1429 
1430 dttags: /* empty */              { $$ = new_dttags(); }
1431         | dttags ORIGINALZONE
1432                                  {
1433                                      $$ = $1;
1434                                      if ($$->zonetag != -1) {
1435                                          parse_error(parse_script,
1436                                                      SIEVE_DUPLICATE_TAG,
1437                                                      ":originalzone");
1438                                          YYERROR; /* pe should call yyerror() */
1439                                      }
1440                                      else $$->zonetag = ORIGINALZONE;
1441                                  }
1442         | dttags zone
1443         | dttags matchtags
1444         | dttags comparator
1445         | dttags idxtags
1446         ;
1447 
1448 cdtags: /* empty */              { $$ = new_dttags(); }
1449         | cdtags zone
1450         | cdtags matchtags
1451         | cdtags comparator
1452         ;
1453 
1454 /* $0 is the symbol which precedes zone (e.g. dttags).
1455    We typecast this pointer into struct comptags *
1456 */
1457 zone: ZONE STRING
1458                                  {
1459                                      struct dttags *dttags = $<dttag>0;
1460                                      if (dttags->zonetag != -1) {
1461                                          parse_error(parse_script,
1462                                                      SIEVE_DUPLICATE_TAG,
1463                                                      ":zone");
1464                                          YYERROR; /* pe should call yyerror() */
1465                                      }
1466                                      else if (verify_zone(parse_script, $2) == -1) {
1467                                          YYERROR; /*vr called yyerror()*/
1468                                      }
1469                                      else {
1470                                          dttags->zone = $2;
1471                                          dttags->zonetag = ZONE;
1472                                      }
1473                                  }
1474         ;
1475 
1476 datepart: STRING
1477                                  {
1478                                      $$ = verify_date_part(parse_script, $1);
1479                                      if ($$ == -1) {
1480                                          YYERROR; /* vdp called yyerror() */
1481                                      }
1482                                  }
1483         ;
1484 
1485 addrparttag: ALL                 { $$ = ALL; }
1486         | LOCALPART              { $$ = LOCALPART; }
1487         | DOMAIN                 { $$ = DOMAIN; }
1488         | USER
1489                                  {
1490                                      if (!parse_script->support.subaddress) {
1491                                          parse_error(parse_script,
1492                                                      SIEVE_MISSING_REQUIRE,
1493                                                      "subaddress");
1494                                          YYERROR; /* pe should call yyerror() */
1495                                      }
1496                                      $$ = USER;
1497                                  }
1498         | DETAIL
1499                                  {
1500                                      if (!parse_script->support.subaddress) {
1501                                          parse_error(parse_script,
1502                                                      SIEVE_MISSING_REQUIRE,
1503                                                      "subaddress");
1504                                          YYERROR; /* pe should call yyerror() */
1505                                      }
1506                                      $$ = DETAIL;
1507                                  }
1508         ;
1509 match: IS                        { $$ = IS; }
1510         | CONTAINS               { $$ = CONTAINS; }
1511         | MATCHES                { $$ = MATCHES; }
1512         | REGEX
1513                                  {
1514                                      if (!parse_script->support.regex) {
1515                                          parse_error(parse_script,
1516                                                      SIEVE_MISSING_REQUIRE,
1517                                                      "regex");
1518                                          YYERROR; /* pe should call yyerror() */
1519                                      }
1520                                      $$ = REGEX;
1521                                  }
1522         ;
1523 
1524 relmatch: COUNT
1525                                  {
1526                                      if (!parse_script->support.relational) {
1527                                          parse_error(parse_script,
1528                                                      SIEVE_MISSING_REQUIRE,
1529                                                      "relational");
1530                                          YYERROR; /* pe should call yyerror() */
1531                                      }
1532                                      $$ = COUNT;
1533                                  }
1534         | VALUE
1535                                  {
1536                                      if (!parse_script->support.relational) {
1537                                          parse_error(parse_script,
1538                                                      SIEVE_MISSING_REQUIRE,
1539                                                      "relational");
1540                                          YYERROR; /* pe should call yyerror() */
1541                                      }
1542                                      $$ = VALUE;
1543                                  }
1544         ;
1545 
1546 
1547 sizetag: OVER                    { $$ = OVER; }
1548         | UNDER                  { $$ = UNDER; }
1549         ;
1550 
1551 copy: COPY
1552                                  {
1553                                      if (!parse_script->support.copy) {
1554                                          parse_error(parse_script,
1555                                                      SIEVE_MISSING_REQUIRE,
1556                                                      "copy");
1557                                          YYERROR; /* pe should call yyerror() */
1558                                      }
1559                                      $$ = 1;
1560                                  }
1561         ;
1562 
1563 creat:  CREATE
1564                                  {
1565                                      if (!parse_script->support.mailbox) {
1566                                          parse_error(parse_script,
1567                                                      SIEVE_MISSING_REQUIRE,
1568                                                      "mailbox");
1569                                          YYERROR; /* pe should call yyerror() */
1570                                      }
1571                                      $$ = 1;
1572                                  }
1573         ;
1574 
1575 ftags: /* empty */               { $$ = new_ftags(); }
1576         | ftags copy
1577                                  {
1578                                      $$ = $1;
1579                                      if ($$->copy) {
1580                                          parse_error(parse_script,
1581                                                      SIEVE_DUPLICATE_TAG,
1582                                                      ":copy");
1583                                          YYERROR; /* pe should call yyerror() */
1584                                      }
1585                                      else $$->copy = $2;
1586                                  }
1587         | ftags creat
1588                                  {
1589                                      $$ = $1;
1590                                      if ($$->create) {
1591                                          parse_error(parse_script,
1592                                                      SIEVE_DUPLICATE_TAG,
1593                                                      ":create");
1594                                          YYERROR; /* pe should call yyerror() */
1595                                      }
1596                                      else $$->create = $2;
1597                                  }
1598         | ftags FLAGS stringlist
1599                                  {
1600                                      if (!parse_script->support.imap4flags) {
1601                                          parse_error(parse_script,
1602                                                      SIEVE_MISSING_REQUIRE,
1603                                                      "imap4flags");
1604                                          YYERROR; /* pe should call yyerror() */
1605                                      }
1606                                      if ($$->flags != NULL) {
1607                                          parse_error(parse_script,
1608                                                      SIEVE_DUPLICATE_TAG,
1609                                                      ":flags");
1610                                          YYERROR; /* pe should call yyerror() */
1611                                      }
1612 
1613                                      $$ = $1;
1614                                      if (!parse_script->support.variables) {
1615                                          verify_flaglist($3);
1616                                      }
1617                                      if (!$3->count) strarray_add($3, "");
1618                                      $$->flags = $3;
1619                                  }
1620         ;
1621 
1622 rtags: /* empty */               { $$ = 0; }
1623         | rtags copy
1624                                  {
1625                                      $$ = $1;
1626                                      if ($$) {
1627                                          parse_error(parse_script,
1628                                                      SIEVE_DUPLICATE_TAG,
1629                                                      ":copy");
1630                                          YYERROR; /* pe should call yyerror() */
1631                                      }
1632                                      else $$ = $2;
1633                                  }
1634         ;
1635 
1636 testlist: '(' tests ')'          { $$ = $2; }
1637         ;
1638 
1639 tests: test                      { $$ = new_testlist($1, NULL); }
1640         | test ',' tests         { $$ = new_testlist($1, $3); }
1641         ;
1642 
1643 %%
1644 
1645 
1646 /*
1647  * Actions
1648  */
1649 
1650 void yyerror(sieve_script_t *parse_script, const char *msg)
1651 {
1652     parse_script->err++;
1653     if (parse_script->interp.err) {
1654         parse_script->interp.err(sievelineno, msg,
1655                                  parse_script->interp.interp_context,
1656                                  parse_script->script_context);
1657     }
1658 }
1659 
parse_error(sieve_script_t * parse_script,int err,...)1660 static void parse_error(sieve_script_t *parse_script, int err, ...)
1661 {
1662     va_list args;
1663 
1664     va_start(args, err);
1665     vsnprintf(parse_script->sieveerr, ERR_BUF_SIZE, error_message(err), args);
1666     yyerror(parse_script, parse_script->sieveerr);
1667     va_end(args);
1668 }
1669 
check_reqs(sieve_script_t * parse_script,strarray_t * sa)1670 static char *check_reqs(sieve_script_t *parse_script, strarray_t *sa)
1671 {
1672     char *s;
1673     struct buf errs = BUF_INITIALIZER;
1674     char *res;
1675 
1676     while ((s = strarray_shift(sa))) {
1677         if (!script_require(parse_script, s)) {
1678             if (!errs.len)
1679                 buf_printf(&errs,
1680                            "Unsupported feature(s) in \"require\": \"%s\"", s);
1681             else buf_printf(&errs, ", \"%s\"", s);
1682         }
1683         free(s);
1684     }
1685     strarray_free(sa);
1686 
1687     res = buf_release(&errs);
1688     if (!res[0]) {
1689         free(res);
1690         return NULL;
1691     }
1692 
1693     return res;
1694 }
1695 
build_address(int t,struct aetags * ae,strarray_t * sl,strarray_t * pl)1696 static test_t *build_address(int t, struct aetags *ae,
1697                              strarray_t *sl, strarray_t *pl)
1698 {
1699     test_t *ret = new_test(t);  /* can be either ADDRESS or ENVELOPE */
1700 
1701     assert((t == ADDRESS) || (t == ENVELOPE));
1702 
1703     if (ret) {
1704         ret->u.ae.comptag = ae->comptags.match;
1705         ret->u.ae.relation=ae->comptags.relation;
1706         ret->u.ae.comparator=xstrdup(ae->comptags.comparator);
1707         ret->u.ae.index = ae->comptags.index;
1708         ret->u.ae.sl = sl;
1709         ret->u.ae.pl = pl;
1710         ret->u.ae.addrpart = ae->addrtag;
1711         free_aetags(ae);
1712 
1713     }
1714     return ret;
1715 }
1716 
build_header(int t,struct comptags * c,strarray_t * sl,strarray_t * pl)1717 static test_t *build_header(int t, struct comptags *c,
1718                             strarray_t *sl, strarray_t *pl)
1719 {
1720     test_t *ret = new_test(t);  /* can be HEADER or HASFLAG or STRINGT */
1721 
1722     assert((t == HEADER) || (t == HASFLAG) || (t == STRINGT));
1723 
1724     if (ret) {
1725         ret->u.h.comptag = c->match;
1726         ret->u.h.relation = c->relation;
1727         ret->u.h.comparator = xstrdup(c->comparator);
1728         ret->u.h.index = c->index;
1729         ret->u.h.sl = sl;
1730         ret->u.h.pl = pl;
1731         free_comptags(c, 1);
1732     }
1733     return ret;
1734 }
1735 
build_body(int t,struct btags * b,strarray_t * pl)1736 static test_t *build_body(int t, struct btags *b, strarray_t *pl)
1737 {
1738     test_t *ret = new_test(t);  /* can be BODY */
1739 
1740     assert(t == BODY);
1741 
1742     if (ret) {
1743         ret->u.b.comptag = b->comptags.match;
1744         ret->u.b.relation = b->comptags.relation;
1745         ret->u.b.comparator = xstrdup(b->comptags.comparator);
1746         ret->u.b.transform = b->transform;
1747         ret->u.b.offset = b->offset;
1748         ret->u.b.content_types = b->content_types; b->content_types = NULL;
1749         ret->u.b.pl = pl;
1750         free_btags(b);
1751     }
1752     return ret;
1753 }
1754 
build_mailboxtest(int t,struct comptags * c,const char * extname,const char * keyname,strarray_t * keylist)1755 static test_t *build_mailboxtest(int t, struct comptags *c,
1756                                  const char *extname, const char *keyname,
1757                                  strarray_t *keylist)
1758 {
1759     test_t *ret = new_test(t);
1760 
1761     if (ret) {
1762         ret->u.mbx.extname = xstrdupnull(extname);
1763         ret->u.mbx.keyname = xstrdupnull(keyname);
1764         ret->u.mbx.keylist = keylist;
1765         if (c) {
1766             canon_comptags(c);
1767             ret->u.mbx.comptag = c->match;
1768             ret->u.mbx.relation = c->relation;
1769             ret->u.mbx.comparator = xstrdup(c->comparator);
1770             free_comptags(c, 1);
1771         }
1772     }
1773 
1774     return ret;
1775 }
1776 
build_vacation(int t,struct vtags * v,char * reason)1777 static commandlist_t *build_vacation(int t, struct vtags *v, char *reason)
1778 {
1779     commandlist_t *ret = new_command(t);
1780 
1781     assert(t == VACATION);
1782 
1783     if (ret) {
1784         ret->u.v.subject = v->subject; v->subject = NULL;
1785         ret->u.v.from = v->from; v->from = NULL;
1786         ret->u.v.handle = v->handle; v->handle = NULL;
1787         ret->u.v.seconds = v->seconds;
1788         ret->u.v.mime = v->mime;
1789         ret->u.v.addresses = v->addresses; v->addresses = NULL;
1790         free_vtags(v);
1791         ret->u.v.message = reason;
1792     }
1793     return ret;
1794 }
1795 
build_notify(int t,struct ntags * n)1796 static commandlist_t *build_notify(int t, struct ntags *n)
1797 {
1798     commandlist_t *ret = new_command(t);
1799 
1800     assert(t == NOTIFY);
1801        if (ret) {
1802         ret->u.n.method = n->method; n->method = NULL;
1803         ret->u.n.id = n->id; n->id = NULL;
1804         ret->u.n.options = n->options; n->options = NULL;
1805         ret->u.n.priority = n->priority;
1806         ret->u.n.message = n->message; n->message = NULL;
1807         free_ntags(n);
1808     }
1809     return ret;
1810 }
1811 
build_denotify(int t,struct dtags * d)1812 static commandlist_t *build_denotify(int t, struct dtags *d)
1813 {
1814     commandlist_t *ret = new_command(t);
1815 
1816     assert(t == DENOTIFY);
1817 
1818     if (ret) {
1819         ret->u.d.comptag = d->comptags.match;
1820         ret->u.d.relation = d->comptags.relation;
1821         ret->u.d.pattern = xstrdupnull(d->pattern);
1822         ret->u.d.priority = d->priority;
1823         free_dtags(d);
1824     }
1825     return ret;
1826 }
1827 
build_keep(int t,struct ftags * f)1828 static commandlist_t *build_keep(int t, struct ftags *f)
1829 {
1830     commandlist_t *ret = new_command(t);
1831 
1832     assert(t == KEEP);
1833 
1834     if (ret) {
1835         ret->u.k.copy = f->copy;
1836         ret->u.k.flags = f->flags; f->flags = NULL;
1837         free_ftags(f);
1838     }
1839     return ret;
1840 }
1841 
build_fileinto(int t,struct ftags * f,char * folder)1842 static commandlist_t *build_fileinto(int t, struct ftags *f, char *folder)
1843 {
1844     commandlist_t *ret = new_command(t);
1845 
1846     assert(t == FILEINTO);
1847 
1848     if (ret) {
1849         ret->u.f.copy = f->copy;
1850         ret->u.f.create = f->create;
1851         ret->u.f.flags = f->flags; f->flags = NULL;
1852         if (config_getswitch(IMAPOPT_SIEVE_UTF8FILEINTO)) {
1853             ret->u.f.folder = xmalloc(5 * strlen(folder) + 1);
1854             UTF8_to_mUTF7(ret->u.f.folder, folder);
1855         }
1856         else {
1857             ret->u.f.folder = xstrdup(folder);
1858         }
1859         free_ftags(f);
1860     }
1861     return ret;
1862 }
1863 
build_redirect(int t,int copy,char * address)1864 static commandlist_t *build_redirect(int t, int copy, char *address)
1865 {
1866     commandlist_t *ret = new_command(t);
1867 
1868     assert(t == REDIRECT);
1869 
1870     if (ret) {
1871         ret->u.r.copy = copy;
1872         ret->u.r.address = address;
1873     }
1874     return ret;
1875 }
1876 
build_include(int t,struct itags * i,char * script)1877 static commandlist_t *build_include(int t, struct itags *i, char* script)
1878 {
1879     commandlist_t *ret = new_command(t);
1880 
1881     assert(t == INCLUDE);
1882 
1883     if (i->location == -1) i->location = PERSONAL;
1884     if (i->once == -1) i->once = 0;
1885     if (i->optional == -1) i->optional = 0;
1886 
1887     if (ret) {
1888         ret->u.inc.location = i->location;
1889         ret->u.inc.once = i->once;
1890         ret->u.inc.optional = i->optional;
1891         ret->u.inc.script = script;
1892         free(i);
1893     }
1894     return ret;
1895 }
1896 
build_date(int t,struct dttags * dt,char * hn,int part,strarray_t * kl)1897 static test_t *build_date(int t, struct dttags *dt,
1898                           char *hn, int part, strarray_t *kl)
1899 {
1900     test_t *ret = new_test(t);
1901     assert(t == DATE || t == CURRENTDATE);
1902 
1903     if (ret) {
1904         ret->u.dt.comptag = dt->comptags.match;
1905         ret->u.dt.relation = dt->comptags.relation;
1906         ret->u.dt.comparator = xstrdup(dt->comptags.comparator);
1907         ret->u.dt.index = dt->comptags.index;
1908         ret->u.dt.zone = xstrdupnull(dt->zone);
1909         ret->u.dt.zonetag = dt->zonetag;
1910         ret->u.dt.date_part = part;
1911         ret->u.dt.header_name = xstrdupnull(hn);
1912         ret->u.dt.kl = kl;
1913         free_dttags(dt);
1914     }
1915     return ret;
1916 }
1917 
build_set(int t,struct stags * s,char * variable,char * value)1918 static commandlist_t *build_set(int t, struct stags *s,
1919                                 char *variable, char *value)
1920 {
1921     commandlist_t *ret = new_command(t);
1922 
1923     assert(t == SET);
1924 
1925     if (ret) {
1926         ret->u.s.mod40 = s->mod40;
1927         ret->u.s.mod30 = s->mod30;
1928         ret->u.s.mod20 = s->mod20;
1929         ret->u.s.mod10 = s->mod10;
1930         ret->u.s.variable = xstrdup(variable);
1931         ret->u.s.value = xstrdup(value);
1932 
1933         free_stags(s);
1934     }
1935 
1936     return ret;
1937 }
1938 
build_flag(int t,char * variable,strarray_t * flags)1939 static commandlist_t *build_flag(int t, char *variable, strarray_t *flags)
1940 {
1941     commandlist_t *ret = new_command(t);
1942 
1943     assert(t == SETFLAG || t == ADDFLAG || t == REMOVEFLAG);
1944 
1945     if (ret) {
1946         ret->u.fl.variable = xstrdup(variable ? variable : "");
1947         ret->u.fl.flags = flags;
1948     }
1949 
1950     return ret;
1951 }
1952 
new_aetags(void)1953 static struct aetags *new_aetags(void)
1954 {
1955     struct aetags *r = (struct aetags *) xmalloc(sizeof(struct aetags));
1956 
1957     init_comptags(&r->comptags);
1958     r->addrtag = -1;
1959 
1960     return r;
1961 }
1962 
canon_aetags(struct aetags * ae)1963 static struct aetags *canon_aetags(struct aetags *ae)
1964 {
1965     canon_comptags(&ae->comptags);
1966     if (ae->addrtag == -1) { ae->addrtag = ALL; }
1967     return ae;
1968 }
1969 
free_aetags(struct aetags * ae)1970 static void free_aetags(struct aetags *ae)
1971 {
1972     free_comptags(&ae->comptags, 0);
1973     free(ae);
1974 }
1975 
new_comptags(void)1976 static struct comptags *new_comptags(void)
1977 {
1978     struct comptags *c = (struct comptags *) xmalloc(sizeof(struct comptags));
1979 
1980     return init_comptags(c);
1981 }
1982 
init_comptags(struct comptags * c)1983 static struct comptags *init_comptags(struct comptags *c)
1984 {
1985     c->match = c->relation = -1;
1986     c->comparator = NULL;
1987     c->index = 0;
1988 
1989     return c;
1990 }
1991 
canon_comptags(struct comptags * c)1992 static struct comptags *canon_comptags(struct comptags *c)
1993 {
1994     if (c->match == -1) c->match = IS;
1995     if (c->comparator == NULL) c->comparator = xstrdup("i;ascii-casemap");
1996     return c;
1997 }
1998 
free_comptags(struct comptags * c,int destroy)1999 static void free_comptags(struct comptags *c, int destroy)
2000 {
2001     free(c->comparator);
2002     if (destroy) free(c);
2003 }
2004 
new_btags(void)2005 static struct btags *new_btags(void)
2006 {
2007     struct btags *r = (struct btags *) xmalloc(sizeof(struct btags));
2008 
2009     init_comptags(&r->comptags);
2010     r->transform = r->offset = -1;
2011     r->content_types = NULL;
2012 
2013     return r;
2014 }
2015 
canon_btags(struct btags * b)2016 static struct btags *canon_btags(struct btags *b)
2017 {
2018     canon_comptags(&b->comptags);
2019     if (b->transform == -1) b->transform = TEXT;
2020     if (b->content_types == NULL) {
2021         b->content_types = strarray_new();
2022         if (b->transform == RAW) strarray_append(b->content_types, "");
2023         else strarray_append(b->content_types, "text");
2024     }
2025     if (b->offset == -1) b->offset = 0;
2026     return b;
2027 }
2028 
free_btags(struct btags * b)2029 static void free_btags(struct btags *b)
2030 {
2031     free_comptags(&b->comptags, 0);
2032     if (b->content_types) strarray_free(b->content_types);
2033     free(b);
2034 }
2035 
new_vtags(void)2036 static struct vtags *new_vtags(void)
2037 {
2038     struct vtags *r = (struct vtags *) xmalloc(sizeof(struct vtags));
2039 
2040     r->seconds = -1;
2041     r->addresses = NULL;
2042     r->subject = NULL;
2043     r->from = NULL;
2044     r->handle = NULL;
2045     r->mime = -1;
2046 
2047     return r;
2048 }
2049 
canon_vtags(sieve_script_t * parse_script,struct vtags * v)2050 static struct vtags *canon_vtags(sieve_script_t *parse_script, struct vtags *v)
2051 {
2052     assert(parse_script->interp.vacation != NULL);
2053 
2054     if (v->seconds == -1) v->seconds = 7 * DAY2SEC;
2055     if (v->seconds < parse_script->interp.vacation->min_response)
2056         v->seconds = parse_script->interp.vacation->min_response;
2057     if (v->seconds > parse_script->interp.vacation->max_response)
2058         v->seconds = parse_script->interp.vacation->max_response;
2059     if (v->mime == -1) v->mime = 0;
2060 
2061     return v;
2062 }
2063 
free_vtags(struct vtags * v)2064 static void free_vtags(struct vtags *v)
2065 {
2066     strarray_free(v->addresses);
2067     free(v->subject);
2068     free(v->from);
2069     free(v->handle);
2070     free(v);
2071 }
2072 
new_itags()2073 static struct itags *new_itags()
2074 {
2075     struct itags *r = (struct itags *) xmalloc(sizeof(struct itags));
2076 
2077     r->once = -1;
2078     r->location = -1;
2079     r->optional = -1;
2080 
2081     return r;
2082 }
2083 
new_dttags(void)2084 static struct dttags *new_dttags(void)
2085 {
2086     struct dttags *dt = (struct dttags *) xmalloc(sizeof(struct dttags));
2087 
2088     init_comptags(&dt->comptags);
2089     dt->zonetag = -1;
2090     dt->zone = NULL;
2091     return dt;
2092 }
2093 
canon_dttags(struct dttags * dt)2094 static struct dttags *canon_dttags(struct dttags *dt)
2095 {
2096     char zone[14];
2097     int gmoffset;
2098     int hours;
2099     int minutes;
2100     struct tm tm;
2101     time_t t;
2102 
2103     canon_comptags(&dt->comptags);
2104     if (dt->comptags.index == 0) dt->comptags.index = 1;
2105     if (dt->zonetag == -1) {
2106         t = time(NULL);
2107         localtime_r(&t, &tm);
2108         gmoffset = gmtoff_of(&tm, t) / 60;
2109         hours = abs(gmoffset) / 60;
2110         minutes = abs(gmoffset) % 60;
2111         snprintf(zone, sizeof(zone), "%c%02d%02d",
2112                  (gmoffset >= 0 ? '+' : '-'), hours, minutes);
2113         dt->zone = xstrdup(zone);
2114         dt->zonetag = ZONE;
2115     }
2116     return dt;
2117 }
2118 
free_dttags(struct dttags * dt)2119 static void free_dttags(struct dttags *dt)
2120 {
2121     free_comptags(&dt->comptags, 0);
2122     free(dt->zone);
2123     free(dt);
2124 }
2125 
2126 
new_ntags(void)2127 static struct ntags *new_ntags(void)
2128 {
2129     struct ntags *r = (struct ntags *) xmalloc(sizeof(struct ntags));
2130 
2131     r->method = NULL;
2132     r->id = NULL;
2133     r->options = NULL;
2134     r->priority = -1;
2135     r->message = NULL;
2136 
2137     return r;
2138 }
2139 
canon_ntags(struct ntags * n)2140 static struct ntags *canon_ntags(struct ntags *n)
2141 {
2142     if (n->priority == -1) n->priority = NORMAL;
2143     if (n->message == NULL) n->message = xstrdup("$from$: $subject$");
2144     if (n->method == NULL) n->method = xstrdup("default");
2145     return n;
2146 }
canon_dtags(struct dtags * d)2147 static struct dtags *canon_dtags(struct dtags *d)
2148 {
2149     canon_comptags(&d->comptags);
2150     if (d->priority == -1) d->priority = ANY;
2151     return d;
2152 }
2153 
free_ntags(struct ntags * n)2154 static void free_ntags(struct ntags *n)
2155 {
2156     free(n->method);
2157     free(n->id);
2158     strarray_free(n->options);
2159     free(n->message);
2160     free(n);
2161 }
2162 
new_dtags(void)2163 static struct dtags *new_dtags(void)
2164 {
2165     struct dtags *r = (struct dtags *) xzmalloc(sizeof(struct dtags));
2166 
2167     init_comptags(&r->comptags);
2168     r->comptags.comparator = xstrdup("i;ascii-casemap");
2169     r->priority = -1;
2170 
2171     return r;
2172 }
2173 
free_dtags(struct dtags * d)2174 static void free_dtags(struct dtags *d)
2175 {
2176     if (!d) return;
2177     free_comptags(&d->comptags, 0);
2178     free(d);
2179 }
2180 
new_ftags(void)2181 static struct ftags *new_ftags(void)
2182 {
2183     struct ftags *f = (struct ftags *) xzmalloc(sizeof(struct ftags));
2184     return f;
2185 }
2186 
canon_ftags(struct ftags * f)2187 static struct ftags *canon_ftags(struct ftags *f)
2188 {
2189     return f;
2190 }
2191 
new_stags(void)2192 static struct stags *new_stags(void)
2193 {
2194     struct stags *s = (struct stags *) xmalloc(sizeof(struct stags));
2195 
2196     s->mod40 = 0;
2197     s->mod30 = 0;
2198     s->mod20 = 0;
2199     s->mod10 = 0;
2200 
2201     return s;
2202 }
2203 
canon_stags(struct stags * s)2204 static struct stags *canon_stags(struct stags *s)
2205 {
2206     return s;
2207 }
2208 
free_stags(struct stags * s)2209 static void free_stags(struct stags *s)
2210 {
2211     free(s);
2212 }
2213 
free_ftags(struct ftags * f)2214 static void free_ftags(struct ftags *f)
2215 {
2216     if (!f) return;
2217     strarray_free(f->flags);
2218     free(f);
2219 }
2220 
verify_identifier(sieve_script_t * parse_script,char * s)2221 static int verify_identifier(sieve_script_t *parse_script, char *s)
2222 {
2223     /* identifier         = (ALPHA / "_") *(ALPHA / DIGIT / "_") */
2224 
2225     if (!is_identifier(s)) {
2226         snprintf(parse_script->sieveerr, ERR_BUF_SIZE,
2227                  "string '%s': not a valid sieve identifier", s);
2228         yyerror(parse_script, parse_script->sieveerr);
2229         return 0;
2230     }
2231     return 1;
2232 }
2233 
verify_stringlist(sieve_script_t * parse_script,strarray_t * sa,int (* verify)(sieve_script_t *,char *))2234 static int verify_stringlist(sieve_script_t *parse_script, strarray_t *sa,
2235                              int (*verify)(sieve_script_t*, char *))
2236 {
2237     int i;
2238 
2239     for (i = 0 ; i < sa->count ; i++) {
2240         if (!verify(parse_script, sa->data[i])) return 0;
2241     }
2242     return 1;
2243 }
2244 
verify_address(sieve_script_t * parse_script,char * s)2245 static int verify_address(sieve_script_t *parse_script, char *s)
2246 {
2247     parse_script->addrerr[0] = '\0';    /* paranoia */
2248     YY_BUFFER_STATE buffer = addr_scan_string(s);
2249     if (addrparse(parse_script)) {
2250         snprintf(parse_script->sieveerr, ERR_BUF_SIZE,
2251                  "address '%s': %s", s, parse_script->addrerr);
2252         yyerror(parse_script, parse_script->sieveerr);
2253         addr_delete_buffer(buffer);
2254         return 0;
2255     }
2256     addr_delete_buffer(buffer);
2257     return 1;
2258 }
2259 
verify_mailbox(sieve_script_t * parse_script,char * s)2260 static int verify_mailbox(sieve_script_t *parse_script, char *s)
2261 {
2262     if (!verify_utf8(parse_script, s)) return 0;
2263 
2264     /* xxx if not a mailbox, call yyerror */
2265     return 1;
2266 }
2267 
verify_header(sieve_script_t * parse_script,char * hdr)2268 static int verify_header(sieve_script_t *parse_script, char *hdr)
2269 {
2270     char *h = hdr;
2271 
2272     while (*h) {
2273         /* field-name      =       1*ftext
2274            ftext           =       %d33-57 / %d59-126
2275            ; Any character except
2276            ;  controls, SP, and
2277            ;  ":". */
2278         if (!((*h >= 33 && *h <= 57) || (*h >= 59 && *h <= 126))) {
2279             snprintf(parse_script->sieveerr, ERR_BUF_SIZE,
2280                      "header '%s': not a valid header", hdr);
2281             yyerror(parse_script, parse_script->sieveerr);
2282             return 0;
2283         }
2284         h++;
2285     }
2286     return 1;
2287 }
2288 
verify_addrheader(sieve_script_t * parse_script,char * hdr)2289 static int verify_addrheader(sieve_script_t *parse_script, char *hdr)
2290 {
2291     const char **h, *hdrs[] = {
2292         "from", "sender", "reply-to",   /* RFC2822 originator fields */
2293         "to", "cc", "bcc",              /* RFC2822 destination fields */
2294         "resent-from", "resent-sender", /* RFC2822 resent fields */
2295         "resent-to", "resent-cc", "resent-bcc",
2296         "return-path",                  /* RFC2822 trace fields */
2297         "disposition-notification-to",  /* RFC2298 MDN request fields */
2298         "delivered-to",                 /* non-standard (loop detection) */
2299         "approved",                     /* RFC1036 moderator/control fields */
2300         NULL
2301     };
2302 
2303     if (!config_getswitch(IMAPOPT_RFC3028_STRICT))
2304         return verify_header(parse_script, hdr);
2305 
2306     for (lcase(hdr), h = hdrs; *h; h++) {
2307         if (!strcmp(*h, hdr)) return 1;
2308     }
2309 
2310     snprintf(parse_script->sieveerr, ERR_BUF_SIZE,
2311              "header '%s': not a valid header for an address test", hdr);
2312     yyerror(parse_script, parse_script->sieveerr);
2313     return 0;
2314 }
2315 
verify_envelope(sieve_script_t * parse_script,char * env)2316 static int verify_envelope(sieve_script_t *parse_script, char *env)
2317 {
2318     lcase(env);
2319     if (!config_getswitch(IMAPOPT_RFC3028_STRICT) ||
2320         !strcmp(env, "from") || !strcmp(env, "to") || !strcmp(env, "auth")) {
2321         return 1;
2322     }
2323 
2324     snprintf(parse_script->sieveerr, ERR_BUF_SIZE,
2325              "env-part '%s': not a valid part for an envelope test", env);
2326     yyerror(parse_script, parse_script->sieveerr);
2327     return 0;
2328 }
2329 
verify_relat(sieve_script_t * parse_script,char * r)2330 static int verify_relat(sieve_script_t *parse_script, char *r)
2331 {
2332     /* this really should have been a token to begin with.*/
2333     lcase(r);
2334     if (!strcmp(r, "gt")) return GT;
2335     else if (!strcmp(r, "ge")) return GE;
2336     else if (!strcmp(r, "lt")) return LT;
2337     else if (!strcmp(r, "le")) return LE;
2338     else if (!strcmp(r, "ne")) return NE;
2339     else if (!strcmp(r, "eq")) return EQ;
2340     else {
2341         snprintf(parse_script->sieveerr, ERR_BUF_SIZE,
2342                  "flag '%s': not a valid relational operation", r);
2343         yyerror(parse_script, parse_script->sieveerr);
2344         return -1;
2345     }
2346 }
2347 
verify_zone(sieve_script_t * parse_script,char * tz)2348 static int verify_zone(sieve_script_t *parse_script, char *tz)
2349 {
2350     int valid = 0;
2351     unsigned hours;
2352     unsigned minutes;
2353     char sign;
2354 
2355     if (sscanf(tz, "%c%02u%02u", &sign, &hours, &minutes) != 3) {
2356         valid |= -1;
2357     }
2358 
2359     // test sign
2360     switch (sign) {
2361     case '+':
2362     case '-':
2363         break;
2364 
2365     default:
2366         valid |= -1;
2367         break;
2368     }
2369 
2370     // test minutes
2371     if (minutes > 59) {
2372             valid |= -1;
2373     }
2374 
2375     if (valid != 0) {
2376         snprintf(parse_script->sieveerr, ERR_BUF_SIZE,
2377                  "flag '%s': not a valid timezone offset", tz);
2378         yyerror(parse_script, parse_script->sieveerr);
2379     }
2380 
2381     return valid;
2382 }
2383 
verify_date_part(sieve_script_t * parse_script,char * dp)2384 static int verify_date_part(sieve_script_t *parse_script, char *dp)
2385 {
2386     lcase(dp);
2387     if (!strcmp(dp, "year")) return YEAR;
2388     else if (!strcmp(dp, "month")) return MONTH;
2389     else if (!strcmp(dp, "day")) return DAY;
2390     else if (!strcmp(dp, "date")) return DATE;
2391     else if (!strcmp(dp, "julian")) return JULIAN;
2392     else if (!strcmp(dp, "hour")) return HOUR;
2393     else if (!strcmp(dp, "minute")) return MINUTE;
2394     else if (!strcmp(dp, "second")) return SECOND;
2395     else if (!strcmp(dp, "time")) return TIME;
2396     else if (!strcmp(dp, "iso8601")) return ISO8601;
2397     else if (!strcmp(dp, "std11")) return STD11;
2398     else if (!strcmp(dp, "zone")) return ZONE;
2399     else if (!strcmp(dp, "weekday")) return WEEKDAY;
2400     else {
2401         snprintf(parse_script->sieveerr, ERR_BUF_SIZE,
2402                  "'%s': not a valid date-part", dp);
2403         yyerror(parse_script, parse_script->sieveerr);
2404     }
2405 
2406     return -1;
2407 }
2408 
2409 #ifdef ENABLE_REGEX
verify_regex(sieve_script_t * parse_script,char * s,int cflags)2410 static int verify_regex(sieve_script_t *parse_script, char *s, int cflags)
2411 {
2412     int ret;
2413     regex_t *reg = (regex_t *) xmalloc(sizeof(regex_t));
2414 
2415     if ((ret = regcomp(reg, s, cflags)) != 0) {
2416         (void) regerror(ret, reg, parse_script->sieveerr, ERR_BUF_SIZE);
2417         yyerror(parse_script, parse_script->sieveerr);
2418         free(reg);
2419         return 0;
2420     }
2421     free(reg);
2422     return 1;
2423 }
2424 
verify_regexs(sieve_script_t * parse_script,const strarray_t * sa,char * comp)2425 static int verify_regexs(sieve_script_t *parse_script,
2426                          const strarray_t *sa, char *comp)
2427 {
2428     int i;
2429     int cflags = REG_EXTENDED | REG_NOSUB;
2430 
2431 #ifdef HAVE_PCREPOSIX_H
2432     /* support UTF8 comparisons */
2433     cflags |= REG_UTF8;
2434 #endif
2435 
2436     if (!strcmp(comp, "i;ascii-casemap")) {
2437         cflags |= REG_ICASE;
2438     }
2439 
2440     for (i = 0 ; i < sa->count ; i++) {
2441         if ((verify_regex(parse_script, sa->data[i], cflags)) == 0)
2442             return 0;
2443     }
2444     return 1;
2445 }
2446 #else
2447 
verify_regexs(sieve_script_t * parse_script,const strarray_t * sa,char * comp)2448 static int verify_regexs(sieve_script_t *parse_script __attribute__((unused)),
2449                          const strarray_t *sa __attribute__((unused)),
2450                          char *comp __attribute__((unused)))
2451 {
2452     return 0;
2453 }
2454 #endif /* ENABLE_REGEX */
2455 
verify_patternlist(sieve_script_t * parse_script,strarray_t * sa,struct comptags * c,int (* verify)(sieve_script_t *,char *))2456 static int verify_patternlist(sieve_script_t *parse_script,
2457                               strarray_t *sa, struct comptags *c,
2458                               int (*verify)(sieve_script_t*, char *))
2459 {
2460     if (verify && !verify_stringlist(parse_script, sa, verify)) return 0;
2461 
2462     return (c->match == REGEX) ?
2463         verify_regexs(parse_script, sa, c->comparator) : 1;
2464 }
2465 
2466 /*
2467  * Valid UTF-8 check (from RFC 2640 Annex B.1)
2468  *
2469  * The following routine checks if a byte sequence is valid UTF-8. This
2470  * is done by checking for the proper tagging of the first and following
2471  * bytes to make sure they conform to the UTF-8 format. It then checks
2472  * to assure that the data part of the UTF-8 sequence conforms to the
2473  * proper range allowed by the encoding. Note: This routine will not
2474  * detect characters that have not been assigned and therefore do not
2475  * exist.
2476  */
verify_utf8(sieve_script_t * parse_script,char * s)2477 static int verify_utf8(sieve_script_t *parse_script, char *s)
2478 {
2479     const char *buf = s;
2480     const char *endbuf = s + strlen(s);
2481     unsigned char byte2mask = 0x00, c;
2482     int trailing = 0;  /* trailing (continuation) bytes to follow */
2483 
2484     while (buf != endbuf) {
2485         c = *buf++;
2486         if (trailing) {
2487             if ((c & 0xC0) == 0x80) {           /* Does trailing byte
2488                                                    follow UTF-8 format? */
2489                 if (byte2mask) {                /* Need to check 2nd byte
2490                                                    for proper range? */
2491                     if (c & byte2mask)          /* Are appropriate bits set? */
2492                         byte2mask = 0x00;
2493                     else
2494                         break;
2495                 }
2496                 trailing--;
2497             }
2498             else
2499                 break;
2500         }
2501         else {
2502             if ((c & 0x80) == 0x00)             /* valid 1 byte UTF-8 */
2503                 continue;
2504             else if ((c & 0xE0) == 0xC0)        /* valid 2 byte UTF-8 */
2505                 if (c & 0x1E) {                 /* Is UTF-8 byte
2506                                                    in proper range? */
2507                     trailing = 1;
2508                 }
2509                 else
2510                     break;
2511             else if ((c & 0xF0) == 0xE0) {      /* valid 3 byte UTF-8 */
2512                 if (!(c & 0x0F)) {              /* Is UTF-8 byte
2513                                                    in proper range? */
2514                     byte2mask = 0x20;           /* If not, set mask
2515                                                    to check next byte */
2516                 }
2517                 trailing = 2;
2518             }
2519             else if ((c & 0xF8) == 0xF0) {      /* valid 4 byte UTF-8 */
2520                 if (!(c & 0x07)) {              /* Is UTF-8 byte
2521                                                    in proper range? */
2522                     byte2mask = 0x30;           /* If not, set mask
2523                                                    to check next byte */
2524                 }
2525                 trailing = 3;
2526             }
2527             else if ((c & 0xFC) == 0xF8) {      /* valid 5 byte UTF-8 */
2528                 if (!(c & 0x03)) {              /* Is UTF-8 byte
2529                                                    in proper range? */
2530                     byte2mask = 0x38;           /* If not, set mask
2531                                                    to check next byte */
2532                 }
2533                 trailing = 4;
2534             }
2535             else if ((c & 0xFE) == 0xFC) {      /* valid 6 byte UTF-8 */
2536                 if (!(c & 0x01)) {              /* Is UTF-8 byte
2537                                                    in proper range? */
2538                     byte2mask = 0x3C;           /* If not, set mask
2539                                                    to check next byte */
2540                 }
2541                 trailing = 5;
2542             }
2543             else
2544                 break;
2545         }
2546     }
2547 
2548     if ((buf != endbuf) || trailing) {
2549         snprintf(parse_script->sieveerr, ERR_BUF_SIZE,
2550                  "string '%s': not valid utf8", s);
2551         yyerror(parse_script, parse_script->sieveerr);
2552         return 0;
2553     }
2554 
2555     return 1;
2556 }
2557