unixnt.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:56k
源码类别:

网络编程

开发平台:

Unix_Linux

  1. /*
  2.  * Program: UNIX mail routines
  3.  *
  4.  * Author: Mark Crispin
  5.  * Networks and Distributed Computing
  6.  * Computing & Communications
  7.  * University of Washington
  8.  * Administration Building, AG-44
  9.  * Seattle, WA  98195
  10.  * Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date: 20 December 1989
  13.  * Last Edited: 22 September 1999
  14.  *
  15.  * Copyright 1999 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35. /* DEDICATION
  36.  *
  37.  *  This file is dedicated to my dog, Unix, also known as Yun-chan and
  38.  * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast.  Unix
  39.  * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
  40.  * a two-month bout with cirrhosis of the liver.
  41.  *
  42.  *  He was a dear friend, and I miss him terribly.
  43.  *
  44.  *  Lift a leg, Yunie.  Luv ya forever!!!!
  45.  */
  46. #include <stdio.h>
  47. #include <ctype.h>
  48. #include <errno.h>
  49. extern int errno; /* just in case */
  50. #include "mail.h"
  51. #include "osdep.h"
  52. #include <time.h>
  53. #include <fcntl.h>
  54. #include <sys/stat.h>
  55. #include <sys/utime.h>
  56. #include "unixnt.h"
  57. #include "pseudo.h"
  58. #include "fdstring.h"
  59. #include "misc.h"
  60. #include "dummy.h"
  61. /* UNIX mail routines */
  62. /* Driver dispatch used by MAIL */
  63. DRIVER unixdriver = {
  64.   "unix", /* driver name */
  65.   DR_LOCAL|DR_MAIL, /* driver flags */
  66.   (DRIVER *) NIL, /* next driver */
  67.   unix_valid, /* mailbox is valid for us */
  68.   unix_parameters, /* manipulate parameters */
  69.   unix_scan, /* scan mailboxes */
  70.   unix_list, /* list mailboxes */
  71.   unix_lsub, /* list subscribed mailboxes */
  72.   NIL, /* subscribe to mailbox */
  73.   NIL, /* unsubscribe from mailbox */
  74.   unix_create, /* create mailbox */
  75.   unix_delete, /* delete mailbox */
  76.   unix_rename, /* rename mailbox */
  77.   NIL, /* status of mailbox */
  78.   unix_open, /* open mailbox */
  79.   unix_close, /* close mailbox */
  80.   NIL, /* fetch message "fast" attributes */
  81.   NIL, /* fetch message flags */
  82.   NIL, /* fetch overview */
  83.   NIL, /* fetch message envelopes */
  84.   unix_header, /* fetch message header */
  85.   unix_text, /* fetch message text */
  86.   NIL, /* fetch partial message text */
  87.   NIL, /* unique identifier */
  88.   NIL, /* message number */
  89.   NIL, /* modify flags */
  90.   unix_flagmsg, /* per-message modify flags */
  91.   NIL, /* search for message based on criteria */
  92.   NIL, /* sort messages */
  93.   NIL, /* thread messages */
  94.   unix_ping, /* ping mailbox to see if still alive */
  95.   unix_check, /* check for new messages */
  96.   unix_expunge, /* expunge deleted messages */
  97.   unix_copy, /* copy messages to another mailbox */
  98.   unix_append, /* append string message to mailbox */
  99.   NIL /* garbage collect stream */
  100. };
  101. /* prototype stream */
  102. MAILSTREAM unixproto = {&unixdriver};
  103. /* driver parameters */
  104. static long unix_fromwidget = T;
  105. /* UNIX mail validate mailbox
  106.  * Accepts: mailbox name
  107.  * Returns: our driver if name is valid, NIL otherwise
  108.  */
  109. DRIVER *unix_valid (char *name)
  110. {
  111.   int fd;
  112.   DRIVER *ret = NIL;
  113.   int c,r;
  114.   char tmp[MAILTMPLEN],file[MAILTMPLEN],*s,*t;
  115.   struct stat sbuf;
  116.   struct utimbuf times;
  117.   errno = EINVAL; /* assume invalid argument */
  118. /* must be non-empty file */
  119.   if ((t = dummy_file (file,name)) && !stat (t,&sbuf) &&
  120.       ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
  121.     if (!sbuf.st_size)errno = 0;/* empty file */
  122.     else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
  123.       memset (tmp,'',MAILTMPLEN);
  124.       if (read (fd,tmp,MAILTMPLEN-1) <= 0) errno = -1;
  125.       else { /* ignore leading whitespace */
  126. for (s = tmp,c = '12';
  127.      (*s == '15') || (*s == '12') || (*s == ' ') || (*s == 't');
  128.      c = *s++);
  129. if (c == '12') { /* at start of a line? */
  130.   VALID (s,t,r,c); /* yes, validate format */
  131.   if (r) ret = &unixdriver;
  132.   else errno = -1; /* invalid format */
  133. }
  134.       }
  135.       close (fd); /* close the file */
  136. /* preserve atime and mtime */
  137.       times.actime = sbuf.st_atime;
  138.       times.modtime = sbuf.st_mtime;
  139.       utime (file,&times); /* set the times */
  140.     }
  141.   }
  142. /* in case INBOX but not unix format */
  143.   else if ((errno == ENOENT) && ((name[0] == 'I') || (name[0] == 'i')) &&
  144.    ((name[1] == 'N') || (name[1] == 'n')) &&
  145.    ((name[2] == 'B') || (name[2] == 'b')) &&
  146.    ((name[3] == 'O') || (name[3] == 'o')) &&
  147.    ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) errno = -1;
  148.   return ret; /* return what we should */
  149. }
  150. /* UNIX manipulate driver parameters
  151.  * Accepts: function code
  152.  *     function-dependent value
  153.  * Returns: function-dependent return value
  154.  */
  155. void *unix_parameters (long function,void *value)
  156. {
  157.   switch ((int) function) {
  158.   case SET_FROMWIDGET:
  159.     unix_fromwidget = (long) value;
  160.     break;
  161.   case GET_FROMWIDGET:
  162.     value = (void *) unix_fromwidget;
  163.     break;
  164.   default:
  165.     value = NIL; /* error case */
  166.     break;
  167.   }
  168.   return value;
  169. }
  170. /* UNIX mail scan mailboxes
  171.  * Accepts: mail stream
  172.  *     reference
  173.  *     pattern to search
  174.  *     string to scan
  175.  */
  176. void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
  177. {
  178.   if (stream) dummy_scan (NIL,ref,pat,contents);
  179. }
  180. /* UNIX mail list mailboxes
  181.  * Accepts: mail stream
  182.  *     reference
  183.  *     pattern to search
  184.  */
  185. void unix_list (MAILSTREAM *stream,char *ref,char *pat)
  186. {
  187.   if (stream) dummy_list (NIL,ref,pat);
  188. }
  189. /* UNIX mail list subscribed mailboxes
  190.  * Accepts: mail stream
  191.  *     reference
  192.  *     pattern to search
  193.  */
  194. void unix_lsub (MAILSTREAM *stream,char *ref,char *pat)
  195. {
  196.   if (stream) dummy_lsub (NIL,ref,pat);
  197. }
  198. /* UNIX mail create mailbox
  199.  * Accepts: MAIL stream
  200.  *     mailbox name to create
  201.  * Returns: T on success, NIL on failure
  202.  */
  203. long unix_create (MAILSTREAM *stream,char *mailbox)
  204. {
  205.   char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
  206.   long ret = NIL;
  207.   int fd;
  208.   time_t ti = time (0);
  209.   if (!(s = dummy_file (mbx,mailbox))) {
  210.     sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
  211.     mm_log (tmp,ERROR);
  212.   }
  213. /* create underlying file */
  214.   else if (dummy_create_path (stream,s)) {
  215. /* done if made directory */
  216.     if ((s = strrchr (s,'\')) && !s[1]) ret = T;
  217.     else if ((fd = open (mbx,O_WRONLY,
  218.     (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
  219.       sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
  220.       mm_log (tmp,ERROR);
  221.       unlink (mbx); /* delete the file */
  222.     }
  223.     else { /* initialize header */
  224.       memset (tmp,'',MAILTMPLEN);
  225.       sprintf (tmp,"From %s %sDate: ",pseudo_from,ctime (&ti));
  226.       rfc822_date (s = tmp + strlen (tmp));
  227.       sprintf (s += strlen (s), /* write the pseudo-header */
  228.        "1512From: %s <%s@%s>1512Subject: %s1512X-IMAP: %010lu 00000000001512Status: RO15121512%s15121512",
  229.        pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
  230.        (unsigned long) ti,pseudo_msg);
  231.       if ((write (fd,tmp,strlen (tmp)) < 0) || close (fd)) {
  232. sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
  233.  strerror (errno));
  234. mm_log (tmp,ERROR);
  235. unlink (mbx); /* delete the file */
  236.       }
  237.       else ret = T; /* success */
  238.     }
  239.   }
  240.   return ret;
  241. }
  242. /* UNIX mail delete mailbox
  243.  * Accepts: MAIL stream
  244.  *     mailbox name to delete
  245.  * Returns: T on success, NIL on failure
  246.  */
  247. long unix_delete (MAILSTREAM *stream,char *mailbox)
  248. {
  249.   return unix_rename (stream,mailbox,NIL);
  250. }
  251. /* UNIX mail rename mailbox
  252.  * Accepts: MAIL stream
  253.  *     old mailbox name
  254.  *     new mailbox name (or NIL for delete)
  255.  * Returns: T on success, NIL on failure
  256.  */
  257. long unix_rename (MAILSTREAM *stream,char *old,char *newname)
  258. {
  259.   long ret = NIL;
  260.   char c,*s = NIL;
  261.   char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN],lockx[MAILTMPLEN];
  262.   int fd,ld;
  263.   struct stat sbuf;
  264.   mm_critical (stream); /* get the c-client lock */
  265.   if (newname && (!((s = dummy_file (tmp,newname)) && *s) ||
  266.   ((s = strrchr (s,'\')) && !s[1])))
  267.     sprintf (tmp,"Can't rename mailbox %.80s to %.80s: invalid name",
  268.      old,newname);
  269.   else if ((ld = lockname (lock,dummy_file (file,old),NIL)) < 0)
  270.     sprintf (tmp,"Can't get lock for mailbox %.80s",old);
  271.   else { /* lock out other c-clients */
  272.     if (flock (ld,LOCK_EX|LOCK_NB)) {
  273.       close (ld); /* couldn't lock, give up on it then */
  274.       sprintf (tmp,"Mailbox %.80s is in use by another process",old);
  275.     }
  276. /* lock out non c-client applications */
  277.     else if ((fd = unix_lock (file,O_BINARY|O_RDWR,S_IREAD|S_IWRITE,lockx,
  278.       LOCK_EX)) < 0)
  279.       sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
  280.     else {
  281.       unix_unlock(fd,NIL,lockx);/* pacify evil NTFS */
  282.       if (newname) { /* want rename? */
  283. /* found superior to destination name? */
  284. if (s && (s != tmp) && ((tmp[1] != ':') || (s != tmp + 2))) {
  285.   c = s[1]; /* remember character after delimiter */
  286.   *s = s[1] = ''; /* tie off name at delimiter */
  287. /* name doesn't exist, create it */
  288.   if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
  289.     *s = '\'; /* restore delimiter */
  290.     if (!dummy_create (stream,tmp)) {
  291.       flock (ld,LOCK_UN);
  292.       close (ld); /* close c-client lock */
  293.       unlink (lock); /* and delete it */
  294.       mm_nocritical (stream);
  295.       return NIL; /* couldn't create superior */
  296.     }
  297.   }
  298.   else *s = '\'; /* restore delimiter */
  299.   s[1] = c; /* restore character after delimiter */
  300. }
  301. if (rename (file,tmp))
  302.   sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
  303.    strerror (errno));
  304. else ret = T; /* set success */
  305.       }
  306.       else if (unlink (file)) /* want delete */
  307. sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
  308.       else ret = T; /* set success */
  309.       flock (ld,LOCK_UN); /* release c-client lock */
  310.       close (ld); /* close c-client lock */
  311.       unlink (lock); /* and delete it */
  312.     }
  313.   }
  314.   mm_nocritical (stream); /* no longer critical */
  315.   if (!ret) mm_log (tmp,ERROR); /* log error */
  316.   return ret; /* return success or failure */
  317. }
  318. /* UNIX mail open
  319.  * Accepts: Stream to open
  320.  * Returns: Stream on success, NIL on failure
  321.  */
  322. MAILSTREAM *unix_open (MAILSTREAM *stream)
  323. {
  324.   int fd;
  325.   char tmp[MAILTMPLEN];
  326. /* return prototype for OP_PROTOTYPE call */
  327.   if (!stream) return &unixproto;
  328.   if (stream->local) fatal ("unix recycle stream");
  329.   stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL));
  330. /* note if an INBOX or not */
  331.   stream->inbox = !strcmp (ucase (strcpy (tmp,stream->mailbox)),"INBOX");
  332. /* canonicalize the stream mailbox name */
  333.   dummy_file (tmp,stream->mailbox);
  334. /* canonicalize name */
  335.   fs_give ((void **) &stream->mailbox);
  336. /* save canonical name */
  337.   stream->mailbox = cpystr (tmp);
  338.   LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
  339.   LOCAL->lname = NIL;
  340.   LOCAL->filesize = 0; /* initialize file information */
  341.   LOCAL->filetime = 0;
  342.   LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNK) + 1);
  343.   stream->sequence++; /* bump sequence number */
  344.   LOCAL->line = NIL;
  345.   LOCAL->dirty = NIL; /* no update yet */
  346.   if (!stream->rdonly) { /* make lock for read/write access */
  347.     if ((fd = lockname (tmp,stream->mailbox,NIL)) < 0)
  348.       mm_log ("Can't open mailbox lock, access is readonly",WARN);
  349. /* can get the lock? */
  350.     else if (flock (fd,LOCK_EX|LOCK_NB)) {
  351.       mm_log ("Mailbox is open by another process, access is readonly",WARN);
  352.       close (fd);
  353.     }
  354.     else { /* got the lock, nobody else can alter state */
  355.       LOCAL->ld = fd; /* note lock's fd and name */
  356.       LOCAL->lname = cpystr (tmp);
  357.     }
  358.   }
  359. /* parse mailbox */
  360.   stream->nmsgs = stream->recent = 0;
  361. /* will we be able to get write access? */
  362.   if ((LOCAL->ld >= 0) && access (stream->mailbox,02) && (errno == EACCES)) {
  363.     mm_log ("Can't get write access to mailbox, access is readonly",WARN);
  364.     flock (LOCAL->ld,LOCK_UN); /* release the lock */
  365.     close (LOCAL->ld); /* close the lock file */
  366.     LOCAL->ld = -1; /* no more lock fd */
  367.     unlink (LOCAL->lname); /* delete it */
  368.   }
  369. /* reset UID validity */
  370.   stream->uid_validity = stream->uid_last = 0;
  371.   if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
  372.     unix_abort (stream); /* abort if can't get RW silent stream */
  373. /* parse mailbox */
  374.   else if (unix_parse (stream,tmp,LOCK_SH)) {
  375.     unix_unlock (LOCAL->fd,stream,tmp);
  376.     mail_unlock (stream);
  377.     mm_nocritical (stream); /* done with critical */
  378.   }
  379.   if (!LOCAL) return NIL; /* failure if stream died */
  380. /* make sure upper level knows readonly */
  381.   stream->rdonly = (LOCAL->ld < 0);
  382. /* notify about empty mailbox */
  383.   if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",NIL);
  384.   if (!stream->rdonly) stream->perm_seen = stream->perm_deleted =
  385.     stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
  386.   stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
  387.   stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
  388.     NIL : T; /* can we create new user flags? */
  389.   return stream; /* return stream alive to caller */
  390. }
  391. /* UNIX mail close
  392.  * Accepts: MAIL stream
  393.  *     close options
  394.  */
  395. void unix_close (MAILSTREAM *stream,long options)
  396. {
  397.   int silent = stream->silent;
  398.   stream->silent = T; /* go silent */
  399. /* expunge if requested */
  400.   if (options & CL_EXPUNGE) unix_expunge (stream);
  401. /* else dump final checkpoint */
  402.   else if (LOCAL->dirty) unix_check (stream);
  403.   stream->silent = silent; /* restore old silence state */
  404.   unix_abort (stream); /* now punt the file and local data */
  405. }
  406. /* UNIX mail fetch message header
  407.  * Accepts: MAIL stream
  408.  *     message # to fetch
  409.  *     pointer to returned header text length
  410.  *     option flags
  411.  * Returns: message header in RFC822 format
  412.  */
  413. /* lines to filter from header */
  414. static STRINGLIST *unix_hlines = NIL;
  415. char *unix_header (MAILSTREAM *stream,unsigned long msgno,
  416.    unsigned long *length,long flags)
  417. {
  418.   MESSAGECACHE *elt;
  419.   char *s;
  420.   *length = 0; /* default to empty */
  421.   if (flags & FT_UID) return "";/* UID call "impossible" */
  422.   elt = mail_elt (stream,msgno);/* get cache */
  423.   if (!unix_hlines) { /* once only code */
  424.     STRINGLIST *lines = unix_hlines = mail_newstringlist ();
  425.     lines->text.size = strlen (lines->text.data = (unsigned char *) "Status");
  426.     lines = lines->next = mail_newstringlist ();
  427.     lines->text.size = strlen (lines->text.data = (unsigned char *)
  428.        "X-Status");
  429.     lines = lines->next = mail_newstringlist ();
  430.     lines->text.size = strlen (lines->text.data = (unsigned char *)
  431.        "X-Keywords");
  432.     lines = lines->next = mail_newstringlist ();
  433.     lines->text.size = strlen (lines->text.data = (unsigned char *) "X-UID");
  434.   }
  435. /* go to header position */
  436.   lseek (LOCAL->fd,elt->private.special.offset +
  437.  elt->private.msg.header.offset,L_SET);
  438.   if (flags & FT_INTERNAL) { /* initial data OK? */
  439.     if (elt->private.msg.header.text.size > LOCAL->buflen) {
  440.       fs_give ((void **) &LOCAL->buf);
  441.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
  442.      elt->private.msg.header.text.size) + 1);
  443.     }
  444. /* read message */
  445.     read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
  446. /* got text, tie off string */
  447.     LOCAL->buf[*length = elt->private.msg.header.text.size] = '';
  448.   }
  449.   else { /* need to make a CRLF version */
  450.     read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
  451.   elt->private.msg.header.text.size);
  452. /* tie off string, and convert to CRLF */
  453.     s[elt->private.msg.header.text.size] = '';
  454.     *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
  455.     elt->private.msg.header.text.size);
  456.     fs_give ((void **) &s); /* free readin buffer */
  457.   }
  458.   *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT);
  459.   return LOCAL->buf; /* return processed copy */
  460. }
  461. /* UNIX mail fetch message text
  462.  * Accepts: MAIL stream
  463.  *     message # to fetch
  464.  *     pointer to returned stringstruct
  465.  *     option flags
  466.  * Returns: T on success, NIL if failure
  467.  */
  468. long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
  469. {
  470.   char *s;
  471.   unsigned long i;
  472.   MESSAGECACHE *elt;
  473. /* UID call "impossible" */
  474.   if (flags & FT_UID) return NIL;
  475.   elt = mail_elt (stream,msgno);/* get cache element */
  476. /* if message not seen */
  477.   if (!(flags & FT_PEEK) && !elt->seen) {
  478.     elt->seen = T; /* mark message as seen */
  479.     LOCAL->dirty = T; /* note stream is now dirty */
  480.     mm_flags (stream,msgno);
  481.   }
  482.   s = unix_text_work (stream,elt,&i,flags);
  483.   INIT (bs,mail_string,s,i); /* set up stringstruct */
  484.   return T; /* success */
  485. }
  486. /* UNIX mail fetch message text worker routine
  487.  * Accepts: MAIL stream
  488.  *     message cache element
  489.  *     pointer to returned header text length
  490.  *     option flags
  491.  */
  492. char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
  493.       unsigned long *length,long flags)
  494. {
  495.   FDDATA d;
  496.   STRING bs;
  497.   char *s,tmp[CHUNK];
  498. /* go to text position */
  499.   lseek (LOCAL->fd,elt->private.special.offset +
  500.  elt->private.msg.text.offset,L_SET);
  501.   if (flags & FT_INTERNAL) { /* initial data OK? */
  502.     if (elt->private.msg.text.text.size > LOCAL->buflen) {
  503.       fs_give ((void **) &LOCAL->buf);
  504.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
  505.      elt->private.msg.text.text.size) + 1);
  506.     }
  507. /* read message */
  508.     read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
  509. /* got text, tie off string */
  510.     LOCAL->buf[*length = elt->private.msg.text.text.size] = '';
  511.   }
  512.   else { /* need to make a CRLF version */
  513.     if (elt->rfc822_size > LOCAL->buflen) {
  514.       /* excessively conservative, but the right thing is too hard to do */
  515.       fs_give ((void **) &LOCAL->buf);
  516.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->rfc822_size) + 1);
  517.     }
  518.     d.fd = LOCAL->fd; /* yes, set up file descriptor */
  519.     d.pos = elt->private.special.offset + elt->private.msg.text.offset;
  520.     d.chunk = tmp; /* initial buffer chunk */
  521.     d.chunksize = CHUNK; /* file chunk size */
  522.     INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
  523.     for (s = LOCAL->buf; SIZE (&bs);) switch (CHR (&bs)) {
  524.     case '15': /* carriage return seen */
  525.       *s++ = SNX (&bs); /* copy it and any succeeding LF */
  526.       if (SIZE (&bs) && (CHR (&bs) == '12')) *s++ = SNX (&bs);
  527.       break;
  528.     case '12':
  529.       *s++ = '15'; /* insert a CR */
  530.     default:
  531.       *s++ = SNX (&bs); /* copy characters */
  532.     }
  533.     *s = ''; /* tie off buffer */
  534.     *length = s - LOCAL->buf; /* calculate length */
  535.   }
  536.   return LOCAL->buf;
  537. }
  538. /* UNIX per-message modify flag
  539.  * Accepts: MAIL stream
  540.  *     message cache element
  541.  */
  542. void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
  543. {
  544. /* only after finishing */
  545.   if (elt->valid) LOCAL->dirty = T;
  546. }
  547. /* UNIX mail ping mailbox
  548.  * Accepts: MAIL stream
  549.  * Returns: T if stream alive, else NIL
  550.  */
  551. long unix_ping (MAILSTREAM *stream)
  552. {
  553.   char lock[MAILTMPLEN];
  554.   struct stat sbuf;
  555. /* big no-op if not readwrite */
  556.   if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
  557.     if (stream->rdonly) { /* does he want to give up readwrite? */
  558. /* checkpoint if we changed something */
  559.       if (LOCAL->dirty) unix_check (stream);
  560.       flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
  561.       close (LOCAL->ld); /* close the readwrite lock file */
  562.       LOCAL->ld = -1; /* no more readwrite lock fd */
  563.       unlink (LOCAL->lname); /* delete the readwrite lock file */
  564.     }
  565.     else { /* get current mailbox size */
  566.       if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
  567.       else stat (stream->mailbox,&sbuf);
  568. /* parse if mailbox changed */
  569.       if ((sbuf.st_size != LOCAL->filesize) &&
  570.   unix_parse (stream,lock,LOCK_SH)) {
  571. /* unlock mailbox */
  572. unix_unlock (LOCAL->fd,stream,lock);
  573. mail_unlock (stream); /* and stream */
  574. mm_nocritical (stream); /* done with critical */
  575.       }
  576.     }
  577.   }
  578.   return LOCAL ? LONGT : NIL; /* return if still alive */
  579. }
  580. /* UNIX mail check mailbox
  581.  * Accepts: MAIL stream
  582.  */
  583. void unix_check (MAILSTREAM *stream)
  584. {
  585.   char lock[MAILTMPLEN];
  586. /* parse and lock mailbox */
  587.   if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
  588.       unix_parse (stream,lock,LOCK_EX)) {
  589. /* any unsaved changes? */
  590.     if (LOCAL->dirty && unix_rewrite (stream,NIL,NIL) && !stream->silent)
  591.       mm_log ("Checkpoint completed",NIL);
  592. /* unlock file */
  593.     unix_unlock (LOCAL->fd,stream,lock);
  594.     mail_unlock (stream); /* unlock the stream */
  595.     mm_nocritical (stream); /* done with critical */
  596.   }
  597. }
  598. /* UNIX mail expunge mailbox
  599.  * Accepts: MAIL stream
  600.  */
  601. void unix_expunge (MAILSTREAM *stream)
  602. {
  603.   unsigned long i;
  604.   char lock[MAILTMPLEN];
  605.   char *msg = NIL;
  606. /* parse and lock mailbox */
  607.   if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
  608.       unix_parse (stream,lock,LOCK_EX)) {
  609. /* count expunged messages if not dirty */
  610.     if (!LOCAL->dirty) for (i = 1; i <= stream->nmsgs; i++)
  611.       if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
  612.     if (!LOCAL->dirty) msg = "No messages deleted, so no update needed";
  613.     else if (unix_rewrite (stream,&i,NIL)) {
  614.       if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
  615.       else msg = "Mailbox checkpointed, but no messages expunged";
  616.     }
  617. /* unlock mailbox */
  618.     unix_unlock (LOCAL->fd,stream,lock);
  619.     mail_unlock (stream); /* unlock the stream */
  620.     mm_nocritical (stream); /* done with critical */
  621.     if (msg && !stream->silent) mm_log (msg,NIL);
  622.   }
  623.   else if (!stream->silent) mm_log("Expunge ignored on readonly mailbox",WARN);
  624. }
  625. /* UNIX mail copy message(s)
  626.  * Accepts: MAIL stream
  627.  *     sequence
  628.  *     destination mailbox
  629.  *     copy options
  630.  * Returns: T if copy successful, else NIL
  631.  */
  632. long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
  633. {
  634.   struct stat sbuf;
  635.   int fd;
  636.   char *s,file[MAILTMPLEN],lock[MAILTMPLEN];
  637.   struct utimbuf times;
  638.   unsigned long i,j;
  639.   MESSAGECACHE *elt;
  640.   long ret = T;
  641.   mailproxycopy_t pc =
  642.     (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
  643.   if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
  644. mail_sequence (stream,sequence))) return NIL;
  645. /* make sure valid mailbox */
  646.   if (!unix_valid (mailbox)) switch (errno) {
  647.   case ENOENT: /* no such file? */
  648.     mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
  649.     return NIL;
  650.   case 0: /* merely empty file? */
  651.     break;
  652.   case EINVAL:
  653.     if (pc) return (*pc) (stream,sequence,mailbox,options);
  654.     sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
  655.     mm_log (LOCAL->buf,ERROR);
  656.     return NIL;
  657.   default:
  658.     if (pc) return (*pc) (stream,sequence,mailbox,options);
  659.     sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox);
  660.     mm_log (LOCAL->buf,ERROR);
  661.     return NIL;
  662.   }
  663.   LOCAL->buf[0] = '';
  664.   mm_critical (stream); /* go critical */
  665.   if ((fd = unix_lock (dummy_file (file,mailbox),
  666.        O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
  667.        lock,LOCK_EX)) < 0) {
  668.     mm_nocritical (stream); /* done with critical */
  669.     sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
  670.     mm_log (LOCAL->buf,ERROR); /* log the error */
  671.     return NIL; /* failed */
  672.   }
  673.   fstat (fd,&sbuf); /* get current file size */
  674. /* write all requested messages to mailbox */
  675.   for (i = 1; ret && (i <= stream->nmsgs); i++)
  676.     if ((elt = mail_elt (stream,i))->sequence) {
  677.       lseek (LOCAL->fd,elt->private.special.offset,L_SET);
  678.       read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
  679.       if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL;
  680.       else { /* internal header succeeded */
  681. s = unix_header (stream,i,&j,NIL);
  682. /* header size, sans trailing newline */
  683. if (j && (s[j - 4] == '15')) j -= 2;
  684. if (write (fd,s,j) < 0) ret = NIL;
  685. else { /* message header succeeded */
  686.   j = unix_xstatus (stream,LOCAL->buf,elt,NIL);
  687.   if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
  688.   else { /* message status succeeded */
  689.     s = unix_text_work (stream,elt,&j,NIL);
  690.     if ((write (fd,s,j) < 0) || (write (fd,"1512",2) < 0))
  691.       ret = NIL;
  692.   }
  693. }
  694.       }
  695.     }
  696.   if (!ret || fsync (fd)) { /* force out the update */
  697.     sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
  698.     ftruncate (fd,sbuf.st_size);
  699.     ret = NIL;
  700.   }
  701.   times.actime = sbuf.st_atime; /* preserve atime */
  702.   times.modtime = time (0); /* set mtime to now */
  703.   utime (file,&times); /* set the times */
  704.   unix_unlock (fd,NIL,lock); /* unlock and close mailbox */
  705.   mm_nocritical (stream); /* release critical */
  706. /* log the error */
  707.   if (!ret) mm_log (LOCAL->buf,ERROR);
  708. /* delete if requested message */
  709.   else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
  710.     if ((elt = mail_elt (stream,i))->sequence) {
  711.       elt->deleted = T; /* mark message deleted */
  712.       LOCAL->dirty = T; /* note stream is now dirty */
  713.     }
  714.   return ret;
  715. }
  716. /* UNIX mail append message from stringstruct
  717.  * Accepts: MAIL stream
  718.  *     destination mailbox
  719.  *     initial flags
  720.  *     internal date
  721.  *     stringstruct of messages to append
  722.  * Returns: T if append successful, else NIL
  723.  */
  724. #define BUFLEN 8*MAILTMPLEN
  725. long unix_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  726.   STRING *message)
  727. {
  728.   MESSAGECACHE elt;
  729.   struct stat sbuf;
  730.   int fd,ti,zn;
  731.   long f,i,ok;
  732.   unsigned long j,n,uf,size;
  733.   char c,*x,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  734.   struct utimbuf times;
  735. /* default stream to prototype */
  736.   if (!stream) stream = &unixproto;
  737. /* get flags */
  738.   f = mail_parse_flags (stream,flags,&uf);
  739. /* parse date */
  740.   if (!date) rfc822_date (date = tmp);
  741.   if (!mail_parse_date (&elt,date)) {
  742.     sprintf (buf,"Bad date in append: %.80s",date);
  743.     mm_log (buf,ERROR);
  744.     return NIL;
  745.   }
  746. /* make sure valid mailbox */
  747.   if (!unix_valid (mailbox)) switch (errno) {
  748.   case ENOENT: /* no such file? */
  749.     if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
  750. ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
  751. ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
  752. ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
  753. ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5])
  754.       unix_create (NIL,"INBOX");
  755.     else {
  756.       mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
  757.       return NIL;
  758.     }
  759. /* falls through */
  760.   case 0: /* INBOX ENOENT or empty file? */
  761.     break;
  762.   case EINVAL:
  763.     sprintf (buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
  764.     mm_log (buf,ERROR);
  765.     return NIL;
  766.   default:
  767.     sprintf (buf,"Not a UNIX-format mailbox: %.80s",mailbox);
  768.     mm_log (buf,ERROR);
  769.     return NIL;
  770.   }
  771.   mm_critical (stream); /* go critical */
  772.   if ((fd = unix_lock (dummy_file (file,mailbox),
  773.        O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
  774.        lock,LOCK_EX)) < 0) {
  775.     mm_nocritical (stream); /* done with critical */
  776.     sprintf (buf,"Can't open append mailbox: %s",strerror (errno));
  777.     mm_log (buf,ERROR);
  778.     return NIL;
  779.   }
  780.   fstat (fd,&sbuf); /* get current file size */
  781.   sprintf (buf,"From %s@%s ",myusername (),mylocalhost ());
  782. /* user wants to suppress time zones? */
  783.   if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
  784.     time_t when = mail_longdate (&elt);
  785.     strcat (buf,ctime (&when));
  786.   }
  787. /* write the date given */
  788.   else mail_cdate (buf + strlen (buf),&elt);
  789. /* replace trailing NL with CRLF */
  790.   strcpy (buf + strlen (buf) -1,"1512");
  791.   sprintf (buf + strlen (buf),"Status: %s1512X-Status: %s%s%s%s1512X-Keywords:",
  792.    f&fSEEN ? "R" : "",f&fDELETED ? "D" : "",
  793.    f&fFLAGGED ? "F" : "",f&fANSWERED ? "A" : "",f&fDRAFT ? "T" : "");
  794.   while (uf) /* write user flags */
  795.     sprintf(buf+strlen(buf)," %s",stream->user_flags[find_rightmost_bit(&uf)]);
  796.   strcat (buf,"1512"); /* tie off flags */
  797. /* copy text */
  798.   for (i = strlen (buf), ok = T, size = SIZE (message); ok && size; ) {
  799.     for (j = 0, c = ''; size && (c != '12') && (j < MAILTMPLEN); size--)
  800.       tmp[j++] = c = SNX (message);
  801. /* possible "From " line? */
  802.     if ((j > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') &&
  803. (tmp[3] == 'm') && (tmp[4] == ' ')) {
  804. /* see if need to write a widget */
  805.       if (!(ti = unix_fromwidget || (c != '12'))) VALID (tmp,x,ti,zn);
  806.       if (ti) ok = unix_append_putc (fd,buf,&i,'>');
  807.     }
  808. /* write the line */
  809.     if (ok) for (n = 0; n < j; n++) ok = unix_append_putc (fd,buf,&i,tmp[n]);
  810. /* handle pathologically long line */
  811.     if (ok && (c != '12') && size) do
  812.       ok = unix_append_putc (fd,buf,&i,c = SNX (message));
  813.     while (ok && --size && (c != '12'));
  814.   }
  815. /* write trailing CRLF */
  816.   if (!(ok && (unix_append_putc (fd,buf,&i,'15') &&
  817.        unix_append_putc (fd,buf,&i,'12') &&
  818.        (i ? (write (fd,buf,i) >= 0) : T) && !fsync (fd)))) {
  819.     sprintf (buf,"Message append failed: %s",strerror (errno));
  820.     mm_log (buf,ERROR);
  821.     ftruncate (fd,sbuf.st_size);
  822.   }
  823.   times.actime = sbuf.st_atime; /* preserve atime */
  824.   times.modtime = time (0); /* set mtime to now */
  825.   utime (file,&times); /* set the times */
  826.   unix_unlock (fd,NIL,lock); /* unlock and close mailbox */
  827.   mm_nocritical (stream); /* release critical */
  828.   return ok; /* return success */
  829. }
  830. /* UNIX mail append character
  831.  * Accepts: file descriptor
  832.  *     output buffer
  833.  *     pointer to current size of output buffer
  834.  *     character to append
  835.  * Returns: T if append successful, else NIL
  836.  */
  837. long unix_append_putc (int fd,char *s,long *i,char c)
  838. {
  839.   s[(*i)++] = c;
  840.   if (*i == BUFLEN) { /* dump if buffer filled */
  841.     if (write (fd,s,*i) < 0) return NIL;
  842.     *i = 0; /* reset */
  843.   }
  844.   return T;
  845. }
  846. /* Internal routines */
  847. /* UNIX mail abort stream
  848.  * Accepts: MAIL stream
  849.  */
  850. void unix_abort (MAILSTREAM *stream)
  851. {
  852.   if (LOCAL) { /* only if a file is open */
  853.     if (LOCAL->fd >= 0) close (LOCAL->fd);
  854.     if (LOCAL->ld >= 0) { /* have a mailbox lock? */
  855.       flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
  856.       close (LOCAL->ld); /* close the lock file */
  857.       unlink (LOCAL->lname); /* and delete it */
  858.     }
  859.     if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
  860. /* free local text buffers */
  861.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  862.     if (LOCAL->line) fs_give ((void **) &LOCAL->line);
  863. /* nuke the local data */
  864.     fs_give ((void **) &stream->local);
  865.     stream->dtb = NIL; /* log out the DTB */
  866.   }
  867. }
  868. /* UNIX open and lock mailbox
  869.  * Accepts: file name to open/lock
  870.  *     file open mode
  871.  *     destination buffer for lock file name
  872.  *     type of locking operation (LOCK_SH or LOCK_EX)
  873.  */
  874. int unix_lock (char *file,int flags,int mode,char *lock,int op)
  875. {
  876.   int fd,ld,j;
  877.   int i = LOCKTIMEOUT * 60 - 1;
  878.   char tmp[MAILTMPLEN];
  879.   time_t t;
  880.   struct stat sb;
  881.   sprintf (lock,"%s.lock",file);/* build lock filename */
  882.   do { /* until OK or out of tries */
  883.     t = time (0); /* get the time now */
  884. /* try to get the lock */
  885.     if ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))>=0)
  886.       close (ld); /* got it, close the lock file! */
  887.     else if (errno != EEXIST) { /* miscellaneous error */
  888.       sprintf (tmp,"Error creating %.80s: %s",lock,strerror (errno));
  889.       if (!(i%15)) mm_log (tmp,WARN);
  890.     }
  891. /* lock exists, still active? */
  892.     else if (!stat (lock,&sb) && (t > sb.st_ctime + LOCKTIMEOUT * 60) &&
  893.      ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT,S_IREAD|S_IWRITE))>=0))
  894.       close (ld); /* got timed-out lock file */
  895.     else { /* active lock, try again */
  896.       if (!(i%15)) {
  897. sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
  898.  file,i);
  899. mm_log (tmp,WARN);
  900.       }
  901.       sleep (1); /* wait a second before next retry */
  902.     }
  903.   } while (*lock && ld < 0 && i--);
  904. /* open file */
  905.   if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
  906.   else { /* open failed */
  907.     j = errno; /* preserve error code */
  908.     if (*lock) unlink (lock); /* flush the lock file if any */
  909.     errno = j; /* restore error code */
  910.   }
  911.   return fd;
  912. }
  913. /* UNIX unlock and close mailbox
  914.  * Accepts: file descriptor
  915.  *     (optional) mailbox stream to check atime/mtime
  916.  *     (optional) lock file name
  917.  */
  918. void unix_unlock (int fd,MAILSTREAM *stream,char *lock)
  919. {
  920.   struct stat sbuf;
  921.   struct utimbuf times;
  922.   fstat (fd,&sbuf); /* get file times */
  923. /* if stream and csh would think new mail */
  924.   if (stream && (sbuf.st_atime <= sbuf.st_mtime)) {
  925. /* set times to now */
  926.     times.modtime = times.actime = time (0);
  927. /* set the times, note change */
  928.     if (!utime (stream->mailbox,&times)) LOCAL->filetime = times.modtime;
  929.   }
  930.   flock (fd,LOCK_UN); /* release flock'ers */
  931.   if (!stream) close (fd); /* close the file if no stream */
  932. /* flush the lock file if any */
  933.   if (lock && *lock) unlink (lock);
  934. }
  935. /* UNIX mail parse and lock mailbox
  936.  * Accepts: MAIL stream
  937.  *     space to write lock file name
  938.  *     type of locking operation
  939.  * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
  940.  */
  941. int unix_parse (MAILSTREAM *stream,char *lock,int op)
  942. {
  943.   int zn;
  944.   unsigned long i,j,k,m;
  945.   char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
  946.   int ti = 0,pseudoseen = NIL;
  947.   unsigned long nmsgs = stream->nmsgs;
  948.   unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
  949.   unsigned long recent = stream->recent;
  950.   unsigned long oldnmsgs = stream->nmsgs;
  951.   short silent = stream->silent;
  952.   struct stat sbuf;
  953.   STRING bs;
  954.   FDDATA d;
  955.   MESSAGECACHE *elt;
  956.   mail_lock (stream); /* guard against recursion or pingers */
  957. /* toss out previous descriptor */
  958.   if (LOCAL->fd >= 0) close (LOCAL->fd);
  959.   mm_critical (stream); /* open and lock mailbox (shared OK) */
  960.   if ((LOCAL->fd = unix_lock (stream->mailbox,
  961.       O_BINARY + ((LOCAL->ld >= 0) ? O_RDWR:O_RDONLY),
  962.       NIL,lock,op)) < 0) {
  963.     sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
  964.     mm_log (tmp,ERROR);
  965.     unix_abort (stream);
  966.     mail_unlock (stream);
  967.     mm_nocritical (stream); /* done with critical */
  968.     return NIL;
  969.   }
  970.   fstat (LOCAL->fd,&sbuf); /* get status */
  971. /* validate change in size */
  972.   if (sbuf.st_size < LOCAL->filesize) {
  973.     sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
  974.      (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
  975.     mm_log (tmp,ERROR); /* this is pretty bad */
  976.     unix_unlock (LOCAL->fd,stream,lock);
  977.     unix_abort (stream);
  978.     mail_unlock (stream);
  979.     mm_nocritical (stream); /* done with critical */
  980.     return NIL;
  981.   }
  982. /* new data? */
  983.   else if (i = sbuf.st_size - LOCAL->filesize) {
  984.     d.fd = LOCAL->fd; /* yes, set up file descriptor */
  985.     d.pos = LOCAL->filesize; /* get to that position in the file */
  986.     d.chunk = LOCAL->buf; /* initial buffer chunk */
  987.     d.chunksize = CHUNK; /* file chunk size */
  988.     INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
  989. /* skip leading whitespace for broken MTAs */
  990.     while (((c = CHR (&bs)) == '15') || (c == '12') ||
  991.    (c == ' ') || (c == 't')) SNX (&bs);
  992.     if (SIZE (&bs)) { /* read new data */
  993. /* remember internal header position */
  994.       j = LOCAL->filesize + GETPOS (&bs);
  995.       s = unix_mbxline (stream,&bs,&i);
  996.       t = NIL,zn = 0;
  997.       if (i) VALID (s,t,ti,zn); /* see if valid From line */
  998.       if (!ti) { /* someone pulled the rug from under us */
  999. sprintf(tmp,"Unexpected changes to mailbox (try restarting): %.20s",s);
  1000. mm_log (tmp,ERROR);
  1001. unix_unlock (LOCAL->fd,stream,lock);
  1002. unix_abort (stream);
  1003. mail_unlock (stream);
  1004. mm_nocritical (stream); /* done with critical */
  1005. return NIL;
  1006.       }
  1007.       stream->silent = T; /* quell main program new message events */
  1008.       do { /* found a message */
  1009. /* instantiate first new message */
  1010. mail_exists (stream,++nmsgs);
  1011. (elt = mail_elt (stream,nmsgs))->valid = T;
  1012. recent++; /* assume recent by default */
  1013. elt->recent = T;
  1014. /* note position/size of internal header */
  1015. elt->private.special.offset = j;
  1016. elt->private.msg.header.offset = elt->private.special.text.size = i;
  1017. /* generate plausible IMAPish date string */
  1018. date[2] = date[6] = date[20] = '-'; date[11] = ' ';
  1019. date[14] = date[17] = ':';
  1020. /* dd */
  1021. date[0] = t[ti - 2]; date[1] = t[ti - 1];
  1022. /* mmm */
  1023. date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
  1024. /* hh */
  1025. date[12] = t[ti + 1]; date[13] = t[ti + 2];
  1026. /* mm */
  1027. date[15] = t[ti + 4]; date[16] = t[ti + 5];
  1028. if (t[ti += 6] == ':') {/* ss */
  1029.   date[18] = t[++ti]; date[19] = t[++ti];
  1030.   ti++; /* move to space */
  1031. }
  1032. else date[18] = date[19] = '0';
  1033. /* yy -- advance over timezone if necessary */
  1034. if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
  1035. date[7] = t[ti + 1]; date[8] = t[ti + 2];
  1036. date[9] = t[ti + 3]; date[10] = t[ti + 4];
  1037. /* zzz */
  1038. t = zn ? (t + zn + 1) : "LCL";
  1039. date[21] = *t++; date[22] = *t++; date[23] = *t++;
  1040. if ((date[21] != '+') && (date[21] != '-')) date[24] = '';
  1041. else { /* numeric time zone */
  1042.   date[24] = *t++; date[25] = *t++;
  1043.   date[26] = ''; date[20] = ' ';
  1044. }
  1045. /* set internal date */
  1046. if (!mail_parse_date (elt,date)) {
  1047.   sprintf (tmp,"Unable to parse internal date: %s",date);
  1048.   mm_log (tmp,WARN);
  1049. }
  1050. do { /* look for message body */
  1051.   s = t = unix_mbxline (stream,&bs,&i);
  1052.   if (i) switch (*s) { /* check header lines */
  1053.   case 'X': /* possible X-???: line */
  1054.     if (s[1] == '-') { /* must be immediately followed by hyphen */
  1055. /* X-Status: becomes Status: in S case */
  1056.       if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
  1057.   s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
  1058. /* possible X-Keywords */
  1059.       else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
  1060.        s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
  1061.        s[8] == 'd' && s[9] == 's' && s[10] == ':') {
  1062. char uf[MAILTMPLEN];
  1063. s += 11; /* flush leading whitespace */
  1064. while (*s && (*s != '15') && (*s != '12')) {
  1065.   while (*s == ' ') s++;
  1066. /* find end of keyword */
  1067.   if (!(u = strpbrk (s," 1512"))) u = s + strlen (s);
  1068. /* got a keyword? */
  1069.   if ((k = (u - s)) && (k < MAILTMPLEN)) {
  1070. /* copy keyword */
  1071.     strncpy (uf,s,k);
  1072. /* make sure tied off */
  1073.     uf[k] = '';
  1074.     ucase (uf); /* coerce upper case */
  1075.     for (j = 0; (j<NUSERFLAGS) && stream->user_flags[j]; ++j)
  1076.       if (!strcmp(uf,
  1077.   ucase (strcpy(tmp,stream->user_flags[j])))) {
  1078. elt->user_flags |= ((long) 1) << j;
  1079. break;
  1080.       }
  1081. /* need to create it? */
  1082.     if (!stream->rdonly && (j < NUSERFLAGS) &&
  1083. !stream->user_flags[j]) {
  1084. /* set the bit */
  1085.       *uf |= 1 << j;
  1086.       stream->user_flags[j] = (char *) fs_get (k + 1);
  1087.       strncpy (stream->user_flags[j],s,k);
  1088.       stream->user_flags[j][k] = '';
  1089. /* if now out of user flags */
  1090.       if (j == NUSERFLAGS - 1) stream->kwd_create = NIL;
  1091.     }
  1092.   }
  1093.   s = u; /* advance to next keyword */
  1094. }
  1095. break;
  1096.       }
  1097. /* possible X-IMAP */
  1098.       else if ((nmsgs == 1) && !stream->uid_validity &&
  1099.        (s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
  1100.        (s[5] == 'P') && (s[6] == ':')) {
  1101. s += 7; /* advance to data */
  1102. /* flush whitespace */
  1103. while (*s == ' ') s++;
  1104. j = 0; /* slurp UID validity */
  1105. /* found a digit? */
  1106. while (isdigit (*s)) {
  1107.   j *= 10; /* yes, add it in */
  1108.   j += *s++ - '0';
  1109. }
  1110. if (!j) break; /* punt if invalid UID validity */
  1111. stream->uid_validity = j;
  1112. /* flush whitespace */
  1113. while (*s == ' ') s++;
  1114. /* must have UID last too */
  1115. if (isdigit (*s)) {
  1116.   j = 0; /* slurp UID last */
  1117.   while (isdigit (*s)) {
  1118.     j *= 10; /* yes, add it in */
  1119.     j += *s++ - '0';
  1120.   }
  1121.   stream->uid_last = j;
  1122. /* process keywords */
  1123.   for (j = 0; (*s != '15') && (*s != '12'); j++) {
  1124. /* flush leading whitespace */
  1125.     while (*s == ' ') s++;
  1126.     u = strpbrk (s," 1512");
  1127. /* got a keyword? */
  1128.     if ((k = (u - s)) && j < NUSERFLAGS) {
  1129.       if (stream->user_flags[j])
  1130. fs_give ((void **) &stream->user_flags[j]);
  1131.       stream->user_flags[j] = (char *) fs_get (k + 1);
  1132.       strncpy (stream->user_flags[j],s,k);
  1133.       stream->user_flags[j][k] = '';
  1134.     }
  1135.     s = u; /* advance to next keyword */
  1136.   }
  1137. /* pseudo-header seen */
  1138.   pseudoseen = T;
  1139. }
  1140. break;
  1141.       }
  1142. /* possible X-UID */
  1143.       else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
  1144.        s[5] == ':') {
  1145. /* only believe if have a UID validity */
  1146. if (stream->uid_validity && (nmsgs > 1)) {
  1147.   s += 6; /* advance to UID value */
  1148. /* flush whitespace */
  1149.   while (*s == ' ') s++;
  1150.   j = 0;
  1151. /* found a digit? */
  1152.   while (isdigit (*s)) {
  1153.     j *= 10; /* yes, add it in */
  1154.     j += *s++ - '0';
  1155.   }
  1156. /* make sure not duplicated */
  1157.   if (elt->private.uid)
  1158.     sprintf (tmp,"Message %lu UID %lu already has UID %lu",
  1159.      pseudoseen ? elt->msgno - 1 : elt->msgno,
  1160.      j,elt->private.uid);
  1161. /* make sure UID doesn't go backwards */
  1162.   else if (j <= prevuid)
  1163.     sprintf (tmp,"Message %lu UID %lu less than %lu",
  1164.      pseudoseen ? elt->msgno - 1 : elt->msgno,
  1165.      j,prevuid + 1);
  1166. /* or skip by mailbox's recorded last */
  1167.   else if (j > stream->uid_last)
  1168.     sprintf (tmp,"Message %lu UID %lu greater than last %lu",
  1169.      pseudoseen ? elt->msgno - 1 : elt->msgno,
  1170.      j,stream->uid_last);
  1171.   else { /* normal UID case */
  1172.     prevuid = elt->private.uid = j;
  1173.     break; /* exit this cruft */
  1174.   }
  1175.   mm_log (tmp,WARN);
  1176. /* invalidate UID validity */
  1177.   stream->uid_validity = 0;
  1178.   elt->private.uid = 0;
  1179. }
  1180. break;
  1181.       }
  1182.     }
  1183. /* otherwise fall into S case */
  1184.   case 'S': /* possible Status: line */
  1185.     if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
  1186. s[4] == 'u' && s[5] == 's' && s[6] == ':') {
  1187.       s += 6; /* advance to status flags */
  1188.       do switch (*s++) {/* parse flags */
  1189.       case 'R': /* message read */
  1190. elt->seen = T;
  1191. break;
  1192.       case 'O': /* message old */
  1193. if (elt->recent) {
  1194.   elt->recent = NIL;
  1195.   recent--; /* it really wasn't recent */
  1196. }
  1197. break;
  1198.       case 'D': /* message deleted */
  1199. elt->deleted = T;
  1200. break;
  1201.       case 'F': /* message flagged */
  1202. elt->flagged = T;
  1203. break;
  1204.       case 'A': /* message answered */
  1205. elt->answered = T;
  1206. break;
  1207.       case 'T': /* message is a draft */
  1208. elt->draft = T;
  1209. break;
  1210.       default: /* some other crap */
  1211. break;
  1212.       } while (*s && (*s != '15') && (*s != '12'));
  1213.       break; /* all done */
  1214.     }
  1215. /* otherwise fall into default case */
  1216.   default: /* ordinary header line */
  1217.     elt->rfc822_size += i + 
  1218.       (((i < 2) || (s[i - 2] != '15')) ? 1 : 0);
  1219.     break;
  1220.   }
  1221. } while (i && (*t != '15') && (*t != '12'));
  1222. /* assign a UID if none found */
  1223. if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid)
  1224.   prevuid = elt->private.uid = ++stream->uid_last;
  1225. /* note size of header, location of text */
  1226. elt->private.msg.header.text.size = 
  1227.   (elt->private.msg.text.offset =
  1228.    (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
  1229.      elt->private.special.text.size;
  1230. k = m = 0; /* no previous line size yet */
  1231. /* note current position */
  1232. j = LOCAL->filesize + GETPOS (&bs);
  1233. if (i) do { /* look for next message */
  1234.   s = unix_mbxline (stream,&bs,&i);
  1235.   if (i) { /* got new data? */
  1236.     VALID (s,t,ti,zn); /* yes, parse line */
  1237.     if (!ti) { /* not a header line, add it to message */
  1238.       elt->rfc822_size += 
  1239. k = i + (m = (((i < 2) || s[i - 2] != '15') ? 1 : 0));
  1240. /* update current position */
  1241.       j = LOCAL->filesize + GETPOS (&bs);
  1242.     }
  1243.   }
  1244. } while (i && !ti); /* until found a header */
  1245. elt->private.msg.text.text.size = j -
  1246.   (elt->private.special.offset + elt->private.msg.text.offset);
  1247. if (k == 2) { /* last line was blank? */
  1248.   elt->private.msg.text.text.size -= (m ? 1 : 2);
  1249.   elt->rfc822_size -= 2;
  1250. }
  1251.       } while (i); /* until end of buffer */
  1252.       if (pseudoseen) { /* flush pseudo-message if present */
  1253. /* decrement recent count */
  1254. if (mail_elt (stream,1)->recent) recent--;
  1255. /* and the exists count */
  1256. mail_exists (stream,nmsgs--);
  1257. mail_expunged(stream,1);/* fake an expunge of that message */
  1258.       }
  1259. /* need to start a new UID validity? */
  1260.       if (!stream->uid_validity) {
  1261. stream->uid_validity = time (0);
  1262. LOCAL->dirty = T; /* make dirty to create pseudo-message */
  1263.       }
  1264.       stream->nmsgs = oldnmsgs; /* whack it back down */
  1265.       stream->silent = silent; /* restore old silent setting */
  1266. /* notify upper level of new mailbox sizes */
  1267.       mail_exists (stream,nmsgs);
  1268.       mail_recent (stream,recent);
  1269. /* mark dirty so O flags are set */
  1270.       if (recent) LOCAL->dirty = T;
  1271.     }
  1272.   }
  1273. /* no change, don't babble if never got time */
  1274.   else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
  1275.     mm_log ("New mailbox modification time but apparently no changes",WARN);
  1276. /* update parsed file size and time */
  1277.   LOCAL->filesize = sbuf.st_size;
  1278.   LOCAL->filetime = sbuf.st_mtime;
  1279.   return T; /* return the winnage */
  1280. }
  1281. /* UNIX read line from mailbox
  1282.  * Accepts: mail stream
  1283.  *     stringstruct
  1284.  *     pointer to line size
  1285.  * Returns: pointer to input line
  1286.  */
  1287. char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
  1288. {
  1289.   unsigned long i,j,k,m;
  1290.   char *s,*t,p1[CHUNK];
  1291.   char *ret = "";
  1292. /* flush old buffer */
  1293.   if (LOCAL->line) fs_give ((void **) &LOCAL->line);
  1294. /* if buffer needs refreshing */
  1295.   if (!bs->cursize) SETPOS (bs,GETPOS (bs));
  1296.   if (SIZE (bs)) { /* find newline */
  1297.     for (t = (s = bs->curpos) + bs->cursize; (s < t) && (*s != 'n'); ++s);
  1298. /* difficult case if line spans buffer */
  1299.     if ((i = s - bs->curpos) == bs->cursize) {
  1300.       memcpy (p1,bs->curpos,i); /* remember what we have so far */
  1301. /* load next buffer */
  1302.       SETPOS (bs,k = GETPOS (bs) + i);
  1303.       for (t = (s = bs->curpos) + bs->cursize; (s < t) && (*s != 'n'); ++s);
  1304. /* huge line? */
  1305.       if ((j = s - bs->curpos) == bs->cursize) {
  1306. SETPOS (bs,GETPOS (bs) + j);
  1307. /* look for end of line */
  1308. for (m = SIZE (bs); m && (SNX (bs) != 'n'); --m,++j);
  1309. SETPOS (bs,k); /* go back to where it started */
  1310.       }
  1311.       ret = LOCAL->line = (char *) fs_get (i + j + 2);
  1312.       memcpy (ret,p1,i); /* copy first chunk */
  1313.       while (j) { /* copy remainder */
  1314. if (!bs->cursize) SETPOS (bs,GETPOS (bs));
  1315. memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
  1316. i += k; /* account for this much read in */
  1317. j -= k;
  1318. bs->curpos += k; /* increment new position */
  1319. bs->cursize -= k; /* eat that many bytes */
  1320.       }
  1321.       if (SIZE (bs)) SNX (bs); /* skip over newline if one seen */
  1322.       ret[i++] = 'n'; /* make sure newline at end */
  1323.       ret[i] = ''; /* makes debugging easier */
  1324.     }
  1325.     else { /* this is easy */
  1326.       ret = bs->curpos; /* string it at this position */
  1327.       bs->curpos += ++i; /* increment new position */
  1328.       bs->cursize -= i; /* eat that many bytes */
  1329.     }
  1330.     *size = i; /* return that to user */
  1331.   }
  1332.   else *size = 0; /* end of data, return empty */
  1333.   return ret;
  1334. }
  1335. /* UNIX make pseudo-header
  1336.  * Accepts: MAIL stream
  1337.  *     buffer to write pseudo-header
  1338.  * Returns: length of pseudo-header
  1339.  */
  1340. unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr)
  1341. {
  1342.   int i;
  1343.   char *s,*t,tmp[MAILTMPLEN];
  1344.   time_t now = time(0);
  1345.   rfc822_date (tmp);
  1346.   sprintf (hdr,"From %s %.24s1512Date: %s1512From: %s <%s@%s>1512Subject: %s1512Message-ID: <%lu@%.80s>\01512X-IMAP: %ld %ld",
  1347.    pseudo_from,ctime (&now),
  1348.    tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
  1349.    (unsigned long) now,mylocalhost (),stream->uid_validity,
  1350.    stream->uid_last);
  1351.   for (s = hdr,i = 0, t = hdr + strlen (hdr); i < NUSERFLAGS; ++i)
  1352.     if (stream->user_flags[i])
  1353.       sprintf (t += strlen (t)," %s",stream->user_flags[i]);
  1354.   strcpy (t += strlen (t),"1512Status: RO15121512");
  1355.   for (s = pseudo_msg,t += strlen (t); *s; *t++ = *s++)
  1356.     if (*s == '12') *t++ = '15';
  1357.   *t++ = '15'; *t++ = '12'; *t++ = '15'; *t++ = '12';
  1358.   *t = ''; /* tie off pseudo header */
  1359.   return t - hdr; /* return length of pseudo header */
  1360. }
  1361. /* UNIX make status string
  1362.  * Accepts: MAIL stream
  1363.  *     destination string to write
  1364.  *     message cache entry
  1365.  *     non-zero flag to write UID as well
  1366.  * Returns: length of string
  1367.  */
  1368. unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
  1369.     long flag)
  1370. {
  1371.   char *t;
  1372.   char *s = status;
  1373.   unsigned long uf = elt->user_flags;
  1374.   /* This used to be an sprintf(), but thanks to certain cretinous C libraries
  1375.      with horribly slow implementations of sprintf() I had to change it to this
  1376.      mess.  At least it should be fast. */
  1377.   *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
  1378.   *s++ = ':'; *s++ = ' ';
  1379.   if (elt->seen) *s++ = 'R';
  1380.   *s++ = 'O'; *s++ = '15'; *s++ = '12';
  1381.   *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
  1382.   *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
  1383.   if (elt->deleted) *s++ = 'D';
  1384.   if (elt->flagged) *s++ = 'F';
  1385.   if (elt->answered) *s++ = 'A';
  1386.   if (elt->draft) *s++ = 'T';
  1387.   *s++ = '15'; *s++ = '12';
  1388.   *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
  1389.   *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
  1390.   while (uf) {
  1391.     *s++ = ' ';
  1392.     for (t = stream->user_flags[find_rightmost_bit (&uf)]; *t; *s++ = *t++);
  1393.   }
  1394.   *s++ = '15'; *s++ = '12';
  1395.   if (flag) { /* want to include UID? */
  1396.     char stack[64];
  1397.     char *p = stack;
  1398. /* push UID digits on the stack */
  1399.     unsigned long n = elt->private.uid;
  1400.     do *p++ = (char) (n % 10) + '0';
  1401.     while (n /= 10);
  1402.     *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
  1403.     *s++ = ' ';
  1404. /* pop UID from stack */
  1405.     while (p > stack) *s++ = *--p;
  1406.     *s++ = '15'; *s++ = '12';
  1407.   }
  1408. /* end of extended message status */
  1409.   *s++ = '15'; *s++ = '12'; *s = '';
  1410.   return s - status; /* return size of resulting string */
  1411. }
  1412. /* Rewrite mailbox file
  1413.  * Accepts: MAIL stream, must be critical and locked
  1414.  *     return pointer to number of expunged messages if want expunge
  1415.  *     lock file (ignored argument)
  1416.  * Returns: T if success, NIL if failure
  1417.  */
  1418. long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock)
  1419. {
  1420.   unsigned long i,j;
  1421.   int e,retry;
  1422.   struct stat sbuf;
  1423.   FILE *f;
  1424.   MESSAGECACHE *elt;
  1425.   unsigned long recent = stream->recent;
  1426.   unsigned long size = 0; /* initially nothing done */
  1427.   if (nexp) *nexp = 0; /* initially nothing expunged */
  1428. /* open scratch file */
  1429.   if (!(f = tmpfile ())) return unix_punt_scratch (NIL);
  1430. /* write pseudo-header */
  1431.   if (!unix_fwrite (f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf),&size))
  1432.     return unix_punt_scratch (f);
  1433.   if (nexp) { /* expunging */
  1434.     for (i = 1; i <= stream->nmsgs; i++)
  1435.       if (!(elt = mail_elt (stream,i))->deleted &&
  1436.   !unix_write_message (f,stream,elt,&size))
  1437. return unix_punt_scratch (f);
  1438.   }
  1439.   else for (i = 1; i <= stream->nmsgs; i++)
  1440.     if (!unix_write_message (f,stream,mail_elt (stream,i),&size))
  1441.       return unix_punt_scratch (f);
  1442. /* write remaining data */
  1443.   if (fflush (f) || fstat (fileno (f),&sbuf)) return unix_punt_scratch (f);
  1444. /* make damn sure stdio isn't lying */
  1445.   if (size != (unsigned long) sbuf.st_size) {
  1446.     char tmp[MAILTMPLEN];
  1447.     sprintf (tmp,"Checkpoint file size mismatch (%lu != %lu)",
  1448.      (unsigned long) size,(unsigned long) sbuf.st_size);
  1449.     mm_log (tmp,ERROR);
  1450.     fclose (f); /* flush the output file */
  1451.     return NIL;
  1452.   }
  1453. /* does the mailbox need to grow? */
  1454.   if (size > (unsigned long) LOCAL->filesize) {
  1455. /* am I paranoid or what? */
  1456.     if ((i = size - LOCAL->filesize) > LOCAL->buflen) {
  1457. /* this user won the lottery all right */
  1458.       fs_give ((void **) &LOCAL->buf);
  1459.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
  1460.     }
  1461.     memset (LOCAL->buf,'',i); /* get a block of nulls */
  1462.     while (i) { /* until write successful or punt */
  1463.       lseek (LOCAL->fd,LOCAL->filesize,L_SET);
  1464.       if (write (LOCAL->fd,LOCAL->buf,i) < 0) {
  1465. j = errno; /* note error before doing ftrunctate */
  1466. ftruncate (LOCAL->fd,LOCAL->filesize);
  1467. fsync (LOCAL->fd);
  1468. if (mm_diskerror (stream,j,NIL)) {
  1469.   sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (j));
  1470.   mm_log (LOCAL->buf,ERROR);
  1471.   fclose (f); /* flush the output file */
  1472.   return NIL;
  1473. }
  1474.       }
  1475.       else i = 0; /* write was successful */
  1476.     }
  1477.   }
  1478. /* update the cache */
  1479.   for (i = 1; i <= stream->nmsgs;) {
  1480.     elt = mail_elt (stream,i); /* get cache */
  1481.     if (nexp && elt->deleted) { /* expunge this message? */
  1482.       if (elt->recent) recent--;/* one less recent message */
  1483.       mail_expunged (stream,i); /* notify upper levels */
  1484.       ++*nexp; /* count up one more expunged message */
  1485.     }
  1486.     else { /* update file pointers from kludgey places */
  1487.       elt->private.special.offset = elt->private.msg.full.offset;
  1488.       elt->private.msg.text.offset = elt->private.msg.full.text.size;
  1489. /* in case header grew */
  1490.       elt->private.msg.header.text.size = elt->private.msg.text.offset -
  1491. elt->private.msg.header.offset;
  1492. /* stomp on these two kludges */
  1493.       elt->private.msg.full.offset = elt->private.msg.full.text.size = 0;
  1494.       i++; /* preserved message */
  1495.     }
  1496.   }
  1497.   do { /* restart point if failure */
  1498.     retry = NIL; /* no need to retry yet */
  1499.     fseek (f,0,L_SET); /* rewind files */
  1500.     lseek (LOCAL->fd,0,L_SET);
  1501.     for (i = size; i; i -= j)
  1502.       if (!((j = fread (LOCAL->buf,1,min ((long) CHUNK,i),f)) &&
  1503.     (write (LOCAL->fd,LOCAL->buf,j) >= 0))) {
  1504. sprintf (LOCAL->buf,"Mailbox rewrite error: %s",strerror (e = errno));
  1505. mm_notify (stream,LOCAL->buf,WARN);
  1506. mm_diskerror (stream,e,T);
  1507. retry = T; /* must retry */
  1508. break;
  1509.       }
  1510.   } while (retry); /* in case need to retry */
  1511.   fclose (f); /* finished with scratch file */
  1512. /* make sure tied off */
  1513.   ftruncate (LOCAL->fd,LOCAL->filesize = size);
  1514.   fsync (LOCAL->fd); /* make sure the updates take */
  1515.   LOCAL->dirty = NIL; /* no longer dirty */
  1516.    /* notify upper level of new mailbox sizes */
  1517.   mail_exists (stream,stream->nmsgs);
  1518.   mail_recent (stream,recent);
  1519.   return T; /* looks good */
  1520. }
  1521. /* Write message
  1522.  * Accepts: destination file
  1523.  *     MAIL stream
  1524.  *     message number
  1525.  *     pointer to current filesize tally
  1526.  * Returns: T if success, NIL if failure
  1527.  */
  1528. long unix_write_message (FILE *f,MAILSTREAM *stream,MESSAGECACHE *elt,
  1529.  unsigned long *size)
  1530. {
  1531.   char *s;
  1532.   unsigned long i;
  1533. /* (kludge alert) note new message offset */
  1534.   elt->private.msg.full.offset = *size;
  1535. /* internal header */
  1536.   lseek (LOCAL->fd,elt->private.special.offset,L_SET);
  1537.   read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
  1538.   if (unix_fwrite (f,LOCAL->buf,elt->private.special.text.size,size)) {
  1539. /* get header */
  1540.     s = unix_header (stream,elt->msgno,&i,NIL);
  1541. /* header size, sans trailing newline */
  1542.     if (i && (s[i - 4] == '15')) i -= 2;
  1543. /* write header */
  1544.     if (unix_fwrite (f,s,i,size) &&
  1545. unix_fwrite (f,LOCAL->buf,unix_xstatus(stream,LOCAL->buf,elt,T),size)){
  1546. /* (kludge alert) note new text offset */
  1547.       elt->private.msg.full.text.size = *size - elt->private.msg.full.offset;
  1548. /* get text */
  1549.       s = unix_text_work (stream,elt,&i,NIL);
  1550. /* write text and trailing newline */
  1551.       if (unix_fwrite (f,s,i,size) && unix_fwrite (f,"1512",2,size))
  1552. return T;
  1553.     }
  1554.   }
  1555.   return NIL; /* failed */
  1556. }
  1557. /* Safely write buffer
  1558.  * Accepts: destination file
  1559.  *     buffer pointer
  1560.  *     number of octets
  1561.  *     pointer to current filesize tally
  1562.  * Returns: T if successful, NIL if failure
  1563.  */
  1564. long unix_fwrite (FILE *f,char *s,unsigned long i,unsigned long *size)
  1565. {
  1566.   unsigned long j;
  1567.   while (i && ((j = fwrite (s,1,i,f)) || (errno == EINTR))) {
  1568.     *size += j;
  1569.     s += j;
  1570.     i -= j;
  1571.   }
  1572.   return i ? NIL : T; /* success if wrote all requested data */
  1573. }
  1574. /* Punt scratch file
  1575.  * Accepts: file pointer
  1576.  * Returns: NIL, always
  1577.  */
  1578. long unix_punt_scratch (FILE *f)
  1579. {
  1580.   char tmp[MAILTMPLEN];
  1581.   sprintf (tmp,"Checkpoint file failure: %s",strerror (errno));
  1582.   mm_log (tmp,ERROR);
  1583.   if (f) fclose (f); /* flush the output file */
  1584.   return NIL;
  1585. }