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