]> diplodocus.org Git - mdeliver/commitdiff
New program.
authorepg <>
Tue, 20 Aug 2002 05:07:37 +0000 (05:07 +0000)
committerepg <>
Tue, 20 Aug 2002 05:07:37 +0000 (05:07 +0000)
Makefile [new file with mode: 0644]
mdeliver.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..2316fcb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+# $Id$
+
+PROG=          mdeliver
+NOMAN=         #defined
+
+CFLAGS=                -Wall
+
+prefix?=       /usr/local
+exec_prefix?=  ${prefix}
+BINDIR?=       ${exec_prefix}/bin
+MANDIR?=       ${prefix}/man
+
+.include <bsd.prog.mk>
diff --git a/mdeliver.c b/mdeliver.c
new file mode 100644 (file)
index 0000000..d65214b
--- /dev/null
@@ -0,0 +1,158 @@
+/* mdeliver.c -- Deliver a message to a Maildir.
+ *
+ * $Id$
+ *
+ * This program is in the public domain.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <time.h>
+#include <unistd.h>
+
+#define LEN_ULONG 40       /* holds 2^128 - 1 in decimal, plus '\0' */
+
+/* Write all of buf, even if write(2) is interrupted. */
+static ssize_t
+full_write(int fd, const char *buf, size_t nbytes)
+{
+    ssize_t r, w = 0;
+
+    /* Loop until nbytes of buf have been written. */
+    while (w < nbytes) {
+        /* Keep trying until write succeeds without interruption. */
+        do {
+            r = write(fd, buf + w, nbytes - w);
+        } while (r < 0 && errno == EINTR);
+
+        if (r < 0) {
+            return r;
+        }
+
+        w += r;
+    }
+
+    return w;
+}
+
+static void
+deliver(char *maildir, char *newfn)
+{
+    char fn[80 + LEN_ULONG * 2], host[64];
+    char *buildfn;              /* used to build up the fn */
+    time_t now;
+    struct tm *t;
+    pid_t pid;
+    struct stat statbuf;
+    int try;
+    int fd, r;
+    char buf[BUFSIZ];
+
+    if (chdir(maildir) != 0) {
+        err(EX_TEMPFAIL, "Failed chdir(%s)", maildir);
+    }
+
+    pid = getpid();
+    gethostname(host, sizeof(host));
+
+    for (try = 0; ; try++) {
+        now = time(NULL);
+        t = gmtime(&now);
+
+        buildfn = fn + sprintf(fn, "tmp/");
+        buildfn += strftime(buildfn, LEN_ULONG + 1, "%s.", t);
+        buildfn += snprintf(buildfn, LEN_ULONG + 1, "%d.", pid);
+        buildfn += sprintf(buildfn, "%s", host);
+
+        if (stat(fn, &statbuf) != 0) {
+            if (errno == ENOENT) {
+                break;
+            }
+        }
+
+        if (try == 2) {
+            errx(EX_TEMPFAIL, "Attempted filename exists.");
+        }
+
+        sleep(2);
+    }
+
+    fd = open(fn, O_WRONLY | O_EXCL | O_CREAT, 0644);
+    if (fd < 0) {
+        err(EX_TEMPFAIL, "Failed open(%s)", fn);
+    }
+
+    /* From this point, we have a tmp file, so if someting fails we
+     * must try to unlink it before bombing. */
+
+    while ((r = read(STDIN_FILENO, buf, BUFSIZ)) > 0) {
+        if (full_write(fd, buf, r) != r) {
+            unlink(fn);
+            err(EX_TEMPFAIL, "Failed write(%s)", fn);
+        }
+    }
+
+    if (fsync(fd) < 0) {
+        unlink(fn);
+        err(EX_TEMPFAIL, "Failed fsync(%s)", fn);
+    }
+    if (close(fd) < 0) {
+        unlink(fn);
+        err(EX_TEMPFAIL, "Failed close(%s)", fn);
+    }
+
+    strcpy(newfn, fn);
+    memcpy(newfn, "new", 3);
+
+    if (rename(fn, newfn) != 0) {
+        unlink(fn);
+        err(EX_TEMPFAIL, "Failed rename(%s, new)", fn);
+    }
+}
+
+int
+main(int argc, char *argv[])
+{
+    char newfn[80 + LEN_ULONG * 2];
+    pid_t pid;
+    FILE *errfile;
+    char *fail;
+    int fd;
+
+    deliver("Maildir", newfn);
+
+    if (argv[1]) {
+        if ((pid = fork()) > 0) {
+            return 0;
+        } else if (pid == 0) {
+            argv[0] = argv[1];
+            argv[1] = newfn;
+            execvp(argv[0], argv);
+            fail = "exec";
+        } else {
+            fail = "fork";
+        }
+
+        /* If we got here fork or exec failed, so try to write an
+         * error message where later mail processors can find it.  We
+         * really can't do anything about it if this fails. */
+        memcpy(newfn, "err", 3);
+        fd = open(newfn, O_WRONLY | O_EXCL | O_CREAT, 0644);
+        errfile = fdopen(fd, "w");
+        if (errfile) {
+            fprintf(errfile, "%s:%d\n", fail, errno);
+        }
+    }
+
+    /* Always return 0 because deliver exits if it fails, and if it
+     * succeeds we cannot return a failure code to the MTA. */
+    return 0;
+}