#include #include #include #include "alloc.h" #include "buffer.h" #include "byte.h" #include "case.h" #include "cdbread.h" #include "env.h" #include "exit.h" #include "fmt.h" #include "getoptb.h" #include "logmsg.h" #include "open.h" #include "readclose.h" #include "seek.h" #include "sig.h" #include "str.h" #include "stralloc.h" #include "wait.h" #include "qmail.h" #include "strset.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[1]; static 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[4096]; 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}; static 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, 1024) == -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; static void cdbreaderror() { logmsg(WHO, 111, FATAL, B("unable to read: ", fncdb)); } static 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; } static 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; } static 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: "); } static 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; } } } static 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(); } static 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)); }