home *** CD-ROM | disk | FTP | other *** search
/ ftp.uv.es / 2014.11.ftp.uv.es.tar / ftp.uv.es / pub / unix / pine4.10.tar.gz / pine4.10.tar / pine4.10 / imap / src / osdep / nt / mbxnt.c < prev    next >
C/C++ Source or Header  |  1999-01-06  |  44KB  |  1,305 lines

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