1 %top {
2 /* GNU Mailutils -- a suite of utilities for electronic mail
3    Copyright (C) 1999-2021 Free Software Foundation, Inc.
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 3 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General
16    Public License along with this library.  If not, see
17    <http://www.gnu.org/licenses/>. */
18 
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22 }
23 
24 %{
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <sys/file.h>
29 #include <sys/stat.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <mailutils/cctype.h>
33 #include <mailutils/wordsplit.h>
34 #include <sieve-priv.h>
35 #include <sieve-gram.h>
36 
37 static char *multiline_delimiter;
38 static int strip_tabs;
39 
40 static int number (void);
41 static int string (void);
42 static void line_begin (void);
43 static void line_add (char const *text, size_t len);
44 static void line_finish (void);
45 static void multiline_begin (void);
46 static void multiline_add (void);
47 static void multiline_finish (void);
48 static char *multiline_strip_tabs (char *text);
49 static void ident (const char *text);
50 static void sieve_include (void);
51 static void sieve_searchpath (void);
52 static int isemptystr (char *text);
53 
54 static mu_linetrack_t trk;
55 static ino_t sieve_source_inode;
56 static mu_stream_t input_stream;
57 
58 static int
fillbuf(char * buf,size_t max_size)59 fillbuf (char *buf, size_t max_size)
60 {
61   int rc;
62 
63   if (!input_stream)
64     return 0;
65 
66   rc = mu_stream_read (input_stream, buf, max_size, &max_size);
67   if (rc)
68     {
69       struct mu_locus_point pt;
70       mu_linetrack_locus (trk, &pt);
71       mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_read", pt.mu_file, rc);
72       mu_locus_point_deinit (&pt);
73       return 0;
74     }
75   return max_size;
76 }
77 
78 #undef YY_INPUT
79 #define YY_INPUT(buf,result,max_size) result = fillbuf (buf, max_size)
80 #define LEX_BUFFER_STATE YY_BUFFER_STATE
81 #define SET_BUFFER_STATE(s) do { \
82         (s) = YY_CURRENT_BUFFER; \
83         yy_switch_to_buffer(yy_create_buffer (yyin, YY_BUF_SIZE)); \
84 } while (0)
85 #define RESTORE_BUFFER_STATE(s) do { \
86         yy_delete_buffer (YY_CURRENT_BUFFER); \
87         yy_switch_to_buffer (s); \
88 } while (0)
89 
90 #define YY_USER_ACTION							\
91   do									\
92     {									\
93       mu_linetrack_advance (trk, &yylloc, yytext, yyleng);		\
94       mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,			\
95 		       MU_IOCTL_LOGSTREAM_SET_LOCUS_RANGE, &yylloc);	\
96     }									\
97   while (0);
98 
99 
100 static void
init_locus(char const * name,ino_t ino)101 init_locus (char const *name, ino_t ino)
102 {
103   if (name)
104     {
105       MU_ASSERT (mu_linetrack_create (&trk, name, 2));
106     }
107   else
108     mu_linetrack_destroy (&trk);
109   sieve_source_inode = ino;
110 }
111 
112 struct buffer_ctx
113 {
114   struct buffer_ctx *prev;
115   mu_linetrack_t trk;
116   struct mu_locus_range incl_range;
117   ino_t i_node;
118   mu_stream_t input;
119   LEX_BUFFER_STATE state;
120 };
121 
122 static struct buffer_ctx *context_stack;
123 
124 static struct buffer_ctx *ctx_lookup (ino_t ino);
125 static int push_source (const char *name);
126 static int pop_source (void);
127 
128 struct buffer_ctx *
ctx_lookup(ino_t ino)129 ctx_lookup (ino_t ino)
130 {
131   struct buffer_ctx *ctx;
132 
133   for (ctx = context_stack; ctx; ctx = ctx->prev)
134     if (ctx->i_node == ino)
135       break;
136   return ctx;
137 }
138 
139 int
push_source(const char * name)140 push_source (const char *name)
141 {
142   int rc;
143   mu_stream_t stream;
144   struct buffer_ctx *ctx;
145   struct stat st;
146 
147   if (stat (name, &st))
148     {
149       mu_error (_("cannot stat `%s': %s"), name, strerror (errno));
150       mu_i_sv_error (mu_sieve_machine);
151       return 1;
152     }
153 
154   if (yylloc.beg.mu_file && st.st_ino == sieve_source_inode)
155     {
156       yyerror (_("recursive inclusion"));
157       return 1;
158     }
159   if ((ctx = ctx_lookup (st.st_ino)))
160     {
161       yyerror (_("recursive inclusion"));
162       if (ctx->prev)
163 	{
164 	  mu_diag_at_locus_range (MU_LOG_ERROR, &ctx->incl_range,
165 				  _("`%s' already included here"),
166 				  name);
167 	  mu_i_sv_error (mu_sieve_machine);
168 	}
169       else
170 	{
171 	  mu_error (_("`%s' already included at top level"), name);
172 	  mu_i_sv_error (mu_sieve_machine);
173 	}
174       return 1;
175     }
176 
177   rc = mu_file_stream_create (&stream, name, MU_STREAM_READ);
178   if (rc)
179     {
180       mu_error (_("cannot open file `%s': %s"), name, mu_strerror (rc));
181       mu_i_sv_error (mu_sieve_machine);
182       return 1;
183     }
184 
185   /* Push current context */
186   if (trk)
187     {
188       ctx = mu_sieve_malloc (mu_sieve_machine, sizeof (*ctx));
189       ctx->trk = trk;
190       mu_locus_range_init (&ctx->incl_range);
191       mu_locus_range_copy (&ctx->incl_range, &yylloc);
192       ctx->i_node = sieve_source_inode;
193       ctx->input = input_stream;
194       ctx->prev = context_stack;
195       context_stack = ctx;
196 
197       /* Switch to the new context */
198       SET_BUFFER_STATE (ctx->state);
199     }
200   input_stream = stream;
201 
202   init_locus (name, st.st_ino);
203 
204   return 0;
205 }
206 
207 int
pop_source()208 pop_source ()
209 {
210   struct buffer_ctx *ctx;
211 
212   mu_stream_destroy (&input_stream);
213 
214   if (!context_stack)
215     {
216       input_stream = NULL;
217       init_locus (NULL, 0);
218       return 1;
219     }
220   /* Restore previous context */
221   input_stream = context_stack->input;
222   mu_linetrack_destroy (&trk);
223   trk = context_stack->trk;
224   mu_locus_range_deinit (&context_stack->incl_range);
225   sieve_source_inode = context_stack->i_node;
226   RESTORE_BUFFER_STATE (context_stack->state);
227   ctx = context_stack->prev;
228   mu_sieve_free (mu_sieve_machine, context_stack);
229   context_stack = ctx;
230 
231   return 0;
232 }
233 %}
234 
235 %option nounput
236 %option noinput
237 
238 %x COMMENT ML STR
239 
240 WS [ \t][ \t]*
241 IDENT [a-zA-Z_][a-zA-Z_0-9]+
242 SIZESUF [kKmMgG]
243 
244 %%
245          /* C-style comments */
246 "/*"                    { BEGIN(COMMENT); }
247 <COMMENT>[^*\n]*        /* eat anything that's not a '*' */
248 <COMMENT>"*"+[^*/\n]*   /* eat up '*'s not followed by '/'s */
249 <COMMENT>\n             ;
250 <COMMENT>"*"+"/"        { BEGIN (INITIAL); }
251          /* Preprocessor directives (an extension) */
252 #[ \t]*include.*\n      { sieve_include (); }
253 #[ \t]*searchpath.*\n   { sieve_searchpath (); }
254          /* End-of-line comments */
255 #.*\n   ;
256 #.*     /* end-of-file comment */;
257          /* Reserved words */
258 require return REQUIRE;
259 if	return IF;
260 elsif	return ELSIF;
261 else	return ELSE;
262 anyof	return ANYOF;
263 allof	return ALLOF;
264 not     return NOT;
265 false   return FALSE;
266 true    return TRUE;
267          /* Identifiers */
268 {IDENT}  { ident (yytext); return IDENT; }
269 :{IDENT} { ident (yytext + 1); return TAG; }
270          /* Numbers */
271 0[0-7]*{SIZESUF}*                     { return number (); }
272 0x[0-9a-fA-F][0-9a-fA-F]+{SIZESUF}*   { return number (); }
273 [1-9][0-9]*{SIZESUF}*                 { return number (); }
274          /* Quoted strings */
275 \"[^\\"]*\"       { return string (); }
276 \"[^\\"]*\\.      { BEGIN(STR);
277                     line_begin ();
278   		    line_add (yytext + 1, yyleng - 3);
279 		    line_add (yytext + yyleng - 1, 1); }
280 <STR>[^\\"]*\\.   { line_add (yytext, yyleng - 2);
281                     line_add (yytext + yyleng - 1, 1); }
282 <STR>[^\\"]*\"   { BEGIN(INITIAL);
283                    if (yyleng > 1)
284                      line_add (yytext, yyleng - 1);
285                    line_finish ();
286 		   return STRING; }
287          /* Multiline strings */
288 text:-?[ \t]*#.*\n       { BEGIN(ML);
289                            multiline_begin (); }
290 text:-?[ \t]*\n          { BEGIN(ML);
291                            multiline_begin (); }
292 text:-?\\?{IDENT}[ \t]*#.*\n { BEGIN(ML);
293                                multiline_begin (); }
294 text:-?\\?{IDENT}[ \t]*\n    { BEGIN(ML);
295                                multiline_begin (); }
296 <ML>#[ \t]*include.*\n    { if (multiline_delimiter[0] == '\\')
297                               multiline_add ();
298                             else
299                               sieve_include (); }
300 <ML>.*\n { char *p = multiline_strip_tabs (yytext);
301 
302            if (strncmp (p, multiline_delimiter, strlen (multiline_delimiter))
303 	        == 0
304 	       && isemptystr (p + strlen (multiline_delimiter)))
305 	     {
306 	       free (multiline_delimiter);
307 	       multiline_delimiter = NULL;
308 	       BEGIN(INITIAL);
309 	       multiline_finish ();
310 	       return MULTILINE;
311 	     }
312 	    multiline_add (); }
313 {WS}     ;
314          /* Other tokens */
315 \n       ;
316 . return yytext[0];
317 
318 %%
319 
320 int
321 yywrap ()
322 {
323   return pop_source();
324 }
325 
326 static char *
327 get_file_name (char *p, char *endp, int *usepath)
328 {
329   char exp, *name, *startp;
330   int n;
331 
332   if (usepath)
333     *usepath = 0;
334   switch (*p)
335     {
336     case '"':
337       exp = '"';
338       break;
339 
340     case '<':
341       exp = '>';
342       if (usepath)
343 	*usepath = 1;
344       break;
345 
346     default:
347       yyerror (_("preprocessor syntax"));
348       return NULL;
349     }
350 
351   for (startp = ++p; p < endp && *p != exp; p++)
352     ;
353 
354   if (*p != exp)
355     {
356       yyerror (_("missing closing quote in preprocessor statement"));
357       return NULL;
358     }
359 
360   n = p - startp;
361   name = mu_sieve_malloc (mu_sieve_machine, n + 1);
362   memcpy (name, startp, n);
363   name[n] = 0;
364   return name;
365 }
366 
367 static int
368 _try_include (void *item, void *data)
369 {
370   char **dir = data;
371   char *name = mu_make_file_name ((char*) item, *dir);
372 
373   if (!name)
374     {
375       mu_diag_funcall (MU_DIAG_ERROR, "mu_make_file_name", NULL, ENOMEM);
376       return 0;
377     }
378   if (access (name, R_OK) == 0)
379     {
380       *(char**) data = name;
381       return MU_ERR_USER0;
382     }
383   free (name);
384   return 0;
385 }
386 
387 static void
388 sieve_include (void)
389 {
390   char *p, *endp = yytext + yyleng, *name;
391   int usepath;
392 
393   p = strstr (yytext, "include");
394   for (p += 7; p < endp && mu_isspace (*p); p++)
395     ;
396 
397   name = get_file_name (p, endp, &usepath);
398   if (!name)
399     return;
400 
401   if (usepath && name[0] != '/' && memcmp (name, "..", 2))
402     {
403       char *p = name;
404       if (mu_sieve_include_path
405 	  && mu_list_foreach (mu_sieve_include_path, _try_include, &p))
406 	{
407 	  push_source (p);
408 	  mu_sieve_free (mu_sieve_machine, name);
409 	  free (p);
410 	  return;
411 	}
412     }
413 
414   push_source (name);
415   mu_sieve_free (mu_sieve_machine, name);
416 }
417 
418 static void
419 sieve_searchpath (void)
420 {
421   char *p, *endp = yytext + yyleng, *name;
422 
423   p = strstr (yytext, "searchpath");
424   for (p += 10; p < endp && mu_isspace (*p); p++)
425     ;
426   name = get_file_name (p, endp, NULL);
427   if (name)
428     {
429       mu_i_sv_load_add_dir (mu_sieve_machine, name);
430       mu_sieve_free (mu_sieve_machine, name);
431     }
432 }
433 
434 int
435 mu_i_sv_lex_begin (const char *name)
436 {
437   return push_source (name);
438 }
439 
440 int
441 mu_i_sv_lex_begin_string (const char *buf, int bufsize,
442 			  struct mu_locus_point const *pt)
443 {
444   int rc;
445 
446   yyrestart (NULL);
447 
448   rc = mu_static_memory_stream_create (&input_stream, buf, bufsize);
449   if (rc)
450     {
451       mu_diag_funcall (MU_DIAG_ERROR, "mu_static_memory_stream_create",
452 		       NULL, rc);
453       return 1;
454     }
455 
456   init_locus (pt->mu_file, 0);
457   mu_linetrack_rebase (trk, pt);
458 
459   return 0;
460 }
461 
462 void
463 mu_i_sv_lex_finish (void)
464 {
465   while (pop_source () == 0)
466     ;
467 }
468 
469 static int
470 number ()
471 {
472   char *p;
473   yylval.number = strtoul (yytext, &p, 0);
474   switch (*p)
475     {
476     case 'k':
477     case 'K':
478       yylval.number *= 1024L;
479       break;
480 
481     case 'm':
482     case 'M':
483       yylval.number *= 1024*1024L;
484       break;
485 
486     case 'g':
487     case 'G':
488       yylval.number *= 1024*1024*1024L;
489     }
490   return NUMBER;
491 }
492 
493 static int
494 string (void)
495 {
496   line_begin ();
497   line_add (yytext + 1, yyleng - 2);
498   line_finish ();
499   return STRING;
500 }
501 
502 static int
503 isemptystr (char *text)
504 {
505   for (; *text && mu_isspace (*text); text++)
506     ;
507   return *text == 0;
508 }
509 
510 static char *
511 multiline_strip_tabs (char *text)
512 {
513   if (strip_tabs)
514     for (; *text == '\t'; text++)
515       ;
516   return text;
517 }
518 
519 static void
520 line_add (char const *text, size_t len)
521 {
522   mu_opool_append (mu_sieve_machine->string_pool, text, len);
523 }
524 
525 static void
526 multiline_add (void)
527 {
528   mu_opool_appendz (mu_sieve_machine->string_pool,
529 		    multiline_strip_tabs (yytext));
530 }
531 
532 static void
533 line_begin (void)
534 {
535   /* nothing */
536 }
537 
538 static void
539 multiline_begin (void)
540 {
541   char *p = yytext + 5; /* past the text: keyword */
542 
543   if (*p == '-')
544     {
545       strip_tabs = 1;
546       p++;
547     }
548   else
549     strip_tabs = 0;
550 
551   if (!mu_isspace (*p))
552     {
553       char *endp;
554       int len;
555 
556       for (endp = p; *endp; endp++)
557 	if (mu_isspace (*endp))
558 	  break;
559 
560       len = endp - p;
561       multiline_delimiter = mu_sieve_malloc (mu_sieve_machine, len + 1);
562       memcpy (multiline_delimiter, p, len);
563       multiline_delimiter[len] = 0;
564     }
565   else
566     {
567       multiline_delimiter = strdup (".");
568       if (!multiline_delimiter)
569 	{
570 	  yyerror (_("not enough memory"));
571 	  exit (1);
572 	}
573     }
574 
575   line_begin ();
576 }
577 
578 static void
579 multiline_finish (void)
580 {
581   line_finish ();
582 }
583 
584 static void
585 ident (const char *text)
586 {
587   yylval.string = strdup (text);
588   if (!yylval.string)
589     {
590       yyerror (_("not enough memory"));
591       exit (1);
592     }
593 }
594 
595 static mu_i_sv_interp_t string_interpreter;
596 
597 static void
598 line_finish (void)
599 {
600   char *str;
601 
602   mu_opool_append_char (mu_sieve_machine->string_pool, 0);
603   str = mu_opool_finish (mu_sieve_machine->string_pool, NULL);
604   if (string_interpreter)
605     {
606       char *exp;
607       int rc = mu_i_sv_string_expand (str, string_interpreter, NULL, &exp);
608       if (rc == 0)
609 	{
610 	  mu_opool_free (mu_sieve_machine->string_pool, str);
611 	  mu_opool_appendz (mu_sieve_machine->string_pool, exp);
612 	  mu_opool_append_char (mu_sieve_machine->string_pool, 0);
613 	  free (exp);
614 	  str = mu_opool_finish (mu_sieve_machine->string_pool, NULL);
615 	}
616       else if (rc != MU_ERR_CANCELED)
617 	{
618 	  mu_error (_("error expandind string: %s"), mu_strerror (rc));
619 	}
620     }
621   yylval.string = str;
622 }
623 
624 int
625 mu_sieve_require_encoded_character (mu_sieve_machine_t mach,
626 				    const char *name)
627 {
628   string_interpreter = mu_i_sv_expand_encoded_char;
629   return 0;
630 }
631