summaryrefslogtreecommitdiff
path: root/src/fastforward.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fastforward.c')
-rw-r--r--src/fastforward.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/src/fastforward.c b/src/fastforward.c
new file mode 100644
index 0000000..52e2e12
--- /dev/null
+++ b/src/fastforward.c
@@ -0,0 +1,399 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#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[1];
+
+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};
+
+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;
+
+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));
+}