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

网络编程

开发平台:

Unix_Linux

  1. /*
  2.  * Program: MBX 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: 3 October 1995
  13.  * Last Edited: 15 November 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. #include <stdio.h>
  36. #include <ctype.h>
  37. #include <errno.h>
  38. extern int errno; /* just in case */
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include <fcntl.h>
  42. #include <time.h>
  43. #include <sys/stat.h>
  44. #include <sys/utime.h>
  45. #include "mbxnt.h"
  46. #include "misc.h"
  47. #include "dummy.h"
  48. /* MBX mail routines */
  49. /* Driver dispatch used by MAIL */
  50. DRIVER mbxdriver = {
  51.   "mbx", /* driver name */
  52.   DR_LOCAL|DR_MAIL|DR_CRLF, /* driver flags */
  53.   (DRIVER *) NIL, /* next driver */
  54.   mbx_valid, /* mailbox is valid for us */
  55.   mbx_parameters, /* manipulate parameters */
  56.   mbx_scan, /* scan mailboxes */
  57.   mbx_list, /* list mailboxes */
  58.   mbx_lsub, /* list subscribed mailboxes */
  59.   NIL, /* subscribe to mailbox */
  60.   NIL, /* unsubscribe from mailbox */
  61.   mbx_create, /* create mailbox */
  62.   mbx_delete, /* delete mailbox */
  63.   mbx_rename, /* rename mailbox */
  64.   NIL, /* status of mailbox */
  65.   mbx_open, /* open mailbox */
  66.   mbx_close, /* close mailbox */
  67.   mbx_flags, /* fetch message "fast" attributes */
  68.   mbx_flags, /* fetch message flags */
  69.   NIL, /* fetch overview */
  70.   NIL, /* fetch message envelopes */
  71.   mbx_header, /* fetch message header */
  72.   mbx_text, /* fetch message body */
  73.   NIL, /* fetch partial message text */
  74.   NIL, /* unique identifier */
  75.   NIL, /* message number */
  76.   mbx_flag, /* modify flags */
  77.   mbx_flagmsg, /* per-message modify flags */
  78.   NIL, /* search for message based on criteria */
  79.   NIL, /* sort messages */
  80.   NIL, /* thread messages */
  81.   mbx_ping, /* ping mailbox to see if still alive */
  82.   mbx_check, /* check for new messages */
  83.   mbx_expunge, /* expunge deleted messages */
  84.   mbx_copy, /* copy messages to another mailbox */
  85.   mbx_append, /* append string message to mailbox */
  86.   NIL /* garbage collect stream */
  87. };
  88. /* prototype stream */
  89. MAILSTREAM mbxproto = {&mbxdriver};
  90. /* MBX mail validate mailbox
  91.  * Accepts: mailbox name
  92.  * Returns: our driver if name is valid, NIL otherwise
  93.  */
  94. DRIVER *mbx_valid (char *name)
  95. {
  96.   char tmp[MAILTMPLEN];
  97.   return mbx_isvalid (name,tmp) ? &mbxdriver : NIL;
  98. }
  99. /* MBX mail test for valid mailbox
  100.  * Accepts: mailbox name
  101.  *     scratch buffer
  102.  * Returns: T if valid, NIL otherwise
  103.  */
  104. int mbx_isvalid (char *name,char *tmp)
  105. {
  106.   int fd;
  107.   int ret = NIL;
  108.   char *s,hdr[HDRSIZE];
  109.   struct stat sbuf;
  110.   struct utimbuf times;
  111.   errno = EINVAL; /* assume invalid argument */
  112. /* if file, get its status */
  113.   if ((s = mbx_file (tmp,name)) && !stat (s,&sbuf) &&
  114.       ((sbuf.st_mode & S_IFMT) == S_IFREG) &&
  115.       ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0)) {
  116.     errno = -1; /* bogus format */
  117.     if ((read (fd,hdr,HDRSIZE) == HDRSIZE) &&
  118. (hdr[0] == '*') && (hdr[1] == 'm') && (hdr[2] == 'b') &&
  119. (hdr[3] == 'x') && (hdr[4] == '*') && (hdr[5] == '15') &&
  120. (hdr[6] == '12') && isxdigit (hdr[7]) && isxdigit (hdr[8]) &&
  121. isxdigit (hdr[9]) && isxdigit (hdr[10]) && isxdigit (hdr[11]) &&
  122. isxdigit (hdr[12]) && isxdigit (hdr[13]) && isxdigit (hdr[14]) &&
  123. isxdigit (hdr[15]) && isxdigit (hdr[16]) && isxdigit (hdr[17]) &&
  124. isxdigit (hdr[18]) && isxdigit (hdr[19]) && isxdigit (hdr[20]) &&
  125. isxdigit (hdr[21]) && isxdigit (hdr[22]) &&
  126. (hdr[23] == '15') && (hdr[24] == '12')) ret = T;
  127.     close (fd); /* close the file */
  128. /* preserve atime and mtime */
  129.     times.actime = sbuf.st_atime;
  130.     times.modtime = sbuf.st_mtime;
  131.     utime (tmp,&times); /* set the times */
  132.   }
  133. /* in case INBOX but not mbx format */
  134.   else if ((errno == ENOENT) && ((name[0] == 'I') || (name[0] == 'i')) &&
  135.    ((name[1] == 'N') || (name[1] == 'n')) &&
  136.    ((name[2] == 'B') || (name[2] == 'b')) &&
  137.    ((name[3] == 'O') || (name[3] == 'o')) &&
  138.    ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) errno = -1;
  139.   return ret; /* return what we should */
  140. }
  141. /* MBX manipulate driver parameters
  142.  * Accepts: function code
  143.  *     function-dependent value
  144.  * Returns: function-dependent return value
  145.  */
  146. void *mbx_parameters (long function,void *value)
  147. {
  148.   switch ((int) function) {
  149.   case SET_ONETIMEEXPUNGEATPING:
  150.     if (value && (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expunged))
  151.       ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->fullcheck = T;
  152.   case GET_ONETIMEEXPUNGEATPING:
  153.     if (value) value = (void *)
  154.       ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->fullcheck;
  155.     break;
  156.   }
  157.   return value;
  158. }
  159. /* MBX mail scan mailboxes
  160.  * Accepts: mail stream
  161.  *     reference
  162.  *     pattern to search
  163.  *     string to scan
  164.  */
  165. void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
  166. {
  167.   if (stream) dummy_scan (NIL,ref,pat,contents);
  168. }
  169. /* MBX mail list mailboxes
  170.  * Accepts: mail stream
  171.  *     reference
  172.  *     pattern to search
  173.  */
  174. void mbx_list (MAILSTREAM *stream,char *ref,char *pat)
  175. {
  176.   if (stream) dummy_list (NIL,ref,pat);
  177. }
  178. /* MBX mail list subscribed mailboxes
  179.  * Accepts: mail stream
  180.  *     reference
  181.  *     pattern to search
  182.  */
  183. void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat)
  184. {
  185.   if (stream) dummy_lsub (NIL,ref,pat);
  186. }
  187. /* MBX mail create mailbox
  188.  * Accepts: MAIL stream
  189.  *     mailbox name to create
  190.  * Returns: T on success, NIL on failure
  191.  */
  192. long mbx_create (MAILSTREAM *stream,char *mailbox)
  193. {
  194.   char *s,mbx[MAILTMPLEN],tmp[HDRSIZE];
  195.   long ret = NIL;
  196.   int i,fd;
  197.   if (!(s = mbx_file (mbx,mailbox))) {
  198.     sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
  199.     mm_log (mbx,ERROR);
  200.     return NIL;
  201.   }
  202. /* create underlying file */
  203.   if (!dummy_create (stream,s)) return NIL;
  204. /* done if made directory */
  205.   if ((s = strrchr (s,'\')) && !s[1]) return T;
  206.   if ((fd = open (mbx,O_BINARY|O_WRONLY,NIL)) < 0) {
  207.     sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
  208.     mm_log (tmp,ERROR);
  209.     unlink (mbx); /* delete the file */
  210.     return NIL;
  211.   }
  212.   memset (tmp,'',HDRSIZE); /* initialize header */
  213.   sprintf (s = tmp,"*mbx*1512%08lx000000001512",
  214.    (unsigned long) time (0));
  215.   s += strlen (s); /* move to end of pointer */
  216.   for (i = 0; i < NUSERFLAGS; ++i) *s++ ='15',*s++ = '12';
  217.   if (write (fd,tmp,HDRSIZE) != HDRSIZE) {
  218.     sprintf (tmp,"Can't initialize mailbox node %.80s: %s",
  219.      mbx,strerror (errno));
  220.     mm_log (tmp,ERROR);
  221.     unlink (mbx); /* delete the file */
  222.     close (fd);
  223.     return NIL;
  224.   }
  225.   return close (fd) ? NIL : T; /* close file */
  226. }
  227. /* MBX mail delete mailbox
  228.  * Accepts: MAIL stream
  229.  *     mailbox name to delete
  230.  * Returns: T on success, NIL on failure
  231.  */
  232. long mbx_delete (MAILSTREAM *stream,char *mailbox)
  233. {
  234.   return mbx_rename (stream,mailbox,NIL);
  235. }
  236. /* MBX mail rename mailbox
  237.  * Accepts: MAIL stream
  238.  *     old mailbox name
  239.  *     new mailbox name (or NIL for delete)
  240.  * Returns: T on success, NIL on failure
  241.  */
  242. long mbx_rename (MAILSTREAM *stream,char *old,char *newname)
  243. {
  244.   long ret = T;
  245.   char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  246.   int ld;
  247.   int fd = open (mbx_file (file,old),O_RDWR,NIL);
  248.   struct stat sbuf;
  249.   if (fd < 0) { /* open mailbox */
  250.     sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
  251.     mm_log (tmp,ERROR);
  252.     return NIL;
  253.   }
  254. /* get parse/append permission */
  255.   if ((ld = lockname (lock,old,LOCK_EX)) < 0) {
  256.     mm_log ("Unable to lock rename mailbox",ERROR);
  257.     return NIL;
  258.   }
  259. /* lock out other users */
  260.   if (flock (fd,LOCK_EX|LOCK_NB)) {
  261.     close (fd); /* couldn't lock, give up on it then */
  262.     sprintf (tmp,"Mailbox %.80s is in use by another process",old);
  263.     mm_log (tmp,ERROR);
  264.     unlockfd (ld,lock); /* release exclusive parse/append permission */
  265.     return NIL;
  266.   }
  267.   if (newname) { /* want rename? */
  268.     if (!((s = mailboxfile (tmp,newname)) && *s) ||
  269. ((s = strrchr (s,'\')) && !s[1])) {
  270.       sprintf (tmp,"Can't rename mailbox %.80s to %.80s: invalid name",
  271.        old,newname);
  272.       mm_log (tmp,ERROR);
  273.       ret = NIL; /* set failure */
  274.     }
  275. /* found superior to destination name? */
  276.     if (s && (s != tmp) && ((tmp[1] != ':') || (s != tmp + 2))) {
  277.       c = s[1]; /* remember character after delimiter */
  278.       *s = s[1] = ''; /* tie off name at delimiter */
  279. /* name doesn't exist, create it */
  280.       if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
  281. *s = '\'; /* restore delimiter */
  282. if (!dummy_create (stream,tmp)) ret = NIL;
  283.       }
  284.       else *s = '\'; /* restore delimiter */
  285.       s[1] = c; /* restore character after delimiter */
  286.     }
  287.     flock (fd,LOCK_UN); /* release lock on the file */
  288.     close (fd); /* pacify NTFS */
  289. /* rename the file */
  290.     if (ret && rename (file,tmp)) {
  291.       sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
  292.        strerror (errno));
  293.       mm_log (tmp,ERROR);
  294.       ret = NIL; /* set failure */
  295.     }
  296.   }
  297.   else {
  298.     flock (fd,LOCK_UN); /* release lock on the file */
  299.     close (fd); /* pacify NTFS */
  300.     if (unlink (file)) {
  301.       sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
  302.       mm_log (tmp,ERROR);
  303.       ret = NIL; /* set failure */
  304.     }
  305.   }
  306.   unlockfd (ld,lock); /* release exclusive parse/append permission */
  307. /* recreate file if renamed INBOX */
  308.   if (ret && !strcmp (ucase (strcpy (tmp,old)),"INBOX"))
  309.     mbx_create (NIL,"INBOX");
  310.   return ret; /* return success */
  311. }
  312. /* MBX mail open
  313.  * Accepts: stream to open
  314.  * Returns: stream on success, NIL on failure
  315.  */
  316. MAILSTREAM *mbx_open (MAILSTREAM *stream)
  317. {
  318.   int fd,ld;
  319.   short silent;
  320.   char tmp[MAILTMPLEN];
  321.   if (!stream) return &mbxproto;/* return prototype for OP_PROTOTYPE call */
  322.   if (stream->local) fatal ("mbx recycle stream");
  323.   if (stream->rdonly ||
  324.       (fd = open (mbx_file (tmp,stream->mailbox),O_BINARY|O_RDWR,NIL)) < 0) {
  325.     if ((fd = open (mbx_file (tmp,stream->mailbox),O_BINARY|O_RDONLY,NIL))<0) {
  326.       sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
  327.       mm_log (tmp,ERROR);
  328.       return NIL;
  329.     }
  330.     else if (!stream->rdonly) { /* got it, but readonly */
  331.       mm_log ("Can't get write access to mailbox, access is readonly",WARN);
  332.       stream->rdonly = T;
  333.     }
  334.   }
  335.   stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL));
  336.   LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1);
  337.   LOCAL->buflen = MAXMESSAGESIZE;
  338. /* note if an INBOX or not */
  339.   stream->inbox = !strcmp(ucase (strcpy (LOCAL->buf,stream->mailbox)),"INBOX");
  340.   fs_give ((void **) &stream->mailbox);
  341.   stream->mailbox = cpystr (tmp);
  342. /* get parse/append permission */
  343.   if ((ld = lockname (tmp,stream->mailbox,LOCK_EX)) < 0) {
  344.     mm_log ("Unable to lock open mailbox",ERROR);
  345.     return NIL;
  346.   }
  347.   flock(LOCAL->fd = fd,LOCK_SH);/* bind and lock the file */
  348.   unlockfd (ld,tmp); /* release shared parse permission */
  349.   LOCAL->filesize = HDRSIZE; /* initialize parsed file size */
  350.   LOCAL->filetime = 0; /* time not set up yet */
  351.   LOCAL->fullcheck = LOCAL->flagcheck = NIL;
  352.   stream->sequence++; /* bump sequence number */
  353. /* parse mailbox */
  354.   stream->nmsgs = stream->recent = 0;
  355.   silent = stream->silent; /* defer events */
  356.   stream->silent = T;
  357.   if (mbx_ping (stream) && !stream->nmsgs)
  358.     mm_log ("Mailbox is empty",(long) NIL);
  359.   stream->silent = silent; /* now notify upper level */
  360.   mail_exists (stream,stream->nmsgs);
  361.   mail_recent (stream,stream->recent);
  362.   if (!LOCAL) return NIL; /* failure if stream died */
  363.   stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
  364.     stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
  365.   stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
  366.   stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
  367.     NIL : T; /* can we create new user flags? */
  368.   return stream; /* return stream to caller */
  369. }
  370. /* MBX mail close
  371.  * Accepts: MAIL stream
  372.  *     close options
  373.  */
  374. void mbx_close (MAILSTREAM *stream,long options)
  375. {
  376.   if (stream && LOCAL) { /* only if a file is open */
  377.     int silent = stream->silent;
  378.     stream->silent = T; /* note this stream is dying */
  379.     if (options & CL_EXPUNGE) mbx_expunge (stream);
  380.     stream->silent = silent; /* restore previous status */
  381.     flock (LOCAL->fd,LOCK_UN); /* unlock local file */
  382.     close (LOCAL->fd); /* close the local file */
  383. /* free local text buffer */
  384.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  385. /* nuke the local data */
  386.     fs_give ((void **) &stream->local);
  387.     stream->dtb = NIL; /* log out the DTB */
  388.   }
  389. }
  390. /* MBX mail fetch flags
  391.  * Accepts: MAIL stream
  392.  *     sequence
  393.  *     option flags
  394.  * Sniffs at file to see if some other process changed the flags
  395.  */
  396. void mbx_flags (MAILSTREAM *stream,char *sequence,long flags)
  397. {
  398.   unsigned long i;
  399.   if (mbx_ping (stream) &&  /* ping mailbox, get new status for messages */
  400.       ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
  401.        mail_sequence (stream,sequence)))
  402.     for (i = 1; i <= stream->nmsgs; i++) 
  403.       if (mail_elt (stream,i)->sequence) mbx_elt (stream,i,NIL);
  404. }
  405. /* MBX mail fetch message header
  406.  * Accepts: MAIL stream
  407.  *     message # to fetch
  408.  *     pointer to returned header text length
  409.  *     option flags
  410.  * Returns: message header in RFC822 format
  411.  */
  412. char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
  413.   long flags)
  414. {
  415.   *length = 0; /* default to empty */
  416.   if (flags & FT_UID) return "";/* UID call "impossible" */
  417. /* get to header position */
  418.   lseek (LOCAL->fd,mbx_hdrpos (stream,msgno,length),L_SET);
  419. /* is buffer big enough? */
  420.   if (*length > LOCAL->buflen) {
  421.     fs_give ((void **) &LOCAL->buf);
  422.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
  423.   }
  424.   LOCAL->buf[*length] = ''; /* tie off string */
  425. /* slurp the data */
  426.   read (LOCAL->fd,LOCAL->buf,*length);
  427.   return LOCAL->buf;
  428. }
  429. /* MBX mail fetch message text (body only)
  430.  * Accepts: MAIL stream
  431.  *     message # to fetch
  432.  *     pointer to returned header text length
  433.  *     option flags
  434.  * Returns: T, always
  435.  */
  436. long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
  437. {
  438.   unsigned long i,j;
  439.   MESSAGECACHE *elt;
  440. /* UID call "impossible" */
  441.   if (flags & FT_UID) return NIL;
  442. /* get message status */
  443.   elt = mbx_elt (stream,msgno,NIL);
  444. /* if message not seen */
  445.   if (!(flags & FT_PEEK) && !elt->seen) {
  446.     elt->seen = T; /* mark message as seen */
  447. /* recalculate status */
  448.     mbx_update_status (stream,msgno,mus_SYNC);
  449.     mm_flags (stream,msgno);
  450.   }
  451. /* find header position */
  452.   i = mbx_hdrpos (stream,msgno,&j);
  453. /* go to text position */
  454.   lseek (LOCAL->fd,i + j,L_SET);
  455. /* is buffer big enough? */
  456.   if ((i = elt->rfc822_size - j) > LOCAL->buflen) {
  457.     fs_give ((void **) &LOCAL->buf);
  458.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
  459.   }
  460.   read (LOCAL->fd,LOCAL->buf,i);/* slurp the data */
  461.   LOCAL->buf[i] = ''; /* tie off string */
  462. /* set up stringstruct */
  463.   INIT (bs,mail_string,LOCAL->buf,i);
  464.   return T; /* success */
  465. }
  466. /* MBX mail modify flags
  467.  * Accepts: MAIL stream
  468.  *     sequence
  469.  *     flag(s)
  470.  *     option flags
  471.  */
  472. void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
  473. {
  474.   struct stat sbuf;
  475.   if (!stream->rdonly) { /* make sure the update takes */
  476.     fsync (LOCAL->fd);
  477.     fstat (LOCAL->fd,&sbuf); /* get current write time */
  478.     LOCAL->filetime = sbuf.st_mtime;
  479.   }
  480.   if ((LOCAL->ffuserflag < NUSERFLAGS)&&stream->user_flags[LOCAL->ffuserflag])
  481.     mbx_update_header (stream); /* update header */
  482. }
  483. /* MBX mail per-message modify flags
  484.  * Accepts: MAIL stream
  485.  *     message cache element
  486.  */
  487. void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
  488. {
  489.   struct stat sbuf;
  490. /* maybe need to do a checkpoint? */
  491.   if (LOCAL->filetime && !LOCAL->flagcheck) {
  492.     fstat (LOCAL->fd,&sbuf); /* get current write time */
  493.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T;
  494.     LOCAL->filetime = 0; /* don't do this test for any other messages */
  495.   }
  496. /* recalculate status */
  497.   mbx_update_status (stream,elt->msgno,NIL);
  498. }
  499. /* MBX mail ping mailbox
  500.  * Accepts: MAIL stream
  501.  * Returns: T if stream still alive, NIL if not
  502.  */
  503. long mbx_ping (MAILSTREAM *stream)
  504. {
  505.   unsigned long i = 1;
  506.   long r = T;
  507.   int ld;
  508.   char lock[MAILTMPLEN];
  509.   struct stat sbuf;
  510.   if (stream && LOCAL) { /* only if stream already open */
  511.     fstat (LOCAL->fd,&sbuf); /* get current file poop */
  512.     if (!LOCAL->fullcheck) { /* don't bother if already full checking */
  513.       if (LOCAL->expunged && mail_parameters (NIL,GET_EXPUNGEATPING,NIL))
  514. LOCAL->fullcheck = T; /* upgrade to expunged message checking */
  515.       else if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime))
  516. LOCAL->flagcheck = T; /* upgrade to flag checking */
  517.     }
  518. /* sweep mailbox for changed message status */
  519.     if (LOCAL->fullcheck || LOCAL->flagcheck) {
  520.       while (i <= stream->nmsgs) if (mbx_elt (stream,i,LOCAL->fullcheck)) ++i;
  521.       LOCAL->flagcheck = NIL; /* got all the updates */
  522.       if (LOCAL->fullcheck) LOCAL->fullcheck = LOCAL->expunged = NIL;
  523.     }
  524. /* get parse/append permission */
  525.     if (((sbuf.st_size != LOCAL->filesize) || !stream->nmsgs) &&
  526. ((ld = lockname (lock,stream->mailbox,LOCK_EX)) >= 0)) {
  527.       r = mbx_parse (stream); /* parse new messages in mailbox */
  528.       unlockfd (ld,lock); /* release shared parse/append permission */
  529.     }
  530.     else if ((sbuf.st_ctime > sbuf.st_atime)||(sbuf.st_ctime > sbuf.st_mtime)){
  531.       struct utimbuf times; /* whack the times if necessary */
  532.       LOCAL->filetime = times.actime = times.modtime = time (0);
  533.       utime (stream->mailbox,&times);
  534.     }
  535.   }
  536.   return r; /* return result of the parse */
  537. }
  538. /* MBX mail check mailbox (reparses status too)
  539.  * Accepts: MAIL stream
  540.  */
  541. void mbx_check (MAILSTREAM *stream)
  542. {
  543. /* mark that a check is desired */
  544.   if (LOCAL) LOCAL->fullcheck = T;
  545.   if (mbx_ping (stream)) mm_log ("Check completed",(long) NIL);
  546. }
  547. /* MBX mail expunge mailbox
  548.  * Accepts: MAIL stream
  549.  */
  550. void mbx_expunge (MAILSTREAM *stream)
  551. {
  552.   struct stat sbuf;
  553.   off_t pos,ppos;
  554.   int ld;
  555.   unsigned long i,j,k,m,n,delta,reclaimed;
  556.   unsigned long recent = 0;
  557.   char lock[MAILTMPLEN];
  558.   MESSAGECACHE *elt;
  559. /* do nothing if stream dead */
  560.   if (!mbx_ping (stream)) return;
  561.   if (stream->rdonly) { /* won't do on readonly files! */
  562.     mm_log ("Expunge ignored on readonly mailbox",WARN);
  563.     return;
  564.   }
  565.   if (LOCAL->filetime && !LOCAL->flagcheck) {
  566.     fstat (LOCAL->fd,&sbuf); /* get current write time */
  567.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T;
  568.   }
  569. /* get parse/append permission */
  570.   if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) {
  571.     mm_log ("Unable to lock expunge mailbox",ERROR);
  572.     return;
  573.   }
  574. /* get exclusive access */
  575.   if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
  576.     flock (LOCAL->fd,LOCK_SH); /* recover previous lock */
  577.     unlockfd (ld,lock); /* release exclusive parse/append permission */
  578.     for (i = 1,n = reclaimed = 0; i <= stream->nmsgs; ) {
  579.       if (elt = mbx_elt (stream,i,T)) {
  580. if (elt->deleted) {
  581.   mbx_update_status (stream,elt->msgno,mus_EXPUNGE);
  582.   mail_expunged(stream,i);/* notify upper levels */
  583.   n++; /* count up one more expunged message */
  584. }
  585. else {
  586.   i++; /* preserved message */
  587.   if (elt->recent) ++recent;
  588. }
  589.       }
  590.       else n++; /* count up one more expunged message */
  591.     }
  592.     fsync (LOCAL->fd); /* force disk update */
  593.   }
  594.   else {
  595.     mm_critical (stream); /* go critical */
  596.     for (i = 1,n = delta = reclaimed = 0,pos = ppos = HDRSIZE;
  597.  i <= stream->nmsgs; ) {
  598.       elt = mbx_elt (stream,i,NIL);
  599. /* note if message not at predicted location */
  600.       if (m = elt->private.special.offset - ppos) {
  601. ppos = elt->private.special.offset;
  602. reclaimed += m; /* note reclaimed message space */
  603. delta += m; /* and as expunge delta  */
  604.       }
  605. /* number of bytes to smash or preserve */
  606.       ppos += (k = elt->private.special.text.size + elt->rfc822_size);
  607.       if (elt->deleted) { /* if deleted */
  608. delta += k; /* number of bytes to delete */
  609. mail_expunged(stream,i);/* notify upper levels */
  610. n++; /* count up one more expunged message */
  611.       }
  612.       else if (i++ && delta) { /* preserved message */
  613. if (elt->recent) ++recent;
  614. /* first byte to preserve */
  615. j = elt->private.special.offset;
  616. do { /* read from source position */
  617.   m = min (k,LOCAL->buflen);
  618.   lseek (LOCAL->fd,j,L_SET);
  619.   read (LOCAL->fd,LOCAL->buf,m);
  620.   pos = j - delta; /* write to destination position */
  621.   while (T) {
  622.     lseek (LOCAL->fd,pos,L_SET);
  623.     if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
  624.     mm_notify (stream,strerror (errno),WARN);
  625.     mm_diskerror (stream,errno,T);
  626.   }
  627.   pos += m; /* new position */
  628.   j += m; /* next chunk, perhaps */
  629. } while (k -= m); /* until done */
  630. /* note the new address of this text */
  631. elt->private.special.offset -= delta;
  632.       }
  633. /* preserved but no deleted messages yet */
  634.       else pos = elt->private.special.offset + k;
  635.     }
  636. /* deltaed file size match position? */
  637.     if (m = (LOCAL->filesize -= delta) - pos) {
  638.       reclaimed += m; /* probably an fEXPUNGED msg */
  639.       LOCAL->filesize = pos; /* set correct size */
  640.     }
  641. /* truncate file after last message */
  642.     ftruncate (LOCAL->fd,LOCAL->filesize);
  643.     fsync (LOCAL->fd); /* force disk update */
  644.     mm_nocritical (stream); /* release critical */
  645.     flock (LOCAL->fd,LOCK_SH); /* allow sharers again */
  646.     unlockfd (ld,lock); /* release exclusive parse/append permission */
  647.   }
  648.   fstat (LOCAL->fd,&sbuf); /* get new write time */
  649.   LOCAL->filetime = sbuf.st_mtime;
  650.   if (n) { /* if expunged any messages */
  651.     sprintf (LOCAL->buf,"Expunged %lu messages",n);
  652.     mm_log (LOCAL->buf,(long) NIL);
  653.   }
  654.   else if (reclaimed) { /* or if any prior expunged space reclaimed */
  655.     sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
  656.     mm_log (LOCAL->buf,(long) NIL);
  657.   }
  658.   else mm_log ("No messages deleted, so no update needed",(long) NIL);
  659. /* notify upper level of new mailbox size */
  660.   mail_exists (stream,stream->nmsgs);
  661.   mail_recent (stream,recent);
  662. }
  663. /* MBX mail copy message(s)
  664.  * Accepts: MAIL stream
  665.  *     sequence
  666.  *     destination mailbox
  667.  *     copy options
  668.  * Returns: T if success, NIL if failed
  669.  */
  670. long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
  671. {
  672.   struct stat sbuf;
  673.   struct utimbuf times;
  674.   MESSAGECACHE *elt;
  675.   unsigned long i,j,k;
  676.   long ret = LONGT;
  677.   int fd,ld;
  678.   char file[MAILTMPLEN],lock[MAILTMPLEN];
  679.   mailproxycopy_t pc =
  680.     (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
  681. /* make sure valid mailbox */
  682.   if (!mbx_isvalid (mailbox,LOCAL->buf)) switch (errno) {
  683.   case ENOENT: /* no such file? */
  684.     mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
  685.     return NIL;
  686.   case EINVAL:
  687.     if (pc) return (*pc) (stream,sequence,mailbox,options);
  688.     sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox);
  689.     mm_log (LOCAL->buf,ERROR);
  690.     return NIL;
  691.   default:
  692.     if (pc) return (*pc) (stream,sequence,mailbox,options);
  693.     sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox);
  694.     mm_log (LOCAL->buf,ERROR);
  695.     return NIL;
  696.   }
  697.   if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
  698. mail_sequence (stream,sequence))) return NIL;
  699. /* got file? */  
  700.   if ((fd = open (mbx_file (file,mailbox),O_BINARY|O_RDWR|O_CREAT,
  701.   S_IREAD|S_IWRITE)) < 0) {
  702.     sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
  703.     mm_log (LOCAL->buf,ERROR);
  704.     return NIL;
  705.   }
  706.   mm_critical (stream); /* go critical */
  707. /* get parse/append permission */
  708.   if ((ld = lockname (lock,mailbox,LOCK_EX)) < 0) {
  709.     mm_log ("Unable to lock copy mailbox",ERROR);
  710.     return NIL;
  711.   }
  712.   fstat (fd,&sbuf); /* get current file size */
  713.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  714. /* for each requested message */
  715.   for (i = 1; ret && (i <= stream->nmsgs); i++) 
  716.     if ((elt = mail_elt (stream,i))->sequence) {
  717.       lseek (LOCAL->fd,elt->private.special.offset +
  718.      elt->private.special.text.size,L_SET);
  719.       mail_date(LOCAL->buf,elt);/* build target header */
  720.       sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-000000001512",
  721.        elt->rfc822_size,elt->user_flags,(unsigned)
  722.        ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  723. (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
  724. (fDRAFT * elt->draft)));
  725. /* write target header */
  726.       if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0))
  727. for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){
  728.   read (LOCAL->fd,LOCAL->buf,j);
  729.   ret = write (fd,LOCAL->buf,j) >= 0;
  730. }
  731.     }
  732. /* make sure all the updates take */
  733.   if (!(ret && (ret = !fsync (fd)))) {
  734.     sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
  735.     mm_log (LOCAL->buf,ERROR);
  736.     ftruncate (fd,sbuf.st_size);
  737.   }
  738.   times.actime = sbuf.st_atime; /* preserve atime and mtime */
  739.   times.modtime = sbuf.st_mtime;
  740.   utime (file,&times); /* set the times */
  741.   unlockfd (ld,lock); /* release exclusive parse/append permission */
  742.   close (fd); /* close the file */
  743.   mm_nocritical (stream); /* release critical */
  744. /* delete all requested messages */
  745.   if (ret && (options & CP_MOVE)) {
  746.     for (i = 1; i <= stream->nmsgs; ) if (elt = mbx_elt (stream,i,T)) {
  747.       if (elt->sequence) { /* want to do this message? */
  748. elt->deleted = T; /* mark message deleted */
  749. /* recalculate status */
  750. mbx_update_status (stream,i,NIL);
  751.       }
  752.       i++; /* move to next message */
  753.     }
  754.     if (!stream->rdonly) { /* make sure the update takes */
  755.       fsync (LOCAL->fd);
  756.       fstat (LOCAL->fd,&sbuf); /* get current write time */
  757.       LOCAL->filetime = sbuf.st_mtime;
  758.     }
  759.   }
  760.   return ret;
  761. }
  762. /* MBX mail append message from stringstruct
  763.  * Accepts: MAIL stream
  764.  *     destination mailbox
  765.  *     stringstruct of messages to append
  766.  * Returns: T if append successful, else NIL
  767.  */
  768. long mbx_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  769.  STRING *message)
  770. {
  771.   struct stat sbuf;
  772.   int fd,ld;
  773.   char *s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  774.   struct utimbuf times;
  775.   MESSAGECACHE elt;
  776.   long i = 0;
  777.   long size = SIZE (message);
  778.   long ret = LONGT;
  779.   unsigned long uf = 0;
  780.   long f = mail_parse_flags (stream ? stream : &mbxproto,flags,&uf);
  781. /* reverse bits (dontcha wish we had CIRC?) */
  782.   if (date) { /* want to preserve date? */
  783. /* yes, parse date into an elt */
  784.     if (!mail_parse_date (&elt,date)) {
  785.       sprintf (tmp,"Bad date in append: %.80s",date);
  786.       mm_log (tmp,ERROR);
  787.       return NIL;
  788.     }
  789.   }
  790. /* N.B.: can't use LOCAL->buf for tmp */
  791. /* make sure valid mailbox */
  792.   if (!mbx_isvalid (mailbox,tmp)) switch (errno) {
  793.   case ENOENT: /* no such file? */
  794.     if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
  795. ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
  796. ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
  797. ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
  798. ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5])
  799.       mbx_create (NIL,"INBOX");
  800.     else {
  801.       mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
  802.       return NIL;
  803.     }
  804. /* falls through */
  805.   case 0: /* merely empty file? */
  806.     break;
  807.   case EINVAL:
  808.     sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox);
  809.     mm_log (tmp,ERROR);
  810.     return NIL;
  811.   default:
  812.     sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox);
  813.     mm_log (tmp,ERROR);
  814.     return NIL;
  815.   }
  816.   if ((fd = open (mbx_file (file,mailbox),O_BINARY|O_RDWR|O_CREAT,
  817.   S_IREAD|S_IWRITE)) < 0) {
  818.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  819.     mm_log (tmp,ERROR);
  820.     return NIL;
  821.   }
  822. /* get parse/append permission */
  823.   if ((ld = lockname (lock,mailbox,LOCK_EX)) < 0) {
  824.     mm_log ("Unable to lock append mailbox",ERROR);
  825.     return NIL;
  826.   }
  827.   s = (char *) fs_get (size); /* copy message */
  828.   for (i = 0; i < size; s[i++] = SNX (message));
  829.   mm_critical (stream); /* go critical */
  830.   fstat (fd,&sbuf); /* get current file size */
  831.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  832.   if (date) mail_date(tmp,&elt);/* write preseved date */
  833.   else internal_date (tmp); /* get current date in IMAP format */
  834. /* add remainder of header */
  835.   sprintf (tmp+26,",%lu;%08lx%04lx-000000001512",(unsigned long) size,uf,
  836.    (unsigned long) f);
  837. /* write header */
  838.   if ((write (fd,tmp,strlen (tmp)) < 0) || ((write (fd,s,size)) < 0) ||
  839.       fsync (fd)) {
  840.     sprintf (tmp,"Message append failed: %s",strerror (errno));
  841.     mm_log (tmp,ERROR);
  842.     ftruncate (fd,sbuf.st_size);
  843.     ret = NIL;
  844.   }
  845.   times.actime = sbuf.st_atime; /* preserve atime and mtime */
  846.   times.modtime= sbuf.st_mtime;
  847.   utime (file,&times); /* set the times */
  848.   close (fd); /* close the file */
  849.   unlockfd (ld,lock); /* release exclusive parse/append permission */
  850.   mm_nocritical (stream); /* release critical */
  851.   fs_give ((void **) &s); /* flush the buffer */
  852.   return ret;
  853. }
  854. /* Internal routines */
  855. /* MBX mail generate file string
  856.  * Accepts: temporary buffer to write into
  857.  *     mailbox name string
  858.  * Returns: local file string or NIL if failure
  859.  */
  860. char *mbx_file (char *dst,char *name)
  861. {
  862.   if (!(mailboxfile (dst,name) && *dst))
  863.     sprintf (dst,"%s\INBOX",myhomedir ());
  864.   return dst;
  865. }
  866. /* MBX mail parse mailbox
  867.  * Accepts: MAIL stream
  868.  * Returns: T if parse OK
  869.  *     NIL if failure, stream aborted
  870.  */
  871. long mbx_parse (MAILSTREAM *stream)
  872. {
  873.   struct stat sbuf;
  874.   MESSAGECACHE *elt = NIL;
  875.   char c,*s,*t,*x;
  876.   char tmp[MAILTMPLEN];
  877.   unsigned long i,j,k,m;
  878.   off_t curpos = LOCAL->filesize;
  879.   unsigned long nmsgs = stream->nmsgs;
  880.   unsigned long recent = stream->recent;
  881.   unsigned long lastuid = 0;
  882.   short silent = stream->silent;
  883.   fstat (LOCAL->fd,&sbuf); /* get status */
  884.   if (sbuf.st_size < curpos) { /* sanity check */
  885.     sprintf (tmp,"Mailbox shrank from %lu to %lu!",
  886.      (unsigned long) curpos,(unsigned long) sbuf.st_size);
  887.     mm_log (tmp,ERROR);
  888.     mbx_close (stream,NIL);
  889.     return NIL;
  890.   }
  891.   lseek (LOCAL->fd,0,L_SET); /* rewind file */
  892. /* read internal header */
  893.   read (LOCAL->fd,LOCAL->buf,HDRSIZE);
  894.   LOCAL->buf[HDRSIZE] = ''; /* tie off header */
  895.   c = LOCAL->buf[15]; /* save first character of last UID */
  896.   LOCAL->buf[15] = '';
  897. /* parse UID validity */
  898.   stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16);
  899.   LOCAL->buf[15] = c; /* restore first character of last UID */
  900. /* parse last UID */
  901.   i = strtoul (LOCAL->buf + 15,NIL,16);
  902.   stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i;
  903. /* parse user flags */
  904.   for (i = 0, s = LOCAL->buf + 25;
  905.        (i < NUSERFLAGS) && (t = strchr (s,'15')) && (t - s);
  906.        i++, s = t + 2) {
  907.     *t = ''; /* tie off flag */
  908.     if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (s);
  909.   }
  910.   LOCAL->ffuserflag = (int) i; /* first free user flag */
  911.   stream->silent = T; /* don't pass up mm_exists() events yet */
  912.   while (sbuf.st_size - curpos){/* while there is stuff to parse */
  913. /* get to that position in the file */
  914.     lseek (LOCAL->fd,curpos,L_SET);
  915.     if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
  916.       sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
  917.        (unsigned long) curpos,(unsigned long) sbuf.st_size,
  918.        i ? strerror (errno) : "no data read");
  919.       mm_log (tmp,ERROR);
  920.       mbx_close (stream,NIL);
  921.       return NIL;
  922.     }
  923.     LOCAL->buf[i] = ''; /* tie off buffer just in case */
  924.     if (!((s = strchr (LOCAL->buf,'15')) && (s[1] == '12'))) {
  925.       sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s",
  926.        (unsigned long) curpos,i,LOCAL->buf);
  927.       mm_log (tmp,ERROR);
  928.       mbx_close (stream,NIL);
  929.       return NIL;
  930.     }
  931.     *s = ''; /* tie off header line */
  932.     i = (s + 2) - LOCAL->buf; /* note start of text offset */
  933.     if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
  934.       sprintf (tmp,"Unable to parse internal header at %lu: %.80s",
  935.        (unsigned long) curpos,LOCAL->buf);
  936.       mm_log (tmp,ERROR);
  937.       mbx_close (stream,NIL);
  938.       return NIL;
  939.     }
  940.     if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) &&
  941.   isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) &&
  942.   isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) &&
  943.   isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) {
  944.       sprintf (tmp,"Unable to parse message flags at %lu: %.80s",
  945.        (unsigned long) curpos,LOCAL->buf);
  946.       mm_log (tmp,ERROR);
  947.       mbx_close (stream,NIL);
  948.       return NIL;
  949.     }
  950.     if ((t[13] != '-') || t[22] ||
  951. !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) &&
  952.   isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) &&
  953.   isxdigit (t[20]) && isxdigit (t[21]))) {
  954.       sprintf (tmp,"Unable to parse message UID at %lu: %.80s",
  955.        (unsigned long) curpos,LOCAL->buf);
  956.       mm_log (tmp,ERROR);
  957.       mbx_close (stream,NIL);
  958.       return NIL;
  959.     }
  960.     *s++ = ''; *t++ = ''; /* break up fields */
  961. /* get message size */
  962.     if (!(j = strtoul (s,&x,10)) && (!(x && *x))) {
  963.       sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s",
  964.        (unsigned long) curpos,LOCAL->buf,s,t);
  965.       mm_log (tmp,ERROR);
  966.       mbx_close (stream,NIL);
  967.       return NIL;
  968.     }
  969. /* make sure didn't run off end of file */
  970.     if (((off_t) (curpos + i + j)) > sbuf.st_size) {
  971.       sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
  972.        (unsigned long) curpos,(unsigned long) (curpos + i + j),
  973.        (unsigned long) sbuf.st_size);
  974.       mm_log (tmp,ERROR);
  975.       mbx_close (stream,NIL);
  976.       return NIL;
  977.     }
  978. /* parse UID */
  979.     if ((m = strtoul (t+13,NIL,16)) &&
  980. ((m <= lastuid) || (m > stream->uid_last))) {
  981.       sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs",
  982.  m,nmsgs+1);
  983.       mm_log (tmp,WARN);
  984.       m = 0; /* lose this UID */
  985. /* restart UID validity and last UID */
  986.       stream->uid_validity = time (0);
  987.       stream->uid_last = lastuid;
  988.     }
  989.     t[12] = ''; /* parse system flags */
  990.     if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED)
  991. /* expunged message, update last UID */
  992.       lastuid = m ? m : ++stream->uid_last;
  993.     else { /* not expunged, swell the cache */
  994.       mail_exists (stream,++nmsgs);
  995. /* instantiate an elt for this message */
  996.       (elt = mail_elt (stream,nmsgs))->valid = T;
  997. /* parse the date */
  998.       if (!mail_parse_date (elt,LOCAL->buf)) {
  999. sprintf (tmp,"Unable to parse message date at %lu: %.80s",
  1000.  (unsigned long) curpos,LOCAL->buf);
  1001. mm_log (tmp,ERROR);
  1002. mbx_close (stream,NIL);
  1003. return NIL;
  1004.       }
  1005. /* note file offset of header */
  1006.       elt->private.special.offset = curpos;
  1007. /* and internal header size */
  1008.       elt->private.special.text.size = i;
  1009. /* header size not known yet */
  1010.       elt->private.msg.header.text.size = 0;
  1011.       elt->rfc822_size = j; /* note message size */
  1012. /* calculate system flags */
  1013.       if (k & fSEEN) elt->seen = T;
  1014.       if (k & fDELETED) elt->deleted = T;
  1015.       if (k & fFLAGGED) elt->flagged = T;
  1016.       if (k & fANSWERED) elt->answered = T;
  1017.       if (k & fDRAFT) elt->draft = T;
  1018.       t[8] = ''; /* get user flags value */
  1019.       elt->user_flags = strtoul (t,NIL,16);
  1020. /* UID already assigned? */
  1021.       if (!(elt->private.uid = m)) {
  1022. elt->recent = T; /* no, mark as recent */
  1023. ++recent; /* count up a new recent message */
  1024. /* assign new UID */
  1025. elt->private.uid = ++stream->uid_last;
  1026. mbx_update_status (stream,elt->msgno,NIL);
  1027.       }
  1028. /* update last parsed UID */
  1029.       lastuid = elt->private.uid;
  1030.     }
  1031.     curpos += i + j; /* update position */
  1032.   }
  1033. /* update header */
  1034.   if (!stream->rdonly) mbx_update_header (stream);
  1035.   fsync (LOCAL->fd); /* make sure all the UID updates take */
  1036. /* update parsed file size and time */
  1037.   LOCAL->filesize = sbuf.st_size;
  1038.   fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
  1039.   LOCAL->filetime = sbuf.st_mtime;
  1040.   stream->silent = silent; /* can pass up events now */
  1041.   mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
  1042.   mail_recent (stream,recent); /* and of change in recent messages */
  1043.   return LONGT; /* return the winnage */
  1044. }
  1045. /* MBX get cache element with status updating from file
  1046.  * Accepts: MAIL stream
  1047.  *     message number
  1048.  *     expunge OK flag
  1049.  * Returns: cache element
  1050.  */
  1051. MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok)
  1052. {
  1053.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1054.   struct { /* old flags */
  1055.     unsigned int seen : 1;
  1056.     unsigned int deleted : 1;
  1057.     unsigned int flagged : 1;
  1058.     unsigned int answered : 1;
  1059.     unsigned int draft : 1;
  1060.     unsigned long user_flags;
  1061.   } old;
  1062.   old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
  1063.   old.answered = elt->answered; old.draft = elt->draft;
  1064.   old.user_flags = elt->user_flags;
  1065. /* get new flags */
  1066.   if (mbx_read_flags (stream,elt) && expok) {
  1067.     mail_expunged (stream,elt->msgno);
  1068.     return NIL; /* return this message was expunged */
  1069.   }
  1070.   if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
  1071.       (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
  1072.       (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
  1073.     mm_flags (stream,msgno); /* let top level know */
  1074.   return elt;
  1075. }
  1076. /* MBX read flags from file
  1077.  * Accepts: MAIL stream
  1078.  *     cache element
  1079.  * Returns: non-NIL if message expunged
  1080.  */
  1081. unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
  1082. {
  1083.   unsigned long i;
  1084. /* noop if readonly and have valid flags */
  1085.   if (stream->rdonly && elt->valid) return NIL;
  1086. /* set the seek pointer */
  1087.   lseek (LOCAL->fd,(off_t) elt->private.special.offset +
  1088.  elt->private.special.text.size - 23,L_SET);
  1089. /* read the new flags */
  1090.   if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
  1091.     sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
  1092.     fatal (LOCAL->buf);
  1093.   }
  1094.   LOCAL->buf[12] = ''; /* tie off buffer */
  1095. /* calculate system flags */
  1096.   i = strtoul (LOCAL->buf+8,NIL,16);
  1097.   elt->seen = i & fSEEN ? T : NIL;
  1098.   elt->deleted = i & fDELETED ? T : NIL;
  1099.   elt->flagged = i & fFLAGGED ? T : NIL;
  1100.   elt->answered = i & fANSWERED ? T : NIL;
  1101.   elt->draft = i & fDRAFT ? T : NIL;
  1102.   LOCAL->expunged |= i & fEXPUNGED ? T : NIL;
  1103.   LOCAL->buf[8] = ''; /* tie off flags */
  1104. /* get user flags value */
  1105.   elt->user_flags = strtoul (LOCAL->buf,NIL,16);
  1106.   elt->valid = T; /* have valid flags now */
  1107.   return i & fEXPUNGED;
  1108. }
  1109. /* MBX update header
  1110.  * Accepts: MAIL stream
  1111.  */
  1112. #define NTKLUDGEOFFSET 7
  1113. void mbx_update_header (MAILSTREAM *stream)
  1114. {
  1115.   int i;
  1116.   char *s = LOCAL->buf;
  1117.   memset (s,'',HDRSIZE); /* initialize header */
  1118.   sprintf (s,"*mbx*1512%08lx%08lx1512",
  1119.    stream->uid_validity,stream->uid_last);
  1120.   for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
  1121.     sprintf (s += strlen (s),"%s1512",stream->user_flags[i]);
  1122.   LOCAL->ffuserflag = i; /* first free user flag */
  1123. /* can we create more user flags? */
  1124.   stream->kwd_create = (i < NUSERFLAGS) ? T : NIL;
  1125. /* write reserved lines */
  1126.   while (i++ < NUSERFLAGS) strcat (s,"1512");
  1127.   while (T) { /* rewind file */
  1128.     lseek (LOCAL->fd,NTKLUDGEOFFSET,L_SET);
  1129. /* write new header */
  1130.     if (write (LOCAL->fd,LOCAL->buf + NTKLUDGEOFFSET,
  1131.        HDRSIZE - NTKLUDGEOFFSET) > 0) break;
  1132.     mm_notify (stream,strerror (errno),WARN);
  1133.     mm_diskerror (stream,errno,T);
  1134.   }
  1135. }
  1136. /* MBX update status string
  1137.  * Accepts: MAIL stream
  1138.  *     message number
  1139.  *     flags
  1140.  */
  1141. void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags)
  1142. {
  1143.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1144.   struct stat sbuf;
  1145.   int expflag;
  1146. /* readonly */
  1147.   if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt);
  1148.   else { /* readwrite */
  1149. /* want to expunge message? */
  1150.     if (elt->deleted && (flags & mus_EXPUNGE)) expflag = fEXPUNGED;
  1151.     else { /* seek to system flags */
  1152.       lseek (LOCAL->fd,(off_t) elt->private.special.offset +
  1153.      elt->private.special.text.size - 15,L_SET);
  1154. /* read the current system flags */
  1155.       if (read (LOCAL->fd,LOCAL->buf,4) < 0) {
  1156. sprintf (LOCAL->buf,"Unable to read system flags: %s",
  1157.  strerror (errno));
  1158. fatal (LOCAL->buf);
  1159.       }
  1160.       LOCAL->buf[4] = ''; /* tie off buffer */
  1161. /* note current set of expunged flag */
  1162.       expflag = (strtoul (LOCAL->buf,NIL,16)) & fEXPUNGED;
  1163.     }
  1164. /* print new flag string */
  1165.     sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned)
  1166.      (expflag + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  1167.       (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
  1168.       (fDRAFT * elt->draft)),elt->private.uid);
  1169.     while (T) { /* get to that place in the file */
  1170.       lseek (LOCAL->fd,(off_t) elt->private.special.offset +
  1171.      elt->private.special.text.size - 23,L_SET);
  1172. /* write new flags and UID */
  1173.       if (write (LOCAL->fd,LOCAL->buf,21) > 0) break;
  1174.       mm_notify (stream,strerror (errno),WARN);
  1175.       mm_diskerror (stream,errno,T);
  1176.     }
  1177.     if (flags & mus_SYNC) { /* sync if requested */
  1178.       fsync (LOCAL->fd);
  1179.       fstat (LOCAL->fd,&sbuf); /* get new write time */
  1180.       LOCAL->filetime = sbuf.st_mtime;
  1181.     }
  1182.   }
  1183. }
  1184. /* MBX locate header for a message
  1185.  * Accepts: MAIL stream
  1186.  *     message number
  1187.  *     pointer to returned header size
  1188.  * Returns: position of header in file
  1189.  */
  1190. unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
  1191.   unsigned long *size)
  1192. {
  1193.   unsigned long siz;
  1194.   long i = 0;
  1195.   int q = 0;
  1196.   char *s,tmp[MAILTMPLEN];
  1197.   MESSAGECACHE *elt = mbx_elt (stream,msgno,NIL);
  1198.   unsigned long ret = elt->private.special.offset +
  1199.     elt->private.special.text.size;
  1200. /* is header size known? */
  1201.   if (!(*size = elt->private.msg.header.text.size)) {
  1202.     lseek (LOCAL->fd,ret,L_SET);/* get to header position */
  1203. /* search message for CRLF CRLF */
  1204.     for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) {
  1205. /* read another buffer as necessary */
  1206.       if (--i <= 0) /* buffer empty? */
  1207. if (read (LOCAL->fd,s = tmp,
  1208.   i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0)
  1209.   return ret; /* I/O error? */
  1210.       switch (q) { /* sniff at buffer */
  1211.       case 0: /* first character */
  1212. q = (*s++ == '15') ? 1 : 0;
  1213. break;
  1214.       case 1: /* second character */
  1215. q = (*s++ == '12') ? 2 : 0;
  1216. break;
  1217.       case 2: /* third character */
  1218. q = (*s++ == '15') ? 3 : 0;
  1219. break;
  1220.       case 3: /* fourth character */
  1221. if (*s++ == '12') { /* have the sequence? */
  1222. /* yes, note for later */
  1223.   elt->private.msg.header.text.size = *size = siz;
  1224.   return ret;
  1225. }
  1226. q = 0; /* lost... */
  1227. break;
  1228.       }
  1229.     }
  1230. /* header consumes entire message */
  1231.     elt->private.msg.header.text.size = *size = elt->rfc822_size;
  1232.   }
  1233.   return ret;
  1234. }