notqmail logo

acceptutils

patch - info
diff --git Makefile Makefile
index 0f0e31a..26f0c1a 100644
--- Makefile
+++ Makefile
@@ -4,6 +4,246 @@ SHELL=/bin/sh
 
 default: it
 
+ACCEPTUTILS_BIN=reup   authup   checknotroot \
+		fixsmtpio   qmail-qfilter-addtlsheader
+ACCEPTUTILS_MAN=reup.8 authup.8 checknotroot.8 \
+		fixsmtpio.8 qmail-qfilter-addtlsheader.8
+
+acceptutils: \
+${ACCEPTUTILS_BIN} ${ACCEPTUTILS_MAN}
+
+acceptutils_base64.o: \
+compile99 acceptutils_base64.c acceptutils_base64.h
+	./compile99 acceptutils_base64.c
+
+acceptutils_stralloc.o: \
+compile99 acceptutils_stralloc.c acceptutils_stralloc.h
+	./compile99 acceptutils_stralloc.c
+
+acceptutils_ucspitls.o: \
+compile99 acceptutils_ucspitls.c acceptutils_ucspitls.h acceptutils_stralloc.h \
+case.h env.h fd.h readwrite.h scan.h substdio.h
+	./compile99 acceptutils_ucspitls.c
+
+acceptutils_unistd.o: \
+compile99 acceptutils_unistd.c acceptutils_unistd.h
+	./compile99 acceptutils_unistd.c
+
+acceptutils-install: \
+acceptutils
+	cp ${ACCEPTUTILS_BIN} `head -1 conf-qmail`/bin && \
+	cp ${ACCEPTUTILS_MAN} `head -1 conf-qmail`/man/man8
+
+acceptutils-memcheck: \
+test_fixsmtpio
+	valgrind --track-origins=yes --leak-check=full --error-exitcode=99 ./test_fixsmtpio >/dev/null
+	#valgrind --track-origins=yes --leak-check=full --error-exitcode=88 ./fixsmtpio echo hi >/dev/null
+
+acceptutils-tests: \
+test_fixsmtpio
+
+acceptutils-tests-run: \
+acceptutils-tests
+	@prove -v -e '' ./test_fixsmtpio | grep -v '^ok'
+
+authup: \
+load authup.o auto_qmail.o acceptutils_base64.o acceptutils_unistd.o acceptutils_ucspitls.o \
+acceptutils_stralloc.o commands.o control.o timeoutread.o timeoutwrite.o now.o \
+case.a env.a fd.a getln.a open.a sig.a wait.a stralloc.a alloc.a \
+substdio.a error.a str.a fs.a \
+socket.lib
+	./load authup auto_qmail.o acceptutils_base64.o acceptutils_unistd.o acceptutils_ucspitls.o \
+	acceptutils_stralloc.o commands.o control.o timeoutread.o timeoutwrite.o now.o \
+	case.a env.a fd.a getln.a open.a sig.a wait.a stralloc.a alloc.a \
+	substdio.a error.a str.a fs.a \
+	`cat socket.lib`
+
+authup.o: \
+compile99 authup.c commands.h fd.h sig.h stralloc.h gen_alloc.h \
+substdio.h alloc.h wait.h str.h byte.h now.h datetime.h fmt.h exit.h \
+readwrite.h timeoutread.h timeoutwrite.h acceptutils_base64.h case.h \
+env.h control.h error.h scan.h auto_qmail.h acceptutils_unistd.h \
+acceptutils_stralloc.h
+	./compile99 authup.c
+
+check.h: \
+conf-check check_stdint.h
+	cat `head -1 conf-check`/include/check.h \
+	| sed 's}<check_stdint\.h>}"check_stdint.h"}g' \
+	> check.h
+
+check_stdint.h: \
+conf-check
+	cp `head -1 conf-check`/include/check_stdint.h .
+
+checknotroot: \
+load checknotroot.o acceptutils_unistd.o substdio.a error.a str.a
+	./load checknotroot acceptutils_unistd.o substdio.a error.a str.a
+
+checknotroot.o: \
+compile99 checknotroot.c exit.h readwrite.h substdio.h acceptutils_unistd.h
+	./compile99 checknotroot.c
+
+fixsmtpio: \
+load fixsmtpio.o fixsmtpio_control.o fixsmtpio_die.o fixsmtpio_filter.o \
+fixsmtpio_eventq.o fixsmtpio_readwrite.o fixsmtpio_munge.o fixsmtpio_glob.o \
+fixsmtpio_proxy.o acceptutils_ucspitls.o auto_qmail.o control.o \
+acceptutils_unistd.o acceptutils_stralloc.o \
+getln.a substdio.a stralloc.a env.a str.a error.a fd.a sig.a \
+alloc.a wait.a case.a open.a fs.a
+	./load fixsmtpio fixsmtpio_control.o fixsmtpio_die.o fixsmtpio_filter.o \
+	fixsmtpio_eventq.o fixsmtpio_readwrite.o fixsmtpio_munge.o fixsmtpio_glob.o \
+	fixsmtpio_proxy.o acceptutils_ucspitls.o auto_qmail.o control.o \
+	acceptutils_unistd.o acceptutils_stralloc.o \
+	getln.a substdio.a stralloc.a env.a str.a error.a fd.a sig.a \
+	alloc.a wait.a case.a open.a fs.a
+
+fixsmtpio.o: \
+compile99 fixsmtpio.c fixsmtpio.h fixsmtpio_die.h fixsmtpio_filter.h \
+fixsmtpio_proxy.h alloc.h auto_qmail.h case.h control.h env.h \
+fd.h scan.h str.h substdio.h wait.h acceptutils_stralloc.h acceptutils_unistd.h
+	./compile99 fixsmtpio.c
+
+fixsmtpio_die.o: \
+compile99 fixsmtpio_die.c fixsmtpio.h fixsmtpio_die.h readwrite.h \
+acceptutils_stralloc.h acceptutils_unistd.h
+	./compile99 fixsmtpio_die.c
+
+test_acceptutils_stralloc.o: \
+compile99 acceptutils_stralloc.c fixsmtpio.h acceptutils_stralloc.h readwrite.h \
+check.h \
+test_acceptutils_stralloc.c
+	./compile99 test_acceptutils_stralloc.c
+
+fixsmtpio_control.o: \
+compile99 fixsmtpio_control.c fixsmtpio.h fixsmtpio_control.h \
+acceptutils_stralloc.h
+	./compile99 fixsmtpio_control.c
+
+test_fixsmtpio_control.o: \
+compile99 fixsmtpio_control.c fixsmtpio.h fixsmtpio_control.h \
+check.h \
+test_fixsmtpio_control.c
+	./compile99 test_fixsmtpio_control.c
+
+fixsmtpio_eventq.o: \
+compile99 fixsmtpio_eventq.c alloc.h str.h fixsmtpio.h fixsmtpio_die.h fixsmtpio_eventq.h
+	./compile99 fixsmtpio_eventq.c
+
+test_fixsmtpio_eventq.o: \
+compile99 fixsmtpio_eventq.c alloc.h str.h fixsmtpio.h fixsmtpio_die.h fixsmtpio_eventq.h \
+check.h \
+test_fixsmtpio_eventq.c
+	./compile99 test_fixsmtpio_eventq.c
+
+fixsmtpio_filter.o: \
+compile99 fixsmtpio_filter.c fixsmtpio_filter.h fixsmtpio_die.h fixsmtpio_munge.h fixsmtpio_glob.h \
+acceptutils_stralloc.h
+	./compile99 fixsmtpio_filter.c
+
+test_fixsmtpio_filter.o: \
+compile99 fixsmtpio_filter.c fixsmtpio_filter.h fixsmtpio_die.h fixsmtpio_munge.h fixsmtpio_glob.h \
+check.h \
+test_fixsmtpio_filter.c
+	./compile99 test_fixsmtpio_filter.c
+
+fixsmtpio_glob.o: \
+compile99 fixsmtpio_glob.c fixsmtpio_glob.h
+	./compile99 fixsmtpio_glob.c
+
+test_fixsmtpio_glob.o: \
+compile99 fixsmtpio_glob.c fixsmtpio_glob.h \
+check.h \
+test_fixsmtpio_glob.c
+	./compile99 test_fixsmtpio_glob.c
+
+fixsmtpio_munge.o: \
+compile99 fixsmtpio_munge.c fixsmtpio_munge.h fixsmtpio_die.h \
+acceptutils_stralloc.h
+	./compile99 fixsmtpio_munge.c
+
+test_fixsmtpio_munge.o: \
+compile99 fixsmtpio_munge.c fixsmtpio_munge.h fixsmtpio_die.h \
+check.h \
+test_fixsmtpio_munge.c
+	./compile99 test_fixsmtpio_munge.c
+
+fixsmtpio_proxy.o: \
+compile99 fixsmtpio_proxy.c fixsmtpio_proxy.h fixsmtpio_readwrite.h \
+fixsmtpio_die.h fixsmtpio_eventq.h fixsmtpio_filter.h fixsmtpio_die.h \
+acceptutils_stralloc.h acceptutils_unistd.h fmt.h
+	./compile99 fixsmtpio_proxy.c
+
+test_fixsmtpio_proxy.o: \
+compile99 fixsmtpio_proxy.c fixsmtpio_proxy.h fixsmtpio_readwrite.h \
+fixsmtpio_die.h fixsmtpio_eventq.h fixsmtpio_filter.h \
+acceptutils_stralloc.h \
+check.h \
+test_fixsmtpio_proxy.c
+	./compile99 test_fixsmtpio_proxy.c
+
+fixsmtpio_readwrite.o: \
+compile99 fixsmtpio_readwrite.c fixsmtpio_readwrite.h fixsmtpio_die.h error.h readwrite.h select.h \
+acceptutils_stralloc.h
+	./compile99 fixsmtpio_readwrite.c
+
+test_fixsmtpio: \
+fixsmtpio \
+load test_fixsmtpio.o fixsmtpio_control.o fixsmtpio_die.o acceptutils_unistd.o \
+fixsmtpio_eventq.o fixsmtpio_readwrite.o fixsmtpio_munge.o fixsmtpio_glob.o \
+fixsmtpio_filter.o fixsmtpio_proxy.o acceptutils_stralloc.o acceptutils_ucspitls.o \
+test_fixsmtpio_control.o \
+test_acceptutils_stralloc.o test_fixsmtpio_eventq.o test_fixsmtpio_filter.o \
+test_fixsmtpio_glob.o test_fixsmtpio_munge.o test_fixsmtpio_proxy.o \
+auto_qmail.o control.o getln.a \
+substdio.a stralloc.a env.a str.a error.a fd.a sig.a alloc.a wait.a \
+case.a open.a fs.a \
+libcheck.a rt.lib
+	./load test_fixsmtpio fixsmtpio_control.o fixsmtpio_die.o acceptutils_unistd.o \
+	fixsmtpio_eventq.o fixsmtpio_readwrite.o fixsmtpio_munge.o fixsmtpio_glob.o \
+	fixsmtpio_filter.o fixsmtpio_proxy.o acceptutils_stralloc.o acceptutils_ucspitls.o \
+	test_fixsmtpio_control.o \
+	test_acceptutils_stralloc.o test_fixsmtpio_eventq.o test_fixsmtpio_filter.o \
+	test_fixsmtpio_glob.o test_fixsmtpio_munge.o test_fixsmtpio_proxy.o \
+	auto_qmail.o control.o getln.a \
+	substdio.a stralloc.a env.a str.a error.a fd.a sig.a alloc.a wait.a \
+	case.a open.a fs.a \
+	libcheck.a -lpthread -lm `cat rt.lib`
+
+test_fixsmtpio.o: \
+compile99 test_fixsmtpio.c fixsmtpio.h check.h
+	./compile99 test_fixsmtpio.c
+
+libcheck.a: \
+conf-check
+	cp `head -1 conf-check`/lib/libcheck.a .
+
+reup: \
+load reup.o acceptutils_unistd.o wait.a error.a getopt.a substdio.a env.a alloc.a str.a fs.a
+	./load reup acceptutils_unistd.o wait.a error.a getopt.a substdio.a env.a alloc.a str.a fs.a
+
+reup.o: \
+compile99 reup.c acceptutils_unistd.h env.h fmt.h readwrite.h scan.h sgetopt.h str.h substdio.h wait.h
+	./compile99 reup.c
+
+qmail-qfilter-addtlsheader: \
+load qmail-qfilter-addtlsheader.o date822fmt.o now.o \
+datetime.a substdio.a env.a error.a str.a fs.a
+	./load qmail-qfilter-addtlsheader date822fmt.o now.o \
+	datetime.a substdio.a env.a error.a str.a fs.a
+
+qmail-qfilter-addtlsheader.o: \
+compile99 qmail-qfilter-addtlsheader.c datetime.h date822fmt.h env.h \
+now.h readwrite.h substdio.h
+	./compile99 qmail-qfilter-addtlsheader.c
+
+rt.lib: \
+compile load
+	( ( echo 'main() { ; }' > tryrt.c && ./compile tryrt.c && \
+	./load tryrt -lrt ) >/dev/null 2>&1 \
+	&& echo -lrt || exit 0 ) > rt.lib
+	rm -f tryrt.c tryrt.o tryrt
+
 addresses.0: \
 addresses.5
 	nroff -man addresses.5 > addresses.0
@@ -303,6 +543,7 @@ exit.h auto_spawn.h
 clean: \
 TARGETS
 	rm -f `cat TARGETS`
+	git checkout -- INSTALL SENDMAIL
 
 coe.o: \
 compile coe.c coe.h
@@ -319,6 +560,12 @@ make-compile warn-auto.sh systype
 	compile
 	chmod 755 compile
 
+compile99: \
+make-compile99 warn-auto.sh systype
+	( cat warn-auto.sh; ./make-compile99 "`cat systype`" ) > \
+	compile99
+	chmod 755 compile99
+
 condredirect: \
 load condredirect.o qmail.o strerr.a fd.a sig.a wait.a seek.a env.a \
 substdio.a error.a str.a fs.a auto_qmail.o
@@ -908,6 +1155,11 @@ make-compile.sh auto-ccld.sh
 	cat auto-ccld.sh make-compile.sh > make-compile
 	chmod 755 make-compile
 
+make-compile99: \
+make-compile99.sh auto-ccld.sh
+	cat auto-ccld.sh make-compile99.sh > make-compile99
+	chmod 755 make-compile99
+
 make-load: \
 make-load.sh auto-ccld.sh
 	cat auto-ccld.sh make-load.sh > make-load
diff --git TARGETS TARGETS
index facdad7..ee7d87a 100644
--- TARGETS
+++ TARGETS
@@ -385,3 +385,40 @@ forgeries.0
 man
 setup
 check
