]> mj.ucw.cz Git - umpf.git/blobdiff - int.c
fix many little bugs, release 0.1
[umpf.git] / int.c
diff --git a/int.c b/int.c
index d4360c6696cb60a5eae84f49c6d4dd7f56a87492..d5a39bfadc8f58d06a81c16a4196a7a802799df2 100644 (file)
--- a/int.c
+++ b/int.c
@@ -7,6 +7,8 @@
 #include <fcntl.h>
 #include <stdarg.h>
 #include <sys/wait.h>
+#include <sysexits.h>
+#include <poll.h>
 
 #include "cond.tab.h"
 #include "umpf.h"
 
 #define INT_TO_STRING_LEN ((sizeof(int)*4*CHAR_BIT)/10 + 6)
 
-static void __attribute__ ((noreturn)) 
+struct procstat {
+       int fd;
+       int exit;
+       char* name;
+};
+
+void __attribute__ ((noreturn)) 
 bye(int code, char* msg, ...)
 {
         va_list args;
@@ -34,6 +42,18 @@ bye(int code, char* msg, ...)
         exit(code);
 }
 
+static void
+set_modif_flag(int var, int flag)
+{
+       var_tab[var].modif = flag;
+}
+
+static int
+get_modif_flag(int var)
+{
+       return var_tab[var].modif;
+}
+
 void
 free_string(char* c)
 {
@@ -44,15 +64,59 @@ free_string(char* c)
 static void
 clear_var(int var)
 {
-       if ((var_tab[var] != NULL))
-               free_string(var_tab[var]);
+       if ((var_tab[var].value != NULL))
+               free_string(var_tab[var].value);
+       var_tab[var].modif = 0;
 }
 
 static void
 set_var(int var, char* value)
 {
        clear_var(var);
-       var_tab[var] = xstrdup(value);
+       var_tab[var].modif = 1;
+       var_tab[var].value = xstrdup(value);
+}
+
+void
+set_cur_mail_length_var(int len, struct list* hash)
+{
+       struct variable* pv;
+       char buf[INT_TO_STRING_LEN];
+
+       pv = get_var_struct(INT_VAR_MAIL_LEN, VAR_INTERN, hash);
+
+       if (! pv) /* user is not interested */
+               return;
+
+       sprintf(buf,"%d", len);
+       set_var(pv->varcode, buf);
+}
+
+static void
+set_exit_code_var(int code, struct list* hash)
+{
+       struct variable* pv;
+       char buf[INT_TO_STRING_LEN];
+
+       pv = get_var_struct(INT_VAR_LAST_EXIT, VAR_INTERN, hash);
+
+       if (! pv) /* user is not interested */
+               return;
+
+       sprintf(buf,"%d", code);
+       set_var(pv->varcode, buf);
+}
+
+static void
+set_last_pipe_var(char* buf, struct list* hash)
+{
+       struct variable* pv;
+
+       pv = get_var_struct(INT_VAR_LAST_EXIT, VAR_INTERN, hash);
+
+       if (! pv) /* user is not interested */
+               return;
+       set_var(pv->varcode, buf);
 }
 
 static char*
@@ -60,23 +124,15 @@ get_var(int var)
 {
        if (var < 0)
                return xstrdup(const_tab[-var]);
-       return xstrdup(var_tab[var]);
+       return xstrdup(var_tab[var].value);
 }
 
-/* return var struct or NULL if not found */
-static struct variable*
-get_var_struct(char* name, struct list* hash)
+static char*
+get_var_nc(int var)
 {
-       int n;
-       struct variable *p;
-
-       n = get_bucket_number(name);
-       int nocase = isupper(*name);
-       LIST_FOREACH(p, hash + n)
-               if (!(nocase ? strcasecmp : strcmp)(p->name,name))
-                       return p;
-
-       return NULL;
+       if (var < 0)
+               return const_tab[-var];
+       return var_tab[var].value;
 }
 
 static int 
@@ -120,7 +176,7 @@ fold(const char* value, int taken)
        int i;
        char* ret; 
        int len = strlen(value);
-       int newlines[len / 78 + 5];
+       int newlines[len / 40 + 5];
        int newl = 0;
        int curr_ws = -1;
        int pos = 0;
@@ -153,9 +209,9 @@ fold(const char* value, int taken)
                if(i != newlines[newl_done])
                        ret[pos++] = value[i];
                else {
-                       newl++;
+                       newl_done++;
                        ret[pos++] = '\n';
-                       ret[pos++] = ' ';
+                       ret[pos++] = '\t';
                }
        }
        return ret;
