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