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