@@ -200,32 +256,32 @@ modify_headers(struct list* headers, struct list* hash)
        
 
        LIST_FOREACH(p, headers){
-               pv = get_var_struct(p->name, hash);
+               pv = get_var_struct(p->name, VAR_HEADER, hash);
                if (!pv)
                        continue;
                u = unfold(p->value);
-               value = get_var(pv->varcode);
+               value = get_var_nc(pv->varcode);
                if (strcmp(u, value)){
-                       pv->modified = 0;
+                       set_modif_flag(pv->varcode, 0);
                        free_string(p->value);
                        p->value = fold(value,
                                 strlen(p->name) + 2);
                }
                free_string(u);
-               free_string(value);
        }
 
        // find new headers 
        for (i = 0; i < HASHSIZE; i++){
                LIST_FOREACH(pv, hash + i){
-                       if (isupper(pv->name[0]) && pv->modified){
-                               pv->modified = 0;
+                       if (pv->type == VAR_HEADER && 
+                               get_modif_flag(pv->varcode)){
 
+                               set_modif_flag(pv->varcode, 0);
                                p = xmalloc(sizeof(struct hlist));
                                p->name = xstrdup(pv->name);
                                p->value = get_var(pv->varcode);
 
-                               list_add_last(headers,&p->car);
+                               list_add_last(headers, &p->car);
                        }
                }
        }
@@ -272,20 +328,42 @@ prepare_email(struct list* hash)
        return em;
 }
 
+static void
+destroy_headers(struct list* h)
+{
+       struct hlist* p;
+
+       while ((p = list_del_last(h))) {
+               free(p->name);
+               free(p->value);
+               free(p);
+       }
+       free(h);
+}
+
+static void
+destroy_body(struct email* b)
+{
+       if (b->tmpfile) {
+               unlink(b->tmpfile);
+               free(b->tmpfile);
+       }
+       if (b->body)
+               free(b->body);
+}
+
 static void
 destroy_email(struct email em)
 {
-       if (em.body)
-               free_string(em.body);
-       if(em.tmpfile)
-               free_string(em.tmpfile);
+       destroy_headers(em.headers);
+       destroy_body(&em);
 }
 
 static void
 do_string_ternary_op(struct code* p)
 {
-       char* l = get_var(p->u.tpop.l);
-       char* r = get_var(p->u.tpop.r);
+       char* l = get_var_nc(p->u.tpop.l);
+       char* r = get_var_nc(p->u.tpop.r);
        char* result;
 
        switch(p->opcode) {
@@ -303,18 +381,18 @@ do_string_ternary_op(struct code* p)
                default:
                        break;
        };
-
+       free_string(result);
        set_var(p->u.tpop.res, result); 
 }
 
 static void
 do_num_ternary_op(struct code* p)
 {
-       int l, r, res;
+       int l = 0, r = 0, res;
        char* result = xmalloc(INT_TO_STRING_LEN);
 
-       sscanf(get_var(p->u.tpop.l),"%d", &l);
-       sscanf(get_var(p->u.tpop.r),"%d", &r);
+       sscanf(get_var_nc(p->u.tpop.l),"%d", &l);
+       sscanf(get_var_nc(p->u.tpop.r),"%d", &r);
 
        switch(p->opcode) {
                case OPC_GT:
@@ -361,13 +439,14 @@ do_num_ternary_op(struct code* p)
        }
 
        sprintf(result, "%d", res);
+       free_string(result);
        set_var(p->u.tpop.res, result);
 }
 
 static int
 eval_cond(int var)
 {
-       char* val = get_var(var);
+       char* val = get_var_nc(var);
        int v;
 
        if (! val)
@@ -391,7 +470,7 @@ deliver(char* where, int copy, struct list* hash)
 
        if (!copy) {
                if (res)
-                       bye(-res, "%m");
+                       bye(EX_TEMPFAIL, "Cannot save mail to mailbox %s: %m", where);
                else
                        bye(0, NULL);
        }
@@ -416,7 +495,8 @@ send_mail(char* where, int copy, struct list* hash)
                dup(pd[0]);     
                close(pd[0]);
                //FIXME From?
-               res = execl("/usr/lib/sendmail", "sendmail", where, NULL);
+               execl("/usr/lib/sendmail", "sendmail", where, NULL);
+               exit(1);
        }
        close(pd[0]);
        write_email_to_fd(pd[1], &em); 
@@ -426,12 +506,207 @@ end:
        destroy_email(em);
        if (!copy) {
                if (res)
-                       bye(-res, "%m");
+                       bye(EX_TEMPFAIL, "Cannot forward e-mail: %m");
                else
                        bye(0, NULL);
        }
 }
 
