#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;
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)
{
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*
{
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
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;
if(i != newlines[newl_done])
ret[pos++] = value[i];
else {
- newl++;
+ newl_done++;
ret[pos++] = '\n';
- ret[pos++] = ' ';
+ ret[pos++] = '\t';
}
}
return ret;
}
+static char*
+unfold(const char* u)
+{
+ char* new;
+ const char* pu = u;
+ char* pn;
+
+ new = xmalloc(strlen(u)+1);
+ pn = new;
+
+#define IS_WHITE(c) ((c) == '\t' || (c)==' ' || c=='\n')
+
+ while (IS_WHITE(*pu))
+ pu++;
+
+ while (*pu != 0){
+ if (IS_WHITE(*pu)){
+ while (IS_WHITE(*pu))
+ pu++;
+ if (*pu != 0)
+ *pn++ = ' ';
+ } else
+ *pn++ = *pu++;
+ }
+ *pn = 0;
+
+ return new;
+}
+
static void
modify_headers(struct list* headers, struct list* hash)
{
struct hlist* p;
int i;
struct variable* pv;
+ char* u, * value;
LIST_FOREACH(p, headers){
- pv = get_var_struct(p->name, hash);
+ pv = get_var_struct(p->name, VAR_HEADER, hash);
if (!pv)
continue;
- if (pv->modified){
- pv->modified = 0;
+ u = unfold(p->value);
+ value = get_var_nc(pv->varcode);
+ if (strcmp(u, value)){
+ set_modif_flag(pv->varcode, 0);
free_string(p->value);
- p->value = fold(get_var(pv->varcode),
+ p->value = fold(value,
strlen(p->name) + 2);
}
+ free_string(u);
}
// 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);
}
}
}
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) {
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:
}
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)
if (!copy) {
if (res)
- bye(-res, "%m");
+ bye(EX_TEMPFAIL, "Cannot save mail to mailbox %s: %m", where);
else
bye(0, NULL);
}
int res;
struct email em = prepare_email(hash);
- //write_email_to_fd(1, &em);
res = pipe(pd);
if (res < 0)
goto end;
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);
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)
{
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:
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);
}
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));
}
}
-static char*
-unfold(const char* u)
-{
- char* new;
- const char* pu = u;
- char* pn;
-
- new = xmalloc(strlen(u)+1);
- pn = new;
-
-#define IS_WHITE(c) ((c) == '\t' || (c)==' ' || c=='\n')
-
- while (IS_WHITE(*pu))
- pu++;
-
- while (*pu != 0){
- if (IS_WHITE(*pu)){
- while (IS_WHITE(*pu))
- pu++;
- if (*pu != 0)
- *pn++ = ' ';
- } else
- *pn++ = *pu++;
- }
- *pn = 0;
-
- return new;
-}
-
void
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;
- if (!get_var(pv->varcode))
- continue;
u = unfold(p->value);
set_var(pv->varcode, u);
- set_var(pv->varcode, p->value);
- pv->modified = 0;
+ free_string(u);
+ set_modif_flag(pv->varcode, 0);
p->have_var = 1;
}
-
}