]> mj.ucw.cz Git - umpf.git/blob - int.c
started with filtering mail
[umpf.git] / int.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <pcre.h>
4 #include <ctype.h>
5 #include <limits.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <stdarg.h>
9 #include <sys/wait.h>
10 #include <sysexits.h>
11 #include <poll.h>
12
13 #include "cond.tab.h"
14 #include "umpf.h"
15
16 #define OVECCOUNT 3
17 #define HASHSIZE 103
18 #define MAGIC 19
19
20 #define INT_TO_STRING_LEN ((sizeof(int)*4*CHAR_BIT)/10 + 6)
21
22 struct tmpfile {
23         int fd;
24         char* name;
25 };
26
27 void __attribute__ ((noreturn)) 
28 bye(int code, char* msg, ...)
29 {
30         va_list args;
31
32         if (current_body->tmpfile)
33                 unlink(current_body->tmpfile);
34         
35         if (msg) {
36                 va_start(args, msg);
37                 vfprintf(stderr, msg, args);
38                 fputc('\n', stderr);
39                 va_end(args);
40         }
41         exit(code);
42 }
43
44 void
45 free_string(char* c)
46 {
47         if (c != empty)
48                 free(c);
49 }
50
51 static void
52 clear_var(int var)
53 {
54         if ((var_tab[var] != NULL))
55                 free_string(var_tab[var]);
56 }
57
58 static void
59 set_var(int var, char* value)
60 {
61         clear_var(var);
62         var_tab[var] = xstrdup(value);
63 }
64
65 static char*
66 get_var(int var)
67 {
68         if (var < 0)
69                 return xstrdup(const_tab[-var]);
70         return xstrdup(var_tab[var]);
71 }
72
73 /* return var struct or NULL if not found */
74 static struct variable*
75 get_var_struct(char* name, struct list* hash)
76 {
77         int n;
78         struct variable *p;
79
80         n = get_bucket_number(name);
81         int nocase = isupper(*name);
82         LIST_FOREACH(p, hash + n)
83                 if (!(nocase ? strcasecmp : strcmp)(p->name,name))
84                         return p;
85
86         return NULL;
87 }
88
89 static int 
90 regex_cmp(char* s, char* r)
91 {
92         pcre *brum;
93         int erroroffset;
94         const char* error;
95         int ovector[OVECCOUNT];
96         
97         brum = pcre_compile(r,0,&error,&erroroffset,NULL);
98         if (!brum)
99                 return -1;
100         
101         int res = pcre_exec(brum,NULL,s,strlen(s),0,0,ovector,OVECCOUNT);
102         pcre_free(brum);
103
104         return res;
105 }
106
107 #define UPPER(a) ((a) >> 8)
108 #define LOWER(a) ((a) & 0xFF)
109
110 static char*
111 xcat(char* left, char* right)
112 {
113         char* res = xmalloc(strlen(left) + strlen(right) + 1);
114
115         strcpy(res, left);
116         strcat(res, right);
117
118         free_string(left);
119         free_string(right);     
120
121         return res;
122 }
123
124 static char*
125 fold(const char* value, int taken) 
126 {
127         int i;
128         char* ret; 
129         int len = strlen(value);
130         int newlines[len / 78 + 5];
131         int newl = 0;
132         int curr_ws = -1;
133         int pos = 0;
134         int newl_done = 0;
135
136         if (len + taken <= 78)
137                 return xstrdup(value);
138         for(i = 0; i < len; i++) {
139                 if (value[i] == ' ' || value[i]=='\t')
140                         curr_ws = i;
141                 taken++;
142                 if (taken >= 78) {
143                         if (curr_ws > 0){
144                                 newlines[newl++] = curr_ws;
145                                 i = curr_ws;
146                                 while(value[i] && (value[i] == '\t' 
147                                         || value[i] == ' '))
148                                         i++;
149                                 taken = i - curr_ws;
150                                 curr_ws = -1;
151                         }
152                 }
153         }
154         ret = xmalloc(2*newl + len + 1);
155         for (i = 0; i < len; i++) {
156                 if (newl_done == newl) {
157                         strcpy(ret + pos, value + i);
158                         break;  
159                 }
160                 if(i != newlines[newl_done])
161                         ret[pos++] = value[i];
162                 else {
163                         newl++;
164                         ret[pos++] = '\n';
165                         ret[pos++] = ' ';
166                 }
167         }
168         return ret;
169 }
170
171 static char*
172 unfold(const char* u)
173 {
174         char* new;
175         const char* pu = u; 
176         char* pn;
177
178         new = xmalloc(strlen(u)+1);
179         pn = new;
180
181 #define IS_WHITE(c) ((c) == '\t' || (c)==' ' || c=='\n')
182
183         while (IS_WHITE(*pu))
184                 pu++;
185
186         while (*pu != 0){
187                 if (IS_WHITE(*pu)){
188                         while (IS_WHITE(*pu))
189                                 pu++;
190                         if (*pu != 0)
191                                 *pn++ = ' ';
192                 } else
193                         *pn++ = *pu++;          
194         }
195         *pn = 0;
196
197         return new;
198 }
199
200 static void
201 modify_headers(struct list* headers, struct list* hash)
202 {
203         struct hlist* p;
204         int i;
205         struct variable* pv;
206         char* u, * value;
207         
208
209         LIST_FOREACH(p, headers){
210                 pv = get_var_struct(p->name, hash);
211                 if (!pv)
212                         continue;
213                 u = unfold(p->value);
214                 value = get_var(pv->varcode);
215                 if (strcmp(u, value)){
216                         pv->modified = 0;
217                         free_string(p->value);
218                         p->value = fold(value,
219                                  strlen(p->name) + 2);
220                 }
221                 free_string(u);
222                 free_string(value);
223         }
224
225         // find new headers 
226         for (i = 0; i < HASHSIZE; i++){
227                 LIST_FOREACH(pv, hash + i){
228                         if (isupper(pv->name[0]) && pv->modified){
229                                 pv->modified = 0;
230
231                                 p = xmalloc(sizeof(struct hlist));
232                                 p->name = xstrdup(pv->name);
233                                 p->value = get_var(pv->varcode);
234
235                                 list_add_last(headers,&p->car);
236                         }
237                 }
238         }
239 }
240
241 static struct list*
242 copy_headers(struct list* orig)
243 {
244         struct list* new = xmalloc(sizeof(struct list));
245         struct hlist* po, *pn;
246
247         list_init(new);
248
249         LIST_FOREACH(po, orig){
250                 pn = xmalloc(sizeof(struct hlist));
251                 pn->name = xstrdup(po->name);
252                 pn->value = xstrdup(po->value);
253                 pn->have_var = 0;
254
255                 list_add_last(new, &pn->car);
256         }
257
258         return new;
259 }
260
261 static struct email 
262 prepare_email(struct list* hash)
263 {
264         struct email em;
265
266         modify_headers(current_headers, hash);
267         em.headers = copy_headers(current_headers);
268         em.body_len = current_body->body_len;
269         em.fd = current_body->fd; 
270         if (current_body->body) {
271                 em.body = xmalloc(em.body_len);
272                 memcpy(em.body, current_body->body, em.body_len);
273                 em.tmpfile = NULL;
274         } else {
275                 em.tmpfile = xstrdup(current_body->tmpfile);
276                 em.body = NULL;
277         }
278
279         return em;
280 }
281
282 static void
283 destroy_email(struct email em)
284 {
285         if (em.body)
286                 free_string(em.body);
287         if(em.tmpfile)
288                 free_string(em.tmpfile);
289 }
290
291 static void
292 do_string_ternary_op(struct code* p)
293 {
294         char* l = get_var(p->u.tpop.l);
295         char* r = get_var(p->u.tpop.r);
296         char* result;
297
298         switch(p->opcode) {
299                 case OPC_RE:
300                         result = xmalloc(INT_TO_STRING_LEN);
301                         sprintf(result, "%d", regex_cmp(l, r));
302                         break;
303                 case OPC_NRE:
304                         result = xmalloc(INT_TO_STRING_LEN);
305                         sprintf(result, "%d", !regex_cmp(l, r));
306                         break;
307                 case OPC_CAT:
308                         result = xcat(l, r);
309                         break;
310                 default:
311                         break;
312         };
313
314         set_var(p->u.tpop.res, result); 
315 }
316
317 static void
318 do_num_ternary_op(struct code* p)
319 {
320         int l, r, res;
321         char* result = xmalloc(INT_TO_STRING_LEN);
322
323         sscanf(get_var(p->u.tpop.l),"%d", &l);
324         sscanf(get_var(p->u.tpop.r),"%d", &r);
325
326         switch(p->opcode) {
327                 case OPC_GT:
328                         res = (l > r);
329                         break;
330                 case OPC_LT:
331                         res = (l < r);
332                         break;
333                 case OPC_LE:
334                         res = (l <= r);
335                         break;
336                 case OPC_GE:
337                         res = (l >= r);
338                         break;
339                 case OPC_EQ:
340                         res = (l == r);
341                         break;
342                 case OPC_NEQ:
343                         res = (l != r);
344                         break;
345                 case OPC_AND:
346                         res = (l && r);
347                         break;
348                 case OPC_OR:
349                         res = (l || r);
350                         break;
351                 case OPC_XOR:
352                         res = ((l || r) && !(l && r));
353                         break;
354                 case OPC_PLUS:
355                         res = (l + r);
356                         break;
357                 case OPC_MINUS:
358                         res = (l - r);
359                         break;
360                 case OPC_MUL:
361                         res = (l * r);
362                         break;
363                 case OPC_DIV:
364                         res = (l / r);
365                         break;
366                 default:
367                         break;
368         }
369
370         sprintf(result, "%d", res);
371         set_var(p->u.tpop.res, result);
372 }
373
374 static int
375 eval_cond(int var)
376 {
377         char* val = get_var(var);
378         int v;
379
380         if (! val)
381                 return 0;
382         if (! *val)
383                 return 0;
384         sscanf(val, "%d", &v);
385
386         return !!v;
387 }
388
389 static void
390 deliver(char* where, int copy, struct list* hash)
391 {
392         int res = 0;
393         struct email em = prepare_email(hash);
394         
395         res = deliver_local_email(where, &em);
396
397         destroy_email(em);
398
399         if (!copy) {
400                 if (res)
401                         bye(EX_TEMPFAIL, "%m");
402                 else
403                         bye(0, NULL);
404         }
405 }
406
407 static void
408 send_mail(char* where, int copy, struct list* hash)
409 {
410         int pd[2];
411         int pid, status;
412         int res;
413         struct email em = prepare_email(hash);
414
415         res = pipe(pd);
416         if (res < 0)
417                 goto end;
418
419         if ((pid = fork()) < 0)
420                 goto end;
421         else if (pid == 0) {
422                 close(0);
423                 dup(pd[0]);     
424                 close(pd[0]);
425                 //FIXME From?
426                 res = execl("/usr/lib/sendmail", "sendmail", where, NULL);
427         }
428         close(pd[0]);
429         write_email_to_fd(pd[1], &em); 
430         close(pd[1]);
431         wait(&status);
432 end:
433         destroy_email(em);
434         if (!copy) {
435                 if (res)
436                         bye(EX_TEMPFAIL, "%m");
437                 else
438                         bye(0, NULL);
439         }
440 }
441
442 static struct tmpfile* 
443 pipe_to(char* program, struct email* em)
444 {
445         int pd_in[2];
446         int pd_out[2];
447         int pid, status;
448         struct pollfd pfd[2];
449         char* name = xstrdup("/tmp/umpf.XXXXXX");
450         char buf[BUFSIZE];
451         int r;
452         int nfds = 2;
453         int data_pos = 0;
454         int fd = mkstemp(name);
455         struct tmpfile* tmp = xmalloc(sizeof(struct tmpfile));
456         char* e = NULL;
457         if (fd < 0)
458                 return NULL;
459         
460         tmp->name = name;
461         tmp->fd = fd;
462
463         int res = pipe(pd_in);
464         if (res < 0)
465                 goto bad;
466         res = pipe(pd_out);
467         if (res < 0)
468                 goto bad;       
469
470         if ((pid = fork()) < 0)
471                 goto bad;       
472         else if (pid == 0) {
473                 close(0);
474                 dup(pd_in[0]);
475
476                 close(1);       
477                 dup(pd_out[1]);
478
479                 close(pd_in[0]);
480                 close(pd_out[1]);
481                 close(pd_in[1]);
482                 close(pd_out[0]);
483                 res = execl("/bin/sh", "sh", "-c", program, NULL);
484         }
485         close(pd_in[0]);
486         close(pd_out[1]);
487
488         pfd[1].fd = pd_in[1];
489         pfd[0].fd = pd_out[0];
490         pfd[1].events = POLLOUT;
491         pfd[0].events = POLLIN;
492
493         open_email();
494         for (;;) {
495                 if (poll(pfd, nfds, -1) < 0)
496                         goto bad;
497                 if ((pfd[0].revents & POLLERR) || (pfd[1].revents & POLLERR))
498                         goto bad;
499
500                 if (pfd[0].revents & POLLIN) {
501                         r = read(pfd[0].fd, buf, BUFSIZE);
502                         if (r <= 0)
503                                 break;
504                         write(fd, buf, r);
505                 }
506                 if (pfd[0].revents & POLLHUP)
507                         break;
508
509                 if (nfds < 2)
510                         continue;
511
512                 if (pfd[1].revents & POLLOUT) {
513                         if (!chars_written) {
514                                 e = read_email(em);
515                                 data_pos = 0;
516                                 if (!chars_written) {
517                                         close(pfd[1].fd);
518                                         nfds = 1;       
519                                         continue;
520                                 }
521                         }
522                         if (data_pos < chars_written) {
523                                 r = write(pfd[1].fd, e + data_pos, 
524                                         chars_written - data_pos);
525                                 data_pos += r;
526                         } else {
527                                 free_string(e);
528                                 data_pos = 0;
529                                 e = read_email(em);
530                         }
531                 }
532         }
533  
534         close(pd_out[0]);
535         wait(&status);
536
537         return tmp;
538 bad:
539         close(fd);
540         unlink(name);
541         return NULL;
542 }
543
544 static void
545 do_filter(char* program, int copy, struct list* hash)
546 {
547         struct email em = prepare_email(hash);
548         struct tmpfile* f;
549         int res = 0;
550         off_t pos;
551
552         f = pipe_to(program, &em);
553         if (!f) {
554                 res++;
555                 goto end;
556         }
557         pos = lseek(f->fd, 0, SEEK_END);
558         lseek(f->fd, 0, SEEK_SET);
559
560 /*
561         FIXME: do some real work ;)
562
563         printf("Tmp filename %s, file is long %d\n", f->name, (int) pos);
564         
565         int i;
566         char c; 
567         for (i = 0; i < pos; i++) {
568                 read(f->fd, &c, 1);
569                 write(1, &c, 1);
570         }
571
572 */
573         unlink(f->name);
574         free(f);
575 end:
576         destroy_email(em);
577         if (!copy) {
578                 if (!res)
579                         bye(EX_TEMPFAIL, "%m");
580                 else
581                         bye(0, NULL);
582         }
583 }
584
585 void
586 interp(struct list* ins, struct list* hash)
587 {
588         struct code* p;
589         char* result;
590         int v;
591
592         LIST_FOREACH(p, ins) {
593         switch (p->opcode) {
594                 case OPC_SET:
595                         set_var(p->u.set.l, get_var(p->u.set.r));
596                         break;  
597                 case OPC_JUMP:
598                         p = p->u.jump.target;
599                         continue;
600                         break;
601                 case OPC_JUMP_IF:
602                         if (eval_cond(p->u.jump_if.cond))
603                                 p = p->u.jump_if.target;
604                         continue;
605                         break;
606                 case OPC_JUMP_UNLESS:
607                         if (!eval_cond(p->u.jump_unless.cond))
608                                 p = p->u.jump_unless.target;
609                         continue;
610                         break;
611                 case OPC_NOP:
612                         continue;
613                         break;
614                 case OPC_GT:
615                 case OPC_LT:
616                 case OPC_LE:
617                 case OPC_GE:
618                 case OPC_EQ:
619                 case OPC_NEQ:
620                 case OPC_AND:
621                 case OPC_OR:
622                 case OPC_XOR:
623                 case OPC_PLUS:
624                 case OPC_MINUS:
625                 case OPC_MUL:
626                 case OPC_DIV:
627                         do_num_ternary_op(p);
628                         break;
629                 case OPC_NOT:
630                         result = xmalloc(INT_TO_STRING_LEN);
631                         sscanf(get_var(p->u.tpop.l),"%d", &v);
632                         sprintf(result, "%d", !v);
633                         set_var(p->u.tpop.res, result);
634                 case OPC_CAT:
635                 case OPC_RE:
636                 case OPC_NRE:
637                         do_string_ternary_op(p);
638                         break;
639                 case OPC_PIPE:
640                         break;
641                 case OPC_FILTER:
642                         do_filter(get_var(p->u.arrow.what), 
643                                 p->u.arrow.copy, hash);
644                         break;
645                 case OPC_MAIL:
646                         send_mail(get_var(p->u.arrow.what), 
647                                 p->u.arrow.copy, hash);
648                         break;
649                 case OPC_DELIVER:
650                         deliver(get_var(p->u.arrow.what), 
651                                 p->u.arrow.copy, hash);
652                         break;
653                 case OPC_DISCARD:
654                         bye(0, NULL);
655                         break;
656         }}
657         deliver(default_mailbox, 0, hash);
658 }
659
660 void
661 print_vars(struct list* hash)
662 {
663         int i;
664         struct variable* p;
665
666         for (i=0; i<HASHSIZE; i++){
667                 LIST_FOREACH(p, hash + i)
668                         printf("%s=%s\n",p->name, get_var(p->varcode));
669         }
670 }
671
672 void
673 save_current_headers(struct list* hash)
674 {
675         struct hlist* p;
676         char* u;
677         struct variable* pv;
678
679         LIST_FOREACH(p, current_headers){
680                 pv = get_var_struct(p->name, hash);
681                 if (!pv)
682                         continue;
683                 u = unfold(p->value);
684                 set_var(pv->varcode, u);
685                 free_string(u);
686                 pv->modified = 0;
687                 p->have_var = 1;
688         }
689 }