#include #include #include #include "readclose.h" #include "stralloc.h" #include "buffer.h" #include "strset.h" #include "getoptb.h" #include "exit.h" #include "logmsg.h" #include "env.h" #include "sig.h" #include "qmail.h" #include "fmt.h" #include "case.h" #include "alloc.h" #include "seek.h" #include "wait.h" #include "byte.h" #include "str.h" #include "open.h" #include "cdbread.h" #define WHO "fastforward" static void usage() { logmsg(WHO,100,USAGE,"fastforward [ -nNpP ] data.cdb"); } static void nomem() { logmsg(WHO,111,FATAL,"out of memory"); } static void print(char *s) { char ch; while ((ch = *s++)) { buffer_put(buffer_2,&ch,1); } } static void printsafe(char *s) { char ch; while ((ch = *s++)) { if (ch < 32) ch = '_'; buffer_put(buffer_2,&ch,1); } } struct qmail qq; char qp[FMT_ULONG]; char qqbuf[BUFSIZE_LINE]; ssize_t qqwrite(int fd,char *buf,int len) { qmail_put(&qq,buf,len); return len; } buffer bufq = BUFFER_INIT(qqwrite,-1,qqbuf,sizeof(qqbuf)); char messbuf[BUFSIZE_MESS]; buffer mess = BUFFER_INIT(read,0,messbuf,sizeof(messbuf)); int flagdeliver = 1; int flagpassthrough = 0; char *dtline; stralloc sender = {0}; stralloc programs = {0}; stralloc forward = {0}; strset done; stralloc todo = {0}; stralloc mailinglist = {0}; void dofile(char *fn) { int fd; struct stat st; int i; int j; if (!stralloc_copys(&mailinglist,"")) nomem(); fd = open_read(fn); if (fd == -1) logmsg(WHO,111,FATAL,B("unable to read: ",fn)); if (fstat(fd,&st) == -1) logmsg(WHO,111,FATAL,B("unable to read: ",fn)); if ((st.st_mode & 0444) != 0444) logmsg(WHO,111,FATAL,B(fn," is not world-readable")); if (readclose_append(fd,&mailinglist,BUFSIZE_LINE) == -1) logmsg(WHO,111,FATAL,B("unable to read: ",fn)); i = 0; for (j = 0; j < mailinglist.len; ++j) if (!mailinglist.s[j]) { if ((mailinglist.s[i] == '.') || (mailinglist.s[i] == '/')) { if (!stralloc_cats(&todo,mailinglist.s + i)) nomem(); if (!stralloc_0(&todo)) nomem(); } else if ((mailinglist.s[i] == '&') && (j - i < 900)) { if (!stralloc_cats(&todo,mailinglist.s + i)) nomem(); if (!stralloc_0(&todo)) nomem(); } i = j + 1; } } char *fncdb; int fdcdb; stralloc key = {0}; uint32 dlen; stralloc data = {0}; struct cdb cdb; void cdbreaderror() { logmsg(WHO,111,FATAL,B("unable to read: ",fncdb)); } int findtarget(int flagwild,char *prepend,char *addr) { int r; int at; if (!stralloc_copys(&key,prepend)) nomem(); if (!stralloc_cats(&key,addr)) nomem(); case_lowerb(key.s,key.len); r = cdb_find(&cdb,key.s,key.len); if (r == -1) cdbreaderror(); if (r) return 1; if (!flagwild) return 0; at = str_rchr(addr,'@'); if (!addr[at]) return 0; if (!stralloc_copys(&key,prepend)) nomem(); if (!stralloc_cats(&key,addr + at)) nomem(); case_lowerb(key.s,key.len); r = cdb_find(&cdb,key.s,key.len); if (r == -1) cdbreaderror(); if (r) return 1; if (!stralloc_copys(&key,prepend)) nomem(); if (!stralloc_catb(&key,addr,at + 1)) nomem(); case_lowerb(key.s,key.len); r = cdb_find(&cdb,key.s,key.len); if (r == -1) cdbreaderror(); if (r) return 1; return 0; } int gettarget(int flagwild,char *prepend,char *addr) { if (!findtarget(flagwild,prepend,addr)) return 0; dlen = cdb_datalen(&cdb); if (!stralloc_ready(&data,(unsigned int) dlen)) nomem(); data.len = dlen; if (cdb_read(&cdb,data.s,data.len,cdb_datapos(&cdb)) == -1) cdbreaderror(); return 1; } void doprogram(char *arg) { char *args[5]; int child; int wstat; if (!flagdeliver) { print("run "); printsafe(arg); print("\n"); buffer_flush(buffer_2); return; } if (*arg == '!') { args[0] = "preline"; args[1] = "sh"; args[2] = "-c"; args[3] = arg + 1; args[4] = 0; } else { args[0] = "sh"; args[1] = "-c"; args[2] = arg + 1; args[3] = 0; } switch (child = vfork()) { case -1: logmsg(WHO,111,FATAL,"unable to fork: "); case 0: sig_pipedefault(); execvp(*args,args); logmsg(WHO,111,FATAL,B("unable to run: ",arg)); } wait_pid(&wstat,child); if (wait_crashed(wstat)) logmsg(WHO,111,FATAL,B("child crashed in: ",arg)); switch (wait_exitcode(wstat)) { case 64: case 65: case 70: case 76: case 77: case 78: case 112: case 100: _exit(100); case 0: break; default: _exit(111); } if (seek_begin(0) == -1) logmsg(WHO,111,FATAL,"unable to rewind input: "); } void dodata() { int i; int j; i = 0; for (j = 0; j < data.len; ++j) if (!data.s[j]) { if ((data.s[i] == '|') || (data.s[i] == '!')) doprogram(data.s + i); else if ((data.s[i] == '.') || (data.s[i] == '/')) { if (!stralloc_cats(&todo,data.s + i)) nomem(); if (!stralloc_0(&todo)) nomem(); } else if ((data.s[i] == '&') && (j - i < 900)) { if (!stralloc_cats(&todo,data.s + i)) nomem(); if (!stralloc_0(&todo)) nomem(); } i = j + 1; } } void dorecip(char *addr) { if (!findtarget(0,"?",addr)) if (gettarget(0,":",addr)) { dodata(); return; } if (!stralloc_cats(&forward,addr)) nomem(); if (!stralloc_0(&forward)) nomem(); } void doorigrecip(char *addr) { if (sender.len) if ((sender.len != 4) || byte_diff(sender.s,4,"#@[]")) if (gettarget(1,"?",addr)) if (!stralloc_copy(&sender,&data)) nomem(); if (!gettarget(1,":",addr)) if (flagpassthrough) _exit(0); else logmsg(WHO,100,ERROR,"Sorry, no mailbox here by that name. (#5.1.1)"); dodata(); } stralloc recipient = {0}; int flagdefault = 0; int main(int argc,char **argv) { int opt; char *x; int i; sig_pipeignore(); dtline = env_get("DTLINE"); if (!dtline) dtline = ""; x = env_get("SENDER"); if (!x) x = "original envelope sender"; if (!stralloc_copys(&sender,x)) nomem(); if (!stralloc_copys(&forward,"")) nomem(); if (!strset_init(&done)) nomem(); while ((opt = getopt(argc,argv,"nNpPdD")) != opteof) switch (opt) { case 'n': flagdeliver = 0; break; case 'N': flagdeliver = 1; break; case 'p': flagpassthrough = 1; break; case 'P': flagpassthrough = 0; break; case 'd': flagdefault = 1; break; case 'D': flagdefault = 0; break; default: usage(); } argv += optind; fncdb = *argv; if (!fncdb) usage(); fdcdb = open_read(fncdb); if (fdcdb == -1) cdbreaderror(); cdb_init(&cdb,fdcdb); if (flagdefault) { x = env_get("DEFAULT"); if (!x) x = env_get("EXT"); if (!x) logmsg(WHO,100,FATAL,"$DEFAULT or $EXT must be set"); if (!stralloc_copys(&recipient,x)) nomem(); if (!stralloc_cats(&recipient,"@")) nomem(); x = env_get("HOST"); if (!x) logmsg(WHO,100,FATAL,"$HOST must be set"); if (!stralloc_cats(&recipient,x)) nomem(); if (!stralloc_0(&recipient)) nomem(); x = recipient.s; } else { x = env_get("RECIPIENT"); if (!x) logmsg(WHO,100,FATAL,"$RECIPIENT must be set"); } if (!strset_add(&done,x)) nomem(); doorigrecip(x); while (todo.len) { i = todo.len - 1; while ((i > 0) && todo.s[i - 1]) --i; todo.len = i; if (strset_in(&done,todo.s + i)) continue; x = alloc(str_len(todo.s + i) + 1); if (!x) nomem(); str_copy(x,todo.s + i); if (!strset_add(&done,x)) nomem(); x = todo.s + i; if (*x == 0) continue; else if ((*x == '.') || (*x == '/')) dofile(x); else dorecip(x + 1); } if (!forward.len) { if (!flagdeliver) { print("no forwarding\n"); buffer_flush(buffer_2); } _exit(flagpassthrough ? 99 : 0); } if (!stralloc_0(&sender)) nomem(); if (!flagdeliver) { print("from <"); printsafe(sender.s); print(">\n"); while (forward.len) { i = forward.len - 1; while ((i > 0) && forward.s[i - 1]) --i; forward.len = i; print("to <"); printsafe(forward.s + i); print(">\n"); } buffer_flush(buffer_2); _exit(flagpassthrough ? 99 : 0); } if (qmail_open(&qq) == -1) logmsg(WHO,111,FATAL,"unable to fork: "); qmail_puts(&qq,dtline); if (buffer_copy(&bufq,&mess) != 0) logmsg(WHO,111,FATAL,"unable to read message: "); buffer_flush(&bufq); qp[fmt_ulong(qp,qmail_qp(&qq))] = 0; qmail_from(&qq,sender.s); while (forward.len) { i = forward.len - 1; while ((i > 0) && forward.s[i - 1]) --i; forward.len = i; qmail_to(&qq,forward.s + i); } x = qmail_close(&qq); if (*x) logmsg(WHO,*x == 'D' ? 100 : 111,FATAL,x + 1); logmsg(WHO,flagpassthrough ? 99 : 0,LOG,B("qp ",qp)); }