+make-compile99
+compile99
+acceptutils_base64.o
+acceptutils_stralloc.o
+acceptutils_ucspitls.o
+acceptutils_unistd.o
+authup
+authup.o
+checknotroot
+checknotroot.o
+reup
+reup.o
+fixsmtpio
+fixsmtpio.o
+test_fixsmtpio
+test_fixsmtpio.o
+fixsmtpio_filter.o
+fixsmtpio_proxy.o
+fixsmtpio_die.o
+fixsmtpio_eventq.o
+fixsmtpio_readwrite.o
+fixsmtpio_munge.o
+fixsmtpio_glob.o
+fixsmtpio_control.o
+check.h
+check_stdint.h
+libcheck.a
+rt.lib
+test_acceptutils_stralloc.o
+test_fixsmtpio_control.o
+test_fixsmtpio_eventq.o
+test_fixsmtpio_filter.o
+test_fixsmtpio_glob.o
+test_fixsmtpio_munge.o
+test_fixsmtpio_proxy.o
+qmail-qfilter-addtlsheader
+qmail-qfilter-addtlsheader.o
diff --git acceptutils_base64.c acceptutils_base64.c
new file mode 100644
index 0000000..8b9d622
--- /dev/null
+++ acceptutils_base64.c
@@ -0,0 +1,119 @@
+#include "acceptutils_base64.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "str.h"
+
+static char *b64alpha =
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+#define B64PAD '='
+
+/* returns 0 ok, 1 illegal, -1 problem */
+
+int b64decode(const unsigned char *in,int l,stralloc *out)
+/* stralloc *out =>  not null terminated */
+{
+  int p = 0;
+  int n;
+  unsigned int x;
+  int i, j;
+  char *s;
+  unsigned char b[3];
+
+  if (l == 0) {
+    if (!stralloc_copys(out,"")) return -1;
+    return 0;
+  }
+
+  while (in[l-1] == B64PAD) {
+    p ++;
+    l--;
+  }
+
+  n = (l + p) / 4;
+  i = (n * 3) - p;
+  if (!stralloc_ready(out,i)) return -1;
+  out->len = i;
+  s = out->s;
+
+  for (i = 0; i < n - 1; i++) {
+    x = 0;
+    for (j = 0; j < 4; j++) {
+      if (in[j] >= 'A' && in[j] <= 'Z')
+        x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
+      else if (in[j] >= 'a' && in[j] <= 'z')
+        x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
+      else if (in[j] >= '0' && in[j] <= '9')
+        x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
+      else if (in[j] == '+')
+        x = (x << 6) + 62;
+      else if (in[j] == '/')
+        x = (x << 6) + 63;
+      else if (in[j] == '=')
+        x = (x << 6);
+    }
+
+    s[2] = (unsigned char)(x & 255); x >>= 8;
+    s[1] = (unsigned char)(x & 255); x >>= 8;
+    s[0] = (unsigned char)(x & 255); x >>= 8;
+    s += 3; in += 4;
+  }
+
+  x = 0;
+  for (j = 0; j < 4; j++) {
+    if (in[j] >= 'A' && in[j] <= 'Z')
+      x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
+    else if (in[j] >= 'a' && in[j] <= 'z')
+      x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
+    else if (in[j] >= '0' && in[j] <= '9')
+      x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
+    else if (in[j] == '+')
+      x = (x << 6) + 62;
+    else if (in[j] == '/')
+      x = (x << 6) + 63;
+    else if (in[j] == '=')
+      x = (x << 6);
+  }
+
+  b[2] = (unsigned char)(x & 255); x >>= 8;
+  b[1] = (unsigned char)(x & 255); x >>= 8;
+  b[0] = (unsigned char)(x & 255); x >>= 8;
+
+  for (i = 0; i < 3 - p; i++)
+    s[i] = b[i];
+
+  return 0;
+}
+
+int b64encode(stralloc *in,stralloc *out)
+{
+  unsigned char a, b, c;
+  int i;
+  char *s;
+
+  if (in->len == 0)
+  {
+    if (!stralloc_copys(out,"")) return -1;
+    return 0;
+  }
+
+  i = in->len / 3 * 4 + 4;
+  if (!stralloc_ready(out,i)) return -1;
+  s = out->s;
+
+  for (i = 0; i < in->len; i += 3) {
+    a = in->s[i];
+    b = i + 1 < in->len ? in->s[i + 1] : 0;
+    c = i + 2 < in->len ? in->s[i + 2] : 0;
+
+    *s++ = b64alpha[a >> 2];
+    *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)];
+
+    if (i + 1 >= in->len) *s++ = B64PAD;
+    else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)];
+
+    if (i + 2 >= in->len) *s++ = B64PAD;
+    else *s++ = b64alpha[c & 63];
+  }
+  out->len = s - out->s;
+  return 0;
+}
diff --git acceptutils_base64.h acceptutils_base64.h
new file mode 100644
index 0000000..4d6ab92
--- /dev/null
+++ acceptutils_base64.h
@@ -0,0 +1,9 @@
+#ifndef BASE64_H
+#define BASE64_H
+
+#include "stralloc.h"
+
+int b64decode(const unsigned char *,int,stralloc *);
+int b64encode(stralloc *,stralloc *);
+
+#endif
diff --git acceptutils_stralloc.c acceptutils_stralloc.c
new file mode 100644
index 0000000..947e40c
--- /dev/null
+++ acceptutils_stralloc.c
@@ -0,0 +1,44 @@
+#include "acceptutils_stralloc.h"
+
+static void (*die_sa)(const char *,const char *);
+
+void stralloc_set_die(void (*die_nomem)(const char *,const char *)) {
+  die_sa = die_nomem;
+}
+
+void contextlogging_append(const char *caller,stralloc *to,char *from) {
+  if (!stralloc_append(to,from)) die_sa(caller,__func__);
+}
+void contextlogging_append0(const char *caller,stralloc *to) {
+  if (!stralloc_0(to)) die_sa(caller,__func__);
+}
+void contextlogging_cat(const char *caller,stralloc *to,stralloc *from) {
+  if (!stralloc_cat(to,from)) die_sa(caller,__func__);
+}
+void contextlogging_catb(const char *caller,stralloc *to,char *buf,int len) {
+  if (!stralloc_catb(to,buf,len)) die_sa(caller,__func__);
+}
+void contextlogging_cats(const char *caller,stralloc *to,char *from) {
+  if (!stralloc_cats(to,from)) die_sa(caller,__func__);
+}
+void contextlogging_copy(const char *caller,stralloc *to,stralloc *from) {
+  if (!stralloc_copy(to,from)) die_sa(caller,__func__);
+}
+void contextlogging_copyb(const char *caller,stralloc *to,char *buf,int len) {
+  if (!stralloc_copyb(to,buf,len)) die_sa(caller,__func__);
+}
+void contextlogging_copys(const char *caller,stralloc *to,char *from) {
+  if (!stralloc_copys(to,from)) die_sa(caller,__func__);
+}
+
+void prepends(stralloc *to,char *from) {
+  stralloc tmp = {0};
+  copy(&tmp,to);
+  copys(to,(char *)from);
+  cat(to,&tmp);
+}
+int starts(stralloc *haystack,char *needle) { return stralloc_starts(haystack,needle); }
+int ends_with_newline(stralloc *sa) {
+  return sa->len > 0 && sa->s[sa->len - 1] == '\n';
+}
+void blank(stralloc *sa) { copys(sa,""); }
diff --git acceptutils_stralloc.h acceptutils_stralloc.h
new file mode 100644
index 0000000..6dbb1bc
--- /dev/null
+++ acceptutils_stralloc.h
@@ -0,0 +1,25 @@
+#include "stralloc.h"
+
+void stralloc_set_die(void (*)(const char *,const char *));
+
+void contextlogging_append(const char *,stralloc *,char *);
+#define append(a,b) contextlogging_append(__func__,a,b)
+void contextlogging_append0(const char *,stralloc *);
+#define append0(a) contextlogging_append0(__func__,a)
+void contextlogging_cat(const char *,stralloc *,stralloc *);
+#define cat(a,b) contextlogging_cat(__func__,a,b)
+void contextlogging_catb(const char *,stralloc *,char *,int);
+#define catb(a,b,c) contextlogging_catb(__func__,a,b,c)
+void contextlogging_cats(const char *,stralloc *,char *);
+#define cats(a,b) contextlogging_cats(__func__,a,b)
+void contextlogging_copy(const char *,stralloc *,stralloc *);
+#define copy(a,b) contextlogging_copy(__func__,a,b)
+void contextlogging_copyb(const char *,stralloc *,char *,int);
+#define copyb(a,b,c) contextlogging_copyb(__func__,a,b,c)
+void contextlogging_copys(const char *,stralloc *,char *);
+#define copys(a,b) contextlogging_copys(__func__,a,b)
+
+void prepends(stralloc *,char *);
+int  starts(stralloc *,char *);
+int  ends_with_newline(stralloc *);
+void blank(stralloc *);
diff --git acceptutils_ucspitls.c acceptutils_ucspitls.c
new file mode 100644
index 0000000..db3f158
--- /dev/null
+++ acceptutils_ucspitls.c
@@ -0,0 +1,111 @@
+#include "case.h"
+#include "env.h"
+#include "fd.h"
+#include "readwrite.h"
+#include "scan.h"
+#include "substdio.h"
+
+#include "acceptutils_stralloc.h"
+#include "acceptutils_ucspitls.h"
+
+int ucspitls_level(void) {
+  char *ucspitls = env_get("UCSPITLS");
+  char *disabletls = env_get("DISABLETLS");
+  env_unset("UCSPITLS");
+  env_unset("DISABLETLS");
+  if (disabletls || !ucspitls || !case_diffs(ucspitls,"-"))
+    return UCSPITLS_UNAVAILABLE;
+  if (!case_diffs(ucspitls,"!")) return UCSPITLS_REQUIRED;
+  return UCSPITLS_AVAILABLE;
+}
+
+static int get_fd_for(char *name) {
+  unsigned long fd;
+  char *fdstr;
+
+  if (!(fdstr = env_get(name))) return 0;
+  if (!scan_ulong(fdstr,&fd)) return 0;
+
+  return (int)fd;
+}
+
+static int notify_control_socket() {
+  unsigned int fd = get_fd_for("SSLCTLFD");
+
+  if (!fd) return 0;
+  if (write(fd, "Y", 1) < 1) return 0;
+
+  return 1;
+}
+
+static int adjust_read_fd() {
+  unsigned int fd = get_fd_for("SSLREADFD");
+
+  if (!fd) return 0;
+  if (fd_move(0,fd) == -1) return 0;
+
+  return 1;
+}
+
+static int adjust_write_fd() {
+  unsigned int fd = get_fd_for("SSLWRITEFD");
+
+  if (!fd) return 0;
+  if (fd_move(1,fd) == -1) return 0;
+
+  return 1;
+}
+
+int tls_init(void) {
+  return notify_control_socket() && adjust_read_fd() && adjust_write_fd();
+}
+
+int tls_info(void (*die_nomem)(const char *caller,const char *alloc_fn)) {
+  unsigned long fd;
+  char envbuf[SUBSTDIO_INSIZE];
+  char *x;
+  int j;
+
+  stralloc ssl_env   = {0};
+  stralloc ssl_parm  = {0};
+  stralloc ssl_value = {0};
+
+  fd = get_fd_for("SSLCTLFD");
+  if (!fd) return 0;
+
+  while ((j = read(fd,envbuf,SUBSTDIO_INSIZE)) > 0) {
+    catb(&ssl_env,envbuf,j);
+    if (ssl_env.len >= 2 && ssl_env.s[ssl_env.len-2] == 0 && ssl_env.s[ssl_env.len-1] == 0)
+      break;
+  }
+  if (j < 0) die_nomem(__func__,"read");
+  if (ssl_env.len == 0) return 0;
+
+  x = ssl_env.s;
+
+  for (j=0;j < ssl_env.len-1;++j) {
+    if ( *x != '=' ) {
+      catb(&ssl_parm,x,1);
+      x++;
+    } else {
+      append0(&ssl_parm);
+      x++;
+
+      for (;j < ssl_env.len-j-1;++j) {
+        if ( *x != '\0' ) {
+          catb(&ssl_value,x,1);
+          x++;
+        } else {
+          append0(&ssl_value);
+          x++;
+          if (!env_put2(ssl_parm.s,ssl_value.s))
+            die_nomem(__func__,"env_put2");
+          ssl_parm.len = 0;
+          ssl_value.len = 0;
+          break;
+        }
+      }
+    }
+  }
+  return j;
+}
diff --git acceptutils_ucspitls.h acceptutils_ucspitls.h
new file mode 100644
index 0000000..e9f2179
--- /dev/null
+++ acceptutils_ucspitls.h
@@ -0,0 +1,7 @@
+#define UCSPITLS_UNAVAILABLE 0
+#define UCSPITLS_AVAILABLE   1
+#define UCSPITLS_REQUIRED    2
+
+int ucspitls_level(void);
+int tls_init(void);
+int tls_info(void (*)(const char *,const char *));
diff --git acceptutils_unistd.c acceptutils_unistd.c
new file mode 100644
index 0000000..fad749e
--- /dev/null
+++ acceptutils_unistd.c
@@ -0,0 +1,21 @@
+#include <unistd.h>
+
+int unistd_chdir(const char *path) { return chdir(path); }
+
+int unistd_close(int fildes) { return close(fildes); }
+
+int unistd_execvp(const char *file, char *const argv[]) {
+  return execvp(file, argv);
+}
+
+void unistd_exit(int status) { return _exit(status); }
+
+int unistd_fork(void) { return fork(); }
+
+int unistd_getpid(void) { return getpid(); }
+
+int unistd_getuid(void) { return getuid(); }
+
+int unistd_pipe(int fildes[2]) { return pipe(fildes); }
+
+int unistd_sleep(int seconds) { return sleep(seconds); }
diff --git acceptutils_unistd.h acceptutils_unistd.h
new file mode 100644
index 0000000..72d0d46
--- /dev/null
+++ acceptutils_unistd.h
@@ -0,0 +1,9 @@
+int unistd_chdir(const char *);
+int unistd_close(int);
+int unistd_execvp(const char *,char *const[]);
+void unistd_exit(int);
+int unistd_fork(void);
+int unistd_getpid(void);
+int unistd_getuid(void);
+int unistd_pipe(int [2]);
+int unistd_sleep(int);
diff --git authup.8 authup.8
new file mode 100644
index 0000000..f3b28a2
--- /dev/null
+++ authup.8
@@ -0,0 +1,163 @@
+.TH AUTHUP 8 2018-12-01
+.SH NAME
+authup \- offer SMTP or POP3 authentication
+.SH SYNOPSIS
+.B authup
+.I protocol
+.I checkpassword
+.I prog
+.SH DESCRIPTION
+.B authup
+speaks just enough SMTP (AUTH LOGIN or PLAIN)
+or POP3 (USER-PASS)
+to parse a username and password.
+It then runs
+.I checkpassword
+via the
+.B checkpassword
+interface.
+
+.B authup
+is most commonly invoked from
+.B sslserver
+as root, so that
+.B checkpassword
+can run
+.B ofmipd
+(or
+.BR qmail-smtpd )
+or
+.B qmail-pop3d
+under the UID of the authenticated user.
+
+.B authup
+waits for
+.I checkpassword
+and
+.I prog
+to finish.
+It prints an error message if
+.I checkpassword
+or
+.IR prog
+crashes or exits nonzero.
+If authentication failed, and
+.I protocol
+is SMTP, it waits 5 seconds
+before printing the message.
+
+When run under
+.BR "sslserver -n" ,
+.B authup
+can be configured to offer TLS and/or refuse to authenticate without it.
+.SH "ENVIRONMENT VARIABLES"
+If
+.B UCSPITLS
+is set to "!",
+.B authup
+will require the client to negotiate TLS before attempting to authenticate.
+
+If
+.B DISABLETLS
+is set,
+.B UCSPITLS
+will not be honored.
+
+If
+.B AUTHUP_SASL_BROKEN_CLIENTS
+is set,
+.B authup
+will additionally advertise AUTH support in a second, non-standard way
+to interoperate with Outlook Express 4, Exchange 5,
+and other SMTP clients that implement an obsolete version of the AUTH command.
+
+If authentication succeeds,
+.IR prog 's
+environment will contain the username in
+.BR AUTHUP_USER .
+
+Since
+.I prog
+will run with the privileges of the authenticated user, so will
+any
+.B qmail-queue
+wrapper configured as
+.BR QMAILQUEUE .
+This can be useful for user-controlled filtering.
+.SH "CONTROL FILES"
+.TP 5
+.I smtpgreeting / pop3greeting
+SMTP and POP3 greeting messages.
+Default:
+.IR me ,
+if that is supplied;
+otherwise
+.B authup
+will refuse to run.
+The first word of the greeting
+should be the current host's name.
+.TP 5
+.I smtpcapabilities / pop3capabilities
+SMTP and POP3 capabilities (one per line) to advertise in
+.I EHLO
+and
+.IR CAPA ,
+respectively.
+Default:
+.IR none .
+Without the needed file,
+.B authup
+will refuse to run.
+
+Typical POP3 capabilities: TOP, UIDL. (Omit STLS and USER.)
+
+Typical SMTP capabilities: PIPELINING, 8BITMIME. (Omit STARTTLS and AUTH.)
+
+Correct values for your system depend on what
+.I prog
+offers.
+For instance, to inspect your installed
+.BR qmail-smtpd :
+
+$ echo EHLO | qmail-smtpd \\
+     | sed -e '1,2d' -e 's|^....||' | egrep -v '^(STARTTLS|AUTH)'
+
+.B authup
+will automatically advertise TLS support when run under
+.B sslserver
+with the
+.B -n
+flag.
+.TP 5
+.I timeoutsmtpd / timeoutpop3d
+Number of seconds
+.B authup
+will wait for each new buffer of data from the remote SMTP or POP3 client.
+Default: 1200.
+.SH "COMPATIBILITY"
+While
+.B CRAM-MD5
+is available in most SMTP AUTH patches, and
+.B APOP
+is available in
+.BR qmail-popup ,
+neither is currently supported by
+.BR authup .
+Nor is authenticating with a client certificate.
+If you rely on any of these, please share your use case with the author.
+.SH "EXAMPLES"
+See
+.IR https://schmonz.com/qmail/acceptutils .
+.SH "AUTHOR"
+.B Amitai Schleier <schmonz-web-acceptutils@schmonz.com>
+.SH "SEE ALSO"
+reup(8),
+checknotroot(8),
+fixsmtpio(8),
+sslserver(1),
+ucspi-tls(2),
+qmail-qfilter-queue(8),
+ofmipd(8),
+qmail-smtpd(8),
+qmail-pop3d(8),
+qmail-popup(8).
diff --git authup.c authup.c
new file mode 100644
index 0000000..1f82ef1
--- /dev/null
+++ authup.c
@@ -0,0 +1,592 @@
+#include "auto_qmail.h"
+#include "commands.h"
+#include "fd.h"
+#include "sig.h"
+#include "substdio.h"
+#include "alloc.h"
+#include "wait.h"
+#include "str.h"
+#include "byte.h"
+#include "now.h"
+#include "fmt.h"
+#include "scan.h"
+#include "exit.h"
+#include "readwrite.h"
+#include "timeoutread.h"
+#include "timeoutwrite.h"
+#include "case.h"
+#include "env.h"
+#include "control.h"
+#include "error.h"
+#include "open.h"
+
+#include "acceptutils_base64.h"
+#include "acceptutils_stralloc.h"
+#include "acceptutils_ucspitls.h"
+#include "acceptutils_unistd.h"
+
+#define HOMEPAGE "https://schmonz.com/qmail/acceptutils"
+#define PROGNAME "authup"
+
+#define EXITCODE_CHECKPASSWORD_UNACCEPTABLE   1
+#define EXITCODE_CHECKPASSWORD_MISUSED        2
+#define EXITCODE_CHECKPASSWORD_TEMPFAIL     111
+/* sync with fixsmtpio.h */
+#define EXITCODE_FIXSMTPIO_TIMEOUT           16
+#define EXITCODE_FIXSMTPIO_PARSEFAIL         18
+
+static int timeout = 1200;
+int tls_level = UCSPITLS_UNAVAILABLE;
+int in_tls = 0;
+
+void die()         { _exit( 1); }
+void die_noretry() { _exit(12); }
+
+int safewrite(int fd,char *buf,int len) {
+  int r;
+  r = timeoutwrite(timeout,fd,buf,len);
+  if (r <= 0) die();
+  return r;
+}
+
+char ssoutbuf[SUBSTDIO_OUTSIZE];
+substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
+
+void out(char *s) { substdio_puts(&ssout,s); }
+void flush() { substdio_flush(&ssout); }
+
+void pop3_err(char *s) { out("-ERR "); out(s); out("\r\n"); flush(); }
+void smtp_out(char *s) {               out(s); out("\r\n"); flush(); }
+
+struct authup_error {
+  char *name;
+  char *message;
+  char *smtpcode;
+  char *smtperror;
+  int sleep;
+  void (*die)();
+};
+
+struct authup_error e[] = {
+  { "control", "unable to read controls",      "421", "4.3.0", 0, die_noretry }
+, { "nomem",   "out of memory",                "451", "4.3.0", 0, die_noretry }
+, { "alarm",   "timeout",                      "451", "4.4.2", 0, die_noretry }
+, { "pipe",    "unable to open pipe",          "454", "4.3.0", 0, die_noretry }
+, { "read",    "unable to read pipe",          "454", "4.3.0", 0, die_noretry }
+, { "write",   "unable to write pipe",         "454", "4.3.0", 0, die_noretry }
+, { "fork",    "unable to fork",               "454", "4.3.0", 0, die_noretry }
+, { "wait",    "unable to wait for child",     "454", "4.3.0", 0, die_noretry }
+, { "crash",   "aack, child crashed",          "454", "4.3.0", 0, die_noretry }
+, { "badauth", "authorization failed",         "535", "5.7.0", 5, die         }
+, { "noauth",  "auth type unimplemented",      "504", "5.5.1", 5, die         }
+, { "input",   "malformed auth input",         "501", "5.5.4", 5, die         }
+, { "authabrt","auth exchange cancelled",      "501", "5.0.0", 5, die         }
+, { "protocol","protocol exchange ended",      "501", "5.0.0", 0, die_noretry }
+, { "starttls","TLS temporarily not available","454", "5.7.3", 0, die         }
+, { "needtls", "Must start TLS first",         "530", "5.7.0", 0, die         }
+, { 0,         "unknown or unspecified error", "421", "4.3.0", 0, die_noretry }
+};
+
+void pop3_auth_error(struct authup_error ae) {
+  out("-ERR");
+  out(" " PROGNAME " ");
+  out(ae.message);
+}
+
+void smtp_auth_error(struct authup_error ae) {
+  out(ae.smtpcode);
+  out(" " PROGNAME " ");
+  out(ae.message);
+  out(" (#");
+  out(ae.smtperror);
+  out(")");
+}
+
+void (*protocol_error)();
+
+void pop3_sleep(int s) { return; }
+void smtp_sleep(int s) { unistd_sleep(s); }
+void (*protocol_sleep)();
+
+char sserrbuf[SUBSTDIO_OUTSIZE];
+substdio sserr = SUBSTDIO_FDBUF(write,2,sserrbuf,sizeof sserrbuf);
+
+void errflush(char *s) {
+  substdio_puts(&sserr,PROGNAME ": ");
+  substdio_puts(&sserr,s);
+  substdio_putsflush(&sserr,"\n");
+}
+
+void authup_die(const char *name) {
+  int i;
+  for (i = 0;e[i].name;++i) if (case_equals(e[i].name,name)) break;
+  protocol_sleep(e[i].sleep);
+  protocol_error(e[i]);
+  out("\r\n");
+  flush();
+  e[i].die();
+}
+
+void die_nomem(const char *caller,const char *alloc_fn) {
+  substdio_puts(&sserr,PROGNAME ": die_nomem: ");
+  if (caller) {
+    substdio_puts(&sserr,caller);
+    substdio_puts(&sserr,": ");
+  }
+  if (alloc_fn) {
+    substdio_puts(&sserr,alloc_fn);
+  }
+  substdio_putsflush(&sserr,"\n");
+  authup_die("nomem");
+}
+
+void die_usage() { errflush("usage: " PROGNAME " <smtp|pop3> prog"); die(); }
+
+void smtp_err_authoriz() { smtp_out("530 " PROGNAME " authentication required (#5.7.1)"); }
+void pop3_err_authoriz() { pop3_err(PROGNAME " authorization first"); }
+
+void pop3_err_syntax()   { pop3_err(PROGNAME " syntax error"); }
+void pop3_err_wantuser() { pop3_err(PROGNAME " USER first"); }
+
+int saferead(int fd,char *buf,int len) {
+  int r;
+  r = timeoutread(timeout,fd,buf,len);
+  if (r == -1) if (errno == error_timeout) authup_die("alarm");
+  if (r <= 0) authup_die("read");
+  return r;
+}
+
+char ssinbuf[SUBSTDIO_INSIZE];
+substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
+
+stralloc greeting = {0};
+stralloc capabilities = {0};
+char **childargs;
+
+void pop3_okay() { out("+OK \r\n"); flush(); }
+void pop3_quit() { pop3_okay(); _exit(0); }
+void smtp_quit() { out("221 "); smtp_out(greeting.s); _exit(0); }
+
+stralloc username = {0};
+stralloc password = {0};
+stralloc timestamp = {0};
+
+void exit_according_to_child_exit(int exitcode) {
+  switch (exitcode) {
+    case EXITCODE_CHECKPASSWORD_UNACCEPTABLE:
+    case EXITCODE_CHECKPASSWORD_MISUSED:
+    case EXITCODE_CHECKPASSWORD_TEMPFAIL:
+      authup_die("badauth");
+    case EXITCODE_FIXSMTPIO_TIMEOUT:
+      authup_die("alarm");
+    case EXITCODE_FIXSMTPIO_PARSEFAIL:
+      authup_die("control");
+    default:
+      _exit(0);
+  }
+}
+
+void logtry(char *username) {
+  substdio_puts(&sserr,PROGNAME ": login attempt by ");
+  substdio_puts(&sserr,username);
+  substdio_putsflush(&sserr,"\n");
+}
+
+void checkpassword(stralloc *username,stralloc *password,stralloc *timestamp) {
+  int child;
+  int wstat;
+  int pi[2];
+  char upbuf[SUBSTDIO_OUTSIZE];
+  substdio ssup;
+
+  unistd_close(3);
+  if (unistd_pipe(pi) == -1) authup_die("pipe");
+  if (pi[0] != 3) authup_die("pipe");
+  switch((child = unistd_fork())) {
+    case -1:
+      authup_die("fork");
+    case 0:
+      unistd_close(pi[1]);
+      sig_pipedefault();
+      append0(username);
+      logtry(username->s);
+      if (!env_put2("AUTHUP_USER",username->s)) authup_die("nomem");
+      unistd_execvp(*childargs,childargs);
+      authup_die("fork");
+  }
+  unistd_close(pi[0]);
+  substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf);
+
+  append0(username);
+  if (substdio_put(&ssup,username->s,username->len) == -1) authup_die("write");
+  byte_zero(username->s,username->len);
+
+  append0(password);
+  if (substdio_put(&ssup,password->s,password->len) == -1) authup_die("write");
+  byte_zero(password->s,password->len);
+
+  append0(timestamp);
+  if (substdio_put(&ssup,timestamp->s,timestamp->len) == -1) authup_die("write");
+  byte_zero(timestamp->s,timestamp->len);
+
+  if (substdio_flush(&ssup) == -1) authup_die("write");
+  unistd_close(pi[1]);
+  byte_zero(upbuf,sizeof upbuf);
+
+  if (wait_pid(&wstat,child) == -1) authup_die("wait");
+  if (wait_crashed(wstat)) authup_die("crash");
+
+  exit_according_to_child_exit(wait_exitcode(wstat));
+}
+
+static char unique[FMT_ULONG + FMT_ULONG + 3];
+
+void pop3_greet() {
+  char *s;
+  s = unique;
+  s += fmt_uint(s,unistd_getpid());
+  *s++ = '.';
+  s += fmt_ulong(s,(unsigned long) now());
+  *s++ = '@';
+  *s++ = 0;
+  out("+OK <");
+  out(unique);
+  out(greeting.s);
+  out(">\r\n");
+  flush();
+}
+
+void pop3_format_capa(stralloc *multiline) {
+  cats(multiline,".\r\n");
+}
+
+void pop3_capa(char *arg) {
+  out("+OK capability list follows\r\n");
+  if (tls_level >= UCSPITLS_AVAILABLE && !in_tls) out("STLS\r\n");
+  out("USER\r\n");
+  out(capabilities.s);
+  flush();
+}
+
+static int seenuser = 0;
+
+void pop3_stls(char *arg) {
+  if (tls_level < UCSPITLS_AVAILABLE || in_tls) return pop3_err("STLS not available");
+  out("+OK starting TLS negotiation\r\n");
+  flush();
+
+  if (!tls_init() || !tls_info(die_nomem)) authup_die("starttls");
+  /* reset state */
+  seenuser = 0;
+
+  in_tls = 1;
+}
+
+void pop3_user(char *arg) {
+  if (tls_level >= UCSPITLS_REQUIRED && !in_tls) authup_die("needtls");
+  if (!*arg) { pop3_err_syntax(); return; }
+  pop3_okay();
+  seenuser = 1;
+  copys(&username,arg);
+}
+
+void pop3_pass(char *arg) {
+  if (!seenuser) { pop3_err_wantuser(); return; }
+  if (!*arg) { pop3_err_syntax(); return; }
+
+  copys(&password,arg);
+  byte_zero(arg,str_len(arg));
+
+  copys(&timestamp,"<");
+  cats(&timestamp,unique);
+  cats(&timestamp,greeting.s);
+  cats(&timestamp,">");
+
+  checkpassword(&username,&password,&timestamp);
+}
+
+void smtp_greet() {
+  out("220 ");
+  out(greeting.s);
+  out(" ESMTP\r\n");
+  flush();
+}
+
+void smtp_helo(char *arg) {
+  out("250 ");
+  smtp_out(greeting.s);
+}
+
+// copy from fixsmtpio_munge.c:change_last_line_fourth_char_to_space()
+void smtp_format_ehlo(stralloc *multiline) {
+  int pos = 0;
+  int i;
+  for (i = multiline->len - 2; i >= 0; i--) {
+    if (multiline->s[i] == '\n') {
+      pos = i + 1;
+      break;
+    }
+  }
+  capabilities.s[pos+3] = ' ';
+}
+
+void smtp_ehlo(char *arg) {
+  char *x;
+  out("250-"); out(greeting.s); out("\r\n");
+  if (tls_level >= UCSPITLS_AVAILABLE && !in_tls) out("250-STARTTLS\r\n");
+  out("250-AUTH LOGIN PLAIN\r\n");
+  if ((x = env_get("AUTHUP_SASL_BROKEN_CLIENTS")))
+    out("250-AUTH=LOGIN PLAIN\r\n");
+  out(capabilities.s);
+  flush();
+}
+
+void smtp_starttls() {
+  if (tls_level < UCSPITLS_AVAILABLE || in_tls) return smtp_out("502 unimplemented (#5.5.1)");
+  smtp_out("220 Ready to start TLS (#5.7.0)");
+
+  if (!tls_init() || !tls_info(die_nomem)) authup_die("starttls");
+  /* reset state */
+  ssin.p = 0;
+
+  in_tls = 1;
+}
+
+static stralloc authin = {0};
+
+void smtp_authgetl() {
+  int i;
+
+  blank(&authin);
+
+  for (;;) {
+    if (!stralloc_readyplus(&authin,1)) authup_die("nomem"); /* XXX */
+    i = substdio_get(&ssin,authin.s + authin.len,1);
+    if (i != 1) authup_die("read");
+    if (authin.s[authin.len] == '\n') break;
+    ++authin.len;
+  }
+
+  if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len;
+  authin.s[authin.len] = 0;
+
+  if (*authin.s == '*' && *(authin.s + 1) == 0) authup_die("authabrt");
+  if (authin.len == 0) authup_die("input");
+}
+
+static int b64decode2(char *c,int i,stralloc *sa) {
+  return b64decode((const unsigned char *)c,i,sa);
+}
+
+void auth_login(char *arg) {
+  int r;
+
+  if (*arg) {
+    if ((r = b64decode2(arg,str_len(arg),&username)) == 1) authup_die("input");
+  }
+  else {
+    smtp_out("334 VXNlcm5hbWU6"); /* Username: */
+    smtp_authgetl();
+    if ((r = b64decode2(authin.s,authin.len,&username)) == 1) authup_die("input");
+  }
+  if (r == -1) authup_die("nomem");
+
+  smtp_out("334 UGFzc3dvcmQ6"); /* Password: */
+
+  smtp_authgetl();
+  if ((r = b64decode2(authin.s,authin.len,&password)) == 1) authup_die("input");
+  if (r == -1) authup_die("nomem");
+
+  if (!username.len || !password.len) authup_die("input");
+  checkpassword(&username,&password,&timestamp);
+}
+
+static stralloc resp = {0};
+
+void auth_plain(char *arg) {
+  int r, id = 0;
+
+  if (*arg) {
+    if ((r = b64decode2(arg,str_len(arg),&resp)) == 1) authup_die("input");
+  }
+  else {
+    smtp_out("334 ");
+    smtp_authgetl();
+    if ((r = b64decode2(authin.s,authin.len,&resp)) == 1) authup_die("input");
+  }
+  if (r == -1) authup_die("nomem");
+  append0(&resp);
+  while (resp.s[id]) id++; /* ignore authorize-id */
+
+  if (resp.len > id + 1)
+    copys(&username,resp.s + id + 1);
+  if (resp.len > id + username.len + 2)
+    copys(&password,resp.s + id + username.len + 2);
+
+  if (!username.len || !password.len) authup_die("input");
+  checkpassword(&username,&password,&timestamp);
+}
+
+void smtp_auth(char *arg) {
+  int i;
+  char *cmd = arg;
+
+  if (tls_level >= UCSPITLS_REQUIRED && !in_tls) authup_die("needtls");
+
+  i = str_chr(cmd,' ');
+  arg = cmd + i;
+  while (*arg == ' ') ++arg;
+  cmd[i] = 0;
+
+  if (case_equals("login",cmd)) auth_login(arg);
+  if (case_equals("plain",cmd)) auth_plain(arg);
+  authup_die("noauth");
+}
+
+void smtp_help() {
+  smtp_out("214 " PROGNAME " home page: " HOMEPAGE);
+}
+
+void smtp_noop() {
+  smtp_out("250 ok");
+}
+
+struct commands pop3commands[] = {
+  { "stls", pop3_stls, 0 }
+, { "user", pop3_user, 0 }
+, { "pass", pop3_pass, 0 }
+, { "quit", pop3_quit, 0 }
+, { "capa", pop3_capa, 0 }
+, { "noop", pop3_okay, 0 }
+, { 0, pop3_err_authoriz, 0 }
+};
+
+struct commands smtpcommands[] = {
+  { "starttls", smtp_starttls, 0 }
+, { "auth", smtp_auth, flush }
+, { "quit", smtp_quit, 0 }
+, { "helo", smtp_helo, 0 }
+, { "ehlo", smtp_ehlo, 0 }
+, { "help", smtp_help, 0 }
+, { "noop", smtp_noop, 0 }
+, { 0, smtp_err_authoriz, 0 }
+};
+
+struct protocol {
+  char *name;
+  char *cap_prefix;
+  void (*cap_format_response)();
+  void (*error)();
+  void (*sleep)();
+  void (*greet)();
+  struct commands *c;
+};
+
+struct protocol p[] = {
+  { "pop3", "",     pop3_format_capa, pop3_auth_error, pop3_sleep, pop3_greet, pop3commands }
+, { "smtp", "250-", smtp_format_ehlo, smtp_auth_error, smtp_sleep, smtp_greet, smtpcommands }
+, { 0,      "",     0,                die_usage,       0,     die_usage,  0            }
+};
+
+int control_readgreeting(char *p) {
+  stralloc file = {0};
+  int retval;
+
+  copys(&file,"control/");
+  cats(&file,p);
+  cats(&file,"greeting");
+  append0(&file);
+
+  retval = control_rldef(&greeting,file.s,1,(char *) 0);
+  if (retval != 1) retval = -1;
+
+  append0(&greeting);
+
+  return retval;
+}
+
+int control_readtimeout(char *p) {
+  stralloc file = {0};
+
+  copys(&file,"control/timeout");
+  cats(&file,p);
+  cats(&file,"d");
+  append0(&file);
+
+  return control_readint(&timeout,file.s);
+}
+
+int control_readcapabilities(struct protocol p) {
+  stralloc file = {0};
+  stralloc lines = {0};
+  int linestart;
+  int pos;
+
+  copys(&file,"control/");
+  cats(&file,p.name);
+  cats(&file,"capabilities");
+  append0(&file);
+
+  if (control_readfile(&lines,file.s,0) != 1) return -1;
+
+  blank(&capabilities);
+  for (linestart = 0, pos = 0; pos < lines.len; pos++) {
+    if (lines.s[pos] == '\0') {
+      cats(&capabilities,p.cap_prefix);
+      cats(&capabilities,lines.s+linestart);
+      cats(&capabilities,"\r\n");
+      linestart = pos + 1;
+    }
+  }
+  p.cap_format_response(&capabilities);
+  append0(&capabilities);
+
+  return 1;
+}
+
+int should_greet() {
+  char *x;
+  int r;
+
+  if (!(x = env_get("REUP"))) return 1;
+  if (!scan_ulong(x,&r)) return 1;
+  if (r > 1) return 0;
+  return 1;
+}
+
+void doprotocol(struct protocol p) {
+  protocol_error = p.error;
+  protocol_sleep = p.sleep;
+
+  if (unistd_chdir(auto_qmail) == -1) authup_die("control");
+  if (control_init() == -1) authup_die("control");
+  if (control_readgreeting(p.name) == -1) authup_die("control");
+  if (control_readtimeout(p.name) == -1) authup_die("control");
+  if (control_readcapabilities(p) == -1) authup_die("control");
+  if (should_greet()) p.greet();
+  commands(&ssin,p.c);
+  authup_die("protocol");
+}
+
+int main(int argc,char **argv) {
+  char *protocol;
+  int i;
+
+  sig_alarmcatch(die);
+  sig_pipeignore();
+
+  stralloc_set_die(die_nomem);
+
+  protocol = argv[1];
+  if (!protocol) die_usage();
+
+  childargs = argv + 2;
+  if (!*childargs) die_usage();
+
+  tls_level = ucspitls_level();
+
+  for (i = 0; p[i].name; ++i)
+    if (case_equals(p[i].name,protocol))
+      doprotocol(p[i]);
+  die_usage();
+}
diff --git checknotroot.8 checknotroot.8
new file mode 100644
index 0000000..2c016a6
--- /dev/null
+++ checknotroot.8
@@ -0,0 +1,32 @@
+.TH CHECKNOTROOT 8 2018-12-01
+.SH NAME
+checknotroot \- refuse to run as UID 0
+.SH SYNOPSIS
+.B checknotroot
+.I prog
+.SH DESCRIPTION
+.B checknotroot
+ensures that another command can never be
+run as UID 0.
+
+.B checknotroot
+is most commonly placed after
+.B checkpassword
+and before its remaining arguments to ensure that if someone guesses the
+root password, they won't know they did.
+
+.SH "EXIT CODES"
+If run by UID 0,
+.B checknotroot
+exits 1.
+Otherwise it exits with the same code as
+.IR prog .
+.SH "EXAMPLES"
+See
+.IR https://schmonz.com/qmail/acceptutils .
+.SH "AUTHOR"
+.B Amitai Schleier <schmonz-web-acceptutils@schmonz.com>
+.SH "SEE ALSO"
+checkpassword(8),
+authup(8),
+fixsmtpio(8).
diff --git checknotroot.c checknotroot.c
new file mode 100644
index 0000000..0f6d887
--- /dev/null
+++ checknotroot.c
@@ -0,0 +1,31 @@
+#include "exit.h"
+#include "readwrite.h"
+#include "substdio.h"
+
+#include "acceptutils_unistd.h"
+
+char sserrbuf[SUBSTDIO_OUTSIZE];
+substdio sserr = SUBSTDIO_FDBUF(write,2,sserrbuf,sizeof sserrbuf);
+
+void errflush(char *s) {
+  substdio_puts(&sserr,"checknotroot: ");
+  substdio_puts(&sserr,s);
+  substdio_puts(&sserr,"\n");
+  substdio_flush(&sserr);
+}
+
+void die() { _exit(1); }
+void die_usage() { errflush("usage: checknotroot prog"); die(); }
+void die_root() { errflush("WAS RUNNING AS ROOT, TERMINATING"); die(); }
+
+int main(int argc,char **argv) {
+  char **childargs;
+ 
+  childargs = argv + 1;
+  if (!*childargs) die_usage();
+
+  if (unistd_getuid() == 0) die_root();
+ 
+  unistd_execvp(*childargs,childargs);
+  die();
+}
diff --git conf-check conf-check
new file mode 100644
index 0000000..566cfbc
--- /dev/null
+++ conf-check
@@ -0,0 +1,3 @@
+/opt/pkg
+
+This is the prefix where libcheck is installed.
diff --git fixsmtpio.8 fixsmtpio.8
new file mode 100644
index 0000000..709b6a6
--- /dev/null
+++ fixsmtpio.8
@@ -0,0 +1,254 @@
+.TH FIXSMTPIO 8 2018-12-01
+.SH NAME
+fixsmtpio \- filter SMTP I/O and exit status
+.SH SYNOPSIS
+.B fixsmtpio
+.I prog
+.SH DESCRIPTION
+.B fixsmtpio
+is a proxy for changing how an SMTP service behaves without changing its code.
+By default, with no configuration, it changes almost no observable behavior.
+In a typical configuration, it makes
+.B ofmipd
+or
+.B qmail-smtpd
+behave as though they were designed to run under
+.B authup
+by adjusting certain
+client requests,
+server responses,
+and exit codes.
+The
+.B "EXIT CODES"
+section is important enough to be all the way up here.
+.SH "EXIT CODES"
+.B authup
+expects
+.B fixsmtpio
+to exit 18
+when
+.I control/fixsmtpio
+exists and fails to parse.
+.B fixsmtpio
+does so.
+
+.B authup
+expects distinct exit codes for distinct conditions.
+.B checkpassword
+exits 1 when a password is unacceptable.
+.B ofmipd
+and
+.B qmail-smtpd
+exit 1 when any error occurs.
+.B fixsmtpio
+can and should be configured to exit
+.TP 3
+14
+when the server greeting code starts with 4 (temporary failure),
+.TP 3
+15
+when the server greeting code starts with 5 (permanent failure),
+.TP 3
+16
+when the server times out,
+and
+.TP 3
+0
+when the server receives EOF from the client.
+.SH "ENVIRONMENT VARIABLES"
+.B authup
+sets
+.B AUTHUP_USER
+in the environment.
+Many typical filter rules apply if and only if this variable is present.
+
+If
+.B UCSPITLS
+is set while running under
+.BR "sslserver -n" ,
+.B fixsmtpio
+supports STARTTLS.
+The special value
+.B !
+requires peers to negotiate TLS before doing much of anything else.
+The empty string is a more sensible choice for public SMTP service.
+.B fixsmtpio
+sets
+.B FIXSMTPIOTLS
+in the environment when TLS has been negotiated.
+
+If
+.B DISABLETLS
+is set,
+.B UCSPITLS
+will not be honored.
+
+If
+.B NOFIXSMTPIO
+is set,
+.B fixsmtpio
+will simply replace itself with
+.IR prog .
+
+If
+.B FIXSMTPIODEBUG
+is set,
+.B fixsmtpio
+will show its work.
+Lines are prefixed like so:
+.TP 3
+1: Request received from client
+.TP 3
+2: Request sent to server
+.TP 3
+3: Response received from server
+.TP 3
+4: Response sent to client
+.TP 3
+D: DATA received from client and sent to server as is
+
+.SH "FILTER RULES"
+Filter rules follow the format:
+
+.I [env]:event:[request-prepend]:response-line-glob:[exitcode]:[response]
+
+(Values other than
+.I response
+may not contain ":", the field separator.)
+.TP 5
+.I env
+.br
+(optional)
+Environment variable which must be present for the rule to apply.
+If empty, none is required.
+.TP 5
+.I event
+.br
+(required)
+SMTP verb (or "clienteof", "greeting", or "timeout") to which the rule applies.
+This field is matched case-insensitively.
+.TP 5
+.I request-prepend
+.br
+(optional)
+String to prepend to the request before passing it to the server.
+"NOOP " (with trailing space) causes the server to trivially accept a request,
+performing no action.
+If empty, the request is sent as is.
+.TP 5
+.I response-line-glob
+.br
+(required)
+.BR fnmatch(3) -style
+glob pattern, with no special options set.
+Each line of the response is compared against this glob.
+.TP 5
+.I exitcode
+.br
+(optional)
+Numeric code with which
+.B fixsmtpio
+is to exit immediately.
+If empty, it will exit whenever it normally would.
+If there are no rules specifying a value for this field,
+.B fixsmtpio
+will always exit with the same code as its child
+.IR prog .
+.TP 5
+.I response
+.br
+(optional)
+String that replaces every matching line of the server response.
+
+If empty, matching lines are removed.
+
+Strings starting with
+"&fixsmtpio"
+are reserved for special handling.
+
+"&fixsmtpio_noop"
+causes the response to be sent as is.
+
+"&fixsmtpio_fixup"
+computes the replacement string via an event-specific internal routine.
+(If no corresponding routine exists,
+.B fixsmtpio
+will refuse to run.)
+Routines exist for
+.TP 10
+.BR HELP :
+.br
+Prepend this program's home page,
+in an attempt to direct support requests to
+.BR AUTHOR .
+Please enable it!
+.TP 10
+.BR "greeting",
+.BR EHLO ,
+.BR HELO ,
+and
+.BR QUIT :
+.br
+Include
+.I smtpgreeting
+in the response.
+This is harmless with
+.BR qmail-smtpd ,
+which already does so,
+and brings
+.BR ofmipd 's
+behavior up to par.
+.TP 0
+Rules are applied in the order written. For instance, if two rules
+match, the later rule modifies the response returned by the
+earlier rule.
+If multiple matching rules for an event set
+.BR exitcode ,
+the last one wins.
+
+Not all rules (alone or in combination) make practical sense.
+An earlier rule can cause a later one to start (or stop) matching.
+Keep your configuration as simple as possible, and test it well.
+.SH "CONTROL FILES"
+.TP 5
+.I fixsmtpio
+Filter rules as described in
+.BR "FILTER RULES" .
+Default: none.
+.TP 5
+.I smtpgreeting
+SMTP greeting message.
+Default:
+.IR me ,
+if that is supplied;
+otherwise
+.B fixsmtpio
+will refuse to run.
+The first word of
+.I smtpgreeting
+should be the current host's name.
+.SH "COMPATIBILITY"
+.B fixsmtpio
+handles STARTTLS internally, terminating TLS in order to continue
+observing requests and responses and do its job.
+If you rely on STARTTLS support directly in
+.B qmail-smtpd
+via a patch,
+please share your use case with the author.
+.SH "EXAMPLES"
+See
+.IR https://schmonz.com/qmail/acceptutils .
+.SH "AUTHOR"
+.B Amitai Schleier <schmonz-web-acceptutils@schmonz.com>
+.SH "SEE ALSO"
+authup(8),
+sslserver(1),
+ucspi-tls(2),
+checkpassword(8),
+checknotroot(8),
+qmail-smtpd(8),
+ofmipd(8),
+qmail-qfilter-queue(8),
+fnmatch(3),
+fixcrio(1),
+spamdyke.
diff --git fixsmtpio.c fixsmtpio.c
new file mode 100644
index 0000000..a9b589e
--- /dev/null
+++ fixsmtpio.c
@@ -0,0 +1,33 @@
+#include "fixsmtpio.h"
+#include "fixsmtpio_die.h"
+#include "fixsmtpio_filter.h"
+#include "fixsmtpio_proxy.h"
+
+#include "acceptutils_stralloc.h"
+#include "acceptutils_unistd.h"
+
+static void load_smtp_greeting(stralloc *greeting,char *configfile) {
+  if (control_init() == -1) die_control();
+  if (control_rldef(greeting,configfile,1,(char *) 0) != 1) die_control();
+}
+
+static void cd_var_qmail() {
+  if (unistd_chdir(auto_qmail) == -1) die_control();
+}
+
+int main(int argc,char **argv) {
+  stralloc greeting = {0};
+  filter_rule *rules;
+
+  argv++; if (!*argv) die_usage();
+
+  if (env_get("NOFIXSMTPIO")) unistd_execvp(*argv,argv);
+
+  stralloc_set_die(die_nomem);
+
+  cd_var_qmail();
+  load_smtp_greeting(&greeting,"control/smtpgreeting");
+  rules = load_filter_rules();
+
+  start_proxy(&greeting,rules,argv);
+}
diff --git fixsmtpio.h fixsmtpio.h
new file mode 100644
index 0000000..77bd0d6
--- /dev/null
+++ fixsmtpio.h
@@ -0,0 +1,31 @@
+#include "alloc.h"
+#include "auto_qmail.h"
+#include "case.h"
+#include "control.h"
+#include "env.h"
+#include "fd.h"
+#include "scan.h"
+#include "str.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "wait.h"
+
+#define HOMEPAGE                 "https://schmonz.com/qmail/acceptutils"
+#define PROGNAME                 "fixsmtpio"
+
+#define EVENT_GREETING           "greeting"
+#define EVENT_TIMEOUT            "timeout"
+#define EVENT_CLIENTEOF          "clienteof"
+#define MUNGE_INTERNALLY         "&" PROGNAME "_fixup"
+#define REQUEST_PASSTHRU         ""
+#define REQUEST_NOOP             "NOOP "
+
+#define RESPONSELINE_NOCHANGE    "&" PROGNAME "_noop"
+
+#define EXIT_LATER_NORMALLY      -1
+#define EXIT_NOW_SUCCESS         0
+#define EXIT_NOW_TEMPFAIL        14
+#define EXIT_NOW_PERMFAIL        15
+/* sync with authup.c */
+#define EXIT_NOW_TIMEOUT         16
+#define EXIT_NOW_PARSEFAIL       18
diff --git fixsmtpio_control.c fixsmtpio_control.c
new file mode 100644
index 0000000..3ec1b6c
--- /dev/null
+++ fixsmtpio_control.c
@@ -0,0 +1,73 @@
+#include "fixsmtpio_control.h"
+#include "fixsmtpio_munge.h"
+
+#include "acceptutils_stralloc.h"
+
+static void parse_field(int *fields_seen, stralloc *value, filter_rule *rule) {
+  char *s;
+
+  (*fields_seen)++;
+
+  if (!value->len) return;
+
+  append0(value);
+  s = (char *)alloc(value->len);
+  str_copy(s, value->s);
+  blank(value);
+
+  switch (*fields_seen) {
+    case 1: rule->env                = s; break;
+    case 2: rule->event              = s; break;
+    case 3: rule->request_prepend    = s; break;
+    case 4: rule->response_line_glob = s; break;
+    case 5:
+      if (!scan_ulong(s,&rule->exitcode))
+        rule->exitcode = 777;
+                                          break;
+    case 6: rule->response           = s; break;
+  }
+}
+
+filter_rule *parse_control_line(char *line) {
+  filter_rule *rule = (filter_rule *)alloc(sizeof(filter_rule));
+  int line_length = str_len(line);
+  stralloc value = {0};
+  int fields_seen = 0;
+  int i;
+
+  rule->next                = 0;
+
+  rule->env                 = 0;
+  rule->event               = 0;
+  rule->request_prepend     = 0;
+  rule->response_line_glob  = 0;
+  rule->exitcode            = EXIT_LATER_NORMALLY;
+  rule->response            = 0;
+
+  for (i = 0; i < line_length; i++) {
+    char c = line[i];
+    if (':' == c && fields_seen < 5) parse_field(&fields_seen, &value, rule);
+    else append(&value, &c);
+  }
+  parse_field(&fields_seen, &value, rule);
+
+  if (fields_seen < 6)            return 0;
+  if (!rule->event)               return 0;
+  if (!rule->response_line_glob)  return 0;
+  if ( rule->exitcode > 255)      return 0;
+  if ( rule->response) {
+    if (!case_diffs(rule->event,"clienteof"))
+                                  return 0;
+    if (want_munge_internally(rule->response)
+        && !munge_line_fn(rule->event))
+                                  return 0;
+    if (str_start(rule->response,"&fixsmtpio")
+        && !want_munge_internally(rule->response)
+        && !want_leave_line_as_is(rule->response))
+                                  return 0;
+  } else {
+    rule->response = "";
+  }
+
+  return rule;
+}
diff --git fixsmtpio_control.h fixsmtpio_control.h
new file mode 100644
index 0000000..ea821a7
--- /dev/null
+++ fixsmtpio_control.h
@@ -0,0 +1,3 @@
+#include "fixsmtpio_filter.h"
+
+filter_rule *parse_control_line(char *);
diff --git fixsmtpio_die.c fixsmtpio_die.c
new file mode 100644
index 0000000..c628c5e
--- /dev/null
+++ fixsmtpio_die.c
@@ -0,0 +1,63 @@
+#include "fixsmtpio.h"
+#include "fixsmtpio_die.h"
+#include "readwrite.h"
+
+#include "acceptutils_stralloc.h"
+#include "acceptutils_unistd.h"
+
+static void die() { unistd_exit(1); }
+
+static char sserrbuf[SUBSTDIO_OUTSIZE];
+substdio sserr = SUBSTDIO_FDBUF(write,2,sserrbuf,sizeof sserrbuf);
+
+static void errflush3(const char *caller,const char *alloc_fn,char *s) {
+  substdio_puts(&sserr,PROGNAME ":");
+  if (caller) {
+    substdio_puts(&sserr,caller);
+    substdio_puts(&sserr,":");
+  }
+  if (alloc_fn) {
+    substdio_puts(&sserr,alloc_fn);
+    substdio_puts(&sserr,":");
+  }
+  substdio_puts(&sserr," ");
+  substdio_puts(&sserr,s);
+  substdio_putsflush(&sserr,"\n");
+}
+
+static void errflush(char *s) {
+  errflush3(0,0,s);
+}
+
+static void dieerrflush(char *s) {
+  errflush(s);
+  die();
+}
+
+void die_usage() { dieerrflush("usage: " PROGNAME " prog [ arg ... ]"); }
+void die_control(){dieerrflush("unable to read controls"); }
+void die_pipe()  { dieerrflush("unable to open pipe"); }
+void die_fork()  { dieerrflush("unable to fork"); }
+void die_exec()  { dieerrflush("unable to exec"); }
+void die_wait()  { dieerrflush("unable to wait for child"); }
+void die_kill()  { dieerrflush("unable to kill child"); }
+void die_crash() { dieerrflush("aack, child crashed"); }
+void die_read()  { dieerrflush("unable to read"); }
+void die_write() { dieerrflush("unable to write"); }
+void die_nomem(const char *caller,const char *alloc_fn) {
+  errflush3(caller,alloc_fn,"out of memory");
+  die();
+}
+void die_tls()   { dieerrflush("TLS temporarily not available"); }
+void die_parse() {    errflush("unable to parse control/fixsmtpio");
+                      unistd_exit(EXIT_NOW_PARSEFAIL); }
+
+void logit(stralloc *logstamp,char logprefix,stralloc *sa) {
+  if (!env_get("FIXSMTPIODEBUG")) return;
+
+  substdio_put (&sserr,logstamp->s,logstamp->len);
+  substdio_put (&sserr,&logprefix,1); substdio_puts(&sserr,": ");
+  substdio_put (&sserr,sa->s,sa->len);
+  if (!ends_with_newline(sa)) substdio_puts(&sserr,"\n");
+  substdio_flush(&sserr);
+}
diff --git fixsmtpio_die.h fixsmtpio_die.h
new file mode 100644
index 0000000..6874ba7
--- /dev/null
+++ fixsmtpio_die.h
@@ -0,0 +1,16 @@
+#include "stralloc.h"
+
+void die_usage(void);
+void die_control(void);
+void die_pipe(void);
+void die_fork(void);
+void die_exec(void);
+void die_wait(void);
+void die_kill(void);
+void die_crash(void);
+void die_read(void);
+void die_write(void);
+void die_nomem(const char *,const char *);
+void die_tls(void);
+void die_parse(void);
+void logit(stralloc *,char,stralloc *);
diff --git fixsmtpio_eventq.c fixsmtpio_eventq.c
new file mode 100644
index 0000000..97ed628
--- /dev/null
+++ fixsmtpio_eventq.c
@@ -0,0 +1,61 @@
+#include "alloc.h"
+#include "str.h"
+#include "fixsmtpio.h"
+#include "fixsmtpio_die.h"
+
+#include "fixsmtpio_eventq.h"
+
+#define NULL 0
+#include <sys/queue.h>
+
+typedef struct node {
+  const char *event;
+  TAILQ_ENTRY(node) nodes;
+} node_t;
+
+typedef TAILQ_HEAD(head_s, node) head_t;
+
+static head_t head;
+static int eventq_inited = 0;
+
+static void eventq_init() {
+  if (eventq_inited) return;
+  TAILQ_INIT(&head);
+  eventq_inited++;
+}
+
+static node_t *eventq_alloc_node() {
+  node_t *e = (node_t *)alloc(sizeof(node_t));
+  if (!e) die_nomem(__func__,"alloc");
+  return e;
+}
+
+static char *eventq_alloc_event(const char *event) {
+  char *s = (char *)alloc(sizeof(char) * (1 + str_len(event)));
+  if (!s) die_nomem(__func__,"alloc");
+  return s;
+}
+
+void eventq_put(const char *event) {
+  node_t *e;
+  eventq_init();
+  e = eventq_alloc_node();
+  e->event = event;
+  TAILQ_INSERT_TAIL(&head, e, nodes);
+}
+
+char *eventq_get() {
+  char *event;
+  node_t *e;
+  if (TAILQ_EMPTY(&head)) {
+    event = EVENT_TIMEOUT;
+  } else {
+    e = TAILQ_FIRST(&head);
+    event = eventq_alloc_event(e->event);
+    str_copy(event,e->event);
+    TAILQ_REMOVE(&head, e, nodes);
+    alloc_free(e);
+  }
+
+  return event;
+}
diff --git fixsmtpio_eventq.h fixsmtpio_eventq.h
new file mode 100644
index 0000000..7597fbc
--- /dev/null
+++ fixsmtpio_eventq.h
@@ -0,0 +1,2 @@
+void eventq_put(const char *);
+char *eventq_get(void);
diff --git fixsmtpio_filter.c fixsmtpio_filter.c
new file mode 100644
index 0000000..47591a2
--- /dev/null
+++ fixsmtpio_filter.c
@@ -0,0 +1,113 @@
+#include "fixsmtpio.h"
+#include "fixsmtpio_control.h"
+#include "fixsmtpio_filter.h"
+#include "fixsmtpio_die.h"
+#include "fixsmtpio_munge.h"
+#include "fixsmtpio_glob.h"
+
+#include "acceptutils_stralloc.h"
+
+int want_munge_internally(char *response) {
+  return !str_diff(MUNGE_INTERNALLY,response);
+}
+
+int want_leave_line_as_is(char *response) {
+  return !str_diff(RESPONSELINE_NOCHANGE,response);
+}
+
+int envvar_exists_if_needed(char *envvar) {
+  if (envvar) {
+    if (!str_diff("",envvar)) return 1;
+    if (env_get(envvar)) return 1;
+    return 0;
+  }
+  return 1;
+}
+
+int filter_rule_applies(filter_rule *rule,const char *event) {
+  return (event_matches(rule->event,event) && envvar_exists_if_needed(rule->env));
+}
+
+void munge_response_line(int lineno,
+                         stralloc *line,int *exitcode,
+                         stralloc *greeting,filter_rule *rules,const char *event,
+                         int tls_level,int in_tls) {
+  filter_rule *rule;
+  stralloc line0 = {0};
+
+  copy(&line0,line);
+  append0(&line0);
+
+  for (rule = rules; rule; rule = rule->next) {
+    if (!filter_rule_applies(rule,event)) continue;
+    if (!string_matches_glob(rule->response_line_glob,line0.s)) continue;
+    munge_exitcode(exitcode,rule);
+    if (want_munge_internally(rule->response))
+      munge_line_internally(line,lineno,greeting,event,tls_level,in_tls);
+    else if (!want_leave_line_as_is(rule->response))
+      copys(line,rule->response);
+  }
+  if (line->len) if (!ends_with_newline(line)) cats(line,"\r\n");
+}
+
+void munge_response(stralloc *response,int *exitcode,
+                    stralloc *greeting,filter_rule *rules,const char *event,
+                    int tls_level,int in_tls) {
+  stralloc munged = {0};
+  stralloc line = {0};
+  int lineno = 0;
+  int i;
+
+  for (i = 0; i < response->len; i++) {
+    append(&line,i + response->s);
+    if (response->s[i] == '\n' || i == response->len - 1) {
+      munge_response_line(lineno++,&line,exitcode,greeting,rules,event,tls_level,in_tls);
+      cat(&munged,&line);
+      blank(&line);
+    }
+  }
+
+  if (munged.len) reformat_multiline_response(&munged);
+  copy(response,&munged);
+}
+
+filter_rule *prepend_rule(filter_rule *next, filter_rule *rule) {
+  rule->next = next;
+  next = rule;
+  return next;
+}
+
+filter_rule *reverse_rules(filter_rule *rules) {
+  filter_rule *reversed_rules = 0;
+  filter_rule *temp;
+
+  while (rules) {
+    temp = rules;
+    rules = rules->next;
+    temp->next = reversed_rules;
+    reversed_rules = temp;
+  }
+
+  return reversed_rules;
+}
+
+filter_rule *load_filter_rules(void) {
+  filter_rule *backwards_rules = 0;
+
+  stralloc lines = {0};
+  int linestart;
+  int pos;
+
+  if (control_readfile(&lines,"control/fixsmtpio",0) == -1) die_control();
+
+  for (linestart = 0, pos = 0; pos < lines.len; pos++) {
+    if (lines.s[pos] == '\0') {
+      filter_rule *rule = parse_control_line(lines.s + linestart);
+      if (0 == rule) die_parse();
+      backwards_rules = prepend_rule(backwards_rules, rule);
+      linestart = pos + 1;
+    }
+  }
+
+  return reverse_rules(backwards_rules);
+}
diff --git fixsmtpio_filter.h fixsmtpio_filter.h
new file mode 100644
index 0000000..7a0b20b
--- /dev/null
+++ fixsmtpio_filter.h
@@ -0,0 +1,29 @@
+#ifndef _FIXSMTPIO_FILTER_H_
+#define _FIXSMTPIO_FILTER_H_
+
+#include "fixsmtpio.h"
+
+typedef struct filter_rule {
+  struct filter_rule *next;
+  char *env;
+  char *event;
+  char *request_prepend;
+  char *response_line_glob;
+  int   exitcode;
+  char *response;
+} filter_rule;
+
+int want_munge_internally(char *);
+int want_leave_line_as_is(char *);
+int envvar_exists_if_needed(char *);
+
+filter_rule * load_filter_rules(void);
+int filter_rule_applies(filter_rule *,const char *);
+
+int event_matches(char *,const char *);
+
+void munge_response(stralloc *,int *,stralloc *,filter_rule *,const char *,int,int);
+void munge_response_line(int,stralloc *,int *,stralloc *,filter_rule *,const char *,int,int);
+filter_rule *prepend_rule(filter_rule *,filter_rule *);
+
+#endif
diff --git fixsmtpio_glob.c fixsmtpio_glob.c
new file mode 100644
index 0000000..5b2c370
--- /dev/null
+++ fixsmtpio_glob.c
@@ -0,0 +1,8 @@
+#include "fixsmtpio_glob.h"
+
+/*
+ maybe this should be regex instead of glob?
+ */
+int string_matches_glob(char *glob,char *string) {
+  return 0 == fnmatch(glob,string,0);
+}
diff --git fixsmtpio_glob.h fixsmtpio_glob.h
new file mode 100644
index 0000000..98ed86b
--- /dev/null
+++ fixsmtpio_glob.h
@@ -0,0 +1,3 @@
+#include <fnmatch.h>
+
+int string_matches_glob(char *,char *);
diff --git fixsmtpio_munge.c fixsmtpio_munge.c
new file mode 100644
index 0000000..94ad0ce
--- /dev/null
+++ fixsmtpio_munge.c
@@ -0,0 +1,116 @@
+#include "fixsmtpio_munge.h"
+
+#include "acceptutils_stralloc.h"
+#include "acceptutils_ucspitls.h"
+
+void munge_exitcode(int *exitcode,filter_rule *rule) {
+  if (rule->exitcode != EXIT_LATER_NORMALLY) *exitcode = rule->exitcode;
+}
+
+void munge_greeting(stralloc *response,int lineno,stralloc *greeting,
+                    int tls_level,int in_tls) {
+  copys(response,"220 "); cat(response,greeting); cats(response," ESMTP");
+}
+
+void munge_helo(stralloc *response,int lineno,stralloc *greeting,
+                int tls_level,int in_tls) {
+  copys(response,"250 "); cat(response,greeting);
+}
+
+static int is_starttls_line(stralloc *response) {
+  return starts(response,"250-STARTTLS\r\n")
+      || starts(response,"250 STARTTLS\r\n");
+}
+
+void munge_ehlo(stralloc *response,int lineno,stralloc *greeting,
+                int tls_level,int in_tls) {
+  switch (lineno) {
+    case 0:
+      munge_helo(response,lineno,greeting,tls_level,in_tls);
+      break;
+    case 1:
+      if (is_starttls_line(response)) blank(response);
+      if (tls_level >= UCSPITLS_AVAILABLE && !in_tls && !env_get("AUTHUP_USER"))
+        prepends(response,"250-STARTTLS\r\n");
+      break;
+    default:
+      if (is_starttls_line(response)) blank(response);
+      break;
+  }
+}
+
+void munge_help(stralloc *response,int lineno,stralloc *greeting,
+                int tls_level,int in_tls) {
+  stralloc munged = {0};
+  copys(&munged,"214 " PROGNAME " home page: " HOMEPAGE "\r\n");
+  cat(&munged,response);
+  copy(response,&munged);
+}
+
+void munge_quit(stralloc *response,int lineno,stralloc *greeting,
+                int tls_level,int in_tls) {
+  copys(response,"221 "); cat(response,greeting);
+}
+
+void change_every_line_fourth_char_to_dash(stralloc *multiline) {
+  int pos = 0;
+  int i;
+  for (i = 0; i < multiline->len; i++) {
+    if (multiline->s[i] == '\n') pos = -1;
+    if (pos == 3) multiline->s[i] = '-';
+    pos++;
+  }
+}
+
+// copy to with authup.c:smtp_ehlo_format()
+void change_last_line_fourth_char_to_space(stralloc *multiline) {
+  int pos = 0;
+  int i;
+  for (i = multiline->len - 2; i >= 0; i--) {
+    if (multiline->s[i] == '\n') {
+      pos = i + 1;
+      break;
+    }
+  }
+  multiline->s[pos+3] = ' ';
+}
+
+void reformat_multiline_response(stralloc *response) {
+  change_every_line_fourth_char_to_dash(response);
+  change_last_line_fourth_char_to_space(response);
+}
+
+int event_matches(char *s,const char *s2) {
+  if (!s || !s2) return 0;
+  if (!str_len(s) || !str_len(s2)) return 0;
+  return !case_diffs(s,s2);
+}
+
+struct munge_command {
+  char *event;
+  void (*munger)();
+};
+
+struct munge_command m[] = {
+  { EVENT_GREETING, munge_greeting }
+, { "ehlo", munge_ehlo }
+, { "helo", munge_helo }
+, { "help", munge_help }
+, { "quit", munge_quit }
+, { 0, 0 }
+};
+
+void *munge_line_fn(const char *event) {
+  int i;
+  for (i = 0; m[i].event; ++i)
+    if (event_matches(m[i].event,event))
+      return m[i].munger;
+  return 0;
+}
+
+void munge_line_internally(stralloc *line,int lineno,
+                           stralloc *greeting,const char *event,
+                           int tls_level,int in_tls) {
+  void (*munger)() = munge_line_fn(event);
+  if (munger) munger(line,lineno,greeting,tls_level,in_tls);
+}
diff --git fixsmtpio_munge.h fixsmtpio_munge.h
new file mode 100644
index 0000000..511e080
--- /dev/null
+++ fixsmtpio_munge.h
@@ -0,0 +1,14 @@
+#include "fixsmtpio_filter.h"
+
+void munge_exitcode(int *,filter_rule *);
+void munge_greeting(stralloc *,int,stralloc *,int,int);
+void munge_helo(stralloc *,int,stralloc *,int,int);
+void munge_ehlo(stralloc *,int,stralloc *,int,int);
+void munge_help(stralloc *,int,stralloc *,int,int);
+void munge_quit(stralloc *,int,stralloc *,int,int);
+void reformat_multiline_response(stralloc *);
+int event_matches(char *,const char *);
+void *munge_line_fn(const char *);
+void munge_line_internally(stralloc *,int,stralloc *,const char *,int,int);
+void change_every_line_fourth_char_to_dash(stralloc *);
+void change_last_line_fourth_char_to_space(stralloc *);
diff --git fixsmtpio_proxy.c fixsmtpio_proxy.c
new file mode 100644
index 0000000..7b70163
--- /dev/null
+++ fixsmtpio_proxy.c
@@ -0,0 +1,414 @@
+#include <libgen.h>
+#include <signal.h>
+
+#include "fmt.h"
+
+#include "fixsmtpio_proxy.h"
+#include "fixsmtpio_readwrite.h"
+#include "fixsmtpio_die.h"
+#include "fixsmtpio_eventq.h"
+#include "fixsmtpio_filter.h"
+
+#include "acceptutils_stralloc.h"
+#include "acceptutils_ucspitls.h"
+#include "acceptutils_unistd.h"
+
+int ends_data(stralloc *r) {
+  int len = r->len;
+
+  if (!len                      ) return 0;
+  if (       r->s[--len] != '\n') return 0;
+  if (len && r->s[--len] != '\r') ++len;
+  if (!len                      ) return 0;
+  if (       r->s[--len] !=  '.') return 0;
+  if (len && r->s[--len] != '\n') return 0;
+
+  return 1;
+}
+
+static int find_first_space(stralloc *request) {
+  int i;
+  for (i = 0; i < request->len; i++) if (request->s[i] == ' ') return i;
+  return -1;
+}
+
+void strip_last_eol(stralloc *sa) {
+  if (sa->len > 0 && sa->s[sa->len-1] == '\n') sa->len--;
+  if (sa->len > 0 && sa->s[sa->len-1] == '\r') sa->len--;
+}
+
+static void all_verb_no_arg(stralloc *verb,stralloc *arg,stralloc *request) {
+  copy(verb,request);
+  strip_last_eol(verb);
+  blank(arg);
+}
+
+static void verb_and_arg(stralloc *verb,stralloc *arg,int pos,stralloc *request) {
+  copyb(verb,request->s,pos-1);
+  copyb(arg,request->s+pos,request->len-pos);
+  strip_last_eol(arg);
+}
+
+void parse_client_request(stralloc *verb,stralloc *arg,stralloc *request) {
+  int pos;
+  pos = find_first_space(request);
+  if (pos == -1)
+    all_verb_no_arg(verb,arg,request);
+  else
+    verb_and_arg(verb,arg,++pos,request);
+}
+
+static int need_starttls_first(int tls_level,int in_tls,char *event) {
+  return tls_level >= UCSPITLS_REQUIRED
+    && !in_tls
+    && !event_matches(EVENT_GREETING,event)
+    && !event_matches(EVENT_TIMEOUT,event)
+    && !event_matches(EVENT_CLIENTEOF,event)
+    && !event_matches("noop",event)
+    && !event_matches("ehlo",event)
+    && !event_matches("starttls",event)
+    && !event_matches("quit",event);
+}
+
+void construct_proxy_request(stralloc *proxy_request,
+                             filter_rule *rules,
+                             char *event,stralloc *arg,
+                             stralloc *client_request,
+                             int tls_level,
+                             int *want_tls,int in_tls,
+                             int *want_data) {
+  filter_rule *rule;
+
+  for (rule = rules; rule; rule = rule->next)
+    if (rule->request_prepend && filter_rule_applies(rule,event))
+      prepends(proxy_request,rule->request_prepend);
+  if (need_starttls_first(tls_level,in_tls,event))
+    prepends(proxy_request,REQUEST_NOOP PROGNAME " STARTTLS FIRST ");
+  else if (event_matches("starttls",event)) {
+    *want_tls = 1;
+    if (tls_level >= UCSPITLS_AVAILABLE) {
+      if (in_tls)
+        prepends(proxy_request,REQUEST_NOOP PROGNAME " STARTTLS AGAIN ");
+      else
+        prepends(proxy_request,REQUEST_NOOP PROGNAME " STARTTLS BEGIN ");
+    } else {
+      prepends(proxy_request,REQUEST_NOOP PROGNAME " STARTTLS BLOCK ");
+    }
+  }
+  else if (event_matches("data",event))
+    *want_data = 1;
+  cat(proxy_request,client_request);
+}
+
+static int accepted_data(stralloc *response) { return starts(response,"354 "); }
+
+void construct_proxy_response(stralloc *proxy_response,
+                              stralloc *greeting,
+                              filter_rule *rules,
+                              char *event,
+                              stralloc *server_response,
+                              int *proxy_exitcode,
+                              int tls_level,
+                              int want_tls,int in_tls,
+                              int *want_data,int *in_data) {
+  if (event_matches("data",event) && *want_data) {
+    *want_data = 0;
+    if (accepted_data(server_response)) {
+      eventq_put("in_data");
+      *in_data = 1;
+    }
+  }
+
+  if (event_matches("starttls",event) && want_tls) {
+    if (tls_level < UCSPITLS_AVAILABLE || in_tls)
+      copys(proxy_response,"502 unimplemented (#5.5.1)\r\n");
+    else
+      copys(proxy_response,"220 Ready to start TLS (#5.7.0)\r\n");
+    return;
+  }
+  if (need_starttls_first(tls_level,in_tls,event))
+    copys(proxy_response,"530 Must start TLS first (#5.7.0)\r\n");
+  else
+    copy(proxy_response,server_response);
+    munge_response(proxy_response,proxy_exitcode,greeting,rules,event,tls_level,in_tls);
+}
+
+int get_one(const char *caller,stralloc *one,stralloc *pile,int (*fn)(stralloc *)) {
+  stralloc caller_sa = {0};
+  int got_one = 0;
+  stralloc next_pile = {0};
+  int pos = 0;
+  int i;
+
+  for (i = pos; i < pile->len; i++) {
+    if (pile->s[i] == '\n') {
+      stralloc line = {0};
+
+      catb(&line,pile->s+pos,i+1-pos);
+      pos = i+1;
+      cat(one,&line);
+
+      if (!fn || fn(&line)) {
+        got_one = 1;
+        break;
+      }
+    }
+  }
+
+  if (got_one) {
+    copys(&caller_sa,(char *)caller);
+    cats(&caller_sa,":");
+    cats(&caller_sa,(char *)__func__);
+    append0(&caller_sa);
+
+    contextlogging_copyb(caller_sa.s,&next_pile,pile->s+pos,pile->len-pos);
+    contextlogging_copy(caller_sa.s,pile,&next_pile);
+    blank(&next_pile);
+
+    blank(&caller_sa);
+  } else {
+    blank(one);
+  }
+
+  return got_one;
+}
+
+int get_one_request(stralloc *one,stralloc *pile) {
+  return get_one(__func__,one,pile,0);
+}
+
+int is_last_line_of_response(stralloc *line) {
+  return line->len >= 4 && line->s[3] == ' ';
+}
+
+int get_one_response(stralloc *one,stralloc *pile) {
+  return get_one(__func__,one,pile,&is_last_line_of_response);
+}
+
+static void handle_data_specially(stralloc *data,int *in_data,stralloc *logstamp) {
+  logit(logstamp,'D',data);
+  if (ends_data(data))
+    *in_data = 0;
+}
+
+static void handle_request(stralloc *proxy_request,stralloc *request,int tls_level,int *want_tls,int in_tls,int *want_data,filter_rule *rules,stralloc *logstamp) {
+  stralloc event = {0}, verb = {0}, arg = {0};
+
+  logit(logstamp,'1',request);
+  parse_client_request(&verb,&arg,request);
+  copy(&event,&verb);
+  append0(&event);
+  eventq_put(event.s);
+  construct_proxy_request(proxy_request,rules,
+                          event.s,&arg,
+                          request,
+                          tls_level,
+                          want_tls,in_tls,
+                          want_data);
+  blank(request);
+  logit(logstamp,'2',proxy_request);
+}
+
+static void handle_response(stralloc *proxy_response,int *exitcode,stralloc *response,int tls_level,int want_tls,int in_tls,int *want_data,int *in_data,filter_rule *rules,stralloc *greeting,stralloc *logstamp) {
+  char *event;
+  logit(logstamp,'3',response);
+  event = eventq_get();
+  construct_proxy_response(proxy_response,
+                           greeting,rules,event,
+                           response,
+                           exitcode,
+                           tls_level,
+                           want_tls,in_tls,
+                           want_data,in_data);
+  logit(logstamp,'4',proxy_response);
+  alloc_free(event);
+  blank(response);
+}
+
+static void use_as_stdin(int fd)  { if (fd_move(0,fd) == -1) die_pipe(); }
+static void use_as_stdout(int fd) { if (fd_move(1,fd) == -1) die_pipe(); }
+
+static void make_pipe(int *from,int *to) {
+  int pi[2];
+  if (unistd_pipe(pi) == -1) die_pipe();
+  *from = pi[0];
+  *to = pi[1];
+}
+
+static void be_proxied(int from_proxy,int to_proxy,
+                       int from_proxied,int to_proxied,
+                       char **argv) {
+  unistd_close(from_proxied);
+  unistd_close(to_proxied);
+  use_as_stdin(from_proxy);
+  use_as_stdout(to_proxy);
+  unistd_execvp(*argv,argv);
+  die_exec();
+}
+
+static void teardown_and_exit(int exitcode,int child,filter_rule *rules,
+                              int from_server,int to_server) {
+  int wstat;
+
+  unistd_close(from_server);
+  unistd_close(to_server);
+
+  if (wait_pid(&wstat,child) == -1) die_wait();
+  if (wait_crashed(wstat)) die_crash();
+
+  if (exitcode == EXIT_LATER_NORMALLY) unistd_exit(wait_exitcode(wstat));
+  else unistd_exit(exitcode);
+}
+
+static char *format_pid(unsigned int pid) {
+  char pidbuf[FMT_ULONG];
+  stralloc sa = {0};
+  if (!sa.len) {
+    int len = fmt_ulong(pidbuf,pid);
+    if (len) copyb(&sa,pidbuf,len);
+    append0(&sa);
+  }
+  return sa.s;
+}
+
+static void prepare_logstamp(stralloc *sa,int kid_pid,char *kid_name) {
+  copys(sa,PROGNAME " ");
+  cats(sa,format_pid(unistd_getpid())); cats(sa," ");
+  cats(sa,kid_name);                    cats(sa," ");
+  cats(sa,format_pid(kid_pid));         cats(sa," ");
+}
+
+static void adjust_proxy_for_new_kid(int from_proxy,int to_proxy,
+                                     stralloc *logstamp,
+                                     int kid_pid,char *kid_name) {
+  unistd_close(from_proxy);
+  unistd_close(to_proxy);
+  prepare_logstamp(logstamp,kid_pid,kid_name);
+  eventq_put(EVENT_GREETING);
+}
+
+static void be_proxy(int from_client,int to_client,
+                     int from_proxy,int to_proxy,
+                     int from_server,int to_server,
+                     stralloc *greeting,filter_rule *rules,
+                     int kid_pid,char **argv) {
+  int exitcode;
+  stralloc logstamp = {0};
+
+  adjust_proxy_for_new_kid(from_proxy,to_proxy,
+                           &logstamp,
+                           kid_pid,basename(argv[0]));
+
+  exitcode = read_and_process_until_either_end_closes(from_client,to_server,
+                                                      from_server,to_client,
+                                                      greeting,rules,
+                                                      &logstamp,
+                                                      &kid_pid,argv);
+  teardown_and_exit(exitcode,kid_pid,rules,from_server,to_server);
+}
+
+static void stop_kid(int kid_pid,int from_server,int to_server) {
+  unistd_close(from_server);
+  unistd_close(to_server);
+  if (-1 == kill(kid_pid,SIGTERM)) die_kill();
+}
+
+static void start_kid(int *kid_pid,int *from_server,int *to_server,
+                      stralloc *logstamp,char **argv) {
+  int from_proxy, to_proxy;
+
+  make_pipe(&from_proxy,to_server);
+  make_pipe(from_server,&to_proxy);
+
+  if ((*kid_pid = unistd_fork()))
+    adjust_proxy_for_new_kid(from_proxy,to_proxy,
+                             logstamp,
+                             *kid_pid,basename(argv[0]));
+  else if (*kid_pid == 0)
+    be_proxied(from_proxy,to_proxy,
+               *from_server,*to_server,
+               argv);
+  else
+    die_fork();
+}
+
+void start_proxy(stralloc *greeting,filter_rule *rules,char **argv) {
+  int from_client = 0;
+  int from_proxy, to_server;
+  int from_server, to_proxy;
+  int to_client = 1;
+  int kid_pid;
+
+  make_pipe(&from_proxy,&to_server);
+  make_pipe(&from_server,&to_proxy);
+
+  if ((kid_pid = unistd_fork()))
+    be_proxy(from_client,to_client,
+             from_proxy,to_proxy,
+             from_server,to_server,
+             greeting,rules,
+             kid_pid,argv);
+  else if (kid_pid == 0)
+    be_proxied(from_proxy,to_proxy,
+               from_server,to_server,
+               argv);
+  else
+    die_fork();
+}
+
+int read_and_process_until_either_end_closes(int from_client,int to_server,
+                                             int from_server,int to_client,
+                                             stralloc *greeting,
+                                             filter_rule *rules,
+                                             stralloc *logstamp,
+                                             int *kid_pid,char **argv) {
+  char     buf               [SUBSTDIO_INSIZE];
+  int      exitcode         = EXIT_LATER_NORMALLY;
+  int      tls_level        = ucspitls_level(),
+           want_tls         =  0, in_tls  = 0,
+           want_data        =  0, in_data = 0;
+  stralloc client_requests  = {0}, one_request  = {0}, proxy_request  = {0},
+           server_responses = {0}, one_response = {0}, proxy_response = {0};
+
+  for (;;) {
+    if (!block_efficiently_until_can_read_either(from_client,from_server)) break;
+
+    if (can_read(from_client)) {
+      if (!safeappend(&client_requests,from_client,buf,sizeof buf)) {
+        munge_response_line(0,&client_requests,&exitcode,greeting,rules,EVENT_CLIENTEOF,tls_level,in_tls);
+        break;
+      }
+      while (client_requests.len) {
+        if (in_data) {
+          handle_data_specially(&client_requests,&in_data,logstamp);
+          safewrite(to_server,&client_requests);
+        } else if (get_one_request(&one_request,&client_requests)) {
+          handle_request(&proxy_request,&one_request,tls_level,&want_tls,in_tls,&want_data,rules,logstamp);
+          safewrite(to_server,&proxy_request);
+        }
+      }
+    }
+
+    if (can_read(from_server)) {
+      if (!safeappend(&server_responses,from_server,buf,sizeof buf)) break;
+      while (server_responses.len && exitcode == EXIT_LATER_NORMALLY && get_one_response(&one_response,&server_responses)) {
+        handle_response(&proxy_response,&exitcode,&one_response,tls_level,want_tls,in_tls,&want_data,&in_data,rules,greeting,logstamp);
+        safewrite(to_client,&proxy_response);
+        if (want_tls) {
+          want_tls = 0;
+          if (tls_level >= UCSPITLS_AVAILABLE && !in_tls) {
+            if (!tls_init() || !tls_info(die_nomem)) die_tls();
+            if (!env_put("FIXSMTPIOTLS=1")) die_nomem(__func__,"env_put");
+            stop_kid(*kid_pid,from_server,to_server);
+            start_kid(kid_pid,&from_server,&to_server,logstamp,argv);
+            in_tls = 1;
+          }
+        }
+      }
+    }
+
+    if (exitcode != EXIT_LATER_NORMALLY) break;
+  }
+
+  return exitcode;
+}
diff --git fixsmtpio_proxy.h fixsmtpio_proxy.h
new file mode 100644
index 0000000..9a7d6a1
--- /dev/null
+++ fixsmtpio_proxy.h
@@ -0,0 +1,11 @@
+#include "fixsmtpio.h"
+#include "fixsmtpio_filter.h"
+
+void strip_last_eol(stralloc *);
+int ends_data(stralloc *);
+int is_last_line_of_response(stralloc *);
+void parse_client_request(stralloc *,stralloc *,stralloc *);
+int get_one_response(stralloc *,stralloc *);
+int read_and_process_until_either_end_closes(int,int,int,int,stralloc *,filter_rule *,stralloc *,int *,char **);
+void construct_proxy_request(stralloc *,filter_rule *,char *,stralloc *,stralloc *,int,int *,int,int *);
+void start_proxy(stralloc *,filter_rule *,char **);
diff --git fixsmtpio_readwrite.c fixsmtpio_readwrite.c
new file mode 100644
index 0000000..42ffa9f
--- /dev/null
+++ fixsmtpio_readwrite.c
@@ -0,0 +1,46 @@
+#include "fixsmtpio_readwrite.h"
+#include "fixsmtpio_die.h"
+#include "error.h"
+#include "readwrite.h"
+#include "select.h"
+
+#include "acceptutils_stralloc.h"
+
+fd_set fds;
+
+static void want_to_read(int fd1,int fd2) {
+  FD_ZERO(&fds);
+  FD_SET(fd1,&fds);
+  FD_SET(fd2,&fds);
+}
+
+int can_read(int fd) { return FD_ISSET(fd,&fds); }
+
+static int max(int a,int b) { return a > b ? a : b; }
+
+int block_efficiently_until_can_read_either(int fd1,int fd2) {
+  int ready;
+  want_to_read(fd1,fd2);
+  ready = select(1+max(fd1,fd2),&fds,(fd_set *)0,(fd_set *)0,(struct timeval *) 0);
+  if (ready == -1 && errno != error_intr) die_read();
+  return ready;
+}
+
+static int saferead(int fd,char *buf,int len) {
+  int r;
+  r = read(fd,buf,len);
+  if (r == -1) if (errno != error_intr) die_read();
+  return r;
+}
+
+int safeappend(stralloc *sa,int fd,char *buf,int len) {
+  int r;
+  r = saferead(fd,buf,len);
+  catb(sa,buf,r);
+  return r;
+}
+
+void safewrite(int fd,stralloc *sa) {
+  if (write(fd,sa->s,sa->len) == -1) die_write();
+  blank(sa);
+}
diff --git fixsmtpio_readwrite.h fixsmtpio_readwrite.h
new file mode 100644
index 0000000..b1d77b9
--- /dev/null
+++ fixsmtpio_readwrite.h
@@ -0,0 +1,6 @@
+#include "stralloc.h"
+
+int can_read(int);
+int block_efficiently_until_can_read_either(int,int);
+int safeappend(stralloc *,int,char *,int);
+void safewrite(int,stralloc *);
diff --git make-compile99.sh make-compile99.sh
new file mode 100644
index 0000000..5699fcf
--- /dev/null
+++ make-compile99.sh
@@ -0,0 +1 @@
+echo exec "$CC" -std=c99 -c '${1+"$@"}'
diff --git qmail-qfilter-addtlsheader.8 qmail-qfilter-addtlsheader.8
new file mode 100644
index 0000000..d4714de
--- /dev/null
+++ qmail-qfilter-addtlsheader.8
@@ -0,0 +1,53 @@
+.TH QMAIL-QFILTER-ADDTLSHEADER 8 2018-12-01
+.SH NAME
+qmail-qfilter-addtlsheader \- Add Received: header with TLS parameters
+.SH SYNOPSIS
+.B qmail-qfilter-addtlsheader
+.SH DESCRIPTION
+.B qmail-qfilter-addtlsheader
+takes a message (headers and body) on standard input
+and prints it to standard output.
+If
+.B UCSPITLS
+connection details are available, it prepends a
+.B Received:
+header.
+.PP
+.B qmail-qfilter-addtlsheader
+is intended to be invoked by
+.BR qmail-qfilter .
+It is typically used under
+.B authup
+or
+.B fixsmtpio
+(via
+.BR qmail-qfilter-queue ).
+.SH "ENVIRONMENT VARIABLES"
+If both
+.B SSL_CIPHER
+and
+.B SSL_PROTOCOL
+are set,
+.B qmail-qfilter-addtlsheader
+will add its header.
+.PP
+If either
+.B FIXSMTPIOTLS
+or
+.B AUTHUP_USER
+is set, the header will mention
+.BR fixsmtpio (8)
+or
+.BR authup (8),
+respectively.
+.SH "EXAMPLES"
+See
+.IR https://schmonz.com/qmail/acceptutils .
+.SH "AUTHOR"
+.B Amitai Schleier <schmonz-web-acceptutils@schmonz.com>
+.SH "SEE ALSO"
+.BR ucspi-tls(2),
+.BR qmail-qfilter(1),
+.BR authup(8),
+.BR fixsmtpio(8),
+.BR qmail-qfilter-queue(8).
diff --git qmail-qfilter-addtlsheader.c qmail-qfilter-addtlsheader.c
new file mode 100755
index 0000000..12e8b0d
--- /dev/null
+++ qmail-qfilter-addtlsheader.c
@@ -0,0 +1,54 @@
+#include "datetime.h"
+#include "date822fmt.h"
+#include "env.h"
+#include "now.h"
+#include "readwrite.h"
+#include "substdio.h"
+
+static char inbuf[SUBSTDIO_INSIZE];
+static substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
+static char outbuf[SUBSTDIO_OUTSIZE];
+static substdio ssout = SUBSTDIO_FDBUF(write,1,outbuf,sizeof(outbuf));
+
+static void out(char *s) { substdio_puts(&ssout,s); }
+
+static char datebuf[DATE822FMT];
+
+static void set_now(char *datebuf) {
+  struct datetime dt;
+  datetime_tai(&dt,now());
+  date822fmt(datebuf,&dt);
+}
+
+static void perhaps_write_tls_header() {
+  char *ssl_cipher, *ssl_protocol, *authup_user;
+
+  if ((ssl_cipher = env_get("SSL_CIPHER"))
+    && (ssl_protocol = env_get("SSL_PROTOCOL"))) {
+    out("Received: (ucspitls");
+    if (env_get("FIXSMTPIOTLS")) {
+      out(" acceptutils fixsmtpio");
+    } else if ((authup_user = env_get("AUTHUP_USER"))) {
+      out(" acceptutils authup ");
+      out(authup_user);
+    }
+    out(  " "); out(ssl_protocol);
+    out(  " "); out(ssl_cipher);
+    set_now(datebuf);
+    out("); "); out(datebuf);
+    substdio_flush(&ssout);
+  }
+}
+
+static void copy_stdin_to_stdout() {
+  int i;
+
+  while ((i = substdio_get(&ssin,inbuf,sizeof(inbuf))) > 0)
+    substdio_putflush(&ssout,inbuf,i);
+}
+
+int main(void) {
+  perhaps_write_tls_header();
+  copy_stdin_to_stdout();
+  return 0;
+}
diff --git reup.8 reup.8
new file mode 100644
index 0000000..1d2c8a5
--- /dev/null
+++ reup.8
@@ -0,0 +1,58 @@
+.TH REUP 8 2018-12-01
+.SH NAME
+reup \- run a program repeatedly
+.SH SYNOPSIS
+.B reup
+[
+.B \-t \fItries
+]
+.I prog
+.SH DESCRIPTION
+.B reup
+is typically used to invoke
+.B authup
+so that a user is given more than one authentication attempt before being disconnected.
+
+.B reup
+prints an error message if
+.I prog
+crashes or can't be started.
+.SH "EXIT CODES"
+.B reup
+stops retrying as soon as
+
+-
+.I prog
+exits 0 (success), or
+
+-
+.I prog
+exits 12 (permanent failure), or
+
+-
+.B reup
+reaches its retry limit.
+
+.B reup
+then exits with the same code as
+.IR prog .
+.SH "ENVIRONMENT VARIABLES"
+Child processes can see how many tries have been made in
+.BR REUP .
+.SH "OPTIONS"
+.TP
+.B \-t \fItries
+Run
+.B prog
+no more than
+.I tries
+times.
+Default: 0 (unlimited).
+.SH "EXAMPLES"
+See
+.IR https://schmonz.com/qmail/acceptutils .
+.SH "AUTHOR"
+.B Amitai Schleier <schmonz-web-acceptutils@schmonz.com>
+.SH "SEE ALSO"
+authup(8),
+fixsmtpio(8).
diff --git reup.c reup.c
new file mode 100644
index 0000000..e52334c
--- /dev/null
+++ reup.c
@@ -0,0 +1,120 @@
+#include "env.h"
+#include "fmt.h"
+#include "readwrite.h"
+#include "scan.h"
+#include "sgetopt.h"
+#include "str.h"
+#include "substdio.h"
+#include "wait.h"
+
+#include "acceptutils_unistd.h"
+
+#define PROGNAME "reup"
+
+static void die() { unistd_exit(1); }
+
+static char sserrbuf[SUBSTDIO_OUTSIZE];
+static substdio sserr = SUBSTDIO_FDBUF(write,2,sserrbuf,sizeof sserrbuf);
+
+static void out(char *s) { substdio_puts(&sserr,s); }
+
+static void dieerrflush(char *s) {
+  out(PROGNAME ": "); out(s);
+  substdio_putsflush(&sserr,"\n");
+  die();
+}
+
+static void die_usage() { dieerrflush("usage: " PROGNAME " [ -t tries ] prog"); }
+static void die_fork()  { dieerrflush("unable to fork"); }
+static void die_nomem() { dieerrflush("out of memory"); }
+
+static char intstr[FMT_ULONG];
+static void format_intstr(int myint) {
+  str_copy(intstr + fmt_ulong(intstr,myint),"");
+}
+
+static void logtry(int mypid,char *childprogname,int childpid,
+                   char *attempt,int maxtries,int exitcode) {
+  out(PROGNAME);
+  format_intstr(mypid);
+  out(" ");  out(intstr);
+  out(" ");  out(childprogname);
+  format_intstr(childpid);
+  out(" ");  out(intstr);
+  out(" ("); out(attempt);
+  format_intstr(maxtries);
+  out("/");  out(intstr);
+  out("):"); out(" exit");
+  format_intstr(exitcode);
+  out(" ");  out(intstr);
+  substdio_putsflush(&sserr,"\n");
+}
+
+static int do_try(int attempt,int maxtries,char **childargs) {
+  int childpid;
+  int wstat;
+  int exitcode;
+  char reup[FMT_ULONG];
+
+  str_copy(reup + fmt_ulong(reup,attempt),"");
+
+  switch ((childpid = unistd_fork())) {
+    case -1:
+      die_fork();
+    case 0:
+      if (!env_put2("REUP",reup)) die_nomem();
+      unistd_execvp(*childargs,childargs);
+      die(); // log something I guess
+  }
+
+  if (wait_pid(&wstat,childpid) == -1) die(); //die why? log something
+  if (wait_crashed(wstat)) die(); //die why? log something
+
+  exitcode = wait_exitcode(wstat);
+  logtry(unistd_getpid(),childargs[0],childpid,reup,maxtries,exitcode);
+
+  return exitcode;
+}
+
+static int keep_trying(int attempt,int max) {
+  if (max == 0) return 1;
+  if (attempt <= max) return 1;
+  return 0;
+}
+
+static int stop_trying(int exitcode) {
+  switch (exitcode) {
+    case 0:  return 1;
+    case 12: return 1;
+    default: return 0;
+  }
+}
+
+int main(int argc,char **argv) {
+  int exitcode;
+  int opt;
+  int tries;
+  int i;
+
+  tries = 0;
+  while ((opt = getopt(argc,argv,"t:")) != opteof) {
+    switch (opt) {
+      case 't':
+        if (!scan_ulong(optarg,&tries)) die_usage();
+        break;
+      default:
+        die_usage();
+    }
+  }
+  argc -= optind;
+  argv += optind;
+
+  if (!*argv) die_usage();
+
+  for (i = 1; keep_trying(i,tries); i++) {
+    exitcode = do_try(i,tries,argv);
+    if (stop_trying(exitcode)) unistd_exit(exitcode);
+  }
+
+  unistd_exit(exitcode);
+}
diff --git test_acceptutils_stralloc.c test_acceptutils_stralloc.c
new file mode 100644
index 0000000..6f1f8d9
--- /dev/null
+++ test_acceptutils_stralloc.c
@@ -0,0 +1,31 @@
+#include "check.h"
+
+#include "acceptutils_stralloc.h"
+
+void assert_prepends(const char *input, char *prepend, const char *expected_output) {
+  stralloc sa = {0}; stralloc_copys(&sa, input);
+
+  prepends(&sa, prepend);
+
+  stralloc_0(&sa);
+  ck_assert_str_eq(sa.s, expected_output);
+}
+
+START_TEST (test_prepends)
+{
+  assert_prepends("", "", "");
+  assert_prepends("", "foo", "foo");
+  assert_prepends("bar", "", "bar");
+  assert_prepends("baz", "foo bar", "foo barbaz");
+  assert_prepends("baz quux", "foo bar ", "foo bar baz quux");
+  assert_prepends(" baz quux", "foo bar", "foo bar baz quux");
+}
+END_TEST
+
+TCase *tc_stralloc(void) {
+  TCase *tc = tcase_create("");
+
+  tcase_add_test(tc, test_prepends);
+
+  return tc;
+}
diff --git test_fixsmtpio.c test_fixsmtpio.c
new file mode 100644
index 0000000..68bea62
--- /dev/null
+++ test_fixsmtpio.c
@@ -0,0 +1,41 @@
+#include "check.h"
+#include <stdlib.h>
+
+extern TCase *tc_stralloc(void);
+extern TCase *tc_control(void);
+extern TCase *tc_eventq(void);
+extern TCase *tc_filter(void);
+extern TCase *tc_glob(void);
+extern TCase *tc_munge(void);
+extern TCase *tc_proxy(void);
+
+Suite * fixsmtpio_suite(void)
+{
+  Suite *s = suite_create("fixsmtpio");
+
+  suite_add_tcase(s, tc_stralloc());
+  suite_add_tcase(s, tc_control());
+  suite_add_tcase(s, tc_eventq());
+  suite_add_tcase(s, tc_filter());
+  suite_add_tcase(s, tc_glob());
+  suite_add_tcase(s, tc_munge());
+  suite_add_tcase(s, tc_proxy());
+
+  return s;
+}
+
+int main(void)
+{
+  int number_failed;
+  Suite *s;
+  SRunner *sr;
+
+  s = fixsmtpio_suite();
+  sr = srunner_create(s);
+
+  srunner_set_tap(sr, "-");
+  srunner_run_all(sr, CK_SILENT);
+  number_failed = srunner_ntests_failed(sr);
+  srunner_free(sr);
+  return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git test_fixsmtpio_control.c test_fixsmtpio_control.c
new file mode 100644
index 0000000..2e4f8e1
--- /dev/null
+++ test_fixsmtpio_control.c
@@ -0,0 +1,200 @@
+#include "check.h"
+
+#include "fixsmtpio_control.h"
+
+#define assert_str_null_or_eq(s1,s2) \
+  if (s1 == NULL) \
+    ck_assert_ptr_null(s2); \
+  else \
+    ck_assert_str_eq(s1, s2);
+
+void assert_parsed_line(char *input,
+                        char *env,char *event,char *request_prepend,
+                        char *response_line_glob,int exitcode,char *response) {
+  filter_rule *rule = parse_control_line(input);
+
+  ck_assert_ptr_nonnull(rule);
+  ck_assert_ptr_null(rule->next);
+  assert_str_null_or_eq(env, rule->env);
+  assert_str_null_or_eq(event, rule->event);
+  assert_str_null_or_eq(request_prepend, rule->request_prepend);
+  assert_str_null_or_eq(response_line_glob, rule->response_line_glob);
+  ck_assert_int_eq(exitcode, rule->exitcode);
+  assert_str_null_or_eq(response, rule->response);
+}
+
+void assert_non_parsed_line(char *input) {
+  ck_assert_ptr_null(parse_control_line(input));
+}
+
+START_TEST (test_reject_blank_line) {
+  assert_non_parsed_line(
+    ""
+  );
+} END_TEST
+
+START_TEST (test_reject_just_a_comma) {
+  assert_non_parsed_line(
+    ","
+  );
+} END_TEST
+
+START_TEST (test_reject_just_a_colon) {
+  assert_non_parsed_line(
+    ":"
+  );
+} END_TEST
+
+START_TEST (test_accept_empty_env) {
+  assert_parsed_line(
+    ":event:prepend:glob:55:response",
+    NULL,"event","prepend","glob",55,"response"
+  );
+} END_TEST
+
+START_TEST (test_reject_empty_event) {
+  assert_non_parsed_line(
+    "env::prepend:glob:55:response"
+  );
+} END_TEST
+
+START_TEST (test_accept_invented_event) {
+  assert_parsed_line(
+    "env:flibbertigibbet:prepend:glob:55:response",
+    "env","flibbertigibbet","prepend","glob",55,"response"
+  );
+} END_TEST
+
+START_TEST (test_accept_empty_request_prepend) {
+  assert_parsed_line(
+    "env:event::glob:55:response",
+    "env","event",NULL,"glob",55,"response"
+  );
+} END_TEST
+
+START_TEST (test_reject_empty_response_line_glob) {
+  assert_non_parsed_line(
+    "env:event:prepend::55:response"
+  );
+} END_TEST
+
+START_TEST (test_accept_empty_exitcode) {
+  assert_parsed_line(
+    "env:event:prepend:glob::response",
+    "env","event","prepend","glob",EXIT_LATER_NORMALLY,"response"
+  );
+} END_TEST
+
+START_TEST (test_reject_exitcode_non_numeric) {
+  assert_non_parsed_line(
+    "env:event:prepend:glob:exitcode:response"
+  );
+} END_TEST
+
+START_TEST (test_reject_exitcode_too_large) {
+  assert_non_parsed_line(
+    "env:event:prepend:glob:500:response"
+  );
+} END_TEST
+
+START_TEST (test_accept_valid_exitcode) {
+  assert_parsed_line(
+    "env:event:prepend:glob:5:response",
+    "env","event","prepend","glob",5,"response"
+  );
+} END_TEST
+
+START_TEST (test_accept_empty_response) {
+  assert_parsed_line(
+    "env:event:prepend:glob:55:",
+    "env","event","prepend","glob",55,""
+  );
+} END_TEST
+
+START_TEST (test_reject_response_not_specified) {
+  assert_non_parsed_line(
+    "env:event:prepend:glob:55"
+  );
+} END_TEST
+
+START_TEST (test_accept_response_containing_space) {
+  assert_parsed_line(
+    "env:event:prepend:glob:55:response 250 ok",
+    "env","event","prepend","glob",55,"response 250 ok"
+  );
+} END_TEST
+
+START_TEST (test_accept_response_containing_colon) {
+  assert_parsed_line(
+    "env:event:prepend:glob:55:response: 250 ok",
+    "env","event","prepend","glob",55,"response: 250 ok"
+  );
+} END_TEST
+
+START_TEST (test_accept_realistic_line) {
+  assert_parsed_line(
+    ":word:NOOP :*::250 indeed",
+    NULL,"word","NOOP ","*",EXIT_LATER_NORMALLY,"250 indeed"
+  );
+} END_TEST
+
+START_TEST (test_reject_clienteof_with_custom_response) {
+  assert_non_parsed_line(
+    "env:clienteof:prepend:glob:55:custom response"
+  );
+} END_TEST
+
+START_TEST (test_accept_fixup_for_applicable_event) {
+  assert_parsed_line(
+    "env:HELO:prepend:glob:55:&fixsmtpio_fixup",
+    "env","HELO","prepend","glob",55,"&fixsmtpio_fixup"
+  );
+} END_TEST
+
+START_TEST (test_reject_fixup_for_inapplicable_event) {
+  assert_non_parsed_line(
+    "env:event:prepend:glob:55:&fixsmtpio_fixup"
+  );
+} END_TEST
+
+START_TEST (test_reject_unknown_response_thingy) {
+  assert_non_parsed_line(
+    "env:event:prepend:glob:55:&fixsmtpio_unknown_thingy"
+  );
+} END_TEST
+
+START_TEST (test_accept_other_ampersand_response) {
+  assert_parsed_line(
+    "env:event:prepend:glob:55:&other_ampersand_response",
+    "env","event","prepend","glob",55,"&other_ampersand_response"
+  );
+} END_TEST
+
+TCase *tc_control(void) {
+  TCase *tc = tcase_create("");
+
+  tcase_add_test(tc, test_reject_blank_line);
+  tcase_add_test(tc, test_reject_just_a_comma);
+  tcase_add_test(tc, test_reject_just_a_colon);
+  tcase_add_test(tc, test_accept_empty_env);
+  tcase_add_test(tc, test_reject_empty_event);
+  tcase_add_test(tc, test_accept_invented_event);
+  tcase_add_test(tc, test_accept_empty_request_prepend);
+  tcase_add_test(tc, test_reject_empty_response_line_glob);
+  tcase_add_test(tc, test_accept_empty_exitcode);
+  tcase_add_test(tc, test_reject_exitcode_non_numeric);
+  tcase_add_test(tc, test_reject_exitcode_too_large);
+  tcase_add_test(tc, test_accept_valid_exitcode);
+  tcase_add_test(tc, test_accept_empty_response);
+  tcase_add_test(tc, test_reject_response_not_specified);
+  tcase_add_test(tc, test_accept_response_containing_space);
+  tcase_add_test(tc, test_accept_response_containing_colon);
+  tcase_add_test(tc, test_accept_realistic_line);
+  tcase_add_test(tc, test_reject_clienteof_with_custom_response);
+  tcase_add_test(tc, test_accept_fixup_for_applicable_event);
+  tcase_add_test(tc, test_reject_fixup_for_inapplicable_event);
+  tcase_add_test(tc, test_reject_unknown_response_thingy);
+  tcase_add_test(tc, test_accept_other_ampersand_response);
+
+  return tc;
+}
diff --git test_fixsmtpio_eventq.c test_fixsmtpio_eventq.c
new file mode 100644
index 0000000..517c5b0
--- /dev/null
+++ test_fixsmtpio_eventq.c
@@ -0,0 +1,28 @@
+#include "check.h"
+
+#include "fixsmtpio_eventq.h"
+
+START_TEST (test_eventq_put_and_get)
+{
+  ck_assert_str_eq(eventq_get(), "timeout");
+
+  eventq_put("foo");
+  ck_assert_str_eq(eventq_get(), "foo");
+
+  eventq_put("bar");
+  eventq_put("baz");
+  eventq_put("quux");
+  ck_assert_str_eq(eventq_get(), "bar");
+  ck_assert_str_eq(eventq_get(), "baz");
+  ck_assert_str_eq(eventq_get(), "quux");
+  ck_assert_str_eq(eventq_get(), "timeout");
+}
+END_TEST
+
+TCase *tc_eventq(void) {
+  TCase *tc = tcase_create("");
+
+  tcase_add_test(tc, test_eventq_put_and_get);
+
+  return tc;
+}
diff --git test_fixsmtpio_filter.c test_fixsmtpio_filter.c
new file mode 100644
index 0000000..6cf4ba4
--- /dev/null
+++ test_fixsmtpio_filter.c
@@ -0,0 +1,120 @@
+#include "check.h"
+
+#include "fixsmtpio_filter.h"
+
+void assert_filter_rule(filter_rule *filter_rule, const char *event, int expected) {
+  ck_assert_int_eq(filter_rule_applies(filter_rule, event), expected);
+}
+
+START_TEST (test_filter_rule_applies)
+{
+  filter_rule rule = {
+    0,
+    0, "caliente",
+    REQUEST_PASSTHRU, "*",
+    EXIT_LATER_NORMALLY, "",
+  };
+  assert_filter_rule(&rule, "clienteof", 0);
+
+  rule.event = "clienteof";
+  assert_filter_rule(&rule, "clienteof", 1);
+}
+END_TEST
+
+START_TEST (test_want_munge_internally) {
+  ck_assert_int_eq(1, want_munge_internally("&fixsmtpio_fixup"));
+  ck_assert_int_eq(0, want_munge_internally("&fixsmtpio_noop"));
+  ck_assert_int_eq(0, want_munge_internally(""));
+  //ck_assert_int_eq(0, want_munge_internally(NULL));
+  ck_assert_int_eq(0, want_munge_internally("random other text\r\n"));
+}
+END_TEST
+
+START_TEST (test_want_leave_line_as_is) {
+  ck_assert_int_eq(1, want_leave_line_as_is("&fixsmtpio_noop"));
+  ck_assert_int_eq(0, want_leave_line_as_is("&fixsmtpio_fixup"));
+  ck_assert_int_eq(0, want_leave_line_as_is(""));
+  //ck_assert_int_eq(0, want_leave_line_as_is(NULL));
+  ck_assert_int_eq(0, want_leave_line_as_is("random other text\r\n"));
+}
+END_TEST
+
+START_TEST (test_envvar_exists_if_needed) {
+  ck_assert_int_eq(0, envvar_exists_if_needed("VERY_UNLIKELY_TO_BE_SET"));
+  ck_assert_int_eq(1, envvar_exists_if_needed(""));
+  ck_assert_int_eq(1, envvar_exists_if_needed(NULL));
+}
+END_TEST
+
+void assert_munge_response_line(char *expected_output, int lineno, char *line, int exitcode, char *greeting, filter_rule *rules, char *event) {
+  stralloc line_sa = {0}; stralloc_copys(&line_sa, line);
+  stralloc greeting_sa = {0}; stralloc_copys(&greeting_sa, greeting);
+
+  munge_response_line(lineno, &line_sa, &exitcode, &greeting_sa, rules, event, 0, 0);
+
+  stralloc_0(&line_sa);
+
+  ck_assert_str_eq(line_sa.s, expected_output);
+}
+
+START_TEST (test_munge_response_line) {
+  filter_rule *rules = 0;
+  filter_rule helo = {
+    0,
+    0, "helo",
+    REQUEST_PASSTHRU, "2*",
+    EXIT_LATER_NORMALLY, MUNGE_INTERNALLY,
+  };
+  filter_rule ehlo = {
+    0,
+    0, "ehlo",
+    REQUEST_PASSTHRU, "2*",
+    EXIT_LATER_NORMALLY, MUNGE_INTERNALLY,
+  };
+
+  assert_munge_response_line("222 sup duuuude\r\n", 0, "222 sup duuuude", 0, "yo.sup.local", rules, "ehlo");
+  assert_munge_response_line("222 OUTSTANDING\r\n", 1, "222 OUTSTANDING", 0, "yo.sup.local", rules, "ehlo");
+
+  rules = prepend_rule(rules, &helo);
+  assert_munge_response_line("222 sup duuuude\r\n", 0, "222 sup duuuude", 0, "yo.sup.local", rules, "ehlo");
+  assert_munge_response_line("222 OUTSTANDING\r\n", 1, "222 OUTSTANDING", 0, "yo.sup.local", rules, "ehlo");
+
+  rules = prepend_rule(rules, &ehlo);
+  assert_munge_response_line("250 yo.sup.local\r\n", 0, "222 sup duuuude", 0, "yo.sup.local", rules, "ehlo");
+  assert_munge_response_line("222 OUTSTANDING\r\n", 1, "222 OUTSTANDING", 0, "yo.sup.local", rules, "ehlo");
+}
+END_TEST
+
+void assert_munge_response(char *expected_output, char *response, int exitcode, char *greeting, filter_rule *rules, char *event) {
+  stralloc response_sa = {0}; stralloc_copys(&response_sa, response);
+  stralloc greeting_sa = {0}; stralloc_copys(&greeting_sa, greeting);
+
+  munge_response(&response_sa, &exitcode, &greeting_sa, rules, event, 0, 0);
+
+  stralloc_0(&response_sa);
+
+  ck_assert_str_eq(response_sa.s, expected_output);
+}
+
+START_TEST (test_munge_response) {
+  filter_rule *rules = 0;
+
+  // annoying to test NULL, unlikely to be bug
+  assert_munge_response("", "", EXIT_LATER_NORMALLY, "yo.sup.local", rules, "ehlo");
+  assert_munge_response("512 grump\r\n", "512 grump", EXIT_LATER_NORMALLY, "yo.sup.local", rules, "ehlo");
+  assert_munge_response("512-grump\r\n256 mump\r\n", "512 grump\r\n256 mump", EXIT_LATER_NORMALLY, "yo.sup.local", rules, "ehlo");
+}
+END_TEST
+
+TCase *tc_filter(void) {
+  TCase *tc = tcase_create("");
+
+  tcase_add_test(tc, test_filter_rule_applies);
+  tcase_add_test(tc, test_want_munge_internally);
+  tcase_add_test(tc, test_want_leave_line_as_is);
+  tcase_add_test(tc, test_envvar_exists_if_needed);
+  tcase_add_test(tc, test_munge_response_line);
+  tcase_add_test(tc, test_munge_response);
+
+  return tc;
+}
diff --git test_fixsmtpio_glob.c test_fixsmtpio_glob.c
new file mode 100644
index 0000000..8e8287c
--- /dev/null
+++ test_fixsmtpio_glob.c
@@ -0,0 +1,35 @@
+#include "check.h"
+
+#include "fixsmtpio_glob.h"
+
+START_TEST (test_string_matches_glob) {
+  ck_assert(string_matches_glob("*", ""));
+  ck_assert(string_matches_glob("*", "foob;;ar"));
+  ck_assert(string_matches_glob("4*", "450 tempfail"));
+  ck_assert(string_matches_glob("4*", "4"));
+  ck_assert(string_matches_glob("250?STARTTLS", "250?STARTTLS"));
+  ck_assert(string_matches_glob("250?AUTH*", "250 AUTH "));
+  ck_assert(string_matches_glob("250?AUTH*", "250 AUTH"));
+  ck_assert(string_matches_glob("250?auth*", "250 auth"));
+  ck_assert(string_matches_glob("2*", "250-auth login"));
+  ck_assert(string_matches_glob("250?auth*", "250-auth login"));
+  ck_assert(string_matches_glob("", ""));
+ 
+ 
+  ck_assert(!string_matches_glob("250 auth", "the anthology contains works by 250 authors"));
+  ck_assert(!string_matches_glob("250?AUTH*", "250  AUTH"));
+  ck_assert(!string_matches_glob("250?AUTH*", " 250  AUTH"));
+  ck_assert(!string_matches_glob("250?AUTH*", " 250 AUTH"));
+  ck_assert(!string_matches_glob("4*", "foob;;ar"));
+  ck_assert(!string_matches_glob("", " 250 AUTH"));
+  ck_assert(!string_matches_glob("4*", "I have eaten 450 french fries"));
+}
+END_TEST
+
+TCase *tc_glob(void) {
+  TCase *tc = tcase_create("");
+
+  tcase_add_test(tc, test_string_matches_glob);
+
+  return tc;
+}
diff --git test_fixsmtpio_munge.c test_fixsmtpio_munge.c
new file mode 100644
index 0000000..8f57a5b
--- /dev/null
+++ test_fixsmtpio_munge.c
@@ -0,0 +1,152 @@
+#include "check.h"
+
+#include "fixsmtpio_munge.h"
+
+void assert_change_every_line_fourth_char_to_dash( char *input, char *expected_response) {
+  stralloc response_sa = {0}; stralloc_copys(&response_sa, input);
+  change_every_line_fourth_char_to_dash(&response_sa);
+  stralloc_0(&response_sa);
+  ck_assert_str_eq(response_sa.s, expected_response);
+
+}
+
+START_TEST (test_change_every_line_fourth_char_to_dash) {
+  // annoying to test, currently don't believe I have this bug:
+  // assert_change_every_line_fourth_char_to_dash(NULL, "");
+
+  assert_change_every_line_fourth_char_to_dash("", "");
+
+  assert_change_every_line_fourth_char_to_dash("ab", "ab");
+
+  assert_change_every_line_fourth_char_to_dash("abc", "abc");
+
+  assert_change_every_line_fourth_char_to_dash("abcd", "abc-");
+
+  assert_change_every_line_fourth_char_to_dash("abcd efgh\n", "abc- efgh\n");
+
+  assert_change_every_line_fourth_char_to_dash(
+      "abcd efgh\nijk\n", "abc- efgh\nijk\n");
+
+  assert_change_every_line_fourth_char_to_dash(
+      "ijk\n"
+      "abcd efgh\n",
+
+      "ijk\n"
+      "abc- efgh\n");
+
+  assert_change_every_line_fourth_char_to_dash(
+      "abcd efgh\n"
+      "ijk\n"
+      "bcde fghi\n",
+
+      "abc- efgh\n"
+      "ijk\n"
+      "bcd- fghi\n");
+}
+END_TEST
+
+void assert_change_last_line_fourth_char_to_space(char *input, char *expected_response) {
+  stralloc response_sa = {0}; stralloc_copys(&response_sa, input);
+  change_last_line_fourth_char_to_space(&response_sa);
+  stralloc_0(&response_sa);
+  ck_assert_str_eq(response_sa.s, expected_response);
+}
+
+START_TEST (test_change_last_line_fourth_char_to_space) {
+  // annoying to test, currently don't believe I have this bug:
+  // assert_change_last_line_fourth_char_to_space(NULL, "");
+
+  assert_change_last_line_fourth_char_to_space("", "");
+
+  assert_change_last_line_fourth_char_to_space("ab", "ab");
+
+  assert_change_last_line_fourth_char_to_space("abc", "abc");
+
+  assert_change_last_line_fourth_char_to_space("abcd", "abc ");
+
+  assert_change_last_line_fourth_char_to_space("abcd efgh\n", "abc  efgh\n");
+
+  assert_change_last_line_fourth_char_to_space(
+      "abcd efgh\nij\n", "abcd efgh\nij\n");
+
+  assert_change_last_line_fourth_char_to_space(
+      "abcd efgh\nijk\n", "abcd efgh\nijk ");
+
+  assert_change_last_line_fourth_char_to_space(
+      "ijk\n"
+      "abcd efgh\n",
+
+      "ijk\n"
+      "abc  efgh\n");
+
+  assert_change_last_line_fourth_char_to_space(
+      "abcd efgh\n"
+      "ijk\n"
+      "bcde fghi\n",
+
+      "abcd efgh\n"
+      "ijk\n"
+      "bcd  fghi\n");
+}
+END_TEST
+
+START_TEST (test_event_matches) {
+  /*
+  char empty_unterminated[] = {                   };
+  char       unterminated[] = {'f', 'o', 'o'      };
+  char         terminated[] = {'f', 'o', 'o', '\0'};
+
+  ck_assert(!event_matches(empty_unterminated, ""));
+  ck_assert(!event_matches("", empty_unterminated));
+  ck_assert( event_matches(empty_unterminated, empty_unterminated));
+
+  ck_assert(!event_matches(unterminated, ""));
+  ck_assert(!event_matches("", unterminated));
+  ck_assert( event_matches(unterminated, unterminated));
+
+  ck_assert(!event_matches(unterminated, NULL));
+  ck_assert(!event_matches(NULL, unterminated));
+
+  ck_assert( event_matches(unterminated, terminated));
+  */
+
+  ck_assert(!event_matches(NULL, ""));
+  ck_assert(!event_matches("", NULL));
+  ck_assert(!event_matches(NULL, NULL));
+  ck_assert(!event_matches("", ""));
+
+  ck_assert(!event_matches("foo", "bar"));
+  ck_assert( event_matches("baz", "baz"));
+  ck_assert( event_matches("Quux", "quuX"));
+}
+END_TEST
+
+void assert_munge_line_internally(char *input, int lineno, char *greeting, char *event, char *expected_output) {
+  stralloc input_sa = {0}; stralloc_copys(&input_sa, input);
+  stralloc greeting_sa = {0}; stralloc_copys(&greeting_sa, greeting);
+  munge_line_internally(&input_sa,lineno,&greeting_sa,event,0,0);
+  stralloc_0(&input_sa);
+  ck_assert_str_eq(input_sa.s, expected_output);
+}
+
+START_TEST (test_munge_line_internally) {
+  assert_munge_line_internally("250 word up, kids", 0, "yo.sup.local", "word", "250 word up, kids");
+  assert_munge_line_internally("250-applesauce", 0, "yo.sup.local", "ehlo", "250 yo.sup.local");
+  assert_munge_line_internally("250-STARTSOMETHING", 1, "yo.sup.local", "ehlo", "250-STARTSOMETHING");
+  assert_munge_line_internally("250 ENDSOMETHING", 2, "yo.sup.local", "ehlo", "250 ENDSOMETHING");
+  assert_munge_line_internally("250 applesauce", 0, "yo.sup.local", "helo", "250 yo.sup.local");
+  assert_munge_line_internally("214 ask your grandmother\r\n", 0, "yo.sup.local", "help", "214 fixsmtpio home page: https://schmonz.com/qmail/acceptutils\r\n214 ask your grandmother\r\n");
+  assert_munge_line_internally("221 get outta here", 0, "yo.sup.local", "quit", "221 yo.sup.local");
+}
+END_TEST
+
+TCase *tc_munge(void) {
+  TCase *tc = tcase_create("");
+
+  tcase_add_test(tc, test_change_every_line_fourth_char_to_dash);
+  tcase_add_test(tc, test_change_last_line_fourth_char_to_space);
+  tcase_add_test(tc, test_event_matches);
+  tcase_add_test(tc, test_munge_line_internally);
+
+  return tc;
+}
diff --git test_fixsmtpio_proxy.c test_fixsmtpio_proxy.c
new file mode 100644
index 0000000..76c766c
--- /dev/null
+++ test_fixsmtpio_proxy.c
@@ -0,0 +1,216 @@
+#include "check.h"
+#include "acceptutils_stralloc.h"
+
+#include "fixsmtpio_proxy.h"
+
+void assert_strip_last_eol(const char *input, const char *expected_output) {
+  stralloc sa = {0}; stralloc_copys(&sa, input);
+
+  strip_last_eol(&sa);
+
+  ck_assert_int_eq(sa.len, strlen(expected_output));
+  stralloc_0(&sa);
+  ck_assert_str_eq(sa.s, expected_output);
+}
+
+START_TEST (test_strip_last_eol)
+{
+  assert_strip_last_eol("", "");
+  assert_strip_last_eol("\n", "");
+  assert_strip_last_eol("\r", "");
+  assert_strip_last_eol("\r\n", "");
+  assert_strip_last_eol("\n\r", "\n");
+  assert_strip_last_eol("\r\r", "\r");
+  assert_strip_last_eol("\n\n", "\n");
+  assert_strip_last_eol("yo geeps", "yo geeps");
+  assert_strip_last_eol("yo geeps\r\n", "yo geeps");
+  assert_strip_last_eol("yo geeps\r\nhow you doin?\r\n", "yo geeps\r\nhow you doin?");
+  assert_strip_last_eol("yo geeps\r\nhow you doin?", "yo geeps\r\nhow you doin?");
+}
+END_TEST
+
+void assert_ends_with_newline(char *input, int expected) {
+  stralloc sa = {0}; stralloc_copys(&sa, input);
+
+  int actual = ends_with_newline(&sa);
+
+  ck_assert_int_eq(actual, expected);
+}
+
+START_TEST (test_ends_with_newline)
+{
+  // annoying to test, currently don't believe I have this bug:
+  // assert_ends_with_newline(NULL, 0);
+  assert_ends_with_newline("", 0);
+  assert_ends_with_newline("123", 0);
+  assert_ends_with_newline("123\n", 1);
+  assert_ends_with_newline("1\n23\n", 1);
+}
+END_TEST
+
+void assert_is_last_line_of_response(const char *input, int expected)
+{
+  stralloc sa = {0}; stralloc_copys(&sa, input);
+  int actual = is_last_line_of_response(&sa);
+  ck_assert_int_eq(actual, expected);
+}
+
+START_TEST (test_is_last_line_of_response)
+{
+  //assert_is_last_line_of_response(NULL, 0);
+  assert_is_last_line_of_response("", 0);
+  assert_is_last_line_of_response("123", 0);
+  assert_is_last_line_of_response("1234", 0);
+  assert_is_last_line_of_response("123 this is a final line", 1);
+  assert_is_last_line_of_response("123-this is NOT a final line", 0);
+  assert_is_last_line_of_response("777-is not\r\n", 0);
+  assert_is_last_line_of_response("777 is\r\n", 1);
+  
+
+  // two surprises, but maybe fine for this function's job:
+  // - "\r\n" can be un-present and it's fine
+  // - it can have nothing after the space and it's fine
+  assert_is_last_line_of_response("123 ", 1);
+  assert_is_last_line_of_response("123\n", 0);
+}
+END_TEST
+
+void assert_parse_client_request(const char *request, const char *verb, const char *arg)
+{
+  stralloc sa_request = {0}; stralloc_copys(&sa_request, request);
+  stralloc sa_request_copy = {0}; stralloc_copy(&sa_request_copy, &sa_request);
+  stralloc sa_verb = {0};
+  stralloc sa_arg = {0};
+
+  parse_client_request(&sa_verb, &sa_arg, &sa_request);
+
+  ck_assert_int_eq(sa_request_copy.len, sa_request.len);
+  stralloc_0(&sa_verb);
+  ck_assert_str_eq(sa_verb.s, verb);
+  stralloc_0(&sa_arg);
+  ck_assert_str_eq(sa_arg.s, arg);
+}
+
+START_TEST (test_parse_client_request)
+{
+  //assert_parse_client_request(NULL, "", "");
+  assert_parse_client_request("", "", "");
+  assert_parse_client_request("MAIL FROM:<schmonz@schmonz.com>\r\n", "MAIL", "FROM:<schmonz@schmonz.com>");
+  assert_parse_client_request("RCPT TO:<geepawhill@geepawhill.org>\r\n", "RCPT", "TO:<geepawhill@geepawhill.org>");
+  assert_parse_client_request("GENIUSPROGRAMMER\r\n", "GENIUSPROGRAMMER", "");
+  assert_parse_client_request(" NEATO\r\n", "", "NEATO");
+  assert_parse_client_request("SWELL \r\n", "SWELL", "");
+  assert_parse_client_request(" \r\n", "", "");
+  assert_parse_client_request("   \r\n", "", "  ");
+  assert_parse_client_request("SUPER WEIRD STUFF\r\n", "SUPER", "WEIRD STUFF");
+  assert_parse_client_request("R WEIRD STUFF\r\n", "R", "WEIRD STUFF");
+  assert_parse_client_request("MAIL FROM:<schmonz@schmonz.com>\r\nRCPT TO:<geepawhill@geepawhill.org>\r\n", "MAIL", "FROM:<schmonz@schmonz.com>\r\nRCPT TO:<geepawhill@geepawhill.org>");
+}
+END_TEST
+
+static void assert_get_one_response(char *input, const char *expected_result, const char *expected_remaining, int expected_return) {
+  stralloc actual_one = {0}, actual_many = {0};
+  int return_value;
+  copys(&actual_many,input);
+
+  return_value = get_one_response(&actual_one,&actual_many);
+
+  ck_assert_int_eq(return_value, expected_return);
+
+  stralloc_0(&actual_one);
+  ck_assert_str_eq(actual_one.s, expected_result);
+
+  stralloc_0(&actual_many);
+  ck_assert_str_eq(actual_many.s, expected_remaining);
+}
+
+START_TEST (test_get_one_response)
+{
+  assert_get_one_response("777 oneline\r\n", "777 oneline\r\n", "", 1);
+  assert_get_one_response("777 separate\r\n888 responses\r\n", "777 separate\r\n", "888 responses\r\n", 1);
+  assert_get_one_response("777-two\r\n777 lines\r\n888 three\r\n", "777-two\r\n777 lines\r\n", "888 three\r\n", 1);
+  assert_get_one_response("777-two\r\n777 lines\r\n888 three\r\n999 four\r\n", "777-two\r\n777 lines\r\n", "888 three\r\n999 four\r\n", 1);
+  assert_get_one_response("777-two\r\n", "", "777-two\r\n", 0);
+}
+END_TEST
+
+static void assert_ends_data(const char *input, const int expected) {
+  stralloc input_sa = {0}; stralloc_copys(&input_sa, input);
+
+  int actual = ends_data(&input_sa);
+
+  ck_assert_int_eq(actual, expected);
+}
+
+START_TEST (test_ends_data)
+{
+  // annoying to test, currently don't believe I have this bug:
+  // assert_is_last_line_of_data(NULL, 0);
+  assert_ends_data("", 0);
+  assert_ends_data("\r\n", 0);
+  assert_ends_data(" \r\n", 0);
+  assert_ends_data(".\r\n", 1);
+  assert_ends_data(" .\r\n", 0);
+  assert_ends_data("\n.\r\n", 1);
+  assert_ends_data("\r\n.\r\n", 1);
+  assert_ends_data("snorf.\r\n", 0);
+  assert_ends_data("snorf.\r\n.\r\n", 1);
+}
+END_TEST
+
+START_TEST (test_construct_proxy_request)
+{
+  stralloc proxy_request = {0},
+           arg = {0},
+           client_request = {0};
+  int want_data = 0;
+
+  filter_rule test_rule = {
+    0,
+    "SPECIFIC_ENV_VAR",   "specific_verb",
+    "prepend me: ",       "*",
+    EXIT_LATER_NORMALLY,  "337 hello friend",
+  };
+  filter_rule *test_rules = prepend_rule(0, &test_rule);
+
+  blank(&proxy_request); copys(&client_request, "SPECIFIC_VERB somearg\r\n");
+  construct_proxy_request(&proxy_request,test_rules,"SPECIFIC_VERB",&arg,&client_request,0,(void *)0,0,&want_data);
+  stralloc_0(&proxy_request); stralloc_0(&client_request);
+  ck_assert_str_eq(proxy_request.s, client_request.s);
+
+  env_put2("SPECIFIC_ENV_VAR","");
+
+  blank(&proxy_request); copys(&client_request, "SPECIFIC_VERB somearg\r\n");
+  construct_proxy_request(&proxy_request,test_rules,"SPECIFIC_VERB",&arg,&client_request,0,(void *)0,0,&want_data);
+  stralloc_0(&proxy_request); stralloc_0(&client_request);
+  ck_assert_str_ne(proxy_request.s, client_request.s);
+
+  env_unset("SPECIFIC_ENV_VAR");
+
+  // XXX not in_data, a rule says to prepend: proxy request is equal to prepended + client_request
+  // XXX not in_data, two rules prepend: proxy request is equal to p1 + p2 + client_request (WILL FAIL)
+}
+END_TEST
+
+START_TEST (test_construct_proxy_response)
+{
+  ck_assert(1);
+  // not sure whether to test:
+  // not want_data, not in_data, no request_received, no verb: it's a timeout
+}
+END_TEST
+
+TCase *tc_proxy(void) {
+  TCase *tc = tcase_create("");
+
+  tcase_add_test(tc, test_strip_last_eol);
+  tcase_add_test(tc, test_ends_with_newline);
+  tcase_add_test(tc, test_is_last_line_of_response);
+  tcase_add_test(tc, test_parse_client_request);
+  tcase_add_test(tc, test_get_one_response);
+  tcase_add_test(tc, test_ends_data);
+  tcase_add_test(tc, test_construct_proxy_request);
+  tcase_add_test(tc, test_construct_proxy_response);
+
+  return tc;
+}
*   7e7a7e2134bd0fb3421a70e1c035ee43d7aed07d 2020-11-28 (refs/pull/190/merge)
|\    Merge d910d912a9b8d801865ab229875c543cefdfe8a5● into d872071ba4319a01778ba3c1990854ba5947034f● 
| | 
| * d910d912a9b8d801865ab229875c543cefdfe8a5● 2020-11-28 (refs/pull/190/head, Dakon-smtpcode-test)
| |   qmail-remote: verify the SMTP result code of remote servers much stricter
| | 
| * dbb7585ba4f1de8a7522e4521e01d146e3765757● 2020-11-28
| |   tests: show several cases where smtpcode() is wrong
| | 
| * 3175f621c29d3647577d6d670074934a20860d66● 2020-11-28
| |   split smtpcode() out of qmail-remote.c
| | 
| * b235c3ecddadcd1eaf64e8ead2e72931e5dcdcf2● 2020-11-28
| |   tests: prevent duplication in Makefile
| |   
| | * 15910f421512e03460e12ae3be1d537150e437ed 2020-11-28 (refs/pull/84/merge)
| |/|   Merge ac1ce75874a84bffe2030ac7a70b21f93fb491fe● into d872071ba4319a01778ba3c1990854ba5947034f● 
|/| | 
| | | 
| | * ac1ce75874a84bffe2030ac7a70b21f93fb491fe● 2020-11-28 (refs/pull/84/head, substdio-ssize_t)
| | |   replace "sizeof foo" with sizeof(foo)
| | | 
| | * 8ce8dcd76eb2a0d6c7387351ee4a8144f9f78c51● 2020-11-28
| | |   add generator macro for substdio wrappers around qmail_put()
| | | 
| | * f44bd674a3b7bfa14183e9885d201309cc8cdc93● 2020-11-28
| | |   add generator macros for substdio wrappers around timeoutread() and timeoutwrite()
| | | 
| | * 237584abb9016ffd68f7e4abfa28defc31fb8cd1● 2020-11-28
| | |   fix read() return value handling in getln2()
| | | 
| | * f5028e9bae4ad8c423556f690091e077e9e103f3● 2020-11-28
| | |   improve checks for return value of read() and write() and it's wrappers
| | | 
| | * bfaaacda422b64b3c073ec7888f905ec7226e796● 2020-11-28
| | |   convert some additional substdio functions to return ssize_t
| | | 
| | * a8c31c0af0892388393162832c14ff7367b60c21● 2020-11-28
| | |   simplify qmail_puts()
| | | 
| | * 974294a6da8ea8ab08dba88339e67a95f0dd77bf● 2020-11-28
| | |   use size_t and ssize_t in substdio
| | | 
* | | d872071ba4319a01778ba3c1990854ba5947034f● 2020-11-28 (HEAD -> master)
|/ /    Fix 17 warnings from -Wparentheses.
| |   
| | * 1829220545bf65562339e987c75c0d169ed2d1f0 2020-11-28 (refs/pull/184/head)
| |/    Fix 17 warnings from -Wparentheses.
|/|   
| |   
| | * d278adc0855b4bef9955de52ddadfdf840643b14● 2020-11-28 (refs/pull/183/merge)
| |/|   Merge f93f6599229c668a6a175d3f1d34e4b87a0de8d4● into 32d55b0acd04898fd601f4a265e714ee3985ed24● 
|/| | 
| | | 
| | * f93f6599229c668a6a175d3f1d34e4b87a0de8d4● 2020-11-17 (refs/pull/183/head, Dakon-own-headers)
| |/    always include the own header first in all C files
| |   
| | * f31cbebea3ec7c2dcefb61c05a88af22e1f7b006● 2020-11-28 (refs/pull/149/merge)
| |/|   Merge eb0013ebf7f2303e77c97e8166f402a57aca33fa● into 32d55b0acd04898fd601f4a265e714ee3985ed24● 
|/| | 
| | | 
| | * eb0013ebf7f2303e77c97e8166f402a57aca33fa● 2020-11-09 (refs/pull/149/head, Dakon-make-dupl)
| | |   Makefile: remove duplication for all object file rules
| | | 
| | * e88b156256aaa9de17cbc43349eb22984dc0a9d2● 2020-11-09
| | |   Makefile: remove code duplication for library components
| | | 
| | * 7a665bfa033d154fc31aa721fc4834fe3f4ca541● 2020-11-09
| | |   Makefile: reduce code duplication for building username files
| | | 
| | * 7391853b304957f4fddbd7cb6f6320f1b419cd4f● 2020-11-09
| | |   tests: remove duplication in Makefile
| | |   
| | | * bdba66311fae21f578a476f519baa9c8cbcf9ef0● 2020-11-28 (refs/pull/184/merge)
| |_|/|   Merge 0daadf1888f7c75d04a5f6b929126f81041f179d into 32d55b0acd04898fd601f4a265e714ee3985ed24● 
|/| | | 
| | | | 
| | | * 0daadf1888f7c75d04a5f6b929126f81041f179d 2020-11-20 (schmonz-Wparentheses)
| | |/    Fix 17 warnings from -Wparentheses.
| |/|   
| | |   
| | | * c8ceb06d4d72f23516ee891573797dcf566fa6f1● 2020-11-28 (refs/pull/191/merge)
| |_|/|   Merge 23d902221786a05b7c94f3a64835233faeee0a63 into 32d55b0acd04898fd601f4a265e714ee3985ed24● 
|/| | | 
| | | | 
| | | * 23d902221786a05b7c94f3a64835233faeee0a63 2020-11-26 (refs/pull/191/head, Dakon-remote-SIZE)
| | | |   qmail-remote: add support for SIZE extension (RfC 1870)
| | | | 
* | | | 32d55b0acd04898fd601f4a265e714ee3985ed24● 2020-11-28
| | | |   Fix 1 warning from -Wempty-body.
| | | |   
| | | | * e25bb317e8c31c0ad7859ecdb0f7fb2c379b422f● 2020-11-28 (refs/pull/188/merge)
| |_|_|/|   Merge a50af5c031b22e5a63c9fc80ecf0fb8a9a709849● into 27f7b6b170441c8d333919ef7cd1117c34c62582● 
|/| | | | 
| | | | | 
| | | | * a50af5c031b22e5a63c9fc80ecf0fb8a9a709849● 2020-11-28 (refs/pull/188/head, schmonz-Wempty-body)
| |_|_|/    Fix 1 warning from -Wempty-body.
|/| | |   
| | | | 
* | | | 27f7b6b170441c8d333919ef7cd1117c34c62582● 2020-11-28
| | | |   Fix 5 warnings from -Wdangling-else.
| | | |   
| | | | * eef4839bd9d301b555dd0bf18f4c2e09fe9df988● 2020-11-28 (refs/pull/185/merge)
| |_|_|/|   Merge c182d377d791605b1d9ae7eff1c390d732168154● into ac7172c7e441ed82235bda227dbaac68e4e3c735● 
|/| | | | 
| | | | | 
| | | | * c182d377d791605b1d9ae7eff1c390d732168154● 2020-11-28 (refs/pull/185/head, schmonz-Wdangling-else)
| |_|_|/    Fix 5 warnings from -Wdangling-else.
|/| | |   
| | | |   
| | | | * d6e3bcf96d7366cc39cec10c586f36734a53e31d● 2020-11-28 (refs/pull/61/merge)
| |_|_|/|   Merge c5aaaf011a5171ef25f3f82243f5ea3b30ce9a3b● into ac7172c7e441ed82235bda227dbaac68e4e3c735● 
|/| | | | 
| | | | | 
| | | | * c5aaaf011a5171ef25f3f82243f5ea3b30ce9a3b● 2020-11-28 (refs/pull/61/head, instqueue)
| | | | |   Extract an "instqueue" program from instpackage.
| | | | | 
| | | | * baf659d62d676c3a8a802fdeb1054b09c3c03b91● 2020-11-28
| | | | |   "install": declare missing dependencies.
| | | | | 
| | | | * 4b1c71451b4a679ee03680b5e1112b3028ccd5b2● 2020-11-28
| | |_|/    NFCI: Make "compile" target match "load" target.
| |/| |   
| | | | 
* | | | ac7172c7e441ed82235bda227dbaac68e4e3c735● 2020-11-28
| | | |   Fix 3 warnings from -Wpointer-sign.
| | | |   
| | | | * 5c8d726151749f22306dd2f32d2cc90587997a96 2020-11-27 (refs/pull/186/merge)
| |_|_|/|   Merge 9421d40570f01cecbf2f6e2b49f19d55957264df into c3d3c72e3ca7bb5102f710aad7bf9ab105bde27e● 
|/| | | | 
| | | | | 
| | | | * 9421d40570f01cecbf2f6e2b49f19d55957264df 2020-11-28 (refs/pull/186/head, schmonz-Wpointer-sign)
| |_|_|/    Fix 3 warnings from -Wpointer-sign.
|/| | |   
| | | | 
* | | | c3d3c72e3ca7bb5102f710aad7bf9ab105bde27e● 2020-11-28
|/ / /    Fix 2 warnings from -Wincompatible-library-redeclaration.
| | |   
| | | * f46856429daf206b1bdc0fc6a8d5d4dfecc3a6ec● 2020-11-27 (refs/pull/187/merge)
| |_|/|   Merge 2bb6d4f90ec14a0af26d6aecd3546acf9478e78f into aedd8beb2484608eb97ee3299e7f3b8cdde061ce 
|/| | | 
| | | | 
| | | * 2bb6d4f90ec14a0af26d6aecd3546acf9478e78f 2020-11-28 (refs/pull/187/head, schmonz-Wincompatible-library-redeclaration)
| |_|/    Fix 2 warnings from -Wincompatible-library-redeclaration.
|/| |   
| | |   
| | | * 5dea5a2436077983244c33fd8d4279f52863866d 2020-11-21 (refs/pull/173/merge)
| |_|/|   Merge 6897896cb410a34f6fd4e80741a28d58678b17d7 into aedd8beb2484608eb97ee3299e7f3b8cdde061ce 
|/| |/  
| | | 
| | * 6897896cb410a34f6fd4e80741a28d58678b17d7 2020-11-21 (refs/pull/173/head, Dakon-remote-EHLO)
| | |   qmail-remote: add infrastructure for EHLO parsing
| | | 
| | * aa36dde7dae059ae1e7834f62f6f7dfb747d85bb● 2020-11-21
| |/    tests: prevent duplication in Makefile
|/|   
| |   
| | * ed2491a3431b69cea3790d14b8b06a155eb72407● 2020-11-20 (refs/pull/148/merge)
| |/|   Merge 2d6324e65cad8e21902ef3beae0ef25bc91ded19 into aedd8beb2484608eb97ee3299e7f3b8cdde061ce 
|/| | 
| | | 
| | * 2d6324e65cad8e21902ef3beae0ef25bc91ded19 2020-11-20 (refs/pull/148/head, schmonz-builds-symlinks)
| |/    Symlink "CI" configs somewhere easier to see.
|/|   
| |   
| | * 1963d79845b6b8c04236b87dc72eb18dd6daa128 2020-11-17 (refs/pull/76/merge)
| |/|   Merge d3d6059b189131c1e83c131b661295e8776a6f08● into aedd8beb2484608eb97ee3299e7f3b8cdde061ce 
|/| | 
| | | 
| | * d3d6059b189131c1e83c131b661295e8776a6f08● 2020-11-17 (refs/pull/76/head, opensuse_buildservice)
| | |   moved debian directory one level up
| | | 
| | * d5a72a0a7943f48b75bda600cb4cf849e83e8d06● 2020-11-16
| | |   updated maintainer email to packages@notqmail.org
| | | 
| | * 26750829cecf1f4c58b9ebfecb028b08aaf86080● 2020-11-16
| | |   removed usage of files in /tmp
| | | 
| | * 8e302f768203e967ca537f931a4378e3725a76a5● 2020-11-16
| | |   updated maintainer email address
| | | 
| | * 22614794d647daa5444718de471ff98d316b0d22● 2020-11-16
| | |   removed not used README, removed binutils from build-requires, fixed clean target in rules
| | | 
| | * 608e75bfc883e9d9d40c447dfa79aaadafe5ade7● 2020-11-16
| | |   refactored obs build files
| | | 
| | * 88da78b07d8d89203f3c559bd5e3ddb9cab0f5cd● 2020-11-16
| | |   fixed syntax eror and include changelog
| | | 
| | * edd8c8d2912e3286aacbfc823f9b29022882b0da● 2020-11-16
| | |   fix for systemd on ubuntu/debian
| | | 
| | * 3856e3e0b9b634e35f6fea4b544107dc6a0de291● 2020-11-16
| | |   1. Change Source to https://github.com/notqmail/notqmail/releases/download/notqmail-1.08/notqmail-1.08.tar.xz in notqmail.spec 2. use _service file to fetch source directly from github 3. Build can now be triggered and fetch source directly from github 4. Revised version number to 1.08 5. Updated ChangeLogs in notqmail.spec and debian/changelog 6. Updated date/timestamp in ChangeLogs
| | | 
| | * 4df1cc31542aa887961e5fb4d080646415d95ab9● 2020-11-16
| | |   do not install obsolete components
| | | 
| | * aa829ef67ddf9873b063dca3d1df262ac628aedd● 2020-11-16
| |/    added files for binary builds on openSUSE build service
|/|   
| |   
| | * e86ec1c6c39cf75f3473f38543cf10704226cbc3● 2020-11-17 (refs/pull/141/merge)
| |/|   Merge 7945bd30458d143d590961fdb63cc11db09adf4d into aedd8beb2484608eb97ee3299e7f3b8cdde061ce 
|/| | 
| | | 
| | * 7945bd30458d143d590961fdb63cc11db09adf4d 2020-05-26 (refs/pull/141/head, docker)
| | |   added comments on /etc/mtab creation
| | | 
| | * 0f1bfce28d09c1e1ff5dec31e71f4ebc3c472966● 2020-05-26
| | |   moved alternatives command out of subshell
| | | 
| | * aa5ac4192f1046058e7d88b0df4a9969b97bda22● 2020-05-23
| | |   added Dockerfiles for notqmail docker/podman container
| | |   
| | | * 38187677ea721800434c829725366cacfaed729e 2020-11-16 (refs/pull/153/merge)
| |_|/|   Merge 2654ffe3730b0e570e68ae6d62292c713fd4d013 into aedd8beb2484608eb97ee3299e7f3b8cdde061ce 
|/| | | 
| | | | 
| | | * 2654ffe3730b0e570e68ae6d62292c713fd4d013 2020-11-16 (refs/pull/153/head, Dakon-remote-groups)
| | | |   reduce prot_uid() to a define
| | | | 
| | | * f46913371291354772f53cd6db507509a356d1ec● 2020-11-16
| |_|/    allow additional groups for qmail-remote
|/| |   
| | |   
| | | * cd2ca43c5702a244fd01f8450186ac6e6db54cb7● 2020-11-14 (refs/pull/172/merge)
| |_|/|   Merge 806e063f2dc921db7cd7e8119bba274bcfdbff2c into aedd8beb2484608eb97ee3299e7f3b8cdde061ce 
|/| | | 
| | | | 
| | | * 806e063f2dc921db7cd7e8119bba274bcfdbff2c 2020-11-14 (refs/pull/172/head, Dakon-ehlo)
| |_|/    qmail-smtpd: introduce an array of EHLO string generators
|/| |   
| | |   
| | | * f4456a26f3a67c049e90414db224e38f705f45b8● 2020-11-14 (refs/pull/143/merge)
| |_|/|   Merge 910f8eed7bc2bf315257d5b754c29fd5eb080b17 into aedd8beb2484608eb97ee3299e7f3b8cdde061ce 
|/| | | 
| | | | 
| | | * 910f8eed7bc2bf315257d5b754c29fd5eb080b17 2020-11-14 (refs/pull/143/head, Dakon-remove-strchr)
| | | |   remove manually unrolled loops in byte*.c
| | | | 
| | | * 889f6db1aa77a735d01c1db6ff1ace2361579b1e● 2020-11-14
| | | |   remove stale declaration case_startb() from case.h
| | | | 
| | | * e3a6c07b0103235348d69f62e7bc78dd9d5cb49d● 2020-11-14
| | | |   add parameters to byte.h declarations
| | | | 
| | | * 630fd56a97b3004373e448fb0642069b62b53f0d● 2020-11-14
| | | |   add parameters to case.h declarations
| | | | 
| | | * 2799acf48cf9167b745a386dee546cd671e2b0f1● 2020-11-14
| | | |   add parameters to str.h declarations
| | | | 
| | | * 8f3ba91e966a01c440dfb19fca834b6647dd5f82● 2020-11-14
| | | |   replace case_diffs() with strcasecmp()
| | | | 
| | | * 7cf5ac84f84802405068e9e68eb0cd4e45c01a02● 2020-11-14
| | | |   tests: add tests for case_diffs()
| | | | 
| | | * 673e52ef69aaaf236929b0fc8cc760d5a813480a● 2020-11-14
| | | |   tests: remove duplication in makefile
| | | | 
| | | * 4ac7c5a079cf0a2feb04d78981789f164b95052d● 2020-11-14
| | | |   replace most usages of str_rchr() with direct strrchr()
| | | | 
| | | * b392039259909a13ef38b73d8feff971ff960eee● 2020-11-14
| | | |   replace str_rchr() with strrchr()
| | | | 
| | | * 42c03dca4e39c14dc6435bb4d1c23880b204facc● 2020-11-14
| | | |   tests: add tests for str_rchr()
| | | | 
| | | * f59f3d9b8bcb19a5b716b0b34d4c059f23fe3cf2● 2020-11-14
| | | |   replace str_start() with strncmp()
| | | | 
| | | * 4d426f559a3400d987df5117a94a754debf56f8d● 2020-11-14
| | | |   tests: add test for str_start()
| | | | 
| | | * 736a65338ce15de0c25c5e579818e040f6dbd8a2● 2020-11-14
| | | |   use strchrnul() to implement str_chr() if present
| | | | 
| | | * a8d5f963ef869904c6f0c0004cb142c4a983ae3c● 2020-11-14
| | | |   replace str_chr() implementation with strchr()
| | | | 
| | | * 8c6fa8a9320cea24410f8fe57d8f5d6c114f284c● 2020-11-14
| |_|/    tests: add test for str_chr()
|/| |   
| | |   
| | | * 713650d9b486b7ee73653b4e658abae1ec8ba1a1 2020-11-12 (refs/pull/182/merge)
| | |/|   Merge aedd8beb2484608eb97ee3299e7f3b8cdde061ce into 52546d2557ba3afa4b5740b2d944b26a20b064ed 
| |/|/  
| |/|   
|/| |   
| | | 
* | | aedd8beb2484608eb97ee3299e7f3b8cdde061ce 2020-11-12 (refs/pull/182/head, Dakon-ids-object)
|/ /    put all auto_user*.o and auto_group*.o files in a library ids.a
| |   
| | * e1fad27e380c809c0b76ca486bb6192c513b11fe● 2020-11-09 (Dakon-const)
| | |   constify some remaining function arguments
| | | 
| | * 72c07c7318909ef9dd1a585932b50e3b2d882777● 2020-11-09
| | |   fixup-control
| | | 
| | * 18a8c284dbbd4c7889b31c59711e8ae4dc64a4be● 2020-11-09
| | |   constify init_[gu]id() arguments
| | | 
| | * ddf898cb2d12d49be05932c2cf2bf23785b63f50● 2020-11-09
| | |   constify scan_*long() function arguments
| | | 
| | * 08240bfdbd050f7f8fb1b4885aa86091d0f7eeeb● 2020-11-09
| | |   constify the install helper functions
| | | 
| | * 7535cfdf7a5d597cb18b3b406dfc40889bd3adaa● 2020-11-09
| | |   constify random function arguments in qmail main binaries
| | | 
| | * 4314fff6d55e1a32bda53ade08b3af9b3df382f6● 2020-11-09
| | |   remove unused function env_pick()
| | | 
| | * 477142ac013348e959a2a7fa38ec86d097bffb18● 2020-11-09
| | |   constify arguments to env_*()
| | | 
| | * 13e8f85c78d29185c719203cc578d8767eb520fa● 2020-11-09
| | |   return const char* from error_str()
| | | 
| | * cd66050b08c82eced7a99320ce6069e985153ae6● 2020-11-09
| | |   constify the arguments to strerr_die() and strerr_warn()
| | | 
| | * 97b37a6e70fdaa444b6668b713c71d768b2c7a38● 2020-11-09
| | |   constify argument to constmap() and it's helper hash()
| | | 
| | * d01effae6b4925f1a359c2b524bb09ccec9fe2c0● 2020-11-09
| | |   constify filename arguments passed to control_*()
| | | 
| | * 7c3105c64895a058540676366585588d49beeb13● 2020-11-09
| | |   constify filename arguments passed to open_*()
| | | 
| | * e53b5686e7593f87f771ebc2b8a735b77fea2b3d● 2020-11-09
| | |   constify arguments to cdb functions
| | | 
| | * 887ab553329cb1c184d0ab49c20e0db56e481aa4● 2020-11-09
| | |   tests: add test for cdb_hash() and cdb_unpack()
| | | 
| | * 2634d3dc7ebb093f54d12165756e04ffb009c273● 2020-11-09
| | |   tests: add abstraction to Makefile to simplify adding further entries
| | | 
| | * 497e03027075810da62b702400a92e2ed20f9890● 2020-11-09
| |/    rename puts() to put_str() in auto-*.c
|/|   
| |   
| | * 9046eb28dbeede1280012a38aa4701b5bb1d9915 2020-11-09 (Dakon-inject-fix)
| |/    DEBUG: try to get qmail-inject behave as before 1.08
|/|   
| | 
* | 52546d2557ba3afa4b5740b2d944b26a20b064ed 2020-11-08 (refs/pull/171/head)
| |   get rid of error_* variables and just #define them to their errno equivalents
| |     
| | *   e9805387b16e88ef957a0a254efd418e905fb39c● 2020-11-08 (Dakon-1.09)
| | |\    Merge remote-tracking branch 'origin/Dakon-remote-EHLO' into Dakon-1.09
| | | | 
| | | * 33dc39a9df7ebe70984d781d0d111e64e30f08e7 2020-11-08
| | | |   qmail-remote: add infrastructure for EHLO parsing
| | | | 
| | | * 56ffd3b94a80a983594f6be24d8e10502468b3b0● 2020-11-08
| |_|/    tests: prevent duplication in Makefile
|/| |   
| | |   
| | *   d9cf8e1dc4436c44f5b59a8dbc7c17d5a7a3fbe3● 2020-07-26
| | |\    Merge branch 'Dakon-ehlo' into Dakon-1.09
| | | | 
| | | * a09a08c7aa68f2944f4d9f9513560cca7f0b224c● 2020-07-26
| | | |   qmail-smtpd: introduce an array of EHLO string generators
| | | |   
| | * |   c949664e762cc566c4adb7648798c327a630c0f3● 2020-07-25
| | |\ \    Merge remote-tracking branch 'origin/Dakon-errno' into Dakon-1.09
| | | | | 
| | | * | 61ebb79d1c748c1ddae7d18cc531581dabe133cc● 2020-07-22
| | | |/    get rid of error_* variables and just #define them to their errno equivalents
| | | |   
| | * |   5a9247de38b0b6493c3cb4c91baf811f4b04c9d6● 2020-07-22
| | |\ \    Merge remote-tracking branch 'origin/Dakon-local-race' into Dakon-1.09
| | | | | 
| | | * | af02dc0fdbbe8db8d3aa12345bdd550727da2039● 2020-07-08
| | | |/    qmail-local: close race window when creating file in tmp/
| | | |   
| | * |   cb78a7220d93563a3429e2efec6b8a2cb1d94375● 2020-07-22
| | |\ \    Merge remote-tracking branch 'origin/Dakon-remote-groups' into Dakon-1.09
| | | | | 
| | | * | 6e98316046f67de310a7ae7efc312d9977e39a3c● 2020-07-08
| | | | |   reduce prot_uid() to a define
| | | | | 
| | | * | 7d883cc30e8afc8d14cd7de55357324beb420fbf● 2020-07-08
| | | | |   allow additional groups for qmail-remote
| | | | | 
| | | * | ccb533b736e064c879142b3bdffa61189c04d74c● 2020-07-08
| | | |/    put all auto_user*.o and auto_group*.o files in a library uids.a
| | | |   
| | * |   839ae630541638c0c764fd8476f8474f1b874076● 2020-07-22
| | |\ \    Merge remote-tracking branch 'origin/crcrlf' into Dakon-1.09
| | | | | 
| | | * | b822661d68e5b71e1fdb485ab8ade7dc222e0ef1● 2020-07-22
| | | |/    qmail-remote: avoid recoding CRLF to CRCRLF
| | | |   
| | * |   40bfd404e8e1f11a783ce55013b7e4731e7bfc78● 2020-07-22
| | |\ \    Merge remote-tracking branch 'origin/Dakon-make-dupl' into Dakon-1.09
| | | | | 
| | | * | 91fc59eb2e2d2db04fb005067f68443a8cd25eaa● 2020-07-22
| | | | |   Makefile: remove duplication for all object file rules
| | | | | 
| | | * | 167bf163c8e7a99053d33a43e6561088f6b3c86b● 2020-07-22
| | | | |   Makefile: remove code duplication for library components
| | | | | 
| | | * | 7aecbda1cf94582eb0f5d911df61fd070a16ca6c● 2020-07-22
| | | |/    Makefile: reduce code duplication for building username files
| | | |   
| | * |   d4db4ffbece97e2afde828ae7d9d1c839800721c● 2020-07-22
| | |\ \    Merge remote-tracking branch 'origin/Dakon-inject-fix' into Dakon-1.09
| | | | | 
| | | * | 8cd0ab7a403339150121d930700a72b345cb03bd● 2020-06-14
| | | | |   DEBUG: try to get qmail-inject behave as before 1.08
| | | | |   
| | * | |   a285446f4a7f201d85127b071bd5135884ab377f● 2020-07-22
| | |\ \ \    Merge remote-tracking branch 'origin/Dakon-remove-strchr' into Dakon-1.09
| | | | | | 
| | | * | | c64752d156cc448bf73b400cfd5ce542cb436761● 2020-07-08
| | | | | |   remove manually unrolled loops in byte*.c
| | | | | | 
| | | * | | 176556e9106fd985fc14ef1fe381f3e1aedfa059● 2020-07-08
| | | | | |   remove stale declaration case_startb() from case.h
| | | | | | 
| | | * | | 520b3e365591465f191eca33821b6e1830636838● 2020-07-08
| | | | | |   add parameters to byte.h declarations
| | | | | | 
| | | * | | c0047e37b1b53b76220457a5d17f8760ad97ddfd● 2020-07-08
| | | | | |   add parameters to case.h declarations
| | | | | | 
| | | * | | dff9b48f07f2b991283635c1ed557f0a91598c86● 2020-07-08
| | | | | |   add parameters to str.h declarations
| | | | | | 
| | | * | | a02e771a67ba21b6512299eed2821a7a453eed25● 2020-07-08
| | | | | |   replace case_diffs() with strcasecmp()
| | | | | | 
| | | * | | 696cc3bb8878745aa8eb4008291755af04030b86● 2020-07-08
| | | | | |   tests: add tests for case_diffs()
| | | | | | 
| | | * | | 7779cfca8df008b1f12f05e2a9e0c99b28144d1b● 2020-07-08
| | | | | |   tests: remove duplication in makefile
| | | | | | 
| | | * | | 7e65d675afcca8e9b5e1596ca724555f1b54bf53● 2020-07-08
| | | | | |   replace most usages of str_rchr() with direct strrchr()
| | | | | | 
| | | * | | 7163823567528c6fe93efd7c057e8cf4e3bb0381● 2020-07-08
| | | | | |   replace str_rchr() with strrchr()
| | | | | | 
| | | * | | 84c23792315e0db950c3db95ac733e84443cd47d● 2020-07-08
| | | | | |   tests: add tests for str_rchr()
| | | | | | 
| | | * | | 266cb0d40106648d0e250fe00f452b06cca8ca93● 2020-07-08
| | | | | |   replace str_start() with strncmp()
| | | | | | 
| | | * | | f70c8bba74fd70a540e16c8e7c9a9295db7a2dd9● 2020-07-08
| | | | | |   tests: add test for str_start()
| | | | | | 
| | | * | | 741a1568f36ae3c40d8a0e4d04daf8aae6e27359● 2020-07-08
| | | | | |   use strchrnul() to implement str_chr() if present
| | | | | | 
| | | * | | 6a96746cfbb0d9ad7166d308711c0c4d117aaaac● 2020-07-08
| | | | | |   replace str_chr() implementation with strchr()
| | | | | | 
| | | * | | ec4cfe478f2b3ae69db8d3008f21d9fc93b25d13● 2020-07-08
| | | | |/    tests: add test for str_chr()
| | | |/|   
| | | | |   
| | * | |   d3dc1817a97b30fd2f42b753d087e302b8dbfba1● 2020-07-22
| | |\ \ \    Merge remote-tracking branch 'origin/substdio-ssize_t' into Dakon-1.09
| | | |/ /  
| | |/| |   
| | | | | 
| | | * | af95e606586406ebed6134554857eaf855992b2d● 2020-07-08
| | | | |   fix read() return value handling in getln2()
| | | | | 
| | | * | 185e3b6888820f9a0969cabac86edc453aab4d77● 2020-07-08
| | | | |   improve checks for return value of read() and write() and it's wrappers
| | | | | 
| | | * | 559f2406c6badc92ef8d8f74b39bb00b4937e368● 2020-07-08
| | | | |   convert some additional substdio functions to return ssize_t
| | | | | 
| | | * | 1223a08da72c85883fa103b6f69147616a1124da● 2020-07-08
| | | | |   simplify qmail_puts()
| | | | | 
| | | * | 32db5c0257284b92ce722de3ac4919b436f6646b● 2020-07-08
| | |/ /    use size_t and ssize_t in substdio
| | | |   
| | | | * d23f758f1e0b5a61e5baf1a0d4f041988b71b47f● 2020-11-08 (refs/pull/171/merge)
| |_|_|/|   Merge 0079c27b10f293ab11cbc76c45be283854dbe46d into 63e5f20194aba81a970f919d73d3d2e6f29e18af● 
|/| | | | 
| | | | | 
| | | | * 0079c27b10f293ab11cbc76c45be283854dbe46d 2020-10-19 (Dakon-errno)
| | | | |   get rid of error_* variables and just #define them to their errno equivalents
| | | | | 
* | | | | 63e5f20194aba81a970f919d73d3d2e6f29e18af● 2020-11-08
| | | | |   Add some tests for prioq.
| | | | |   
| | | | | * e224bd6c02a9d093d0e0fc8294517bc6a48377dd● 2020-11-08 (refs/pull/179/head)
| |_|_|_|/    Add some tests for prioq.
|/| | | |   
| | | | |     
| | | | | *   85c19150d716e3051e86b824b3fc59a1ccc912ae 2020-11-08 (refs/pull/65/merge)
| | | | | |\    Merge ca02043fb46f131ed507d81b3df33cbac051c129● into 0b1896650c2ba7100df450d698752090e6ba3b22 
| | | | | | | 
| | | | | | * ca02043fb46f131ed507d81b3df33cbac051c129● 2020-10-19 (refs/pull/65/head, std-functions)
| | | | | | |   add needed system header for getpid()
| | | | | | | 
| | | | | | * 8b7043415736c33eddb6e010bcba6dd0feec69f5● 2020-10-19
| | | | | | |   add needed system header for chdir()
| | | | | | | 
| | | | | | * b9b7c7d808582b352aaff60e3783c886a180b736● 2020-10-19
| | | | | | |   add needed system headers for setuid() and setgid()
| | | | | | | 
| | | | | | * 91c500d36f56cd656d975dfc607fe9899af26e4a● 2020-10-19
| | | | | | |   add needed system header for close()
| | | | | | | 
| | | | | | * 3b77883f36a1bb328a872c83a15392f68e998498● 2020-10-19
| | | | | | |   add needed system headers for umask()
| | | | | | | 
| | | | | | * fc8951e2f4d401b39d2f733dd8fdfc3f3c3948d6● 2020-10-19
| | | | | | |   get rid of error_* variables and just #define them to their errno equivalents
| | | | | | | 
| | | | | | * c251a2222ffcc7ec733427b9f3b1041cd6edd22c● 2020-10-19
| | | | | | |   get rid of byte_zero() and use the default memset() instead
| | | | | | | 
| | | | | | * 42bef0d487632051185f77cc6332ab86c4ef0dd8● 2020-10-19
| | | | | | |   fix read() return value handling in getln2()
| | | | | | | 
| | | | | | * 55a6d4a130ee161f1da6d2116016e2adb3be1859● 2020-10-19
| | | | | | |   improve checks for return value of read() and write() and it's wrappers
| | | | | | | 
| | | | | | * bf9c7dabf7f1f9be0adaa2a10ded833054f8a01b● 2020-10-19
| | | | | | |   convert some additional substdio functions to return ssize_t
| | | | | | | 
| | | | | | * 163fd6258fc7766fc2e9911e428d8002eea17ca6● 2020-10-19
| | | | | | |   simplify qmail_puts()
| | | | | | | 
| | | | | | * 8f0f07e93bf5a8a0bfe6ed57b6d0ad9b2e63946b● 2020-10-19
| | | | | |/    use size_t and ssize_t in substdio
| | | | |/|   
| | | | | |   
| | | | | | * e0243345a407e7000f13a0f34cc173d78b948055● 2020-11-07 (refs/pull/122/merge)
| | | | | |/|   Merge 02ff7ddcee1680a573260e05f2f9456ddffe87b2 into 0b1896650c2ba7100df450d698752090e6ba3b22 
| | | | | | | 
| | | | | | * 02ff7ddcee1680a573260e05f2f9456ddffe87b2 2020-05-24 (refs/pull/122/head)
| | | | | | |   generate compile, load, makelib directly with cat
| | | | | | | 
| | | | | | * 7214b3383ce390ab07cdde60f9f8a912cc95b666● 2020-05-24
| | | | | | |   Make "ar" and "ranlib" configurable like "cc" and "ld" are
| | | | | | |   
| | | | | | | * 447fcab0269ad31fb2a73748789d3133f98dd6bc 2020-11-07 (refs/pull/181/merge)
| | | | | | |/|   Merge b2a089c9c561fe73a11b75356b7427005fa84b76 into 0b1896650c2ba7100df450d698752090e6ba3b22 
| | | | | |/|/  
| |_|_|_|_|/|   
|/| | | | | |   
| | | | | | | 
* | | | | | | b2a089c9c561fe73a11b75356b7427005fa84b76 2020-11-07 (refs/pull/181/head, Dakon-CI-GitHub)
| | | | | | |   GitHub: add CI build without obsolete functions
| | | | | | | 
* | | | | | | 6281b2176efb32293f75976e30885a6bd4ac1170● 2020-11-07
| | | | | | |   GitHub: add build set without catman pages
| | | | | | | 
* | | | | | | 172ce82f8484f597dc10411882ee34710eb80b1e● 2020-11-07
| |_|_|_|/ /    GitHub: also build on MacOS and with clang
|/| | | | |   
| | | | | |   
| | | | | | * acf8ff62ecee706bd61185712a21e91337ace76b 2020-11-07 (refs/pull/179/merge)
| |_|_|_|_|/|   Merge dd3963dfe79b62aa7c8ddbb12fa0d97e698f8e66● into 0b1896650c2ba7100df450d698752090e6ba3b22 
|/| | | | | | 
| | | | | | | 
| | | | | | * dd3963dfe79b62aa7c8ddbb12fa0d97e698f8e66● 2020-11-04 (tests-prioq)
| | | | | | |   Add some tests for prioq.
| | | | | | | 
* | | | | | | 0b1896650c2ba7100df450d698752090e6ba3b22 2020-11-04 (refs/pull/180/head)
| |_|_|_|_|/    add .gitattributes to keep some files out of release tarballs
|/| | | | |   
| | | | | |   
| | | | | | * 3950904fcf3c327a3ac88448850bc8a706ce12f6 2020-11-03 (refs/pull/89/merge)
| |_|_|_|_|/|   Merge fc3642a947c7c264bd72dbd023fa8253a730a844● into 129357b0a0eb10ec2b714ad198f63d9d016b8d59 
|/| | | | | | 
| | | | | | | 
| | | | | | * fc3642a947c7c264bd72dbd023fa8253a730a844● 2020-11-03 (refs/pull/89/head, notqmail-tai-leapsecs)
| | | | | | |   code cleanup
| | | | | | | 
| | | | | | * 7e1dba3eddee53417c3ece7dc5d811baf088e5d1● 2020-10-25
| | | | | |/    Fix leap second handling on systems running TAI
| | | | |/|   
| | | | | | 
* | | | | | 129357b0a0eb10ec2b714ad198f63d9d016b8d59 2020-10-19 (refs/pull/119/head)
| |_|_|/ /    qmail-local: close race window when creating file in tmp/
|/| | | |   
| | | | |   
| | | | | * fc5a1ba10f074a0b534a9e06c574dcf59306cdfd● 2020-11-03 (refs/pull/90/merge)
| |_|_|_|/|   Merge 5c6612203203c23ca5c3ece01ecf813d324b92b3 into 83e8e3215d2f664d6bb369562fcabb427fb18b5a 
|/| | | | | 
| | | | | | 
| | | | | * 5c6612203203c23ca5c3ece01ecf813d324b92b3 2020-10-19 (refs/pull/90/head, notqmail-maildir-uniqueness)
| | | | | |   Improve Maildir file uniqueness in qmail-local.c
| | | | | | 
* | | | | | 83e8e3215d2f664d6bb369562fcabb427fb18b5a 2020-09-12 (refs/pull/18/head)
| |_|_|_|/    qmail-remote: avoid recoding CRLF to CRCRLF
|/| | | |   
| | | | | 
* | | | | 0117ad3ff8a1354e0c6af3ebe2435c9bd5c4483a 2020-08-31 (refs/pull/176/head)
| |/ / /    GitHub: enable codescan
|/| | |   
| | | |   
| | | | * 920b644c4d7382b2a26dabab6177debbf0cfb08f 2020-07-26 (patches/notqmail/smtp-tls)
| | | | |   qmail-smtpd: factor out STARTTLS ehlo into it's own function
| | | | | 
| | | | * f4113db4af13aaf6e6a25be0254f8d617268701f● 2020-07-26
| | | | |   qmail-smtpd: move SSL implementation to it's own file
| | | | | 
| | | | * a9f70304662a87f5c475758626ff5f16e0ed0e02● 2020-07-26
| | | | |   Remove TLS define from the C code and conf-cc.
| | | | | 
| | | | * 2861b834b0bfe45cdbb5218b0fe89bdb034fc27d● 2020-07-26
| | | | |   Do not add directories to PATH in Makefile-cert.mk, update_tmprsadh.sh.
| | | | | 
| | | | * 600e3b74136872bdc8554c06ea76462a65cbf03d● 2020-07-26
| | | | |   conf-cc: remove -I/usr/local/ssl/include
| | | | | 
| | | | * a60a2ee8656f13e8a034166601024f5ab5c1d1cd● 2020-07-26
| | | | |   Makefile: remove -L/usr/local/ssl/lib qmail-remote, qmail-smtpd
| | | | | 
| | | | * f9031840b23a5729683b252ed6794d4085bdc485● 2020-07-26
| | | | |   Remove trailing whitespace in Makefile qmail-remote[.8c] qmail-smtpd.[8c]
| | | | | 
| | | | * 6579adb2672b297e9b099bd5f3c120608be09134● 2020-07-26
| | | | |   https://inoa.net/qmail-tls/ version 20200107
| | | | |   
| | | | | * d950cc34491afe90432cafcaeda61d1c1a9508e9● 2020-07-26 (patches/Gentoo/qmail-spp-tls)
| | | | | |   Combined TLS and qmail-SPP patch
| | | | | | 
| | | | | * c467ba6880aaecfe1d3f592a7738de88cb5ac79a● 2020-06-07
| | | | |/    SMTP authentication for qmail-smtpd, qmail-remote, v0.8.3
| | | | |   
| | | | | * 0dc6a3aa9cb3440fe589ca5384ea27d683f05625 2020-07-25 (patches/Gentoo/auth+tls)
| | | | |/    combined TLS and AUTH patches
| | | | |   
| | | | | * 6c91888f9360aeb1133dfdf94164d0a45d3aabf5 2020-07-08 (patches/notqmail/ext-todo)
| | | | | |   qmail-todo: remove unneeded define
| | | | | | 
| | | | | * ef65fa7ad25d633c4b142544fe591dd856df3134● 2020-07-08
| | | | | |   qmail-todo: add fixes that were already done in the notqmail code
| | | | | | 
| | | | | * cef9ded6c5cf43cfcea594f21405c219b3e7ee7a● 2020-07-08
| | | | | |   move EXTTODO to EXTTODO.md
| | | | | | 
| | | | | * f840a046b9cc6db12b1a10f1a5f8557d2eb8c9c7● 2020-07-08
| | | | | |   Remove all code guarded by ifndef EXTERNAL_TODO.
| | | | | | 
| | | | | * f574065202647086b0d317ac0c084ce1a85f843a● 2020-07-08
| | | | | |   remove EXTERNAL_TODO define.
| | | | | | 
| | | | | * e41e83528d509d6237d2e45de833c1155c8b10a7● 2020-07-08
| |_|_|_|/    add ext_todo-20030105.patch
|/| | | |   
| | | | | 
* | | | | b52f6d20817fd850144b087bcc668407947dfb1a 2020-07-08 (refs/pull/159/head)
| | | | |   reintroduce alloc_re()
| | | | | 
* | | | | 0f35ad122eaba02b66fcd8432dd06ae71141b978 2020-07-08 (refs/pull/162/head)
| | | | |   qmail-remote: fix warnings about get() being used with wrong pointer signedness
| | | | | 
* | | | | b832c07698ea195277d30558d0e8e070cca625cd 2020-07-08 (refs/pull/155/head)
| | | | |   move auto_uidq variable into spawn.c
| | | | | 
* | | | | a0b5dd0044200f84234b7e7243be9bfb72c3f4c7● 2020-07-08
| | | | |   move extern declaration of hier() to hier.h
| | | | | 
* | | | | ade7b953a30ece73625e97a4bd0ad76e5ca3ed3a● 2020-07-08
| | | | |   move extern declarations out of spawn.c into a dedicated header
| | | | | 
* | | | | 4d9143831d8100617b8423a1deb342229f0bffbe● 2020-07-08
| | | | |   use C89 declarations in hier.h
| | | | | 
* | | | | 8e2f1b03fdd61685d2c0f09e9392542c558a8bff 2020-07-08 (refs/pull/170/head)
| | | | |   qmail-smtpd: allow [] to appear in received lines
| | | | | 
* | | | | 7fbaaa3d09293232ad0d88ce89fdda940c2c2919 2020-07-06 (refs/pull/169/head)
| | | | |   remove orphaned now.c
| | | | | 
* | | | | 1c58ce6710051a30ec908f31c30523b6a89c9e7e 2020-07-04 (refs/pull/163/head)
| | | | |   trynpbg1.c: add missing include
| | | | | 
* | | | | 096e794bad8dd074caa67315a96d069121081571● 2020-07-04
| | | | |   select.h: do not redeclare select() if including <sys/select.h>
| | | | | 
* | | | | f3a2c387477fbf28948c54afc4f8c475535d04cf● 2020-07-04 (refs/pull/164/head)
| | | | |   qmail-send: use utimes() instead of utime()
| | | | |   
| | | | | * 100498b616a97f8c7a18e1e62b9b2c93fb6a4b8e 2020-07-01 (refs/pull/106/merge)
| |_|_|_|/|   Merge 6d05348d2044e2a9854e8e4f7d26c1573a6b2d5e into 619e7fcc9c90fe318d00d6e168e554f7b160e0ec 
|/| | | | | 
| | | | | | 
| | | | | * 6d05348d2044e2a9854e8e4f7d26c1573a6b2d5e 2020-07-01 (refs/pull/106/head, Dakon-compile)
| |_|_|_|/    get rid of "compile" command
|/| | | |   
| | | | | 
* | | | | 619e7fcc9c90fe318d00d6e168e554f7b160e0ec 2020-06-30 (refs/pull/161/head)
| | | | |   add missing includes for NULL
| | | | |   
| | | | | * 2bb94bec4d8b58fcecab170289d52f2f8c670d82 2020-06-21 (refs/pull/160/head)
| |_|_|_|/    check presence of struct utimbuf and use it if present
|/| | | |   
| | | | | 
* | | | | f3a33fdc5300a516682296018c580a13dc4af821● 2020-06-21 (refs/pull/158/head)
| | | | |   make now() an inline function
| | | | | 
* | | | | 8a5ef2bef9844e066cab9f4a48fccfc4c40b3de0 2020-06-18 (refs/pull/93/head)
| | | | |   remove inefficient maildirwatch
| | | | | 
* | | | | 8bdeb55fde017cf79c5dafb199d6a04f8c8bea00 2020-06-15 (refs/pull/154/head)
| | | | |   TARGETS: drop needless auto_uid*.o and auto_gid*.o entries
| | | | | 
* | | | | 2a497f1483239b111ce7cba7279f171aa7e02821 2020-06-14 (refs/pull/152/head)
| | | | |   use NULL instead of casting 0 to a pointer type
| | | | | 
* | | | | 763638654f6d62ebb129f1b60a2fbf9d409590f4● 2020-06-14
| | | | |   dns: remove dns_cname, unused.
| | | | | 
* | | | | 796dbc19914bd6039cca7b6e19c9d38a8eb3a10f● 2020-06-14
| | | | |   qmail-remote: remove flagalias and flagallaliases, now useless
| | | | | 
* | | | | 0ed82f58cd3869b3d0196b692c65685b8d1faf8b● 2020-06-14
| | | | |   qmail-remote: remove flagcname, deprecated feature
| | | | |   
| | | | | * 81825951ac448e66376349e9e761a0489996b003 2020-06-14 (refs/pull/121/head)
| | | | | |   dns: remove dns_cname, unused.
| | | | | | 
| | | | | * 2858e23e275d06192af329bfe2bdbb9e30f8a5c0● 2020-06-14
| | | | | |   qmail-remote: remove flagalias and flagallaliases, now useless
| | | | | | 
| | | | | * 56de24543a6aa33b38aa94ec00c52983bbd5dc71● 2020-06-14
| |_|_|_|/    qmail-remote: remove flagcname, deprecated feature
|/| | | |   
| | | | | 
* | | | | 17dc89aeebb0dc3bdcab9a34da6d44c40fd337dc 2020-06-14 (refs/pull/150/head)
| | | | |   predate: use better version of calling time()
| | | | | 
* | | | | c556526488eee88c58b4cb91a9bb31a3f97170ee● 2020-06-13 (refs/pull/151/head)
| | | | |   get rid of alloc_re()
| | | | | 
* | | | | 63d64d7cf894b83b43ce7c9ef9aa0b4cc0129723● 2020-06-13
| |/ / /    alloc.h: make first argument of alloc_re() void**
|/| | |   
| | | | 
* | | | 23acf56fffa0b317acdf5fad76387a9bda838f1c 2020-06-11 (refs/pull/145/head)
| | | |   use "return" to leave main() instead of _exit()
| | | | 
* | | | 3212ea53fde91e4394a5195b5fcd2d48d447735d● 2020-06-11
| | | |   make all main() functions return int
| | | | 
* | | | cff53ac08dadec27debcbfe6ea9bf76ae979247c● 2020-06-11 (refs/pull/144/head)
| | | |   make most output of auto-str readable
| | | | 
* | | | 21d898f96dd02101759fcea39980aa81381b532a 2020-06-11 (refs/pull/146/head)
| | | |   add all missing headers
| | | | 
* | | | b34b6bdc740c611ffbba62269a13a559a3bc9aac● 2020-06-11
| | | |   add missing include in byte.h
| | | | 
* | | | 7dd9d3d076820faf6cb71d1bd6b1aed44fee9a0b● 2020-06-11
| | | |   get rid of seek.a
| | | | 
* | | | b39f9cae21ae20f36b836f8811b08adcb7881178● 2020-06-11
| | | |   Fix Markdown formatting in examples.
| | | | 
* | | | b7fce5a9aba53123a8f8f2cb353d034cd0ce39c3● 2020-06-11
| | | |   Replace --- with ### for headings.
| | | | 
* | | | 44bedb7d602e26e5e9f24e8489a564f46e8a4973● 2020-06-11
| | | |   Make THANKS an unordered list.
| | | | 
* | | | 0d0816c0fd11a85c7667c1c933628864b4980580● 2020-06-11
| | | |   Format unordered lists more consistently.
| | | | 
* | | | 95b68a746c390564b531e0c269b4ec5e9762b0b4● 2020-06-11
| | | |   Wrap URLs in <>.
| | | | 
* | | | 544752f57e23395f409a724aa427f9228a704813● 2020-06-11
| | | |   Make CHANGES an unordered list.
| | | | 
* | | | c488390edf026bbbb62439990274a6ee9c72cc3b● 2020-06-11
| | | |   Tabs to spaces.
| | | | 
* | | | c0a12823677de70f50b79a1f59b932e081b720b1● 2020-06-11
| | | |   Replace ```foo``` with `foo` for one-liners.
| | | | 
* | | | e2009717cb61419bc817b2fbef3e2a0951b7e8eb● 2020-06-11
| | | |   Replace ``foo'' with "foo".
| | | | 
* | | | e276badd22c5bb81d6c0ebdde6da4d670a9332d9● 2020-06-11
| | | |   Replace --- with -- when it's simulating em-dash.
| | | | 
* | | | 8b510badbbbbb5e4888f1b2d2ba525c9aa51a2c6● 2020-06-11
| | | |   Improve README Markdown.
| | | | 
* | | | 07e448198e2a00743d0fe5cd5f7263b564cd583f● 2020-06-11
| |/ /    Append .md suffix to text docs. Adjust references.
|/| |   
| | |   
| | | * 079513a911aed8d10f90ae496922cf18d5e45180 2020-06-10 (refs/pull/36/head)
| | | |   Fix Markdown formatting in examples.
| | | | 
| | | * 0a7996e281b884eb2ed41487f513071da06da5ad● 2020-06-10
| | | |   Replace --- with ### for headings.
| | | | 
| | | * a0b2188e8ad54cf088f3d7ff6693cfd460217611● 2020-06-10
| | | |   Make THANKS an unordered list.
| | | | 
| | | * 48dcdd63577d11b6185720f1cb73b7d8526984ae● 2020-06-10
| | | |   Format unordered lists more consistently.
| | | | 
| | | * c1c3e31ae7f3069e7cf18e56f81e2b821f6241d3● 2020-06-10
| | | |   Wrap URLs in <>.
| | | | 
| | | * d6a49c5bb7f03a7d73b2bad730ca1730f91cb86d● 2020-06-10
| | | |   Make CHANGES an unordered list.
| | | | 
| | | * e271049010e0e8fde24cec3b88ed9281af3a5b64● 2020-06-10
| | | |   Tabs to spaces.
| | | | 
| | | * 3db562602211d1837486613ed3f09e80468528f3● 2020-06-10
| | | |   Replace ```foo``` with `foo` for one-liners.
| | | | 
| | | * 3e0fa0d9a90fc6ce78a70644cc6f7de6f9be80ab● 2020-06-10
| | | |   Replace ``foo'' with "foo".
| | | | 
| | | * d77c7225f7cf1c977d1c67611de0d959afde6a0c● 2020-06-10
| | | |   Replace --- with -- when it's simulating em-dash.
| | | | 
| | | * 2e86dabbe4140ac3413a56a304fd774791e923ca● 2020-06-10
| | | |   Improve README Markdown.
| | | | 
| | | * 9746afb1dadbe99094cf1659a0ea311c4d82fe7e● 2020-06-10
| |_|/    Append .md suffix to text docs. Adjust references.
|/| |   
| | |   
| | | * b36d52a0dd7315a969f2a9a7455717466e45be23 2020-06-07 (patches/Gentoo/qmail-spp)
| | | |   qmail SPP patch
| | | | 
| | | * 92520a31d1628a8da91343721b8d3c111ac5bb14● 2020-06-06
| | |/    SMTP authentication for qmail-smtpd, qmail-remote, v0.8.3
| | |   
| | | * c31fe4d0324f03fc9466c9bb8720102b4c773760● 2020-06-07 (patches/notqmail/qmail-spp-0.42)
| | | |   qmail-spp: remove needless initialization of global and static variables
| | | | 
| | | * 8060d75fe20619b93def79090d16446d33b540fb● 2020-06-07
| | | |   qmail-spp: fix incorrect usage of vfork()
| | | | 
| | | * c3c68df605ba3fa2e161dfca02085debfb31a2bd● 2020-06-07
| | |/    qmail SPP patch
| | |   
| | | * fc17cd1927fde8ee434f76ed899d5c7086197d38● 2020-06-07 (patches/netqmail/qmail-spp-0.42)
| | | |   qmail SPP patch
| | | |   
| | | | * 4aa45158ceaed4b575c681c4f8a85f7bd843b829 2020-06-03 (patches/notqmail/rcptcheck)
| |_|_|/    RCPTCHECK patch
|/| | |   
| | | | 
* | | | 2bd54624f2c71c17b3b7ab5c955e3b17cdc9c53b 2020-05-24 (refs/pull/103/head)
| | | |   tests: remove now useless tests again
| | | | 
* | | | 3f6f81b6a15af234493af55c05948b73ef966126● 2020-05-24
| | | |   get rid of str_copy() and use the default strcpy() instead
| | | | 
* | | | 11047ed0a85a6afa5d147e3c5080326cfe4a5fe0● 2020-05-24
| | | |   get rid of byte_diff() and use the default memcmp() instead
| | | | 
* | | | 8759bd432bd7154d537e78284a3771668829e9c3● 2020-05-24
| | | |   qmail-local: replace !byte_diff() with byte_equal()
| | | | 
* | | | 7959e15d7b7326fef40d64f187af87612288b78e● 2020-05-24
| | | |   get rid of str_diffn() and use the default strncmp() instead
| | | | 
* | | | f7ec63b8edb59a179aa98e670df9aaffcd8eaf34● 2020-05-24
| | | |   get rid of str_diff() and use the default strcmp() instead
| | | | 
* | | | 3f9f95ccac176b30c0a8064589c0638561178023● 2020-05-24
| | | |   get rid of str_len() and just use the default strlen() instead
| | | | 
* | | | a10fd0cb80e1297d01f0fd2a4d96c4ea01c0b7bb● 2020-05-24
| | | |   tests: verify byte_diff() and some str_* functions
| | | | 
* | | | 213e9f2465744fe34d2c16e9acaa582f5011da98● 2020-05-24
| | | |   tests: add results to .gitignore
| | | | 
* | | | 626e9a5a860a4a187bd0188b0019c72514da1ef7● 2020-05-23
| | | |   Remove qsmhook, long since replaced by preline.
| | | |   
| | | | * c175631f5f621d71c7fce328060de6869afc141f● 2020-05-23 (refs/pull/87/head)
| |_|_|/    Remove qsmhook, long since replaced by preline.
|/| | |   
| | | | 
* | | | 1beb767af91f28fd619421a46c885a8a48034ef3● 2020-05-23
| | | |   scan_*.c: convert to prototypes
| | | | 
* | | | c9e89f04af23f5ac55744476b9c88ada4b8fbb93● 2020-05-23
| | | |   scan.h: use prototypes, remove missing functions
| | | |     
| | | | *   7cac39099b81c588733965899242bd22a8034aee 2020-05-23 (refs/pull/129/head)
| | | | |\    Merge branch 'master' into scan-prototypes
| |_|_|_|/  
|/| | | |   
| | | | | 
* | | | | 5a214f931f1b5575e3cf4b037179eb5f665028c1 2020-05-23 (refs/pull/140/head)
| | | | |   remove unused substdio_bget()
| | | | |   
| | | | *   0a69ff2b142c4a5c84379de09761b2bb82dfe5c9● 2020-05-23
| | | | |\    Merge branch 'master' into scan-prototypes
| |_|_|_|/  
|/| | | |   
| | | | | 
* | | | | 72c58ae8b2b8d5185449c120fd0844be1933a899 2020-05-23 (refs/pull/126/head)
| | | | |   fmt_*.c: convert to prototypes
| | | | | 
* | | | | ada4bb3eddc37f74d9318b06551b601b62a45328● 2020-05-23
|/ / / /    fmt.h: use prototypes, remove missing functions
| | | | 
| | | * 6228fcbb1e523cedad8f608ec89f730d3cbe105a● 2020-05-23
| | | |   scan_*.c: convert to prototypes
| | | | 
| | | * c28f0ff4b98530c9512aec2adee919b160a2c86e● 2020-05-23
| |_|/    scan.h: use prototypes, remove missing functions
|/| |   
| | | 
* | | e90b5a7888155c1714468ad7c2860e714cd603e7● 2020-05-23 (refs/pull/139/head, refs/pull/105/head)
| | |   use POSIX compliant calling of head and tail
| | | 
* | | 1720e0b1be1ffc2ededf8366c6b4a5a831b31d52 2020-05-23 (refs/pull/124/head)
| | |   remove the custom allocator
| | | 
* | | 618c2d113760f1a98c33a135dd81693879c7e1d6● 2020-05-23
| | |   remove the "alloc" parameter from cdbmake_add() and cdbmake_split()
| | | 
* | | 43b7cb5e1c97005a3995c5d46da6ec5d4142e76e 2020-05-23 (refs/pull/130/head)
| | |   use default strdup() instead of open coding it
| | | 
* | | 2b3f38616ec0ae151e8556566fce4b4a3139fdd1 2020-05-22 (refs/pull/137/head)
| | |   add back accidentially removed typecast
| | |   
| | | * 2689abf6277e9f1bbb0ff4cee4a800905a8944c8 2020-05-22 (patches/netqmail/badmailfrom-wildcard)
| | | |   grammar cleanup in qmail-smtpd.8 to match the rest of that manpage.
| | | | 
| | | * 58d671f8e3b07f84a1e352edf88ff68d13f5c77e● 2020-05-22
| | |/    badmailfrom wildcard patch
| | |   
| | | * 3a22b45974ddd1230da0dfa21f886c3401bee020 2020-05-21 (notqmail-big-todo)
| |_|/    Apply Russell Nelson's big-todo.103.patch.
|/| |   
| | |   
| | | * 28f8ec8d5cbecf4d3d1f446a06ef64df4f220a69 2020-05-21 (patches/notqmail/smtp-auth)
| | | |   Remove FILES.auth.
| | | | 
| | | * b224a3ceb63ff8ebc57648bf304e079d0bf55023● 2020-05-21
| |_|/    SMTP authentication for qmail-smtpd, qmail-remote, v0.8.3
|/| |   
| | | 
* | | f6c2a90177f88c0353c75fb4aaa74d2ff3b1c533● 2020-05-20 (refs/pull/99/head)
|/ /    remove obsolete mail client wrappers
| | 
* | bf692cf45ec6d679ba194f80862f17db4c415eb4● 2020-05-20 (tag: notqmail-1.08)
| |   Note CVE and other overflow fixes in CHANGES.
| | 
* | a385b5e9b2fb8f84c48a14d49452918ad2f3fa45● 2020-05-20
| |   This is notqmail 1.08.
| |   
| | * 69f8ce0c4bbc48937c41283dbc65201eb8ed0a58 2020-05-20 (refs/pull/135/head)
| | |   Note CVE and other overflow fixes in CHANGES.
| | | 
| | * 7846b4ee3215af2efceaeddd4eeb456754f8248e● 2020-05-20
| |/    This is notqmail 1.08.
|/|   
| | 
* | 0ed8a00eec3520d2dcbcd73c8eaacc96ccfbd8e8● 2020-05-20
| |   Add missing include for strncmp, found in CI.
| | 
* | 95f86dafbb94c00dbbecd01fadb7c0c659aafe51● 2020-05-20
| |   Let NROFF be redefined in the environment.
| |   
| | * 1b937a7bd138665ab3fc0ffd86a86b7c3ef3f8da 2020-05-20 (refs/pull/132/head)
| | |   Add missing include for strncmp, found in CI.
| | | 
| | * 795244f93f76697445a99da2fddee656c6c77d58● 2020-05-20
| |/    Let NROFF be redefined in the environment.
|/|   
| | 
* | aa064ddaf2116a5ff210048dc30d75ce06169bd5 2020-05-19 (refs/pull/134/head)
| |   Make: make catman dependency rule portable
| | 
* | 6ba74b15e1beae4526cbe8a73411be9acf25e21b● 2020-05-19
| |   CI: make the NROFF tests to actually override nroff
| | 
* | 348e57528018d5b93e8d7e81d622d553c4a86401 2020-05-19 (refs/pull/133/head)
| |   use unsigned variables in qmail-send
| | 
* | 22b04144743507f991d142a4b67f6fe550c64361● 2020-05-19
| |   use unsigned variables to store the result of str_len()
| | 
* | 9aba4fcedcb5005df99354261f0a427808a8f2d6● 2020-05-19
| |   use unsigned variables in qmail-pop3d
| | 
* | e4fa409afaf82ba7e3beab24cbb1bb6268ca7604● 2020-05-19
| |   use unsigned variables for array offsets in main() of qmail-local
| | 
* | ba8c208bbdde16cc8da9ee7ff0b14086a65b37c3● 2020-05-19
| |   fix possible length calculation overflow in stralloc_catb() and stralloc_copyb()
| | 
* | ad73be052759c9472bcd1b871bd402eacf80da78● 2020-05-19
| |   fix string length calculation overflow in quote() helper
| | 
* | 628b8661ef57c9d5badd1a808482f88a31c4da4d● 2020-05-19
| |   fix possible signed integer overflow in commands() (CVE-2005-1514)
| | 
* | 0cbc0ef8319a4b077cf1abc93c498d53eecb68ef● 2020-05-19
| |   fix possible overflows in array allocation length calculations
| | 
* | b105c46dd05027f2cdb551e721253b3b8319ba16● 2020-05-19
| |   fix signedness wraparound in substdio_bput()
| | 
* | 6d879abedf97733273fc1011d44b26f8980d04fa● 2020-05-19
| |   unwrap lines in substdio_bput()
| | 
* | e794b49925c1a1762aece35a5d7e09f1444d1ab7● 2020-05-19
| |   fix signedness wraparound in substdio_put() (CVE-2005-1515)
| | 
* | 2fe3ac71d35e68d1d42273a3925b9f7dc8020742● 2020-05-19
| |   genalloc: make sure allocation sizes never overflow
| | 
* | 8038bf6547e85382dcb6696c55bec8d8b127e668● 2020-05-19
| |   genalloc: make sure the new count variable does not overflow
| | 
* | 105e01c5cbdd7f042043f595794f12ff8664dda3● 2020-05-19
| |   add compat version of __builtin_{add,mul}_overflow()
| | 
* | edf54c818e5549026098acd95ae44f0ddf6a7722● 2020-05-19 (refs/pull/128/head)
| |   include alloc.h in gen_allocdefs.h
| | 
* | 8fd5473a2572c7ac39459f5059f1cc8bdc32d59c● 2020-05-19
| |   genalloc: unfold tail code
| | 
* | 8b68e39ca627bf0bbb3432beed0143e088af4307● 2020-05-19
| |   genalloc: invert code flow
| | 
* | a41404c584fb9c7f6a8d0980fac2dd6016ccf2cc● 2020-05-18
| |   genalloc: do not rename parameter names
| | 
* | 5806288c361b44e976e7db89a79c11efd7af068a● 2020-05-18
| |   genalloc: generate C89 function interfaces
| | 
* | 20c4811b01dce3946ee30fe13795c67316acda29● 2020-05-18
| |   genalloc: unify GEN_ALLOC_readyplus and GEN_ALLOC_ready internals
| | 
* | 721a288c135438335a7a5031f3237612d3213e1a● 2020-05-18
| |   genalloc: reorder callers
| | 
* | 9175e79ba34ff745dfdb0948ba04aedff07f2f8e● 2020-05-18
| |   genalloc: reorder functions in header
| | 
* | d52e4b288fe54c6b056d51b0e42768f95d6563d0● 2020-05-18 (refs/pull/131/head)
| |   add GitHub actions test build
| | 
* | 30a44402454eca17beb8339a8b661cc8683be5b3 2020-05-14 (refs/pull/92/head)
| |   qmail-pop3d: exit 1, pronto, if running as root.
| | 
* | 2a469cc632ac74bc2cb915e6f8cd0ccc330d88f6● 2020-05-14
| |   qmail-popup: leave kid's stderr alone for logging.
| | 
* | 691e99b6dff1aef5dfe8e5ab5e73da8bc3145bc7 2020-05-14 (refs/pull/79/head)
| |   add exit.h to qmail-pw2u.c and qmail-qmtpd.c.
| | 
* | 174a1ba70e9b8e3109570a4cbc2ff70614aacbd6● 2020-05-14
| |   remove exit.h from forward.c, maildirwatch.c.
| | 
* | 1a5f99f9b545d78a7c9354f6f10162fb6fd42653● 2020-05-14
| |   include unistd.h in exit.h to declare _exit.
| | 
* | 7e89111010ad51dedb0038a052d4991f06e24944 2020-05-12 (refs/pull/72/head)
| |   change function signatures to C89 style that were touched in last commit
| | 
* | f7b6124770eb34b2e8ff835928cf30e8f6e8d976● 2020-05-12
| |   remove HASSHORTSETGROUPS test, use system headers and types instead.
| | 
* | b75af9b4adebaa3208029df33a82f5fdabf230b0 2020-05-11 (refs/pull/113/head)
| |   rename local variables shadowing global variables of the same name
| | 
* | 8fa4b8b8dc70bab372d2ffd8fcb05546bfe7e2cc 2020-05-11 (refs/pull/102/head)
| |   add minimal unittest documentation
| | 
* | 0609d1b3eb8381cf3144f362c3e7bad668d5511c● 2020-05-10
| |   tests: check stralloc allocation size behavior
| | 
* | edb4c964489bdf862bf0db918ebe264bf12bb3e3● 2020-05-10
| |   Add a test target and one unit test, using Check.
| |   
| | * e8a1e037afc8729bd65d4bda36dedf444f301c0f● 2020-05-19 (netqmail-CVE-2005-minimal)
| | |   fix additional length overflows
| | | 
| | * bb92ea678c2a2a524d2ee6e9d598275a659168d2● 2020-05-19
| | |   mimimum fix for CVE-2005-1513
| | | 
| | * dc617a2f2d31e4c448b806791b3f8736cf9d1ffb● 2020-05-19
| | |   fix possible signed integer overflow in commands() (CVE-2005-1514)
| | | 
| | * 5540e1b47ac043033e6661b4e04dcaf958db0110● 2020-05-19
| |/    fix signedness wraparound in substdio_put() (CVE-2005-1515)
| |   
| | * 6d02c0e233d29091e38f9760201a2f70c6375e9e 2020-05-06 (refs/pull/107/merge)
| |/|   Merge bef9faf2a0ce5ccdb505eff63d926320bd390d7d● into 2ad04dbba5e70dd1ce9ebef8c492ec846371e42b 
|/| | 
| | | 
| | * bef9faf2a0ce5ccdb505eff63d926320bd390d7d● 2019-10-17 (refs/pull/107/head, Dakon-configure)
| | |   TravisCI: use configure
| | | 
| | * 09549b2d779ce50649a4afe5ab6fc2c522f30fe8● 2019-10-17
| | |   remove needless space in conf-qmail
| | | 
| | * bb01855879a6f0cf9f47f92997aaa7ecd6e916a2● 2019-10-17
| | |   add configure program for easy configuration
| | | 
* | | 2ad04dbba5e70dd1ce9ebef8c492ec846371e42b 2020-04-14 (refs/pull/125/head)
| | |   CirrusCI: disable 11.2 image for the moment
| | | 
* | | 53d89cb9b5f7efba9a52565889b5b54dcedfae07● 2020-04-14
| | |   CirrusCI: update to FreeBSD 12.1
| | | 
* | | 83cba28f3d70445f150bbcbebec16a1a2612ef29 2020-03-07 (refs/pull/116/head)
| | |   allow to override nroff during build, and optionally skip installing cat pages
| | |   
| | | * 3b4861cbd406e4b30b157baa7d206562cd8fae49 2020-04-13 (refs/pull/123/head)
| |_|/    remove alloc.c, wrapper over malloc and use malloc directly
|/| |   
| | | 
* | | 7c7122a2d3a18af32c977ea9b34ebe526cc2fa0e 2020-02-03 (refs/pull/108/head)
| | |   remove the need for exit.h in named pipe bug check
| | |   
| | | * 69793eb3052733e038abb68f36f24cfd6c2474e1 2020-02-03 (refs/pull/117/head, Dakon-queue-perm)
| |_|/    make the queue directory world readable
|/| |   
| | | 
* | | 10057e900be66128f16fb7fb8d4d2a08f6934742 2020-02-03 (refs/pull/118/head)
| | |   Add acknowledgement for the bug fix contribution to qmail-local.c
| | | 
* | | e09fa0bd55f42d93a3c174405da411706a7c885a● 2020-01-02 (refs/pull/114/head)
| | |   optionally create a systemd service file
| | | 
* | | 8f0f1403e461b588596a8c34316ce5494f79540e 2019-12-30 (refs/pull/54/head)
| | |   replace many pobox.com URLs
| | | 
* | | 3300f247a6c11e7bc5488314062ae5defe7c8299 2019-12-28 (refs/pull/110/head)
| | |   add deprecation message to obsolete mail client wrappers
| | | 
* | | 1961479e0099897c37621362bf872306cb70dcc1 2019-12-27 (refs/pull/111/head)
| | |   move some variables to a more local scope
| | | 
* | | 75a935b5def0bc2fe3e282c643ca5f48fe8c9a90 2019-12-27 (refs/pull/109/head)
| | |   fix possible integer overflow in alloc()
| | |   
| | | * f9103270f603eda4147ebc06861f9a49f19c2dc0● 2019-12-27 (refs/pull/50/head, spawn-filter)
| | | |   ignore regex compilation error for invalid regular expressions
| | | |     
| | | | *   a1be2d963a220e72bcdc41e2bbf4ec297b97e07d 2019-12-27 (refs/pull/37/head)
| | | | |\    Merge branch 'master' into alloc_overflow-fix
| |_|_|_|/  
|/| | | |   
| | | | | 
* | | | | cd8193f4bfaaa2650c2472763ec6bf41ae882539● 2019-12-26 (refs/pull/85/head)
| |/ / /    add missing return types to main()
|/| | |   
| | | | 
* | | | ecf4d7faa041aed1345f47d931f0e2e8fa6a6b70● 2019-09-22 (refs/pull/101/head)
| | | |   use system headers for files introduced since netqmail-1.06
| | | | 
* | | | b05ec6cbdacdf40d6c75326394461e22b7f8ab20● 2019-09-22
| | | |   Apply Jonathan de Boyne Pollard's any-to-cname patch.
| | | | 
| | | * b0f59739fc3db8dbe9d4159df5915bc8764a7e18● 2019-08-31
| | | |   added #ifdef HAS_BUILTIN_OVERFLOW to use __builtin_add_overflow()
| | | | 
| | | * 0c5582375e974669d0f2d1f378243bcc4cb347bc● 2019-08-31
| | | |   added trybltnoverflow.c to check for builtin_overflow_add support in the compiler
| | | | 
| | | * 45269a8a147bac37e5d7157f758d54d8ddbd7ed5● 2019-08-31
| | | |   added check for compiler suport for builtin_overflow_add()
| | | | 
| | | * 2159c37da559026eda20f11e2b538bf05ead7be1● 2019-08-31
| | | |   use __builtin_add_overflow
| | | | 
| | | * 6d1578c0f488b31d70cf3d4476a414066e446f3f● 2019-08-31
| | | |   fix overflow in alloc.c
| | | |     
| | | | *   51847d8909892c9486444f338df0118618c017ac 2019-09-22 (refs/pull/97/head)
| | | | |\    Merge branch 'master' into notqmail-dns-any-to-cname
| |_|_|_|/  
|/| | | |   
| | | | | 
* | | | | 9ef349af5c83f54b4d62fdcc90e9603aa55c149d 2019-09-22 (refs/pull/100/head)
| | | | |   change function signatures to C89 style that were touched since netqmail-1.06
| | | | | 
* | | | | b8fb15d71f70bddff87ea5aa2b56b0da0f91bea7● 2019-09-22
| | | | |   ipme: fix detection of multiple IP addresses on the same link
| | | | | 
* | | | | 390afb7cf05fbafef68788c6388037bf87d1f862● 2019-09-22
| | | | |   ask kernel for the correct buffer size to satisfy SIOCGIFCONF before looping
| | | | | 
| | | | * 97a635e8a7fd4d57c8ec6614484ed03cd2831d76● 2019-09-01
| | | |/    Apply Jonathan de Boyne Pollard's any-to-cname patch.
| | | |   
| | | | * 9d6c05d092e3cf94a6591cd5420f8026fcd4691f 2019-09-22 (refs/pull/96/head)
| | | | |   ipme: fix detection of multiple IP addresses on the same link
| | | | | 
| | | | * d24a34857afc33ed11da9ba62736c0bb9b3e5b94● 2019-09-22
| |_|_|/    ask kernel for the correct buffer size to satisfy SIOCGIFCONF before looping
|/| | |   
| | | | 
* | | | cc9df61c573e7cd0bc2f2758d5cd65f95c4614d1● 2019-09-04
| | | |   use <stdint.h> to get a really portable 32 bit unsigned type
| | | |   
| | | | * 43e2641de84d22c85f5955e44dd1a260aaa5a036 2019-09-04 (refs/pull/30/head)
| |_|_|/    use <stdint.h> to get a really portable 32 bit unsigned type
|/| | |   
| | | | 
* | | | 56e7c4a74087d47889355c2c55352cc4d0482020 2019-09-03 (refs/pull/8/head)
| | | |   qmail-inject: do not parse header recipients if "-a" is given
| | | |   
| | | | * 6b61c52ebc65fa49391a9399684af687346caf1b 2019-09-03 (refs/pull/50/merge)
| |_|_|/|   Merge b27c6ae498c547570ba813638011fddc44aa7c46● into fc0227c2a792f6674674447395bd7c4209df831c● 
|/| | |/  
| | |/|   
| | | | 
| | * | b27c6ae498c547570ba813638011fddc44aa7c46● 2019-09-03
| | | |   removed wildmat funciton
| | | | 
| | * | a656f62e79c28b89da2ea1e1534a495d06cb7447● 2019-09-03
| | | |   removed wildmat function
| | | | 
| | * | 9ad653402daf55dc0a13469848008ac73f202a48● 2019-09-03
| | | |   updated for adding spawn-filter
| | | | 
| | * | 7d377c1be84aa4889a217e99ad6fc1272efe1e5b● 2019-09-03
| | | |   added reference to spawn-filter
| | | | 
| | * | ca1ee988b2b61a77b362a762bf181fe9fd676e5a● 2019-09-03
| | | |   added note on QMAILLOCAL environment variable
| | | | 
| | * | 8241af826fc313a59bd2c224353fffc997fc0bb3● 2019-09-03
| | | |   added note on QMAILREMOTE environment variable
| | | | 
| | * | 20a989d6eda1008784809306d4358aaf05cf3439● 2019-09-03
| | | |   added reference to filterargs control file used by spawn-filter
| | | | 
| | * | 88201e311beeb006c97c0483bddcf6cf6ceba310● 2019-09-03
| | | |   fix dependencies to envread()
| | | | 
| | * | 10dcf812b6bf30613461c7bf45a0485c0bfcc35c● 2019-09-03
| |/ /    added spawn-filter for alternate qmail-remote/qmail-local
|/| |   
| | | 
* | | fc0227c2a792f6674674447395bd7c4209df831c● 2019-09-02
| | |   remove TODO.
| | |   
| | | * 907100f60cb062d133cc2663bcb4b1c8117e79bc 2019-09-02 (refs/pull/68/head)
| |_|/    remove TODO.
|/| |   
| | | 
* | | e604c91941478c74b73f17946847ab0d4546b7bc● 2019-09-01
| |/    CI: add FreeBSD builds via CirrusCI
|/|   
| |   
| | * 893f4bd344d612c0b271059e4476b8eddeb87a97 2019-09-01 (refs/pull/98/head)
| |/    CI: add FreeBSD builds via CirrusCI
|/|   
| |   
| | * 7bea29adb781f06a9a1c633e965d6b46f84e7542 2019-08-27 (refs/pull/70/merge)
| |/|   Merge 110bf49647758f0720b3f38244612a147a46586d into 143784b98ec99a96da60edccd2095cbbc77c6577● 
|/| | 
| | | 
| | * 110bf49647758f0720b3f38244612a147a46586d 2019-08-27 (refs/pull/70/head)
| |/    remove register storage class declaration from codebase.
|/|   
| |   
| | * 005c5752ae4c25fcead04bb4cbe3677470324f8a 2019-08-27 (refs/pull/77/merge)
| |/|   Merge 3b2ef5d932be9bf5e23719de28f938f60096e6a6 into 143784b98ec99a96da60edccd2095cbbc77c6577● 
|/| | 
| | | 
| | * 3b2ef5d932be9bf5e23719de28f938f60096e6a6 2019-08-27 (refs/pull/77/head)
| | |   quote.c, quote.h function declarations are now C89.
| | | 
| | * 33db3bf64737c3bf77582debcce108f18021955a● 2019-08-27
| | |   fifo.c, fifo.h function declarations are now C89.
| | | 
| | * 76f3576a7e2aa8a47761923480d3cf7acf061e5f● 2019-08-27
| | |   wait_*.c, wait.h function declarations are now C89.
| | | 
| | * 659d0c21f41b4062db882b235e56d26e053c079e● 2019-08-27
| | |   ndelay*.c, ndelay.h function declarations are now C89.
| | | 
| | * 8a9d80d97d04082ef7ca9a184f7b1ec44d19db8e● 2019-08-27
| | |   seek_*.c, seek.h function declarations are now C89.
| | | 
| | * 7032a3693baefe6f6305d4d06f81a658a227dad0● 2019-08-27
| | |   open_*.c, open.h function declarations are now C89.
| | | 
| | * ac7a2eb21283d05b981b6856e127fe3fb7f1f5bf● 2019-08-27
| | |   prot.c, prot.h function declarations are now C89.
| | | 
| | * 0e2316ba85fe6276dabb5b8ed80f09b924c97cfb● 2019-08-27
| | |   lock_*.c, lock.h function declarations are now C89.
| | | 
| | * cb274e19e432f6e622e22f5c8d2a3da761336d79● 2019-08-27
| | |   fd_copy.c, fd_move,c, fd.h function declarations are now C89.
| | | 
| | * 7b70927c94213cdfb4a5477a6eb6fa6d87171f56● 2019-08-27
| | |   coe.c, coe.h function declarations are now C89.
| | | 
| | * ca2ce0932b3d72ad8f1b141449f31d0c7533c5ea● 2019-08-27
| | |   all main entry-point declarations are now C89.
| | | 
| | * dce6c4d36643c114f7e79642ece726ed8100b093● 2019-08-27
| |/    add missing return types to main()
|/|   
| | 
* | 143784b98ec99a96da60edccd2095cbbc77c6577● 2019-08-27
| |   Run alternate qmail-remote by setting QMAILREMOTE.
| |   
| | * e8e169c03815f9650bd2108bdb8e7a57fd5cf5ab● 2019-08-27 (refs/pull/71/merge)
| |/|   Merge 60d60d0afe18096a3913fd28d2731ebac5ea4572 into 7db974d412ac0e6e7ed80df9738ebbb3d1eaa582● 
|/| | 
| | | 
| | * 60d60d0afe18096a3913fd28d2731ebac5ea4572 2019-08-25 (refs/pull/71/head)
| | |   Remove the install script.
| | |   
| | | * 175529d0dfe0900978a8975baa1dd986474425d9 2019-08-26 (refs/pull/46/head)
| |_|/    Run alternate qmail-remote by setting QMAILREMOTE.
|/| |   
| | |   
| | | * 33f38ece6530c617b216400bb74accfb331eb874 2019-08-26 (refs/pull/67/merge)
| |_|/|   Merge 59f3d296e9af096f4d4ae78e0f5b39583377db84 into 7db974d412ac0e6e7ed80df9738ebbb3d1eaa582● 
|/| | | 
| | | | 
| | | * 59f3d296e9af096f4d4ae78e0f5b39583377db84 2019-08-25 (refs/pull/67/head)
| | |/    remove qmail-upq.
| | | 
* | | 7db974d412ac0e6e7ed80df9738ebbb3d1eaa582● 2019-08-25
| | |   add forward declaration for scan_8long()
| | | 
* | | f224e8c03268b5f9d9feae0da928af2e54b6dde6● 2019-08-25
| | |   add forward declarations for cdbmss_*
| | | 
* | | c4425a94f38d291daaab5659cacdf2ac806be710● 2019-08-25
| | |   add forward declarations for functions from hier.c
| | |   
| | | * a2bc2ec3cc30a42f52057dfe4755f492267e5fc3 2019-08-25 (refs/pull/64/head)
| | | |   add forward declaration for scan_8long()
| | | | 
| | | * 9c3711f127614ac1b9d58b5ac8b6e303f81b3ba3● 2019-08-25
| | | |   add forward declarations for cdbmss_*
| | | | 
| | | * e6ae57c2911e3bfadc6801d7bddb56167d7d212a● 2019-08-25
| |_|/    add forward declarations for functions from hier.c
|/| |   
| | | 
* | | 089e7543ed6d517804586f0404bab870949eb549● 2019-08-25
| |/    TravisCI: avoid needless indirection for environment variable
|/|   
| |   
| | * 5b99ccbd5ea74f97cb2b6b26cf74876a687f0465 2019-08-25 (refs/pull/58/head)
| |/    TravisCI: avoid needless indirection for environment variable
|/|   
| |   
| | * 889c75e0946caf787717fef369e5adece91f21bf 2019-08-25 (refs/pull/35/merge)
| |/|   Merge 8756cbb9b76e78c0f2a205b3a51f8f5de7d9c61c into eed60957b5cf8de9abf8fd3bac4d329586536e01● 
|/| | 
| | | 
| | * 8756cbb9b76e78c0f2a205b3a51f8f5de7d9c61c 2019-08-25 (refs/pull/35/head)
| | |   last commit: remove auto_qmail
| | | 
| | * 54b487e69367f29aa0391842032a9caa8a3e78c0● 2019-08-25
| | |   make qmail-inject die_chdir error message more sendmail-like.
| | | 
| | * 3192536f3bdfd40f14565e6285d40e079b381dd0● 2019-08-25
| | |   auto_qmail_users
| | | 
| | * 7651981c5449ebed250d575f8f36fe0a873fac4f● 2019-08-25
| | |   auto_qmail_queue optional
| | | 
| | * 93caecb490f9aefc542c620712a99a18bd7e60d8● 2019-08-25
| | |   auto_qmail_queue
| | | 
| | * cf70c28ea80b4165264cb5f8edfa668484a6c912● 2019-08-25
| | |   auto_qmail_control
| | | 
| | * 3391e88392cfdc3cae8d2e646df093f6c8faedae● 2019-08-25
| | |   auto_qmail_bin
| | | 
| | * 0332428342f143e8a05426ddf570eb7ac93b02e8● 2019-08-25
| | |   QMAILUSERS
| | | 
| | * e196e01e0c374ef178c47bec7f7d66064a425f31● 2019-08-25
| | |   QMAILQUEUE
| | | 
| | * 3a86ef6d27017870bfc43f69ebcfb7be731b4025● 2019-08-25
| | |   QMAILCONTROL
| | | 
| | * 3fa401ff50bde3d32fd7a953baff459b45e1be9e● 2019-08-25
| | |   QMAILBIN
| | | 
| | * a5bb55d36b76276c2f8c43bc753d5375b0cd6664● 2019-08-25
| | |   QMAILALIAS
| | | 
| | * 319df5061985451951b5a63fc140f55bbd597af9● 2019-08-25
| | |   qmail-smtpd: auto_qmail_bin, auto_qmail_control
| | | 
| | * ed3898ff913eddccadbdb61b53f324b0009177dc● 2019-08-25
| | |   qmail-showctl: report all auto_qmail_* directory paths.
| | | 
| | * a3f218bc413c963d6786a737c5acad0620c4d6b8● 2019-08-25
| | |   qmail-send: auto_qmail_bin, auto_qmail_control, and auto_qmail_queue
| | | 
| | * 59f98deb22a50c870cb160f55fd61a9ba62f9c66● 2019-08-25
| | |   qmail-rspawn: chdir to auto_qmail_bin before execv.
| | | 
| | * 0c42822c60d9e6c83138d6848b81031abf758df6● 2019-08-25
| | |   qmail-remote: auto_qmail_control, auto_qmail_queue.
| | | 
| | * 9baccd9877f4171c48607296f3fba4a105d4738f● 2019-08-25
| | |   qmail-qmtpd: auto_qmail_bin, auto_qmail_control
| | | 
| | * 0fcda4cd6f789fda4e0c7a9d0f9b4340079d2c25● 2019-08-25
| | |   qmail-qmqpd: auto_qmail_bin, auto_qmail_queue
| | | 
| | * 41b73ee684108fa6bc31ad74f6265a3d0ebd3118● 2019-08-25
| | |   qmail-lspawn: depend on -bin, -qmail, and -users.
| | | 
| | * 538ec2bc2a635182ed0ef66a056a223f66df233c● 2019-08-25
| | |   qmail-inject: auto_qmail_bin, auto_qmail_control
| | | 
| | * 5d59a9f3d69f131a9ad385105e8422d41d0232ba● 2019-08-25
| | |   add conf-qmail-* dependencies to various install targets.
| | | 
| | * e3147339447241b3577a5dce7a015a03df6662ba● 2019-08-25
| | |   split conf-qmail in to per-directory configuration.
| | | 
| | * 3986a7f3cb4f8aed853f1b23bd66178fdb7d6d27● 2019-08-25
| | |   Makefile: de-SPLIT.
| | | 
| | * 65a0e659d4dadb70d4353c74e077fd0c19b82866● 2019-08-25
| | |   Makefile: de-SPAWN.
| | | 
| | * 94a36e83c8bbd8d7294b7a22eb8b42e40b7607ab● 2019-08-25
| | |   Makefile: de-BREAK.
| | | 
| | * dd97c9dbb96df1dc81a0857519084900047b5748● 2019-08-25
| |/    Makefile: de-QMAILHOME.
|/|   
| |   
| | * 5eeef0ea6fab500b6efc7045fa05198a0cada19a 2019-08-25 (refs/pull/43/merge)
| |/|   Merge 7a10f657054cf31cdeefdcde7c5b03c1a1bd5b2d into eed60957b5cf8de9abf8fd3bac4d329586536e01● 
|/| | 
| | | 
| | * 7a10f657054cf31cdeefdcde7c5b03c1a1bd5b2d 2019-08-25 (refs/pull/43/head, pr-remove-readwrite-h)
| |/    cleanup: remove readwrite.h, use unistd.h instead.
|/|   
| |   
| | * 8096d4422bc2a4635845c9e82891e2553f38ef34 2019-08-25 (refs/pull/44/merge)
| |/|   Merge ab198937360aaf0d7f8382be557a226d9efb30c5 into eed60957b5cf8de9abf8fd3bac4d329586536e01● 
|/| | 
| | | 
| | * ab198937360aaf0d7f8382be557a226d9efb30c5 2019-08-25 (refs/pull/44/head)
| | |   cleanup: remove exit.h, use unistd.h instead.
| | | 
| | * 7743d6aa95300c8d6380dfb97151c5c4baf65424● 2019-08-25
| | |   remove exit.h from forward.c, maildirwatch.c.
| | | 
| | * bb1b4a78ea5f42bd2be2d358c741598f8731e89c● 2019-08-25
| | |   add exit.h to qmail-pw2u.c and qmail-qmtpd.c.
| | | 
| | * b83e827e60990db19f31565c8163798a170a3eb9● 2019-08-25
| |/    include unistd.h in exit.h to declare _exit.
|/|   
| |   
| | * d13b047c943e839c6cf39f2a6a692ca14ddef047● 2019-08-25 (TravisCI)
| |/    TravisCI: test external patch(es)
|/|   
| |   
| | * babf55fedf46d889f7295ca01332f995993157ef 2019-08-25 (refs/pull/45/head)
| |/    use uid_t and gid_t
|/|   
| | 
* | eed60957b5cf8de9abf8fd3bac4d329586536e01● 2019-08-25
| |   remove systype and attendant platform detection.
| |   
| | * 97c43155ea650c46a0746c070adee5f9a09fc3c9 2019-08-25 (refs/pull/34/head)
| |/    remove systype and attendant platform detection.
|/|   
| | 
* | 77f058cb8c01cc257624b0f4e1d07979b69a4e12● 2019-08-25
| |   remove dnsmxip.
| | 
* | 123fa007ca4e6e5ba55c62775c5d24a6c3ab794d● 2019-08-25
| |   remove dnscname.
| |   
| | * cb838b5400f2fe2eb00623ccf352a9f8f882be13● 2019-08-25 (refs/pull/69/head)
| | |   remove dnsmxip.
| | | 
| | * aa5b01854d3d7fc03cefb47f53507371b2d1fb89● 2019-08-25
| |/    remove dnscname.
|/|   
| | 
* | fcfed83f715a788718280bdbeb2f4a44a4ab1011● 2019-08-25
| |   remove unused variable r from maildir.c.
| |   
| | * f844095392010bcd8c582dd5563061946b9067f8● 2019-08-25 (refs/pull/78/head)
| |/    remove unused variable r from maildir.c.
|/|   
| | 
* | d9fb9363e2db1648d7405ba543899d5020189ec3● 2019-08-25
| |   use correct system headers for fork()
| |   
| | * 60ea85e1ff5085abcf88491d3bbe6c461a7b9b06 2019-08-25 (refs/pull/82/head)
| |/    use correct system headers for fork()
|/|   
| | 
* | 77d529483b0359852224104b43e9321061fe0718● 2019-08-25
| |   include stdlib.h and stop redeclaring malloc, free in alloc.c
| |   
| | * 14ef2aeef1f5ce1808f67810f552547982758cf8 2019-08-24 (refs/pull/81/head)
| |/    include stdlib.h and stop redeclaring malloc, free in alloc.c
|/|   
| | 
* | ff45251efbbb6518cabc8ec39a89cd3eff73af27● 2019-08-24
| |   include unistd.h in readwrite.h to declare read, write.
| |   
| | * 69cb502d9cdaed04bdb09227f2eb3d980de94ef0 2019-08-24 (refs/pull/80/head)
| |/    include unistd.h in readwrite.h to declare read, write.
|/|   
| |   
| | * 184d858dbdbd009ffaf491aa774d93ed9f479f18 2019-08-22 (refs/pull/75/head)
| |/    lock_*.c, lock.h are now C89.
|/|   
| |   
| | * 3d4ce629ee8255aa6fe1906e76c024ecc339de45 2019-08-22 (refs/pull/74/head)
| |/    fd_copy.c, fd_move,c, fd.h are now C89.
|/|   
| |   
| | * 453002bc430736f9af8864bca455812b6c21a2c0 2019-08-22 (refs/pull/73/head)
| |/    coe.c, coe.h are now C89.
|/|   
| |   
| | * b04d366fe85c5f969e6242089b1a941d1083a753 2019-08-19 (notqmail-smtpd-spf)
| |/    Apply Jana Saout's qmail-spf-rc5.patch
|/|   
| |   
| | * 2dc6aeb56c981323ac42d6e43884f55c1d72f459 2019-08-19 (notqmail-smtpd-logging)
| | |   qmail-smtpd: log the name of any unimplemented command.
| | | 
| | * b873edd626323298def9443549ad29e9419e9a68● 2019-08-19
| | |   qmail-smtpd: log pid in square brackets after program name.
| | | 
| | * 80714fed9893544a961c7f59ac9f0fda42cb3e52● 2019-08-19
| | |   add errbits.[ch] deps to errbits.o, qmail-smtpd.o in Makefile.
| | | 
| | * af0baaccd3ed3ffa8166d056e8ac76a4935ec433● 2019-08-19
| |/    Apply Andrew Richards' qmail-logmsg logging patch, v1.3.
|/|   
| |   
| | * b2f5ad68ffcb6d15c9cc2bf3ddf4e43e2d4991fa 2019-08-19 (notqmail-dns-oversize)
| |/    Apply Christopher K. Davis's oversize DNS packet patch.
|/|   
| |   
| | * 23a0d13119135568632cb11ef272c1c6feb98092 2019-08-19 (notqmail-big-concurrency)
| | |   conf-spawn cannot be higher than 65535 (0xff)
| | | 
| | * 77d05636ad5a623dbaaaa3aaf1967ae92fb095af● 2019-08-19
| | |   restore conf-spawn to original default, 120.
| | | 
| | * ce050fefe84350be81a41e61b22134bdf5abfa32● 2019-08-19
| |/    Apply Johannes Erdfelt's big-concurrency.patch
|/|   
| |   
| | * e8d2f9954684b62e68ed9205fa5c5919da213e0d● 2019-08-19 (notqmail-badmailfrom-x-relayclient)
| |/    qmail-smtpd: ignore badmailfrom when RELAYCLIENT is set.
|/|   
| |   
| | * 2d346400a663401aecfd1da75c648408f7a8fff9 2019-08-19 (notqmail-badmailfrom-wildcard)
| | |   grammar cleanup in qmail-smtpd.8 to match the rest of that manpage.
| | | 
| | * 2b6d23258d67ab2dc061aa049500c7e970663c93● 2019-08-19
| |/    Apply Tom Clegg's badmailfrom wildcard patch.
|/|   
| | 
* | 3a48c5b56a3031afad411545ecff77be4368e300● 2019-08-19 (tag: notqmail-1.07)
| |   notqmail 1.07
| |   
| | * 3acb67b3f966d114323ae82af50e207ec777e4e4 2019-08-19 (refs/pull/63/head)
| |/    notqmail 1.07
|/|   
| | 
* | 1b28ced36c341d3b94c053ab9e04313304ca3d44● 2019-08-14
| |   remove SYSDEPS, the list of platform-specific build artifacts.
| |   
| | * 68d3c62e023bfbcc0b03cc1e19d1f280acd95b31 2019-08-13 (refs/pull/33/head)
| |/    remove SYSDEPS, the list of platform-specific build artifacts.
|/|   
| |   
| | * 5faa255aa26891bf2f2459328d3833c54a1ab9e0 2019-08-13 (refs/pull/62/head)
| |/    Remove one last reference to SYSDEPS.
|/|   
| | 
* | 0057e5ff94114dd10781808cb41ffa5eb5f4a5bf● 2019-08-10
| |   remove incorrect usage of vfork()
| |   
| | * 4220b97412faac10576e3a602fd0a8238e4a72d0 2019-08-10 (refs/pull/38/head)
| |/    remove incorrect usage of vfork()
|/|   
| | 
* | 75b8eb439b60646480e2b3afb7cfe00f96213616● 2019-08-06
| |   TravisCI: force one build without utmpx
| | 
* | f9efcbfdc2d47795d193c9a851a9c4b6d41661d5● 2019-08-06
| |   Fix the non-compiling utmp case, found on OpenBSD.
| |   
| | * 492f803a596bd2b18b9723ac8a88a35bb84b3d54 2019-08-06 (refs/pull/57/head)
| | |   TravisCI: force one build without utmpx
| | | 
| | * 267983a6c90e2a1cda4da93ee9e1a72a9abcda4b● 2019-08-06
| |/    Fix the non-compiling utmp case, found on OpenBSD.
|/|   
| |   
| | * 4c5b794c9a8daa92049e346d60bed7b124715787 2019-08-05 (refs/pull/56/head)
| | |   CHANGES: don't build or install catted manpages.
| | | 
| | * d4706c3cbf6794349fec875d60fbbb922cdaa5d5● 2019-08-05
| | |   Don't build catted manpages (*.0).
| | | 
| | * 4abca1b6d3c38d2dc060675f22a8a0e598e9bec3● 2019-08-05
| | |   Don't install catted manpages (*.0).
| | | 
| | * 9b97b849840aab676905075c3c90275c7e2fdb77● 2019-08-05
| |/    Don't refer to catted manpages (*.0).
|/|   
| | 
* | ea3a0e4107ab96c1f7916a05a4d9b9412f506c66● 2019-08-05
| |   Update docs: we're not netqmail, we're notqmail.
| |   
| | * 76dc19c3081c67d07ddfd33c049fd60d80c3d4ad 2019-08-05 (refs/pull/42/head)
| |/    Update docs: we're not netqmail, we're notqmail.
|/|   
| | 
* | d4dcbf2dcce24ed7e42c2b328762f0f9576ae2cd● 2019-08-05
| |   man rules are variously missing dependency on conf-qmail.
| |   
| | * 2dcd4ae135110921bf00ecc05d9d0c68e2c88f4a 2019-08-05 (refs/pull/55/head)
| |/    man rules are variously missing dependency on conf-qmail.
|/|   
| | 
* | c589f2da2f17e64ce0402f2466a361ec28b8e5e8● 2019-08-05
| |   add more missing local includes
| |   
| | * 483e592774c3b823d072586075812a368115f8d2 2019-08-05 (refs/pull/53/head)
| |/    add more missing local includes
|/|   
| | 
* | fb2b0571a7c09a140188b4442c42fcc74cc1963a● 2019-08-04
| |   CI: build & package before creating user accounts.
| | 
* | de6a762737e86ae714b38d5323b9d5c9237f9a8b● 2019-08-04
| |   split install in to instpackage, instchown.
| | 
* | 2f552835c907eee2db06cda5a6b7cfe09cb319ee● 2019-08-04
| |   Remove support for precompiled var-qmail packages.
| | 
* | 580bf26e8a211fdff3ad3083e5e771662a921e3a● 2019-08-04
| |   detect qmail system uid/gid at runtime.
| | 
* | 1fabc9dcfa3e670875895d60fd5432318cb0e5ff● 2019-08-04
| |   add missing mode argument to recursive mkdir_p.
| |   
| | * a49421670f68d4091692b3abd0e39085d6addcd8 2019-08-04 (refs/pull/15/head)
| | |   CI: build & package before creating user accounts.
| | | 
| | * 439fe87a82f80f9ee84a9e9c1ba744fee80f732f● 2019-08-04
| | |   split install in to instpackage, instchown.
| | | 
| | * 429896534fbc7bc10c4ae833d6f065a2a2733aba● 2019-08-04
| | |   Remove support for precompiled var-qmail packages.
| | | 
| | * 65f0e3b413c79696bbeadc8a9b99f69d2e9e4869● 2019-08-04
| | |   detect qmail system uid/gid at runtime.
| | | 
| | * 4b0f26eb82117b9a9928853e5b642bc6cc49011b● 2019-08-02
| |/    add missing mode argument to recursive mkdir_p.
|/|   
| | 
* | bb7ec78bf79e6ee69cd44ed3c8102b6ec66b9bc5● 2019-07-22
| |   Point SMTP HELP at notqmail's home page.
| |   
| | * 9e1e1512b0019601d1ea0412b4a0a98ff662c6cf 2019-07-22 (refs/pull/39/head)
| |/    Point SMTP HELP at notqmail's home page.
|/|   
| | 
* | 5d18a307a5048c92555755e5e6c3fa55e58c93be● 2019-07-21
| |   CHANGES: add Mac OS X portability changes.
| |   
| | * 1d2e326ddeb6864e3f71f9a675ac5d15e034d34a 2019-07-21 (refs/pull/40/head)
| |/    CHANGES: add Mac OS X portability changes.
|/|   
| | 
* | f1196059ec92ee2b50e6729bfd0fdf06365d18f0● 2019-07-21
| |   define BIND_8_COMPAT before including arpa/nameser.h in dns.c
| | 
* | 71580a477d784996287da2e6590070b7ecbdb47f● 2019-07-21
| |   explicitly initialize strerr.
| | 
* | df0004cf7a42e503facab6235fcfee6c026ade8e● 2019-07-21
| |   rename INSTALL to INSTALL.md, SENDMAIL to SENDMAIL.md.
| |   
| | * fdf3b7278f00da7d2178d892d2cc44affe5337b9● 2019-07-20 (refs/pull/16/head)
| | |   define BIND_8_COMPAT before including arpa/nameser.h in dns.c
| | | 
| | * e5f3c504eb787f36b693744bff50c1641213e8b5● 2019-07-20
| | |   explicitly initialize strerr.
| | | 
| | * d36ffd156abd88052fbb9cf3e73343a2058471e9● 2019-07-20
| |/    rename INSTALL to INSTALL.md, SENDMAIL to SENDMAIL.md.
|/|   
| | 
* | 3357eb2476698649929e19d423ef24ca73c356dc● 2019-07-20
| |   symlink TARGETS to .gitignore.
| | 
* | 131db6a9399cee16eb4eca59dd9112308d1c44f0● 2019-07-20
| |   remove phony targets from TARGETS, add them as .PHONY prerequisites.
| |   
| | * 69135fde2d86861a79d909c7123817b31fc94cf3 2019-07-20 (refs/pull/25/head)
| | |   symlink TARGETS to .gitignore.
| | | 
| | * dd8bf671b438c5b139c49d889cf927a3fc3bb90d● 2019-07-20
| |/    remove phony targets from TARGETS, add them as .PHONY prerequisites.
|/|   
| | 
* | e55694bdbbce60572c4a59a1a6dc69cde012f308● 2019-07-19
| |   control_readfile() called with less arguments
| |     
| | *   c69e518e87d2eac3df03a2db800b461305bbff6f● 2019-07-20 (refs/pull/31/head)
| | |\    Merge branch 'master' into qmail-showctl-bug
| |_|/  
|/| |   
| | | 
* | | 97c5ef197909671816d3001a2914b5568f962756● 2019-07-19
| | |   remove FILES, shar target from Makefile.
| | |   
| | *   3af66d03984d299967144e7b3ae22170c6e9823e● 2019-07-19
| | |\    Merge branch 'master' into qmail-showctl-bug
| |_|/  
|/| |   
| | | 
| | * 15245e87348f23de24d9794646f65214f4bac56a● 2019-07-19
| | |   control_readfile() called with less arguments
| | |   
| | | * 99e9dab766cb7b8e1e82f7f8c0660be8f22af102 2019-07-19 (refs/pull/27/head)
| |_|/    remove FILES, shar target from Makefile.
|/| |   
| | |   
| | | * 31621bbe07dc355fbcc727ef42cf308c4e5b2613 2019-07-19 (refs/pull/32/head)
| |_|/    remove FILES.
|/| |   
| | | 
* | | 3772dd95f6584cc6788fa4ff28d7b232a4c8d43a● 2019-07-19
| |/    add missing project includes to several .c files
|/|   
| |   
| | * a1eaa877d18d9d0542a4849465aa2700105f7cba 2019-07-16 (refs/pull/20/head)
| |/    add missing project includes to several .c files
|/|   
| | 
* | ac43d259085369f64209b105db0139e85a6bd816● 2019-07-15
| |   add qtmp.h to SYSDEPS.
| |   
| | * 38699fb9fa955720061f3a71dc1f5eb7b3aa0d4c 2019-07-15 (refs/pull/29/head)
| |/    add qtmp.h to SYSDEPS.
|/|   
| | 
* | 47baf7859ec511c4939fbf5b44e292bb60485aac● 2019-07-15
| |   Fix build on utmpx-only systems, such as FreeBSD.
| |   
| | * f3d015dd01f83edc73c7934c59b39adf296d2e0c● 2019-07-15 (refs/pull/24/head)
| |/    Fix build on utmpx-only systems, such as FreeBSD.
|/|   
| |   
| | * b0ed55726f91658a78aba4978ff57f4b4e409c68 2019-07-14 (refs/pull/22/head)
| | |   Remove support for precompiled var-qmail packages.
| | | 
| | * 02dd7473e0a8baa1ef23a52f2215847060b42bbd● 2019-07-14
| |/    remove shar target from Makefile.
|/|   
| | 
* | 1dd7b07f688d0fa38353914d2a2c4d4393abd430● 2019-07-13
| |   honor DESTDIR on install
| |   
| | * ff3a5002ec9031e4b93874da527cec90cef6be82● 2019-07-13 (refs/pull/4/head)
| |/    honor DESTDIR on install
|/|   
| |   
| | * acbe245ec56b0520b294c894ea29ad6e8ce5f34f 2019-07-13 (netqmail-smtpd-logging)
| | |   add errbits.[ch] to FILES, fix errbits.o, qmail-smtpd.o Makefile deps
| | | 
| | * a0982661b24df01e65f84ef96708bc002a313c76● 2019-07-12
| |/    Apply Andrew Richards' qmail-logmsg logging patch, v1.3.
| |   
| | * 1550b0b0867bc8c8753ecb6b9558677cfc67591a 2019-07-13 (netqmail-smtp-auth)
| | |   Remove FILES.auth, add new files to FILES.
| | | 
| | * 36d7678ec9c9c8f61406e946d263bd3afd6100a1● 2019-07-12
| |/    Apply Erwin Hoffmann's SMTP authentication for qmail-smtpd, qmail-remote.
| |   
| | * 9cec488b10b3a3a085676f7757248ad5d6c6aa96 2019-07-13 (netqmail-ext-todo)
| | |   Remove all code guarded by ifndef EXTERNAL_TODO.
| | | 
| | * a95253370cfdbd2c79e1f2e9fa1c9a0002f66cb1● 2019-07-13
| | |   remove EXTERNAL_TODO define.
| | | 
| | * 0df8ccc44c94e3f7a9dca8fed078b7a84970f19b● 2019-07-12
| |/    add ext_todo-20030105.patch
| |   
| | * f032501bb363ad450a7cb801d867c79a5d3c21c1● 2019-07-13 (netqmail-big-concurrency)
| | |   conf-spawn cannot be higher than 65535 (0xff)
| | | 
| | * 37efcbd2c442fb0a0aae8e8c172f377c13eb6ea9● 2019-07-13
| | |   restore conf-spawn to original default, 120.
| | | 
| | * 94962395482052f12ca28bfcf7c170178a67caae● 2019-07-12
| |/    Apply Johannes Erdfelt's big-concurrency.patch
| |   
| | * f6ee08feae86931bb9e05e79e9c17f4eb1709c3c● 2019-07-13 (netqmail-smtpd-spf)
| |/    Apply Jana Saout's qmail-spf-rc5.patch
| |   
| | * 980428a420c7670ec7bae40149088f894247d1f7 2019-07-13 (netqmail-smtp-tls)
| | |   Remove TLS define from the C code and conf-cc.
| | | 
| | * 1468ae0084e8c710db605d47ae22da429a28c9da● 2019-07-13
| | |   Do not add directories to PATH in Makefile-cert.mk, update_tmprsadh.sh.
| | | 
| | * adb025ec4dbe4fa012f5861be12534e75e114045● 2019-07-13
| | |   conf-cc: remove -I/usr/local/ssl/include
| | | 
| | * c88bcfb7d0e19ceade1a946c38a50f40e7fbe15e● 2019-07-13
| | |   Makefile: remove -L/usr/local/ssl/lib qmail-remote, qmail-smtpd
| | | 
| | * c38f92e425942a777da28f9f2245c040e72d8678● 2019-07-13
| | |   Remove trailing whitespace in Makefile qmail-remote[.8c] qmail-smtpd.[8c]
| | | 
| | * 01cda8253b0ab4a19995dd7f5d6eba5cc005a6aa● 2019-07-13
| |/    Apply Frederik Vermeulen's qmail-smtp-tls 20190517
| |   
| | * 2160c4deec0ebc671bb1b3f171d48110b20f010b 2019-07-12 (netqmail-dns-oversize)
| |/    Apply Christopher K. Davis's oversize DNS packet patch.
| |   
| | * e13e7a335b6b98854113655f44774f32b3d0ad99● 2019-07-12 (netqmail-big-todo)
| |/    Apply Russell Nelson's big-todo.103.patch.
| |   
| | * 78475d20b2ce03b91387de7f74e94a63e6fddbf7 2019-07-12 (netqmail-badmailfrom-x-relayclient)
| |/    qmail-smtpd: ignore badmailfrom when RELAYCLIENT is set.
| |   
| | * 384b1ed10155d4bf6f2cb1e133f0fc55bea45b1d 2019-07-12 (refs/pull/21/head)
| |/    TravisCI: test external patch(es)
|/|   
| |   
| | * 42a67e3d3abdfa2e87e6cacb559d5b27ee906d2e 2019-07-12 (refs/pull/13/head)
| |/    add .gitignore
|/|   
| |   
* |   774f27053a7eaec5799e3a4a33b583d5a464439c● 2019-07-11
|\ \    Merge pull request #19 from notqmail/TravisCI
| | | 
| * | 1b61fb158fb6af56fef8bd9af08f307616b3d10a 2019-07-11 (refs/pull/19/head)
|/ /    add TravisCI control file
| |   
| | * e3327ba437d777489d45cb526a36be587ac1eb75● 2019-07-09 (refs/pull/11/head)
| |/    detect qmail system uid/gid at runtime.
|/|   
| |   
| | * 2eaa01ea04644bbe44cf9f49ca2c9032d6403334 2019-07-09 (refs/pull/12/head)
| |/    Fix build on utmpx-only systems, such as FreeBSD.
|/|   
| | 
* | 6fbd90376ebf2fdb2d4cf16dbc3ac4f6d5074c86● 2019-07-08
|/    qmail.o rule is missing dependency on env.h via qmail.c.
|   
| * ee04fb5d58c9b3b51e6ce0d16c127109b8a32f23● 2019-07-08 (refs/pull/5/head)
| |   added destdir argument to be passed as command line argument
| | 
| * e8fed2ec03831d63e7f5718cd52c5a8bd0165ec6● 2019-07-08
|/    use DESTDIR variable and pass it as argument to install
|   
| * 8d48e8d99ecc6e9ab5dd4cdab61484ab2bb582a0 2019-07-07 (refs/pull/3/head)
|/    destdir: build as non-root, without hardcoded IDs.
|   
| * b928f08d979a064345d8b857dccba4bef848a2f2 2019-07-06 (refs/pull/1/head)
|/    qmail.o rule is missing dependency on env.h via qmail.c.
| 
* 08a0b794f3c12c7cbfa8e6217028d32cf841c3ba 2016-01-17 (tag: netqmail-1.06, netqmail)
|   netqmail 1.06
| 
* 39cfb0cfb64b3e35500f70f174d45921b5e65bfd 2016-01-17 (tag: netqmail-1.05)
|   netqmail 1.05
| 
* c4de3cc1ac2b75d87b551c9b88513d14ba57410d● 2016-01-17
|   This patch improves ISO C conformance of qmail code -- specifically, of qmail-lspawn, qmail-newmrh, qmail-newu, qmail-pop3d, qmail-popup, qmail-rspawn, and qmail-smtpd.  This fixes two known bugs:
| 
* 2bb7433cc9aaa89edf223d57e904553b31cce4b9 2016-01-17 (tag: netqmail-1.04)
|   netqmail 1.04
| 
* f5df10d17375e5f1137c4e59f0e527a12a991ed7● 2016-01-17 (tag: qmail-1.03, qmail)
|   qmail 1.03
| 
* d15ee51c9ddb9d233ad5ebc28fddf1de8378657d● 2016-01-17 (tag: qmail-1.02)
|   qmail 1.02
| 
* af9625ddf9464e85fd781e8ad889201ce340d9fc 2009-12-18 (tag: qmail-1.01)
    qmail 1.01