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