+static struct procstat* 
+pipe_to(char* program, struct email* em)
+{
+       int pd_in[2];
+       int pd_out[2];
+       int pid, status;
+       struct pollfd pfd[2];
+       char* name = xstrdup("/tmp/umpf.XXXXXX");
+       char buf[BUFSIZE];
+       int r;
+       int nfds = 2;
+       int data_pos = 0;
+       int fd = mkstemp(name);
+       struct procstat* tmp = xmalloc(sizeof(struct procstat));
+       char* e = NULL;
+       if (fd < 0)
+               return NULL;
+       
+       tmp->name = name;
+       tmp->fd = fd;
+
+       int res = pipe(pd_in);
+       if (res < 0)
+               goto bad;
+       res = pipe(pd_out);
+       if (res < 0)
+               goto bad;       
+
+       if ((pid = fork()) < 0)
+               goto bad;       
+       else if (pid == 0) {
+               close(0);
+               dup(pd_in[0]);
+
+               close(1);       
+               dup(pd_out[1]);
+
+               close(pd_in[0]);
+               close(pd_out[1]);
+               close(pd_in[1]);
+               close(pd_out[0]);
+               execl("/bin/sh", "sh", "-c", program, NULL);
+               exit(1);
+       }
+       close(pd_in[0]);
+       close(pd_out[1]);
+
+       pfd[1].fd = pd_in[1];
+       pfd[0].fd = pd_out[0];
+       pfd[1].events = POLLOUT;
+       pfd[0].events = POLLIN;
+
+       open_email();
+       for (;;) {
+               if (poll(pfd, nfds, -1) < 0)
+                       goto bad;
+               if ((pfd[0].revents & POLLERR) || (pfd[1].revents & POLLERR))
+                       goto bad;
+
+               if (pfd[0].revents & POLLIN) {
+                       r = read(pfd[0].fd, buf, BUFSIZE);
+                       if (r <= 0)
+                               break;
+                       write(fd, buf, r);
+               }
+               if (pfd[0].revents & POLLHUP) {
+                       if (e)
+                               free_string(e);
+                       break;
+               }
+
+               if (nfds < 2)
+                       continue;
+
+               if (pfd[1].revents & POLLOUT) {
+                       if (!chars_written) {
+                               e = read_email(em);
+                               data_pos = 0;
+                               if (!chars_written) {
+                                       close(pfd[1].fd);
+                                       nfds = 1;       
+                                       continue;
+                               }
+                       }
+                       if (data_pos < chars_written) {
+                               r = write(pfd[1].fd, e + data_pos, 
+                                       chars_written - data_pos);
+                               data_pos += r;
+                       } else {
+                               free_string(e);
+                               data_pos = 0;
+                               e = read_email(em);
+                       }
+               }
+       }
+       close(pd_out[0]);
+       wait(&status);
+       
+       tmp->exit = WEXITSTATUS(status);
+       return tmp;
+bad:
+       close(fd);
+       unlink(name);
+       return NULL;
+}
+
+static void
+fix_content_length(struct list* h, int len)
+{
+       struct hlist* ph;
+       int found = 0;
+       char* clheader = "Content-Length: ";
+
+       LIST_FOREACH(ph, h) {
+               if (!strcasecmp("Content-Length", ph->name)) {
+                       found = 1;
+                       free_string(ph->value);
+                       ph->value = xmalloc(INT_TO_STRING_LEN);
+                       sprintf(ph->value, "%d", len);
+                       break;
+               }
+       }
+       if (! found) {
+               char* buf, * tmpbuf;
+               buf = xmalloc(INT_TO_STRING_LEN + strlen(clheader));
+               tmpbuf = xmalloc(INT_TO_STRING_LEN);
+               sprintf(tmpbuf, "%d", len);
+               strcpy(buf, clheader);
+               strcat(buf, tmpbuf);
+               new_header(buf, h);
+               free_string(buf); 
+               free_string(tmpbuf); 
+       }
+}
+
+static void
+do_filter(char* program, struct list* hash)
+{
+       struct email em = prepare_email(hash);
+       struct procstat* f;
+       int res = 0;
+
+       f = pipe_to(program, &em);
+       if (!f) {
+               res++;
+               goto end;
+       }
+       lseek(f->fd, 0, SEEK_SET);
+
+       destroy_headers(current_headers);
+       destroy_body(current_body);
+       current_headers = make_hlist(f->fd);
+       current_body = get_body(f->fd);
+       fix_content_length(current_headers, current_body->body_len);
+       save_current_headers(hash);
+       set_cur_mail_length_var(curr_email_len, hash);
+       set_exit_code_var(f->exit, hash);
+
+       unlink(f->name);
+       free(f);
+end:
+       //FIXME: what to do with exit code when pipe failed?
+       destroy_email(em);
+}
+
+static void
+do_pipe(char* program, struct list* hash)
+{
+       struct email em = prepare_email(hash);
+       struct procstat* f;
+       int res = 0;
+       char* buf;
+       off_t pos;
+
+       f = pipe_to(program, &em);
+       if (!f) {
+               res++;
+               goto end;
+       }
+       pos = lseek(f->fd, 0, SEEK_END);
+       lseek(f->fd, 0, SEEK_SET);
+       buf = xmalloc(pos);
+       
+       read(f->fd, buf, pos);
+       set_exit_code_var(f->exit, hash);
+       set_last_pipe_var(buf, hash);
+       free(buf);
+       
+       unlink(f->name);
+       free(f);
+end:
+       destroy_email(em);
+}
+
 void
 interp(struct list* ins, struct list* hash)
 {
@@ -439,27 +714,29 @@ interp(struct list* ins, struct list* hash)
        char* result;
        int v;
 
-       LIST_FOREACH(p, ins) {
+       p = (void*) ins->head.next; 
+       while ((struct node*) p != &ins->head) {
        switch (p->opcode) {
                case OPC_SET:
-                       set_var(p->u.set.l, get_var(p->u.set.r));
+                       result = get_var_nc(p->u.set.r);
+                       set_var(p->u.set.l, result);
                        break;  
                case OPC_JUMP:
                        p = p->u.jump.target;
-                       continue;
-                       break;
+                       continue;       
                case OPC_JUMP_IF:
-                       if (eval_cond(p->u.jump_if.cond))
+                       if (eval_cond(p->u.jump_if.cond)) {
                                p = p->u.jump_if.target;
-                       continue;
+                               continue;       
+                       }
                        break;
                case OPC_JUMP_UNLESS:
-                       if (!eval_cond(p->u.jump_unless.cond))
+                       if (!eval_cond(p->u.jump_unless.cond)) {
                                p = p->u.jump_unless.target;
-                       continue;
+                               continue;       
+                       }
                        break;
                case OPC_NOP:
-                       continue;
                        break;
                case OPC_GT:
                case OPC_LT:
@@ -478,30 +755,36 @@ interp(struct list* ins, struct list* hash)
                        break;
                case OPC_NOT:
                        result = xmalloc(INT_TO_STRING_LEN);
-                       sscanf(get_var(p->u.tpop.l),"%d", &v);
+                       sscanf(get_var_nc(p->u.dpop.par),"%d", &v);
                        sprintf(result, "%d", !v);
-                       set_var(p->u.tpop.res, result);
+                       set_var(p->u.dpop.res, result);
+                       free_string(result);
+                       break;
                case OPC_CAT:
                case OPC_RE:
                case OPC_NRE:
                        do_string_ternary_op(p);
                        break;
                case OPC_PIPE:
+                       do_pipe(get_var_nc(p->u.arrow.what), hash);
+                       break;
+               case OPC_FILTER:
+                       do_filter(get_var_nc(p->u.arrow.what), hash);
                        break;
                case OPC_MAIL:
-                       send_mail(get_var(p->u.arrow.what), 
+                       send_mail(get_var_nc(p->u.arrow.what), 
                                p->u.arrow.copy, hash);
                        break;
                case OPC_DELIVER:
-                       deliver(get_var(p->u.arrow.what), 
+                       deliver(get_var_nc(p->u.arrow.what), 
                                p->u.arrow.copy, hash);
                        break;
-               case OPC_CALL_EXT:
-                       break;
                case OPC_DISCARD:
                        bye(0, NULL);
                        break;
-       }}
+       }
+               p = (void*) ((struct node*) p)->next;
+       }
        deliver(default_mailbox, 0, hash);
 }
 
@@ -513,7 +796,7 @@ print_vars(struct list* hash)
 
        for (i=0; i<HASHSIZE; i++){
                LIST_FOREACH(p, hash + i)
-                       printf("%s=%s\n",p->name, get_var(p->varcode));
+                       printf("%s=%s\n",p->name, get_var_nc(p->varcode));
        }
 }
 
@@ -525,13 +808,13 @@ save_current_headers(struct list* hash)
        struct variable* pv;
 
        LIST_FOREACH(p, current_headers){
-               pv = get_var_struct(p->name, hash);
+               pv = get_var_struct(p->name, VAR_HEADER, hash);
                if (!pv)
                        continue;
                u = unfold(p->value);
                set_var(pv->varcode, u);
                free_string(u);
-               pv->modified = 0;
+               set_modif_flag(pv->varcode, 0);
                p->have_var = 1;
        }
 }