tmaildir.c - mixmaster - mixmaster 3.0 patched for libressl
 (HTM) git clone git://parazyd.org/mixmaster.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
       tmaildir.c (8242B)
       ---
            1 /* Mixmaster version 3.0  --  (C) 1999 - 2006 Anonymizer Inc. and others.
            2 
            3    Mixmaster may be redistributed and modified under certain conditions.
            4    This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
            5    ANY KIND, either express or implied. See the file COPYRIGHT for
            6    details.
            7 
            8    Maildir support routines
            9    $Id: $ */
           10 
           11 
           12 /* Maildir support for Mixmaster 3 - see
           13    http://www.qmail.org/man/man5/maildir.html and
           14    http://cr.yp.to/proto/maildir.html
           15 
           16    Added by and (C) 2001 Doobee R. Tzeck 
           17    drt@un.bewaff.net - http://c0re.jp/
           18 
           19    To test it try:
           20    $ gcc maildir.c -DUNITTEST -o test_maildir
           21    $ ./test_maildir
           22    this should print a single line saying "OK"
           23 */
           24 
           25 #include "mix3.h"
           26 
           27 #ifdef WIN32
           28 #include <io.h>
           29 #include <direct.h>
           30 #include <process.h>
           31 #define S_IWUSR _S_IWRITE
           32 #define S_IRUSR _S_IREAD
           33 #else /* end of WIN32 */
           34 #include <unistd.h>
           35 #endif /* else not WIN32 */
           36 #include <fcntl.h>
           37 #include <time.h>
           38 #include <string.h>
           39 #include <sys/stat.h>
           40 #include <sys/types.h>
           41 #include <errno.h>
           42 #include <stdarg.h>
           43 #include <assert.h>
           44 
           45 #if defined(S_IFDIR) && !defined(S_ISDIR)
           46 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
           47 #endif /* defined(S_IFDIR) && !defined(S_ISDIR) */
           48 
           49 #ifndef SHORTNAMES
           50 
           51 static unsigned long namecounter = 0;
           52 
           53 int checkDirectory(char *dir, char *append, int create) {
           54   char tmp[PATHMAX];
           55   struct stat buf;
           56   int err;
           57 
           58   tmp[0] = '\0';
           59   strcatn(tmp, dir, PATHMAX);
           60   if (append)
           61     strcatn(tmp, append, PATHMAX);
           62 
           63   err = stat(tmp, &buf);
           64   if (err == -1) {
           65     if (create) {
           66 #ifndef POSIX
           67       err = mkdir(tmp);
           68 #else /* end of not POSIX */
           69       err = mkdir(tmp, S_IRWXU);
           70 #endif /* else if POSIX */
           71       if (err == 0)
           72         errlog(NOTICE, "Creating directory %s.\n", tmp);
           73     } else
           74       err = 1;
           75   } else if (!S_ISDIR(buf.st_mode))
           76     err = -1;
           77 
           78   return err;
           79 }
           80 
           81 /* Write "message" to "maildir", retunr 0 on success, -1 on failure */
           82 #define MAX_BASENAME 113 /* actual length should be smaller than 111 bytes */
           83 #define MAX_SUBNAME 123 /* actual length should be smaller than 115 bytes */
           84 int maildirWrite(char *maildir, BUFFER *message, int create) {
           85   int fd;
           86   int count;
           87   int returnValue;
           88   char hostname[64];
           89   struct stat statbuf;
           90   char basename[MAX_BASENAME];
           91   char tmpname[MAX_SUBNAME];
           92   char newname[MAX_SUBNAME];
           93   int messagesize;
           94   char olddirectory[PATHMAX] = "";
           95   char normalizedmaildir[PATHMAX];
           96 
           97   /* Declare a handler for SIGALRM so we can time out. */
           98   /* set_handler(SIGALRM, alarm_handler);  */
           99   /* alarm(86400); */
          100 
          101   hostname[0] = '\0';
          102   gethostname(hostname, 63);
          103   hostname[63] = '\0';
          104 
          105   mixfile(normalizedmaildir, maildir);
          106   if ((checkDirectory(normalizedmaildir, NULL, create) != 0) ||
          107       (checkDirectory(normalizedmaildir, "tmp", create) != 0) ||
          108       (checkDirectory(normalizedmaildir, "cur", create) != 0) ||
          109       (checkDirectory(normalizedmaildir, "new", create) != 0)) {
          110     returnValue = -1;
          111     goto realend;
          112   }
          113 
          114   messagesize = message->length;
          115 
          116   /* Step 1: chdir to maildir (and save current dir) */
          117   if (getcwd(olddirectory, PATHMAX) == NULL) {
          118     returnValue = -1;
          119     goto realend;
          120   }
          121   olddirectory[PATHMAX-1] = '\0';
          122   if(chdir(normalizedmaildir) != 0) {
          123     returnValue = -1;
          124     goto functionExit;
          125   }
          126 
          127   /* Step 2:  Stat the temporary file.  Wait for ENOENT as a response. */
          128   for (count = 0;; count++) {
          129     tmpname[0] = '\0';
          130     newname[0] = '\0';
          131     snprintf(basename, MAX_BASENAME, "%lu.%u_%lu.%s,S=%u",
          132       time(NULL), getpid(), namecounter++, hostname, messagesize);
          133     basename[MAX_BASENAME-1] = '\0';
          134     strcatn(tmpname, "tmp" DIRSEPSTR, MAX_SUBNAME);
          135     strcatn(tmpname, basename, MAX_SUBNAME);
          136     strcatn(newname, "new" DIRSEPSTR, MAX_SUBNAME);
          137     strcatn(newname, basename, MAX_SUBNAME);
          138 
          139     if (stat(tmpname, &statbuf) == 0)
          140       errno = EEXIST;
          141     else if (errno == ENOENT) {
          142       /* Step 4: create the file (at least try) */
          143       fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL, S_IWUSR|S_IRUSR);
          144       if (fd >= 0)
          145         break; /* we managed to open the file */
          146     }
          147 
          148     if (count > 5) {
          149       /* Too many retries - give up */
          150       errlog(ERRORMSG, "Can't create message in %s\n", maildir);
          151       returnValue = -1;
          152       goto functionExit;
          153     }
          154 
          155     /* Step 3: sleep and retry */
          156     sleep(2);
          157   }
          158 
          159   /* Step 5:  write file */
          160   if(write(fd, message->data, message->length) != message->length) {
          161     returnValue = -1;
          162     goto functionExit;
          163   }
          164 
          165   /* on NFS this could fail */
          166 #ifndef WIN32
          167   if((fsync(fd) != 0) || (close(fd) != 0)) {
          168 #else /* end of not WIN32 */
          169   if((_commit(fd) != 0) || (close(fd) != 0)) {
          170 #endif /* else if WIN32 */
          171     returnValue = -1;
          172     goto functionExit;
          173   }
          174 
          175   /* Step 6: move message to 'cur' */
          176 #ifdef POSIX
          177   for (count = 0;; count++) {
          178     if(link(tmpname, newname) != 0) {
          179       if (errno == EXDEV || errno == EPERM) {
          180         /* We probably are on coda or some other filesystem that does not allow
          181          * hardlinks. rename() the file instead of link() and unlink()
          182          * I know, It's evil (PP).
          183          */
          184         if (rename(tmpname, newname) != 0) {
          185           returnValue = -1;
          186           goto functionExit;
          187         };
          188         break;
          189       } else if (errno != EEXIST) {
          190         returnValue = -1;
          191         goto functionExit;
          192       }
          193     } else {
          194       /* We successfully linked the message in new/. Now let's get
          195        * rid of our tmp/ entry
          196        */
          197       if(unlink(tmpname) != 0) {
          198         /* unlinking failed */
          199         returnValue = -1;
          200         goto functionExit;
          201       }
          202       break;
          203     }
          204 
          205     if (count > 5) {
          206       /* Too many retries - give up */
          207       errlog(ERRORMSG, "Can't move message to %s/new/\n", maildir);
          208       returnValue = -1;
          209       goto functionExit;
          210     }
          211 
          212     sleep(2);
          213     newname[0] = '\0';
          214     snprintf(basename, MAX_BASENAME, "%lu.%u_%lu.%s,S=%u",
          215       time(NULL), getpid(), namecounter++, hostname, messagesize);
          216     basename[MAX_BASENAME-1] = '\0';
          217     strcatn(newname, "new" DIRSEPSTR, MAX_SUBNAME);
          218     strcatn(newname, basename, MAX_SUBNAME);
          219   }
          220 #else /* end of POSIX */
          221   /* On non POSIX systems we simply use rename(). Let's hope DJB
          222    * never finds out
          223    */
          224   if (rename(tmpname, newname) != 0) {
          225     returnValue = -1;
          226     goto functionExit;
          227   };
          228 #endif /* else if not POSIX */
          229 
          230   returnValue = 0;
          231 
          232 functionExit:
          233   /* return to original directory */
          234   assert(olddirectory[0] != '\0');
          235   if(chdir(olddirectory) != 0)
          236     returnValue = -1;
          237 
          238 realend:
          239 
          240   return returnValue;
          241 }
          242 
          243 #else /* end of SHORTNAMES */
          244 int maildirWrite(char *maildir, BUFFER *message, int create) {
          245 {
          246   errlog(ERRORMSG, "Maildir delivery does not work with SHORTNAMES.\n");
          247   return -1;
          248 }
          249 #endif /* else if not SHORTNAMES */
          250 
          251 
          252 #ifdef UNITTEST
          253 
          254 #ifdef NDEBUG
          255 #undef NDEBUG
          256 #endif /* NDEBUG */
          257 
          258 #include <dirent.h>
          259 
          260 /* mock-up of errlog for unittest */
          261 void errlog(int type, char *fmt,...)
          262 {
          263   va_list ap;
          264 
          265   va_start(ap, fmt);
          266   vfprintf(stderr, fmt, ap);
          267   va_end(ap);
          268 }
          269 
          270 /* main for unittest */
          271 int main()
          272 {
          273   int i, count = 23;
          274   int fd;
          275   DIR *d;
          276   struct dirent *de;
          277   BUFFER message;
          278   char text[] = "From: nobody@un.bewaff.net\nTo: hackers@c0re.jp\nSubject: testing\n\nthis is just a test\n";
          279   char buf[1024];
          280 
          281   /* create buffer with test data */
          282   message.data = text;
          283   message.length = strlen(text);
          284 
          285   /* write <count> messages to maildir */
          286   for(i = 0; i < count; i++)
          287     assert(maildirWrite("Maildir.test_maildir", message, 1) == 0);
          288 
          289   /* read them back */
          290   assert((d = opendir("Maildir.test_maildir/new")) != NULL);
          291   for (i = 0; i < count + 2; i++)
          292     {
          293       de = readdir(d);
          294       if(de->d_name[0] != '.')
          295         {
          296           buf[0] = '\0';
          297           strcat(buf, "Maildir.test_maildir/new/");
          298           strcat(buf, de->d_name);
          299           fd = open(buf, O_RDONLY);
          300           assert(unlink(buf) == 0);
          301           assert(read(fd, buf, strlen(text)) == strlen(text));
          302           buf[strlen(text)] = '\0';
          303           /* check if they match the original message */
          304           assert(strcmp(text, buf) == 0);
          305           close(fd);
          306         }
          307     }
          308 
          309   /* no files left in directory? */
          310   assert(readdir(d) == NULL);
          311 
          312   /* delete maildir */
          313   assert(rmdir("Maildir.test_maildir/tmp") == 0);
          314   assert(rmdir("Maildir.test_maildir/new") == 0);
          315   assert(rmdir("Maildir.test_maildir/cur") == 0);
          316   assert(rmdir("Maildir.test_maildir") == 0);
          317 
          318   /* check if writing to a non existant maildir yilds an error */
          319   assert(maildirWrite("Maildir.test_maildir", &message, 0) == -1);
          320 
          321   puts("OK");
          322 }
          323 #endif /* UNITTEST */