Commit 758a80c5 758a80c5696e5f7716f59e00077ea2bc6742b61b by Sam Roberts

Patches from Sergey to make the auto* stuff work, now uses lex/yacc

portably, and puts all the necessary files into the distribution.
1 parent 77037aee
1 CFLAGS = -Wall -pedantic -g -DTESTING 1 CFLAGS = -Wall -pedantic -g -DTESTING
2 INCLUDES = -I${top_srcdir}/include -I${top_srcdir}/lib 2 INCLUDES = -I${top_srcdir}/include -I${top_srcdir}/lib
3 YLWRAP = $(SHELL) $(srcdir)/ylwrap
4 YFLAGS = -d
5
6 BUILT_SOURCES=sieve-gram.c sieve-gram.h \
7 sieve-lex.c addr-lex.c \
8 addr.c addr.h
3 9
4 MOSTLYCLEANFILES= 10 MOSTLYCLEANFILES=
5 CLEANFILES= 11 CLEANFILES=
6 DISTCLEANFILES= 12 DISTCLEANFILES=
7 MAINTAINERCLEANFILES=\ 13 MAINTAINERCLEANFILES=$(BUILT_SOURCES)
8 sieve-gram.c sieve-gram.h \ 14 EXTRA_DIST = $(BUILT_SOURCES)
9 sieve-lex.c \
10 addr-lex.c \
11 addr.c addr.h
12 15
13 bin_PROGRAMS = test sieve 16 bin_PROGRAMS = sieve
14 17
15 SRC = \ 18 SRC = \
16 addr-lex.c \ 19 addr-lex.c \
...@@ -30,40 +33,51 @@ SRC = \ ...@@ -30,40 +33,51 @@ SRC = \
30 util.c \ 33 util.c \
31 xmalloc.c 34 xmalloc.c
32 35
33 test_DEPENDENCIES = 36 HDR = \
34 test_LDADD = 37 comparator.h \
35 test_SOURCES = \ 38 exitcodes.h \
36 test.c \ 39 hmac-md5.h \
37 ${SRC} 40 imparse.h \
38 41 interp.h \
42 md5.h \
43 md5global.h \
44 message.h \
45 parseaddr.h \
46 script.h \
47 sieve_err.h \
48 sieve_interface.h \
49 svfield.h \
50 sysexits.h \
51 tree.h \
52 util.h \
53 xmalloc.h
39 54
40 sieve_DEPENDENCIES = ../mailbox/libmailbox.la 55 sieve_DEPENDENCIES = ../mailbox/libmailbox.la
41 sieve_LDADD = ../mailbox/libmailbox.la ../lib/libmailutils.a 56 sieve_LDADD = ../mailbox/libmailbox.la ../lib/libmailutils.a
42 sieve_SOURCES = \ 57 sieve_SOURCES = \
43 sieve.c \ 58 sieve.c \
44 ${SRC} 59 ${SRC} $(HDR)
45 60
46 YACC = bison -y 61 addr-lex.c: $(srcdir)/addr-lex.l addr.h
47 YFLAGS = -d 62 $(YLWRAP) "$(LEX)" $(srcdir)/addr-lex.l lex.yy.c \
48 LEX = flex 63 addr-lex.c -- -yy addr
49 64
50 addr-lex.c: addr-lex.l addr.h 65 addr.c addr.h: $(srcdir)/addr.y
51 $(LEX) -t -Paddr addr-lex.l > $@ 66 $(YLWRAP) "$(YACC) $(YFLAGS)" $(srcdir)/addr.y \
67 y.tab.c addr.c y.tab.h addr.h \
68 -- -yy addr
52 69
53 addr.c addr.h: addr.y 70 sieve-lex.c: $(srcdir)/sieve-lex.l sieve-gram.h
54 $(YACC) $(YFLAGS) -p addr addr.y 71 $(YLWRAP) "$(LEX)" $(srcdir)/sieve-lex.l lex.yy.c sieve-lex.c
55 mv -f y.tab.c addr.c
56 mv -f y.tab.h addr.h
57 72
58 sieve-lex.c: sieve-lex.l sieve-gram.h 73 sieve-gram.c sieve-gram.h: $(srcdir)/sieve-gram.y
59 $(LEX) -t sieve-lex.l > $@ 74 $(YLWRAP) "$(YACC) $(YFLAGS)" $(srcdir)/sieve-gram.y \
60 75 y.tab.c sieve-gram.c y.tab.h sieve-gram.h
61 sieve-gram.c sieve-gram.h: sieve-gram.y
62 $(YACC) $(YFLAGS) sieve-gram.y
63 mv -f y.tab.c sieve-gram.c
64 mv -f y.tab.h sieve-gram.h
65 76
66 comparator.o: sieve-gram.h 77 comparator.o: sieve-gram.h
67 script.o: sieve-gram.h 78 script.o: sieve-gram.h
68 tree.o: sieve-gram.h 79 tree.o: sieve-gram.h
69 80
81 empty: clean
82 rm -f $(BUILT_SOURCES)
83
......
1 /* test.c -- tester for libsieve
2 * Larry Greenfield
3 * $Id$
4 *
5 * usage: "test message script"
6 */
7 /***********************************************************
8 Copyright 1999 by Carnegie Mellon University
9
10 All Rights Reserved
11
12 Permission to use, copy, modify, and distribute this software and its
13 documentation for any purpose and without fee is hereby granted,
14 provided that the above copyright notice appear in all copies and that
15 both that copyright notice and this permission notice appear in
16 supporting documentation, and that the name of Carnegie Mellon
17 University not be used in advertising or publicity pertaining to
18 distribution of the software without specific, written prior
19 permission.
20
21 CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
22 THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
23 FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
24 ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
25 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
26 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
27 OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28 ******************************************************************/
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <stdio.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 #include <ctype.h>
41 #include <string.h>
42 #include <stdlib.h>
43
44 #include "sieve_interface.h"
45
46 #define HEADERCACHESIZE 1019
47
48 typedef struct Header {
49 char *name;
50 int ncontents;
51 char *contents[1];
52 } header_t;
53
54 typedef struct message_data {
55 char *name;
56 FILE *data;
57 int size;
58
59 int cache_full;
60 header_t *cache[HEADERCACHESIZE];
61 } message_data_t;
62
63 int hashheader(char *header)
64 {
65 int x = 0;
66 /* any CHAR except ' ', :, or a ctrl char */
67 for (; !iscntrl(*header) && (*header != ' ') && (*header != ':');
68 header++) {
69 x *= 256;
70 x += *header;
71 x %= HEADERCACHESIZE;
72 }
73 return x;
74 }
75
76 /* take a list of headers, pull the first one out and return it in
77 name and contents.
78
79 returns 0 on success, negative on failure */
80 typedef enum {
81 NAME_START,
82 NAME,
83 COLON,
84 BODY_START,
85 BODY
86 } state;
87
88 int parseheader(FILE *f, char **headname, char **contents) {
89 char c;
90 char name[80], body[1024];
91 int off = 0;
92 state s = NAME_START;
93
94
95 /* there are two ways out of this loop, both via gotos:
96 either we successfully read a character (got_header)
97 or we hit an error (ph_error) */
98 while ((c = getc(f))) { /* examine each character */
99 switch (s) {
100 case NAME_START:
101 if (c == '\r' || c == '\n') {
102 /* no header here! */
103 goto ph_error;
104 }
105 if (!isalpha(c))
106 goto ph_error;
107 name[0] = tolower(c);
108 off = 1;
109 s = NAME;
110 break;
111
112 case NAME:
113 if (c == ' ' || c == '\t' || c == ':') {
114 name[off] = '\0';
115 s = (c == ':' ? BODY_START : COLON);
116 break;
117 }
118 if (iscntrl(c)) {
119 goto ph_error;
120 }
121 name[off++] = tolower(c);
122 break;
123
124 case COLON:
125 if (c == ':') {
126 s = BODY_START;
127 } else if (c != ' ' && c != '\t') {
128 goto ph_error;
129 }
130 break;
131
132 case BODY_START:
133 if (c == ' ' || c == '\t') /* eat the whitespace */
134 break;
135 off = 0;
136 s = BODY;
137 /* falls through! */
138 case BODY:
139 if (c == '\r' || c == '\n') {
140 int peek = getc(f);
141
142 /* we should peek ahead to see if it's folded whitespace */
143 if (c == '\r' && peek == '\n') {
144 c = getc(f);
145 } else {
146 c = peek; /* single newline seperator */
147 }
148 if (c != ' ' && c != '\t') {
149 /* this is the end of the header */
150 body[off] = '\0';
151 ungetc(c, f);
152 goto got_header;
153 }
154 /* ignore this whitespace, but we'll copy all the rest in */
155 break;
156 } else {
157 /* just an ordinary character */
158 body[off++] = c;
159 }
160 }
161 }
162
163 /* if we fall off the end of the loop, we hit some sort of error
164 condition */
165
166 ph_error:
167 if (headname != NULL) *headname = NULL;
168 if (contents != NULL) *contents = NULL;
169 return -1;
170
171 got_header:
172 if (headname != NULL) *headname = strdup(name);
173 if (contents != NULL) *contents = strdup(body);
174
175 return 0;
176 }
177
178 void fill_cache(message_data_t *m)
179 {
180 rewind(m->data);
181
182 /* let's fill that header cache */
183 for (;;) {
184 char *name, *body;
185 int cl, clinit;
186
187 if (parseheader(m->data, &name, &body) < 0) {
188 break;
189 }
190
191 /* put it in the hash table */
192 clinit = cl = hashheader(name);
193 while (m->cache[cl] != NULL && strcmp(name, m->cache[cl]->name)) {
194 cl++; /* resolve collisions linearly */
195 cl %= HEADERCACHESIZE;
196 if (cl == clinit) break; /* gone all the way around, so bail */
197 }
198
199 /* found where to put it, so insert it into a list */
200 if (m->cache[cl]) {
201 /* add this body on */
202
203 m->cache[cl]->contents[m->cache[cl]->ncontents++] = body;
204
205 /* whoops, won't have room for the null at the end! */
206 if (!(m->cache[cl]->ncontents % 8)) {
207 /* increase the size */
208 m->cache[cl] = (header_t *)
209 realloc(m->cache[cl],sizeof(header_t) +
210 ((8 + m->cache[cl]->ncontents) * sizeof(char *)));
211 if (m->cache[cl] == NULL) {
212 fprintf(stderr, "realloc() returned NULL\n");
213 exit(1);
214 }
215 }
216
217 } else {
218 /* create a new entry in the hash table */
219 m->cache[cl] = (header_t *) malloc(sizeof(header_t) +
220 8 * sizeof(char*));
221 if (m->cache[cl] == NULL) {
222 fprintf(stderr, "malloc() returned NULL\n");
223 exit(1);
224 }
225 m->cache[cl]->name = name;
226 m->cache[cl]->contents[0] = body;
227 m->cache[cl]->ncontents = 1;
228 }
229
230 /* we always want a NULL at the end */
231 m->cache[cl]->contents[m->cache[cl]->ncontents] = NULL;
232 }
233
234 m->cache_full = 1;
235 }
236
237 /* gets the header "head" from msg. */
238 int getheader(void *v, const char *phead, const char ***body)
239 {
240 message_data_t *m = (message_data_t *) v;
241 int cl, clinit;
242 char *h;
243 char *head;
244
245 *body = NULL;
246
247 if (!m->cache_full) {
248 fill_cache(m);
249 }
250
251 /* copy header parameter so we can mangle it */
252 head = malloc(strlen(phead)+1);
253 if (!head) return SIEVE_FAIL;
254 strcpy(head, phead);
255
256 h = head;
257 while (*h != '\0') {
258 *h = tolower(*h);
259 h++;
260 }
261
262 /* check the cache */
263 clinit = cl = hashheader(head);
264 while (m->cache[cl] != NULL) {
265 if (!strcmp(head, m->cache[cl]->name)) {
266 *body = (const char **) m->cache[cl]->contents;
267 break;
268 }
269 cl++; /* try next hash bin */
270 cl %= HEADERCACHESIZE;
271 if (cl == clinit) break; /* gone all the way around */
272 }
273
274 free(head);
275
276 if (*body) {
277 return SIEVE_OK;
278 } else {
279 return SIEVE_FAIL;
280 }
281 }
282
283 message_data_t *new_msg(FILE *msg, int size, char *name)
284 {
285 int i;
286 message_data_t *m;
287
288 m = (message_data_t *) malloc(sizeof(message_data_t));
289 if (m == NULL) {
290 fprintf(stderr, "malloc() returned NULL\n");
291 exit(1);
292 }
293 m->data = msg;
294 m->size = size;
295 m->name = name;
296 for (i = 0; i < HEADERCACHESIZE; i++) {
297 m->cache[i] = NULL;
298 }
299 m->cache_full = 0;
300
301 return m;
302 }
303
304 int getsize(void *mc, int *size)
305 {
306 message_data_t *m = (message_data_t *) mc;
307
308 *size = m->size;
309 return SIEVE_OK;
310 }
311
312 int getenvelope(void *v, const char *head, const char ***body)
313 {
314 static char *buf[2];
315
316 if (buf[0] == NULL) { buf[0] = malloc(sizeof(char) * 256); buf[1] = NULL; }
317 printf("Envelope body of '%s'? ", head);
318 scanf("%s", buf[0]);
319 *body = (const char**)buf;
320
321 return SIEVE_OK;
322 }
323
324 int redirect(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
325 {
326 sieve_redirect_context_t *rc = (sieve_redirect_context_t *) ac;
327 message_data_t *m = (message_data_t *) mc;
328 printf("redirecting message '%s' to '%s'\n", m->name, rc->addr);
329 return SIEVE_OK;
330 }
331
332 int discard(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
333 {
334 message_data_t *m = (message_data_t *) mc;
335 printf("discarding message '%s'\n", m->name);
336 return SIEVE_OK;
337 }
338
339 int reject(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
340 {
341 sieve_reject_context_t *rc = (sieve_reject_context_t *) ac;
342 message_data_t *m = (message_data_t *) mc;
343 printf("rejecting message '%s' with '%s'\n", m->name, rc->msg);
344 return SIEVE_OK;
345 }
346
347 int fileinto(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
348 {
349 sieve_fileinto_context_t *fc = (sieve_fileinto_context_t *) ac;
350 message_data_t *m = (message_data_t *) mc;
351
352 printf("filing message '%s' into '%s'\n", m->name, fc->mailbox);
353
354 if (fc->imapflags->flag) {
355 int n;
356 printf("\twith flags");
357 for (n = 0; n < fc->imapflags->nflags; n++)
358 printf(" '%s'", fc->imapflags->flag[n]);
359 printf("\n");
360 }
361
362 return SIEVE_OK;
363 }
364
365 int keep(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
366 {
367 sieve_keep_context_t *kc = (sieve_keep_context_t *) ac;
368 message_data_t *m = (message_data_t *) mc;
369
370 printf("keeping message '%s'\n", m->name);
371
372 if (kc->imapflags->flag) {
373 int n;
374 printf("\twith flags");
375 for (n = 0; n < kc->imapflags->nflags; n++)
376 printf(" '%s'", kc->imapflags->flag[n]);
377 printf("\n");
378 }
379
380 return SIEVE_OK;
381 }
382
383 int notify(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
384 {
385 sieve_notify_context_t *nc = (sieve_notify_context_t *) ac;
386
387 printf("notify msg = '%s' with priority = %s\n",nc->message, nc->priority);
388
389 return SIEVE_OK;
390 }
391
392 int mysieve_error(int lineno, const char *msg, void *i, void *s)
393 {
394 fprintf(stderr, "line %d: %s\r\n", lineno, msg);
395
396 return SIEVE_OK;
397 }
398
399 int mysieve_execute_error(const char *msg, void *i, void *s, void *m)
400 {
401 fprintf(stderr, "%s\r\n", msg);
402
403 return SIEVE_OK;
404 }
405
406
407 int autorespond(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
408 {
409 sieve_autorespond_context_t *arc = (sieve_autorespond_context_t *) ac;
410 char yn;
411 int i;
412
413 printf("Have I already responded to '");
414 for (i = 0; i < arc->len; i++) {
415 printf("%x", arc->hash[i]);
416 }
417 printf("' in %d days? ", arc->days);
418 scanf(" %c", &yn);
419
420 if (tolower(yn) == 'y') return SIEVE_DONE;
421 if (tolower(yn) == 'n') return SIEVE_OK;
422
423 return SIEVE_FAIL;
424 }
425
426 int send_response(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
427 {
428 sieve_send_response_context_t *src = (sieve_send_response_context_t *) ac;
429 message_data_t *m = (message_data_t *) mc;
430 printf("echo '%s' | mail -s '%s' '%s' for message '%s'\n",
431 src->msg, src->subj, src->addr, m->name);
432 return SIEVE_OK;
433 }
434
435 sieve_vacation_t vacation = {
436 0, /* min response */
437 0, /* max response */
438 &autorespond, /* autorespond() */
439 &send_response /* send_response() */
440 };
441
442 char *markflags[] = { "\\flagged" };
443 sieve_imapflags_t mark = { markflags, 1 };
444
445 int main(int argc, char *argv[])
446 {
447 sieve_interp_t *i;
448 sieve_script_t *s;
449 message_data_t *m;
450 FILE *f;
451 int fd, res;
452 struct stat sbuf;
453
454 if (argc != 3) {
455 fprintf(stderr, "usage:\n");
456 fprintf(stderr, "%s message script\n", argv[0]);
457 fprintf(stderr, "%s -v script\n", argv[0]);
458 exit(1);
459 }
460
461 res = sieve_interp_alloc(&i, NULL);
462 if (res != SIEVE_OK) {
463 printf("sieve_interp_alloc() returns %d\n", res);
464 exit(1);
465 }
466
467 res = sieve_register_redirect(i, &redirect);
468 if (res != SIEVE_OK) {
469 printf("sieve_register_redirect() returns %d\n", res);
470 exit(1);
471 }
472 res = sieve_register_discard(i, &discard);
473 if (res != SIEVE_OK) {
474 printf("sieve_register_discard() returns %d\n", res);
475 exit(1);
476 }
477 res = sieve_register_reject(i, &reject);
478 if (res != SIEVE_OK) {
479 printf("sieve_register_reject() returns %d\n", res);
480 exit(1);
481 }
482 res = sieve_register_fileinto(i, &fileinto);
483 if (res != SIEVE_OK) {
484 printf("sieve_register_fileinto() returns %d\n", res);
485 exit(1);
486 }
487 res = sieve_register_keep(i, &keep);
488 if (res != SIEVE_OK) {
489 printf("sieve_register_keep() returns %d\n", res);
490 exit(1);
491 }
492
493 res = sieve_register_size(i, &getsize);
494 if (res != SIEVE_OK) {
495 printf("sieve_register_size() returns %d\n", res);
496 exit(1);
497 }
498
499 res = sieve_register_header(i, &getheader);
500 if (res != SIEVE_OK) {
501 printf("sieve_register_header() returns %d\n", res);
502 exit(1);
503 }
504
505 res = sieve_register_envelope(i, &getenvelope);
506 if (res != SIEVE_OK) {
507 printf("sieve_register_envelope() returns %d\n", res);
508 exit(1);
509 }
510
511 res = sieve_register_vacation(i, &vacation);
512 if (res != SIEVE_OK) {
513 printf("sieve_register_vacation() returns %d\n", res);
514 exit(1);
515 }
516
517 res = sieve_register_imapflags(i, &mark);
518
519 if (res != SIEVE_OK) {
520 printf("sieve_register_imapflags() returns %d\n", res);
521 exit(1);
522 }
523
524 res = sieve_register_notify(i, &notify);
525 if (res != SIEVE_OK) {
526 printf("sieve_register_notify() returns %d\n", res);
527 exit(1);
528 }
529
530 res = sieve_register_parse_error(i, &mysieve_error);
531 if (res != SIEVE_OK) {
532 printf("sieve_register_parse_error() returns %d\n", res);
533 exit(1);
534 }
535
536 res = sieve_register_execute_error(i, &mysieve_execute_error);
537 if (res != SIEVE_OK) {
538 printf("sieve_register_execute_error() returns %d\n", res);
539 exit(1);
540 }
541
542
543 f = fopen(argv[2], "r");
544 if (!f) {
545 printf("can not open script '%s'\n", argv[2]);
546 exit(1);
547 }
548
549 res = sieve_script_parse(i, f, NULL, &s);
550 if (res != SIEVE_OK) {
551 exit(1);
552 }
553
554 fclose(f);
555
556 if (strcmp(argv[1], "-v") != 0) {
557 fd = open(argv[1], O_RDONLY);
558 res = fstat(fd, &sbuf);
559 if (res != 0) {
560 perror("fstat");
561 }
562
563 m = new_msg(fdopen(fd, "r"), sbuf.st_size, argv[1]);
564 if (res != SIEVE_OK) {
565 printf("sieve_msg_parse() returns %d\n", res);
566 exit(1);
567 }
568
569 res = sieve_execute_script(s, m);
570 if (res != SIEVE_OK) {
571 printf("sieve_execute_script() returns %d\n", res);
572 exit(1);
573 }
574
575 close(fd);
576 }
577
578 res = sieve_script_free(&s);
579 if (res != SIEVE_OK) {
580 printf("sieve_script_free() returns %d\n", res);
581 exit(1);
582 }
583 res = sieve_interp_free(&i);
584 if (res != SIEVE_OK) {
585 printf("sieve_interp_free() returns %d\n", res);
586 exit(1);
587 }
588
589 return 0;
590 }
591
592 void fatal(char* message, int rc) {
593 fprintf(stderr, "fatal error: %s\n", message);
594 exit(rc);
595 }