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

网络编程

开发平台:

Unix_Linux

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