--- /dev/null
+/* 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;
+}