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 / mtxnt.c < prev    next >
C/C++ Source or Header  |  1999-01-06  |  37KB  |  1,102 lines

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