This patch enable Mixmaster-2.9beta31 to deliver to a maildir. Maildirs have proven far more robust then traditional Unix style mboxes. In fact I wasen't able to successfully read the mboxes created by Mixmaster-2.9beta31 with mutt. Since I wasn't in the mood to fiddle with teh mbox format I decided to add maildir support? The real Unix way would have been to use Mixmasters pipe feature and to pipe to a maildir delivery program like 'safecat'. But Mixmaster should be easy very easy to use and not depend on external tools for its work, because this makes auditing a lot harder. At the moment it works only in POSIX Systems. I would be glad if somebody with knowledge on other systems could have a look into this. --drt@un.bewaff.net - http://c0re.jp/ diff -ruN Mix-2.9beta31-orig/Src/maildir.c Mix-2.9beta31/Src/maildir.c --- Mix-2.9beta31-orig/Src/maildir.c Thu Jan 1 01:00:00 1970 +++ Mix-2.9beta31/Src/maildir.c Wed Oct 3 17:46:11 2001 @@ -0,0 +1,216 @@ +/* Maildir support for Mixmaster 3 - see + http://www.qmail.org/man/man5/maildir.html and + http://cr.yp.to/proto/maildir.html + + hacked by drt@un.bewaff.net - http://c0re.jp/ + + To test it try: + $ gcc maildir.c -DUNITTEST -o test_maildir + $ ./test_maildir + this should print a single line saying "OK" +*/ + +#include "mix3.h" + +#ifdef POSIX +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned long namecounter = 0; + +/* Write "message" to "maildir", retunr 0 on success, -1 on failure */ +int maildirWrite(char *maildir, BUFFER *message) +{ + int fd; + int currDir; + int count; + int returnValue; + char hostname[64]; + struct stat statbuf; + char basename[100]; /* actual length should be smaller than 98 bytes */ + char tmpname[110]; /* actual length should be smaller than 102 bytes */ + char newname[110]; /* actual length should be smaller than 102 bytes */ + + /* Declare a handler for SIGALRM so we can time out. */ + /* set_handler(SIGALRM, alarm_handler); */ + /* alarm(86400); */ + + hostname[0] = '\0'; + gethostname(hostname, 63); + hostname[63] = '\0'; + + /* Step 1: chdir to maildir (and save actual dir by opening it) */ + currDir = open(".", O_RDONLY); + if(chdir(maildir) != 0) + { + returnValue = -1; + goto functionExit; + } + + /* Step 2: Stat the temporary file. Wait for ENOENT as a response. */ + for (count = 0;; count++) + { + tmpname[0] = '\0'; + newname[0] = '\0'; + sprintf(basename, "%lu.%lu-%u.%s", time(NULL), namecounter, getpid(), hostname); + strcat(tmpname, "tmp/"); + strncat(tmpname, basename, 100); + strcat(newname, "new/"); + strncat(newname, basename, 100); + namecounter++; + + if (stat(tmpname, &statbuf) == 0) + errno = EEXIST; + else if (errno == ENOENT) + { + /* Step 4: create the file (at least try) */ + fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL, S_IWUSR|S_IRUSR); + if (fd >= 0) + break; /* we managed to open the file */ + } + + if (count > 5) + { + /* Too many retries - give up */ + errlog(ERRORMSG, "Can't create message in %s\n", maildir); + returnValue = -1; + goto functionExit; + } + + /* Step 3: sleep and retry */ + sleep(2); + } + + /* Step 5: write file */ + if(write(fd, message->data, message->length) != message->length) + { + returnValue = -1; + goto functionExit; + } + + /* on NFS this could fail */ + if((fsync(fd) != 0) || (close(fd) != 0)) + { + returnValue = -1; + goto functionExit; + } + + /* Step 6: move message to 'cur' */ + if(link(tmpname, newname) != 0) + { + returnValue = -1; + goto functionExit; + } + + if(unlink(tmpname) != 0) + { + /* linking failed */ + returnValue = -1; + goto functionExit; + } + + returnValue = 0; + +functionExit: + /* return to original directory */ + if ((fchdir(currDir) != 0) || (close(currDir) != 0)) + returnValue = -1; + + return returnValue; +} + +#else + +int maildirWrite(char *maildir, BUFFER *message) +{ + /* Ihave no Idea if this could be implemented on non-POSIX Systems */ + errlog(ERRORMSG, "Maildir delivery is only supported on POSIX systems for now.\n"); + return -1; +} +#endif + +#ifdef UNITTEST + +#ifdef NDEBUG +#undef NDEBUG +#endif + +#include +#include + +/* mock-up of errlog for unittest */ +void errlog(int type, char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +/* main for unittest */ +int main() +{ + int i, count = 23; + int fd; + DIR *d; + struct dirent *de; + BUFFER message; + char text[] = "From: nobody@un.bewaff.net\nTo: hackers@c0re.jp\nSubject: testing\n\nthis is just a test\n"; + char buf[1024]; + + /* create buffer with test data */ + message.data = text; + message.length = strlen(text); + + /* create maildir for testing */ + mkdir("Maildir.test_maildir", 0700); + mkdir("Maildir.test_maildir/tmp", 0700); + mkdir("Maildir.test_maildir/new", 0700); + mkdir("Maildir.test_maildir/cur", 0700); + + /* write messages to maildir */ + for(i = 0; i < count; i++) + assert(maildirWrite("Maildir.test_maildir", message) == 0); + + /* read them back */ + assert((d = opendir("Maildir.test_maildir/new")) != NULL); + for (i = 0; i < count + 2; i++) + { + de = readdir(d); + if(de->d_name[0] != '.') + { + buf[0] = '\0'; + strcat(buf, "Maildir.test_maildir/new/"); + strcat(buf, de->d_name); + fd = open(buf, O_RDONLY); + assert(unlink(buf) == 0); + assert(read(fd, buf, strlen(text)) == strlen(text)); + buf[strlen(text)] = '\0'; + /* check if they match the original message */ + assert(strcmp(text, buf) == 0); + close(fd); + } + } + + /* no files left in directory? */ + assert(readdir(d) == NULL); + + /* delete maildir */ + assert(rmdir("Maildir.test_maildir/tmp") == 0); + assert(rmdir("Maildir.test_maildir/new") == 0); + assert(rmdir("Maildir.test_maildir/cur") == 0); + assert(rmdir("Maildir.test_maildir") == 0); + + /* check if writing to a non existant maildir yilds an error */ + assert(maildirWrite("Maildir.test_maildir", &message) == -1); + + puts("OK"); +} +#endif diff -ruN Mix-2.9beta31-orig/Src/mix3.h Mix-2.9beta31/Src/mix3.h --- Mix-2.9beta31-orig/Src/mix3.h Tue Sep 18 23:15:03 2001 +++ Mix-2.9beta31/Src/mix3.h Wed Oct 3 17:34:15 2001 @@ -109,6 +109,7 @@ FILE *mix_openfile(const char *name, const char *a); FILE *openpipe(const char *prog); int closepipe(FILE *fp); +int maildirWrite(char *maildir, BUFFER *message); typedef struct { char *name; diff -ruN Mix-2.9beta31-orig/Src/rem.c Mix-2.9beta31/Src/rem.c --- Mix-2.9beta31-orig/Src/rem.c Tue Sep 18 23:16:55 2001 +++ Mix-2.9beta31/Src/rem.c Wed Oct 3 19:44:30 2001 @@ -296,7 +296,7 @@ void logmail(char *mailbox, BUFFER *message) { - /* mailbox is "|program", "user@host", "stdout" or "filename" */ + /* mailbox is "|program", "user@host", "stdout", "Maildir" or "filename" */ buf_rewind(message); if (mailbox[0] == '\0') /* default action */ mailbox = MAILBOX; @@ -326,6 +326,12 @@ isloop: buf_free(field); buf_free(content); + } else if (mailbox[strlen(mailbox)-1] == '/') { + /* the user is requesting Maildir delivery */ + if(maildirWrite(mailbox, message) != 0) { + errlog(ERRORMSG, "Can't write to maildir %s\n", mailbox); + return; + } } else { FILE *mbox; diff -ruN Mix-2.9beta31-orig/mix.1 Mix-2.9beta31/mix.1 --- Mix-2.9beta31-orig/mix.1 Wed Sep 19 04:12:31 2001 +++ Mix-2.9beta31/mix.1 Wed Oct 3 16:59:56 2001 @@ -346,9 +346,11 @@ .B @ sign, the message is forwarded to the given address (with an .B X-Loop: -header to prevent mail loops), otherwise the message is appended +header to prevent mail loops). If it ends with a +.B / +it is threated as a Maildir, otherwise the message is appended to the given file name or written to standard output if -.B MAIL +.B MAILBOX is .BR stdout . Default: