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

网络编程

开发平台:

Unix_Linux

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