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