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