]> mj.ucw.cz Git - umpf.git/blob - int.c
e1c906f058342ada6c8d264876d5f4fcc48bed35
[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
12 #include "cond.tab.h"
13 #include "umpf.h"
14
15 #define OVECCOUNT 3
16 #define HASHSIZE 103
17 #define MAGIC 19
18
19 #define INT_TO_STRING_LEN ((sizeof(int)*4*CHAR_BIT)/10 + 6)
20
21 void __attribute__ ((noreturn)) 
22 bye(int code, char* msg, ...)
23 {
24         va_list args;
25
26         if (current_body->tmpfile)
27                 unlink(current_body->tmpfile);
28         
29         if (msg) {
30                 va_start(args, msg);
31                 vfprintf(stderr, msg, args);
32                 fputc('\n', stderr);
33                 va_end(args);
34         }
35         exit(code);
36 }
37
38 void
39 free_string(char* c)
40 {
41         if (c != empty)
42                 free(c);
43 }
44
45 static void
46 clear_var(int var)
47 {
48         if ((var_tab[var] != NULL))
49                 free_string(var_tab[var]);
50 }
51
52 static void
53 set_var(int var, char* value)
54 {
55         clear_var(var);
56         var_tab[var] = xstrdup(value);
57 }
58
59 static char*
60 get_var(int var)
61 {
62         if (var < 0)
63                 return xstrdup(const_tab[-var]);
64         return xstrdup(var_tab[var]);
65 }
66
67 /* return var struct or NULL if not found */
68 static struct variable*
69 get_var_struct(char* name, struct list* hash)
70 {
71         int n;
72         struct variable *p;
73
74         n = get_bucket_number(name);
75         int nocase = isupper(*name);
76         LIST_FOREACH(p, hash + n)
77                 if (!(nocase ? strcasecmp : strcmp)(p->name,name))
78                         return p;
79
80         return NULL;
81 }
82
83 static int 
84 regex_cmp(char* s, char* r)
85 {
86         pcre *brum;
87         int erroroffset;
88         const char* error;
89         int ovector[OVECCOUNT];
90         
91         brum = pcre_compile(r,0,&error,&erroroffset,NULL);
92         if (!brum)
93                 return -1;
94         
95         int res = pcre_exec(brum,NULL,s,strlen(s),0,0,ovector,OVECCOUNT);
96         pcre_free(brum);
97
98         return res;
99 }
100
101 #define UPPER(a) ((a) >> 8)
102 #define LOWER(a) ((a) & 0xFF)
103
104 static char*
105 xcat(char* left, char* right)
106 {
107         char* res = xmalloc(strlen(left) + strlen(right) + 1);
108
109         strcpy(res, left);
110         strcat(res, right);
111
112         free_string(left);
113         free_string(right);     
114
115         return res;
116 }
117
118 static char*
119 fold(const char* value, int taken) 
120 {
121         int i;
122         char* ret; 
123         int len = strlen(value);
124         int newlines[len / 78 + 5];
125         int newl = 0;
126         int curr_ws = -1;
127         int pos = 0;
128         int newl_done = 0;
129
130         if (len + taken <= 78)
131                 return xstrdup(value);
132         for(i = 0; i < len; i++) {
133                 if (value[i] == ' ' || value[i]=='\t')
134                         curr_ws = i;
135                 taken++;
136                 if (taken >= 78) {
137                         if (curr_ws > 0){
138                                 newlines[newl++] = curr_ws;
139                                 i = curr_ws;
140                                 while(value[i] && (value[i] == '\t' 
141                                         || value[i] == ' '))
142                                         i++;
143                                 taken = i - curr_ws;
144                                 curr_ws = -1;
145                         }
146                 }
147         }
148         ret = xmalloc(2*newl + len + 1);
149         for (i = 0; i < len; i++) {
150                 if (newl_done == newl) {
151                         strcpy(ret + pos, value + i);
152                         break;  
153                 }
154                 if(i != newlines[newl_done])
155                         ret[pos++] = value[i];
156                 else {
157                         newl++;
158                         ret[pos++] = '\n';
159                         ret[pos++] = ' ';
160                 }
161         }
162         return ret;
163 }
164
165 static char*
166 unfold(const char* u)
167 {
168         char* new;
169         const char* pu = u; 
170         char* pn;
171
172         new = xmalloc(strlen(u)+1);
173         pn = new;
174
175 #define IS_WHITE(c) ((c) == '\t' || (c)==' ' || c=='\n')
176
177         while (IS_WHITE(*pu))
178                 pu++;
179
180         while (*pu != 0){
181                 if (IS_WHITE(*pu)){
182                         while (IS_WHITE(*pu))
183                                 pu++;
184                         if (*pu != 0)
185                                 *pn++ = ' ';
186                 } else
187                         *pn++ = *pu++;          
188         }
189         *pn = 0;
190
191         return new;
192 }
193
194 static void
195 modify_headers(struct list* headers, struct list* hash)
196 {
197         struct hlist* p;
198         int i;
199         struct variable* pv;
200         char* u, * value;
201         
202
203         LIST_FOREACH(p, headers){
204                 pv = get_var_struct(p->name, hash);
205                 if (!pv)
206                         continue;
207                 u = unfold(p->value);
208                 value = get_var(pv->varcode);
209                 if (strcmp(u, value)){
210                         pv->modified = 0;
211                         free_string(p->value);
212                         p->value = fold(value,
213                                  strlen(p->name) + 2);
214                 }
215                 free_string(u);
216                 free_string(value);
217         }
218
219         // find new headers 
220         for (i = 0; i < HASHSIZE; i++){
221                 LIST_FOREACH(pv, hash + i){
222                         if (isupper(pv->name[0]) && pv->modified){
223                                 pv->modified = 0;
224
225                                 p = xmalloc(sizeof(struct hlist));
226                                 p->name = xstrdup(pv->name);
227                                 p->value = get_var(pv->varcode);
228
229                                 list_add_last(headers,&p->car);
230                         }
231                 }
232         }
233 }
234
235 static struct list*
236 copy_headers(struct list* orig)
237 {
238         struct list* new = xmalloc(sizeof(struct list));
239         struct hlist* po, *pn;
240
241         list_init(new);
242
243         LIST_FOREACH(po, orig){
244                 pn = xmalloc(sizeof(struct hlist));
245                 pn->name = xstrdup(po->name);
246                 pn->value = xstrdup(po->value);
247                 pn->have_var = 0;
248
249                 list_add_last(new, &pn->car);
250         }
251
252         return new;
253 }
254
255 static struct email 
256 prepare_email(struct list* hash)
257 {
258         struct email em;
259
260         modify_headers(current_headers, hash);
261         em.headers = copy_headers(current_headers);
262         em.body_len = current_body->body_len;
263         em.fd = current_body->fd; 
264         if (current_body->body) {
265                 em.body = xmalloc(em.body_len);
266                 memcpy(em.body, current_body->body, em.body_len);
267                 em.tmpfile = NULL;
268         } else {
269                 em.tmpfile = xstrdup(current_body->tmpfile);
270                 em.body = NULL;
271         }
272
273         return em;
274 }
275
276 static void
277 destroy_email(struct email em)
278 {
279         if (em.body)
280                 free_string(em.body);
281         if(em.tmpfile)
282                 free_string(em.tmpfile);
283 }
284
285 static void
286 do_string_ternary_op(struct code* p)
287 {
288         char* l = get_var(p->u.tpop.l);
289         char* r = get_var(p->u.tpop.r);
290         char* result;
291
292         switch(p->opcode) {
293                 case OPC_RE:
294                         result = xmalloc(INT_TO_STRING_LEN);
295                         sprintf(result, "%d", regex_cmp(l, r));
296                         break;
297                 case OPC_NRE:
298                         result = xmalloc(INT_TO_STRING_LEN);
299                         sprintf(result, "%d", !regex_cmp(l, r));
300                         break;
301                 case OPC_CAT:
302                         result = xcat(l, r);
303                         break;
304                 default:
305                         break;
306         };
307
308         set_var(p->u.tpop.res, result); 
309 }
310
311 static void
312 do_num_ternary_op(struct code* p)
313 {
314         int l, r, res;
315         char* result = xmalloc(INT_TO_STRING_LEN);
316
317         sscanf(get_var(p->u.tpop.l),"%d", &l);
318         sscanf(get_var(p->u.tpop.r),"%d", &r);
319
320         switch(p->opcode) {
321                 case OPC_GT:
322                         res = (l > r);
323                         break;
324                 case OPC_LT:
325                         res = (l < r);
326                         break;
327                 case OPC_LE:
328                         res = (l <= r);
329                         break;
330                 case OPC_GE:
331                         res = (l >= r);
332                         break;
333                 case OPC_EQ:
334                         res = (l == r);
335                         break;
336                 case OPC_NEQ:
337                         res = (l != r);
338                         break;
339                 case OPC_AND:
340                         res = (l && r);
341                         break;
342                 case OPC_OR:
343                         res = (l || r);
344                         break;
345                 case OPC_XOR:
346                         res = ((l || r) && !(l && r));
347                         break;
348                 case OPC_PLUS:
349                         res = (l + r);
350                         break;
351                 case OPC_MINUS:
352                         res = (l - r);
353                         break;
354                 case OPC_MUL:
355                         res = (l * r);
356                         break;
357                 case OPC_DIV:
358                         res = (l / r);
359                         break;
360                 default:
361                         break;
362         }
363
364         sprintf(result, "%d", res);
365         set_var(p->u.tpop.res, result);
366 }
367
368 static int
369 eval_cond(int var)
370 {
371         char* val = get_var(var);
372         int v;
373
374         if (! val)
375                 return 0;
376         if (! *val)
377                 return 0;
378         sscanf(val, "%d", &v);
379
380         return !!v;
381 }
382
383 static void
384 deliver(char* where, int copy, struct list* hash)
385 {
386         int res = 0;
387         struct email em = prepare_email(hash);
388         
389         res = deliver_local_email(where, &em);
390
391         destroy_email(em);
392
393         if (!copy) {
394                 if (res)
395                         bye(EX_TEMPFAIL, "%m");
396                 else
397                         bye(0, NULL);
398         }
399 }
400
401 static void
402 send_mail(char* where, int copy, struct list* hash)
403 {
404         int pd[2];
405         int pid, status;
406         int res;
407         struct email em = prepare_email(hash);
408
409         res = pipe(pd);
410         if (res < 0)
411                 goto end;
412
413         if ((pid = fork()) < 0)
414                 goto end;
415         else if (pid == 0) {
416                 close(0);
417                 dup(pd[0]);     
418                 close(pd[0]);
419                 //FIXME From?
420                 res = execl("/usr/lib/sendmail", "sendmail", where, NULL);
421         }
422         close(pd[0]);
423         write_email_to_fd(pd[1], &em); 
424         close(pd[1]);
425         wait(&status);
426 end:
427         destroy_email(em);
428         if (!copy) {
429                 if (res)
430                         bye(EX_TEMPFAIL, "%m");
431                 else
432                         bye(0, NULL);
433         }
434 }
435
436 void
437 interp(struct list* ins, struct list* hash)
438 {
439         struct code* p;
440         char* result;
441         int v;
442
443         LIST_FOREACH(p, ins) {
444         switch (p->opcode) {
445                 case OPC_SET:
446                         set_var(p->u.set.l, get_var(p->u.set.r));
447                         break;  
448                 case OPC_JUMP:
449                         p = p->u.jump.target;
450                         continue;
451                         break;
452                 case OPC_JUMP_IF:
453                         if (eval_cond(p->u.jump_if.cond))
454                                 p = p->u.jump_if.target;
455                         continue;
456                         break;
457                 case OPC_JUMP_UNLESS:
458                         if (!eval_cond(p->u.jump_unless.cond))
459                                 p = p->u.jump_unless.target;
460                         continue;
461                         break;
462                 case OPC_NOP:
463                         continue;
464                         break;
465                 case OPC_GT:
466                 case OPC_LT:
467                 case OPC_LE:
468                 case OPC_GE:
469                 case OPC_EQ:
470                 case OPC_NEQ:
471                 case OPC_AND:
472                 case OPC_OR:
473                 case OPC_XOR:
474                 case OPC_PLUS:
475                 case OPC_MINUS:
476                 case OPC_MUL:
477                 case OPC_DIV:
478                         do_num_ternary_op(p);
479                         break;
480                 case OPC_NOT:
481                         result = xmalloc(INT_TO_STRING_LEN);
482                         sscanf(get_var(p->u.tpop.l),"%d", &v);
483                         sprintf(result, "%d", !v);
484                         set_var(p->u.tpop.res, result);
485                 case OPC_CAT:
486                 case OPC_RE:
487                 case OPC_NRE:
488                         do_string_ternary_op(p);
489                         break;
490                 case OPC_PIPE:
491                         break;
492                 case OPC_MAIL:
493                         send_mail(get_var(p->u.arrow.what), 
494                                 p->u.arrow.copy, hash);
495                         break;
496                 case OPC_DELIVER:
497                         deliver(get_var(p->u.arrow.what), 
498                                 p->u.arrow.copy, hash);
499                         break;
500                 case OPC_CALL_EXT:
501                         break;
502                 case OPC_DISCARD:
503                         bye(0, NULL);
504                         break;
505         }}
506         deliver(default_mailbox, 0, hash);
507 }
508
509 void
510 print_vars(struct list* hash)
511 {
512         int i;
513         struct variable* p;
514
515         for (i=0; i<HASHSIZE; i++){
516                 LIST_FOREACH(p, hash + i)
517                         printf("%s=%s\n",p->name, get_var(p->varcode));
518         }
519 }
520
521 void
522 save_current_headers(struct list* hash)
523 {
524         struct hlist* p;
525         char* u;
526         struct variable* pv;
527
528         LIST_FOREACH(p, current_headers){
529                 pv = get_var_struct(p->name, hash);
530                 if (!pv)
531                         continue;
532                 u = unfold(p->value);
533                 set_var(pv->varcode, u);
534                 free_string(u);
535                 pv->modified = 0;
536                 p->have_var = 1;
537         }
538 }