+#define ENVBUF 4096
+#define NRCPT 16
+struct envelope {
+ unsigned f;
+#define EF_TRUNC 1u
+ char buf[ENVBUF + 1];
+ size_t n;
+ const char *send;
+ const char *rcpt[NRCPT];
+ size_t nrcpt;
+};
+
+static int readenv(struct envelope *e)
+{
+ ssize_t n;
+ const char *p, *l;
+
+ /* Read the raw envelope data. */
+ e->n = 0;
+ e->f = 0;
+ while (e->n < ENVBUF) {
+ n = read(1, e->buf + e->n, ENVBUF - e->n);
+ if (n < 0 && errno != EINTR && errno != EAGAIN) {
+ syslog(LOG_ERR, "failed to read envelope: %m");
+ return (-1);
+ }
+ if (!n) break;
+ e->n += n;
+ }
+ e->buf[e->n] = 0;
+
+ /* Parse up the envelope data. */
+ p = e->buf; l = p + e->n;
+ e->nrcpt = 0;
+ if (*p++ != 'F') {
+ e->send = "<invalid>";
+ syslog(LOG_ERR, "corrupt envelope (no sender marker)");
+ return (-1);
+ }
+ e->send = p;
+ for (;;) {
+ if (p >= l) goto trunc;
+ else if (!*p++) break;
+ }
+
+ for (;;) {
+ if (p >= l) goto trunc;
+ if (!*p) break;
+ else if (*p++ != 'T') {
+ syslog(LOG_ERR, "corrupt envelope (no recipient marker)");
+ return (-1);
+ }
+ if (p >= l) goto trunc;
+ if (e->nrcpt < NRCPT) e->rcpt[e->nrcpt++] = p;
+ for (;;) {
+ if (p >= l) goto trunc;
+ else if (!*p++) break;
+ }
+ }
+ return (0);
+
+ /* Failed to reach the final terminator; maybe there's more. This isn't a
+ * very bad situation.
+ */
+trunc:
+ e->f |= EF_TRUNC;
+ return (0);
+}
+
+static void real(void)
+{
+ const char *qmq =
+ strenv("QMAIL_CHECKSPAM_QUEUE", "/var/qmail/bin/qmail-queue");
+ execlp(qmq, qmq, (char *)0);
+ syslog(LOG_ERR, "failed to exec %s: %m", qmq);
+ exit(56);
+}
+
+static int split(int *fd_m, int *fd_e)
+{
+ pid_t kid;
+ int pm[2], pe[2];
+
+ if (pipe(pm) || pipe(pe)) return (-1);
+ if ((kid = fork()) < 0) return (-1);
+ if (kid) {
+ dup2(pm[0], 0); close(pm[0]); close(pm[1]);
+ dup2(pe[0], 1); close(pe[0]); close(pe[1]);
+ real();
+ } else {
+ close(pm[0]); *fd_m = pm[1];
+ close(pe[0]); *fd_e = pe[1];
+ }
+ return (0);
+}
+
+static int writemsg(struct message *m, int fd_m, int shovelp)
+{
+ if (message_write(fd_m, m) < 0) {
+ syslog(LOG_ERR, "failed to write message body: %m");
+ return (-1);
+ }
+ if (shovelp && shovel(0, fd_m, "message body"))
+ return (-1);
+ close(fd_m);
+ return (0);
+}
+
+static int writeenv(struct envelope *e, int fd_e)
+{
+ if (safewrite(fd_e, e->buf, e->n) < 0) {
+ syslog(LOG_ERR, "failed to write envelope: %m");
+ return (-1);
+ }
+ if ((e->f & EF_TRUNC) && shovel(1, fd_e, "envelope"))
+ return (-1);
+ close(fd_e);
+ return (0);
+}
+
+static void logenv(struct envelope *e)
+{
+ size_t i;
+
+ syslog(LOG_NOTICE, "sender = %s", e->send);
+ for (i = 0; i < e->nrcpt; i++)
+ syslog(LOG_NOTICE, "recipient %lu = %s", (unsigned long)i, e->rcpt[i]);
+}
+