home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / pine3.07 / c-client / bezerk.c next >
Encoding:
C/C++ Source or Header  |  1993-03-29  |  58.5 KB  |  1,981 lines

  1. /*
  2.  * Program:    Berkeley 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:    20 December 1989
  13.  * Last Edited:    26 May 1992/maintenance release 29 March 1993
  14.  *
  15.  * Copyright 1992 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. /* Dedication:
  38.  *  This file is dedicated with affection to those Merry Marvels of Musical
  39.  * Madness . . .
  40.  *  ->  The Incomparable Leland Stanford Junior University Marching Band  <-
  41.  * who entertain, awaken, and outrage Stanford fans in the fact of repeated
  42.  * losing seasons and shattered Rose Bowl dreams [Cardinal just don't have
  43.  * HUSKY FEVER!!!].
  44.  *
  45.  */
  46.  
  47. #include <stdio.h>
  48. #include <ctype.h>
  49. #include <pwd.h>
  50. #include <netdb.h>
  51. #include <errno.h>
  52. extern int errno;        /* just in case */
  53. #include <sys/types.h>
  54. #include <sys/file.h>
  55. #include <sys/stat.h>
  56. #include <sys/time.h>
  57. #include <sys/uio.h>
  58. #include "osdep.h"
  59. #include "mail.h"
  60. #include "bezerk.h"
  61. #include "rfc822.h"
  62. #include "misc.h"
  63.  
  64. /* Berkeley mail routines */
  65.  
  66.  
  67. /* Driver dispatch used by MAIL */
  68.  
  69. DRIVER bezerkdriver = {
  70.   (DRIVER *) NIL,        /* next driver */
  71.   bezerk_valid,            /* mailbox is valid for us */
  72.   bezerk_find,            /* find mailboxes */
  73.   bezerk_find_bboards,        /* find bboards */
  74.   bezerk_open,            /* open mailbox */
  75.   bezerk_close,            /* close mailbox */
  76.   bezerk_fetchfast,        /* fetch message "fast" attributes */
  77.   bezerk_fetchflags,        /* fetch message flags */
  78.   bezerk_fetchenvelope,        /* fetch message envelopes */
  79.   bezerk_fetchheader,        /* fetch message header only */
  80.   bezerk_fetchtext,        /* fetch message body only */
  81.   bezerk_fetchbody,        /* fetch message body section */
  82.   bezerk_setflag,        /* set message flag */
  83.   bezerk_clearflag,        /* clear message flag */
  84.   bezerk_search,        /* search for message based on criteria */
  85.   bezerk_ping,            /* ping mailbox to see if still alive */
  86.   bezerk_check,            /* check for new messages */
  87.   bezerk_expunge,        /* expunge deleted messages */
  88.   bezerk_copy,            /* copy messages to another mailbox */
  89.   bezerk_move,            /* move messages to another mailbox */
  90.   bezerk_gc            /* garbage collect stream */
  91. };
  92.  
  93. /* Berkeley mail validate mailbox
  94.  * Accepts: mailbox name
  95.  * Returns: our driver if name is valid, otherwise calls valid in next driver
  96.  */
  97.  
  98. DRIVER *bezerk_valid (name)
  99.     char *name;
  100. {
  101.   return bezerk_isvalid (name) ? &bezerkdriver :
  102.     (bezerkdriver.next ? (*bezerkdriver.next->valid) (name) : NIL);
  103. }
  104.  
  105.  
  106. /* Test for valid header */
  107.  
  108. #define VALID (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
  109.   (s[4] == ' ') && (t = strchr (s+5,'\n')) && (t-s >= 27) && (t[-5] == ' ') &&\
  110.   (t[(rn = -4 * (t[-9] == ' '))-8] == ':') && \
  111.   ((sysv = 3 * (t[rn-11] == ' ')) || t[rn-11] == ':') && \
  112.   (t[rn+sysv-14] == ' ') && (t[rn+sysv-17] == ' ') && \
  113.   (t[rn+sysv-21] == ' ')
  114.  
  115.  
  116. /* Berkeley mail test for valid mailbox
  117.  * Accepts: mailbox name
  118.  * Returns: T if valid, NIL otherwise
  119.  */
  120.  
  121. int bezerk_isvalid (name)
  122.     char *name;
  123. {
  124.   int fd;
  125.   int rn,sysv;
  126.   int ret = NIL;
  127.   char *t;
  128.   char s[MAILTMPLEN];
  129.   struct stat sbuf;
  130.                 /* INBOX is always accepted */
  131.   if (!strcmp (ucase (strcpy (s,name)),"INBOX")) return T;
  132.                 /* if file, get its status */
  133.   if (*name != '{' && (stat (bezerk_file (s,name),&sbuf) == 0) &&
  134.       ((fd = open (s,O_RDONLY,NIL)) >= 0)) {
  135.     if (sbuf.st_size == 0) {     /* allow empty file if not .txt */
  136.       if (!strstr (name,".txt")) ret = T;
  137.     }
  138.     else if ((read (fd,s,MAILTMPLEN-1) >= 0) && (s[0] == 'F') && VALID)
  139.       ret = T;            /* we accept the honors */
  140.     close (fd);            /* close the file */
  141.   }
  142.   return ret;            /* return what we should */
  143. }
  144.  
  145. /* Berkeley mail find list of mailboxes
  146.  * Accepts: mail stream
  147.  *        pattern to search
  148.  */
  149.  
  150. void bezerk_find (stream,pat)
  151.     MAILSTREAM *stream;
  152.     char *pat;
  153. {
  154.   int fd;
  155.   char tmp[MAILTMPLEN];
  156.   char *s,*t;
  157.   struct stat sbuf;
  158.                 /* make file name */
  159.   sprintf (tmp,"%s/.mailboxlist",getpwuid (geteuid ())->pw_dir);
  160.   if ((fd = open (tmp,O_RDONLY,NIL)) >= 0) {
  161.     fstat (fd,&sbuf);        /* get file size and read data */
  162.     read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
  163.     close (fd);            /* close file */
  164.     s[sbuf.st_size] = '\0';    /* tie off string */
  165.     if (t = strtok (s,"\n"))    /* get first mailbox name */
  166.       do if ((*t != '{') && strcmp (t,"INBOX") && pmatch (t,pat) &&
  167.          bezerk_isvalid (t)) mm_mailbox (t);
  168.                 /* for each mailbox */
  169.     while (t = strtok (NIL,"\n"));
  170.     fs_give ((void **) &s);
  171.   }
  172. }
  173.  
  174.  
  175. /* Berkeley mail find list of bboards
  176.  * Accepts: mail stream
  177.  *        pattern to search
  178.  */
  179.  
  180. void bezerk_find_bboards (stream,pat)
  181.     MAILSTREAM *stream;
  182.     char *pat;
  183. {
  184.   /* Always a no-op */
  185. }
  186.  
  187. /* Berkeley mail open
  188.  * Accepts: stream to open
  189.  * Returns: stream on success, NIL on failure
  190.  */
  191.  
  192. MAILSTREAM *bezerk_open (stream)
  193.     MAILSTREAM *stream;
  194. {
  195.   long i;
  196.   int fd;
  197.   char tmp[MAILTMPLEN];
  198.   struct hostent *host_name;
  199.   if (LOCAL) {            /* close old file if stream being recycled */
  200.     bezerk_close (stream);    /* dump and save the changes */
  201.     stream->dtb = &bezerkdriver;/* reattach this driver */
  202.                 /* clean up cache (again) */
  203.     mail_free_cache (&stream->cache,&stream->cachesize);
  204.   }
  205.   stream->local = fs_get (sizeof (BEZERKLOCAL));
  206.                 /* canonicalize the stream mailbox name */
  207.   bezerk_file (tmp,stream->mailbox);
  208.   fs_give ((void **) &stream->mailbox);
  209.   stream->mailbox = cpystr (tmp);
  210.   LOCAL->fname = cpystr (stream->mailbox);
  211.   gethostname(tmp,MAILTMPLEN);    /* get local host name */
  212.   LOCAL->host = cpystr ((host_name = gethostbyname (tmp)) ?
  213.             host_name->h_name : tmp);
  214.                 /* build name of our lock file */
  215.   sprintf (tmp,"/tmp/.%s",LOCAL->fname);
  216.   for (i = 6; i < strlen (tmp); ++i) if (tmp[i] == '/') tmp[i] = '\\';
  217.   LOCAL->lname = cpystr (tmp);    /* note name for later */
  218.   LOCAL->ld = NIL;        /* no state locking yet */
  219.   LOCAL->filesize = 0;        /* initialize file information */
  220.   LOCAL->filetime = 0;
  221.   LOCAL->msgs = NIL;        /* no cache yet */
  222.   LOCAL->cachesize = 0;
  223.   LOCAL->buf = (char *) fs_get ((LOCAL->buflen = 2*CHUNK) + 1);
  224.   stream->sequence++;        /* bump sequence number */
  225.  
  226.   LOCAL->dirty = NIL;        /* no update yet */
  227.                 /* make lock for state */
  228.   if ((i = open (LOCAL->lname,O_RDWR|O_CREAT,0666)) < 0)
  229.     mm_log ("Can't open mailbox lock, access is readonly",WARN);
  230.                 /* try lock it */
  231.   else if (flock (i,LOCK_EX|LOCK_NB)) {
  232.     close (i);            /* couldn't lock, give up on it then */
  233.     mm_log ("Mailbox is open by another user or process, access is readonly",
  234.         WARN);
  235.   }
  236.   else {            /* got the lock, nobody else can alter state */
  237.     LOCAL->ld = i;        /* note lock's fd */
  238.     chmod (LOCAL->lname,0666);    /* some folks don't have fchmod() */
  239.   }
  240.                 /* parse mailbox */
  241.   stream->nmsgs = stream->recent = 0;
  242.                 /* will we be able to get write access? */
  243.   if (LOCAL->ld && access (LOCAL->fname,W_OK) && (errno == EACCES)) {
  244.     mm_log ("Can't get write access to mailbox, access is readonly",WARN);
  245.     flock (LOCAL->ld,LOCK_UN);    /* release the lock */
  246.     close (LOCAL->ld);        /* close the lock file */
  247.     LOCAL->ld = NIL;        /* no more lock fd */
  248.     unlink (LOCAL->lname);    /* delete it */
  249.     fs_give ((void **) &LOCAL->lname);
  250.   }
  251.                 /* parse mailbox */
  252.   if ((fd = bezerk_parse (stream,tmp)) >= 0) {
  253.     bezerk_unlock (fd,stream,tmp);
  254.     mail_unlock (stream);
  255.   }
  256.   if (!LOCAL) return NIL;    /* failure if stream died */
  257.   stream->readonly = !LOCAL->ld;/* make sure upper level knows readonly */
  258.                 /* notify about empty mailbox */
  259.   if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",NIL);
  260.   return stream;        /* return stream alive to caller */
  261. }
  262.  
  263. /* Berkeley mail close
  264.  * Accepts: MAIL stream
  265.  */
  266.  
  267. void bezerk_close (stream)
  268.     MAILSTREAM *stream;
  269. {
  270.   int fd;
  271.   int silent = stream->silent;
  272.   char lock[MAILTMPLEN];
  273.   if (LOCAL && LOCAL->ld) {    /* is stream alive? */
  274.     stream->silent = T;        /* note this stream is dying */
  275.                 /* lock mailbox and parse new messages */
  276.     if (LOCAL->dirty && (fd = bezerk_parse (stream,lock)) >= 0) {
  277.       flock (fd,LOCK_EX);    /* upgrade the lock to exclusive access */
  278.                 /* dump any changes not saved yet */
  279.       if (bezerk_extend    (stream,fd,"Close failed to update mailbox"))
  280.     bezerk_save (stream,fd);
  281.                 /* flush locks */
  282.       bezerk_unlock (fd,stream,lock);
  283.       mail_unlock (stream);
  284.     }
  285.     stream->silent = silent;    /* restore previous status */
  286.   }
  287.   bezerk_abort (stream);    /* now punt the file and local data */
  288. }
  289.  
  290.  
  291. /* Berkeley mail fetch fast information
  292.  * Accepts: MAIL stream
  293.  *        sequence
  294.  */
  295.  
  296. void bezerk_fetchfast (stream,sequence)
  297.     MAILSTREAM *stream;
  298.     char *sequence;
  299. {
  300.   return;            /* no-op for local mail */
  301. }
  302.  
  303.  
  304. /* Berkeley mail fetch flags
  305.  * Accepts: MAIL stream
  306.  *        sequence
  307.  */
  308.  
  309. void bezerk_fetchflags (stream,sequence)
  310.     MAILSTREAM *stream;
  311.     char *sequence;
  312. {
  313.   return;            /* no-op for local mail */
  314. }
  315.  
  316. /* Berkeley mail fetch envelope
  317.  * Accepts: MAIL stream
  318.  *        message # to fetch
  319.  * Returns: envelope of this message
  320.  *
  321.  * Fetches the "fast" information as well
  322.  */
  323.  
  324. ENVELOPE *bezerk_fetchenvelope (stream,msgno)
  325.     MAILSTREAM *stream;
  326.     long msgno;
  327. {
  328.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  329.   long i = max (m->headersize,m->bodysize);
  330.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  331.   if (i > LOCAL->buflen) {    /* make sure buffer can hold the text */
  332.                 /* fs_resize would do an unnecessary copy */
  333.     fs_give ((void **) &LOCAL->buf);
  334.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
  335.   }
  336.   if (!elt->env)        /* make envelope now if don't have one */
  337.     rfc822_parse_msg (&elt->env,&elt->body,m->header,m->headersize,
  338.               m->body,m->bodysize,LOCAL->host,LOCAL->buf);
  339.   return elt->env;        /* return the envelope */
  340. }
  341.  
  342.  
  343. /* Berkeley mail snarf message, only for Tenex driver
  344.  * Accepts: MAIL stream
  345.  *        message # to snarf
  346.  *        pointer to size to return
  347.  * Returns: message text in RFC822 format
  348.  */
  349.  
  350. char *bezerk_snarf (stream,msgno,size)
  351.     MAILSTREAM *stream;
  352.     long msgno;
  353.     long *size;
  354. {
  355.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  356.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  357.                   /* make sure stream can hold the text */
  358.   if ((*size = m->headersize + m->bodysize) > LOCAL->buflen) {
  359.                 /* fs_resize would do an unnecessary copy */
  360.     fs_give ((void **) &LOCAL->buf);
  361.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *size) + 1);
  362.   }
  363.                 /* copy the text */
  364.   if (m->headersize) memcpy (LOCAL->buf,m->header,m->headersize);
  365.   if (m->bodysize) memcpy (LOCAL->buf + m->headersize,m->body,m->bodysize);
  366.   LOCAL->buf[*size] = '\0';    /* tie off string */
  367.   return LOCAL->buf;
  368. }
  369.  
  370. /* Berkeley mail fetch message header
  371.  * Accepts: MAIL stream
  372.  *        message # to fetch
  373.  * Returns: message header in RFC822 format
  374.  */
  375.  
  376. char *bezerk_fetchheader (stream,msgno)
  377.     MAILSTREAM *stream;
  378.     long msgno;
  379. {
  380.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  381.                 /* copy the string */
  382.   return strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,m->header,m->headersize);
  383. }
  384.  
  385.  
  386. /* Berkeley mail fetch message text (only)
  387.     body only;
  388.  * Accepts: MAIL stream
  389.  *        message # to fetch
  390.  * Returns: message text in RFC822 format
  391.  */
  392.  
  393. char *bezerk_fetchtext (stream,msgno)
  394.     MAILSTREAM *stream;
  395.     long msgno;
  396. {
  397.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  398.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  399.   if (!elt->seen) {        /* if message not seen */
  400.     elt->seen = T;        /* mark message as seen */
  401.                 /* recalculate Status/X-Status lines */
  402.     bezerk_update_status (m->status,elt);
  403.     LOCAL->dirty = T;        /* note stream is now dirty */
  404.   }
  405.   return strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,m->body,m->bodysize);
  406. }
  407.  
  408. /* Berkeley fetch message body as a structure
  409.  * Accepts: Mail stream
  410.  *        message # to fetch
  411.  *        section specifier
  412.  *        pointer to length
  413.  * Returns: pointer to section of message body
  414.  */
  415.  
  416. char *bezerk_fetchbody (stream,m,s,len)
  417.     MAILSTREAM *stream;
  418.     long m;
  419.     char *s;
  420.     unsigned long *len;
  421. {
  422.   BODY *b;
  423.   PART *pt;
  424.   unsigned long i;
  425.   char *base = LOCAL->msgs[m - 1]->body;
  426.   unsigned long offset = 0;
  427.   MESSAGECACHE *elt = mail_elt (stream,m);
  428.                 /* make sure have a body */
  429.   if (!(bezerk_fetchenvelope (stream,m) && (b = mail_elt (stream,m)->body) &&
  430.     s && *s && ((i = strtol (s,&s,10)) > 0))) return NIL;
  431.   do {                /* until find desired body part */
  432.                 /* multipart content? */
  433.     if (b->type == TYPEMULTIPART) {
  434.       pt = b->contents.part;    /* yes, find desired part */
  435.       while (--i && (pt = pt->next));
  436.       if (!pt) return NIL;    /* bad specifier */
  437.                 /* note new body, check valid nesting */
  438.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  439.       base += offset;        /* calculate new base */
  440.       offset = pt->offset;    /* and its offset */
  441.     }
  442.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  443.                 /* need to go down further? */
  444.     if (i = *s) switch (b->type) {
  445.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  446.       base += offset + b->contents.msg.offset;
  447.       offset = 0;        /* no offset any more */
  448.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  449.     case TYPEMULTIPART:        /* multipart, get next section */
  450.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  451.     default:            /* bogus subpart specification */
  452.       return NIL;
  453.     }
  454.   } while (i);
  455.                 /* lose if body bogus */
  456.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  457.   if (!elt->seen) {        /* if message not seen */
  458.     elt->seen = T;        /* mark message as seen */
  459.                 /* recalculate Status/X-Status lines */
  460.     bezerk_update_status (LOCAL->msgs[m - 1]->status,elt);
  461.     LOCAL->dirty = T;        /* note stream is now dirty */
  462.   }
  463.   return rfc822_contents (&LOCAL->buf,&LOCAL->buflen,len,base + offset,
  464.               b->size.ibytes,b->encoding);
  465. }
  466.  
  467. /* Berkeley mail set flag
  468.  * Accepts: MAIL stream
  469.  *        sequence
  470.  *        flag(s)
  471.  */
  472.  
  473. void bezerk_setflag (stream,sequence,flag)
  474.     MAILSTREAM *stream;
  475.     char *sequence;
  476.     char *flag;
  477. {
  478.   MESSAGECACHE *elt;
  479.   long i;
  480.   short f = bezerk_getflags (stream,flag);
  481.   if (!f) return;        /* no-op if no flags to modify */
  482.                 /* get sequence and loop on it */
  483.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  484.     if ((elt = mail_elt (stream,i))->sequence) {
  485.                 /* set all requested flags */
  486.       if (f&fSEEN) elt->seen = T;
  487.       if (f&fDELETED) elt->deleted = T;
  488.       if (f&fFLAGGED) elt->flagged = T;
  489.       if (f&fANSWERED) elt->answered = T;
  490.                 /* recalculate Status/X-Status lines */
  491.       bezerk_update_status (LOCAL->msgs[i - 1]->status,elt);
  492.       LOCAL->dirty = T;        /* note stream is now dirty */
  493.     }
  494. }
  495.  
  496.  
  497. /* Berkeley mail clear flag
  498.  * Accepts: MAIL stream
  499.  *        sequence
  500.  *        flag(s)
  501.  */
  502.  
  503. void bezerk_clearflag (stream,sequence,flag)
  504.     MAILSTREAM *stream;
  505.     char *sequence;
  506.     char *flag;
  507. {
  508.   MESSAGECACHE *elt;
  509.   long i;
  510.   short f = bezerk_getflags (stream,flag);
  511.   if (!f) return;        /* no-op if no flags to modify */
  512.                 /* get sequence and loop on it */
  513.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  514.     if ((elt = mail_elt (stream,i))->sequence) {
  515.                 /* clear all requested flags */
  516.       if (f&fSEEN) elt->seen = NIL;
  517.       if (f&fDELETED) elt->deleted = NIL;
  518.       if (f&fFLAGGED) elt->flagged = NIL;
  519.       if (f&fANSWERED) elt->answered = NIL;
  520.                 /* recalculate Status/X-Status lines */
  521.       bezerk_update_status (LOCAL->msgs[i - 1]->status,elt);
  522.       LOCAL->dirty = T;        /* note stream is now dirty */
  523.     }
  524. }
  525.  
  526. /* Berkeley mail search for messages
  527.  * Accepts: MAIL stream
  528.  *        search criteria
  529.  */
  530.  
  531. void bezerk_search (stream,criteria)
  532.     MAILSTREAM *stream;
  533.     char *criteria;
  534. {
  535.   long i,n;
  536.   char *d;
  537.   search_t f;
  538.                 /* initially all searched */
  539.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  540.                 /* get first criterion */
  541.   if (criteria && (criteria = strtok (criteria," "))) {
  542.                 /* for each criterion */
  543.     for (; criteria; (criteria = strtok (NIL," "))) {
  544.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  545.       switch (*ucase (criteria)) {
  546.       case 'A':            /* possible ALL, ANSWERED */
  547.     if (!strcmp (criteria+1,"LL")) f = bezerk_search_all;
  548.     else if (!strcmp (criteria+1,"NSWERED")) f = bezerk_search_answered;
  549.     break;
  550.       case 'B':            /* possible BCC, BEFORE, BODY */
  551.     if (!strcmp (criteria+1,"CC"))
  552.       f = bezerk_search_string (bezerk_search_bcc,&d,&n);
  553.     else if (!strcmp (criteria+1,"EFORE"))
  554.       f = bezerk_search_date (bezerk_search_before,&n);
  555.     else if (!strcmp (criteria+1,"ODY"))
  556.       f = bezerk_search_string (bezerk_search_body,&d,&n);
  557.     break;
  558.       case 'C':            /* possible CC */
  559.     if (!strcmp (criteria+1,"C"))
  560.       f = bezerk_search_string (bezerk_search_cc,&d,&n);
  561.     break;
  562.       case 'D':            /* possible DELETED */
  563.     if (!strcmp (criteria+1,"ELETED")) f = bezerk_search_deleted;
  564.     break;
  565.       case 'F':            /* possible FLAGGED, FROM */
  566.     if (!strcmp (criteria+1,"LAGGED")) f = bezerk_search_flagged;
  567.     else if (!strcmp (criteria+1,"ROM"))
  568.       f = bezerk_search_string (bezerk_search_from,&d,&n);
  569.     break;
  570.       case 'K':            /* possible KEYWORD */
  571.     if (!strcmp (criteria+1,"EYWORD"))
  572.       f = bezerk_search_flag (bezerk_search_keyword,&d);
  573.     break;
  574.       case 'N':            /* possible NEW */
  575.     if (!strcmp (criteria+1,"EW")) f = bezerk_search_new;
  576.     break;
  577.  
  578.       case 'O':            /* possible OLD, ON */
  579.     if (!strcmp (criteria+1,"LD")) f = bezerk_search_old;
  580.     else if (!strcmp (criteria+1,"N"))
  581.       f = bezerk_search_date (bezerk_search_on,&n);
  582.     break;
  583.       case 'R':            /* possible RECENT */
  584.     if (!strcmp (criteria+1,"ECENT")) f = bezerk_search_recent;
  585.     break;
  586.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  587.     if (!strcmp (criteria+1,"EEN")) f = bezerk_search_seen;
  588.     else if (!strcmp (criteria+1,"INCE"))
  589.       f = bezerk_search_date (bezerk_search_since,&n);
  590.     else if (!strcmp (criteria+1,"UBJECT"))
  591.       f = bezerk_search_string (bezerk_search_subject,&d,&n);
  592.     break;
  593.       case 'T':            /* possible TEXT, TO */
  594.     if (!strcmp (criteria+1,"EXT"))
  595.       f = bezerk_search_string (bezerk_search_text,&d,&n);
  596.     else if (!strcmp (criteria+1,"O"))
  597.       f = bezerk_search_string (bezerk_search_to,&d,&n);
  598.     break;
  599.       case 'U':            /* possible UN* */
  600.     if (criteria[1] == 'N') {
  601.       if (!strcmp (criteria+2,"ANSWERED")) f = bezerk_search_unanswered;
  602.       else if (!strcmp (criteria+2,"DELETED")) f = bezerk_search_undeleted;
  603.       else if (!strcmp (criteria+2,"FLAGGED")) f = bezerk_search_unflagged;
  604.       else if (!strcmp (criteria+2,"KEYWORD"))
  605.         f = bezerk_search_flag (bezerk_search_unkeyword,&d);
  606.       else if (!strcmp (criteria+2,"SEEN")) f = bezerk_search_unseen;
  607.     }
  608.     break;
  609.       default:            /* we will barf below */
  610.     break;
  611.       }
  612.       if (!f) {            /* if can't determine any criteria */
  613.     sprintf (LOCAL->buf,"Unknown search criterion: %.30s",criteria);
  614.     mm_log (LOCAL->buf,ERROR);
  615.     return;
  616.       }
  617.                 /* run the search criterion */
  618.       for (i = 1; i <= stream->nmsgs; ++i)
  619.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  620.       mail_elt (stream,i)->searched = NIL;
  621.     }
  622.                 /* report search results to main program */
  623.     for (i = 1; i <= stream->nmsgs; ++i)
  624.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  625.   }
  626. }
  627.  
  628. /* Berkeley mail ping mailbox
  629.  * Accepts: MAIL stream
  630.  * Returns: T if stream alive, else NIL
  631.  * No-op for readonly files, since read/writer can expunge it from under us!
  632.  */
  633.  
  634. long bezerk_ping (stream)
  635.     MAILSTREAM *stream;
  636. {
  637.   char lock[MAILTMPLEN];
  638.   struct stat sbuf;
  639.   int fd;
  640.                 /* make sure it is alright to do this at all */
  641.   if (LOCAL && LOCAL->ld && !stream->lock) {
  642.     stat (LOCAL->fname,&sbuf);    /* get current mailbox size */
  643.                 /* parse if mailbox changed */
  644.     if ((sbuf.st_size != LOCAL->filesize) &&
  645.     ((fd = bezerk_parse (stream,lock)) >= 0)) {
  646.                 /* unlock mailbox */
  647.       bezerk_unlock (fd,stream,lock);
  648.       mail_unlock (stream);    /* and stream */
  649.     }
  650.   }
  651.   return LOCAL ? T : NIL;    /* return if still alive */
  652. }
  653.  
  654.  
  655. /* Berkeley mail check mailbox
  656.  * Accepts: MAIL stream
  657.  * No-op for readonly files, since read/writer can expunge it from under us!
  658.  */
  659.  
  660. void bezerk_check (stream)
  661.     MAILSTREAM *stream;
  662. {
  663.   char lock[MAILTMPLEN];
  664.   int fd;
  665.                 /* parse and lock mailbox */
  666.   if (LOCAL && LOCAL->ld && ((fd = bezerk_parse (stream,lock)) >= 0)) {
  667.     if (LOCAL->dirty) {        /* only if checkpoint needed */
  668.       flock (fd,LOCK_EX);    /* upgrade the lock to exclusive access */
  669.                 /* dump a checkpoint */
  670.       if (bezerk_extend (stream,fd,"Unable to update mailbox"))
  671.     bezerk_save (stream,fd);
  672.     }
  673.                 /* flush locks */
  674.     bezerk_unlock (fd,stream,lock);
  675.     mail_unlock (stream);
  676.   }
  677.   if (LOCAL && LOCAL->ld) mm_log ("Check completed",NIL);
  678. }
  679.  
  680. /* Berkeley mail expunge mailbox
  681.  * Accepts: MAIL stream
  682.  */
  683.  
  684. void bezerk_expunge (stream)
  685.     MAILSTREAM *stream;
  686. {
  687.   int fd,j;
  688.   long i = 1;
  689.   long n = 0;
  690.   unsigned long recent;
  691.   MESSAGECACHE *elt;
  692.   char *r = "No messages deleted, so no update needed";
  693.   char lock[MAILTMPLEN];
  694.   if (LOCAL && LOCAL->ld) {    /* parse and lock mailbox */
  695.     if ((fd = bezerk_parse (stream,lock)) >= 0) {
  696.       recent = stream->recent;    /* get recent now that new ones parsed */
  697.       while ((j = (i<=stream->nmsgs)) && !(elt = mail_elt (stream,i))->deleted)
  698.     i++;            /* find first deleted message */
  699.       if (j) {            /* found one? */
  700.     flock (fd,LOCK_EX);    /* upgrade the lock to exclusive access */
  701.                 /* make sure we can do the worst case thing */
  702.     if (bezerk_extend (stream,fd,"Unable to expunge mailbox")) {
  703.       do {            /* flush deleted messages */
  704.         if ((elt = mail_elt (stream,i))->deleted) {
  705.                 /* if recent, note one less recent message */
  706.           if (elt->recent) --recent;
  707.                 /* flush local cache entry */
  708.           fs_give ((void **) &LOCAL->msgs[i - 1]);
  709.           for (j = i; j < stream->nmsgs; j++)
  710.         LOCAL->msgs[j - 1] = LOCAL->msgs[j];
  711.           LOCAL->msgs[stream->nmsgs - 1] = NIL;
  712.                 /* notify upper levels */
  713.           mail_expunged (stream,i);
  714.           n++;        /* count another expunged message */
  715.         }
  716.         else i++;        /* otherwise try next message */
  717.       } while (i <= stream->nmsgs);
  718.                 /* dump checkpoint of the results */
  719.       bezerk_save (stream,fd);
  720.       sprintf ((r = LOCAL->buf),"Expunged %d messages",n);
  721.     }
  722.       }
  723.                 /* notify upper level, free locks */
  724.       mail_exists (stream,stream->nmsgs);
  725.       mail_recent (stream,recent);
  726.       bezerk_unlock (fd,stream,lock);
  727.       mail_unlock (stream);
  728.     }
  729.   }
  730.   else r = "Expunge ignored on readonly mailbox";
  731.   if (LOCAL && !stream->silent) mm_log (r,NIL);
  732. }
  733.  
  734. /* Berkeley mail copy message(s)
  735.     s;
  736.  * Accepts: MAIL stream
  737.  *        sequence
  738.  *        destination mailbox
  739.  * Returns: T if copy successful, else NIL
  740.  */
  741.  
  742. long bezerk_copy (stream,sequence,mailbox)
  743.     MAILSTREAM *stream;
  744.     char *sequence;
  745.     char *mailbox;
  746. {
  747.                 /* copy the messages */
  748.   return (mail_sequence (stream,sequence)) ?
  749.     bezerk_copy_messages (stream,mailbox) : NIL;
  750. }
  751.  
  752.  
  753. /* Berkeley mail move message(s)
  754.     s;
  755.  * Accepts: MAIL stream
  756.  *        sequence
  757.  *        destination mailbox
  758.  * Returns: T if move successful, else NIL
  759.  */
  760.  
  761. long bezerk_move (stream,sequence,mailbox)
  762.     MAILSTREAM *stream;
  763.     char *sequence;
  764.     char *mailbox;
  765. {
  766.   long i;
  767.   MESSAGECACHE *elt;
  768.   if (!(mail_sequence (stream,sequence) &&
  769.     bezerk_copy_messages (stream,mailbox))) return NIL;
  770.                 /* delete all requested messages */
  771.   for (i = 1; i <= stream->nmsgs; i++)
  772.     if ((elt = mail_elt (stream,i))->sequence) {
  773.       elt->deleted = T;        /* mark message deleted */
  774.                 /* recalculate Status/X-Status lines */
  775.       bezerk_update_status (LOCAL->msgs[i - 1]->status,elt);
  776.       LOCAL->dirty = T;        /* note stream is now dirty */
  777.     }
  778.   return T;
  779. }
  780.  
  781.  
  782. /* Berkeley garbage collect stream
  783.  * Accepts: Mail stream
  784.  *        garbage collection flags
  785.  */
  786.  
  787. void bezerk_gc (stream,gcflags)
  788.     MAILSTREAM *stream;
  789.     long gcflags;
  790. {
  791.   /* nothing here for now */
  792. }
  793.  
  794. /* Internal routines */
  795.  
  796.  
  797. /* Berkeley mail abort stream
  798.  * Accepts: MAIL stream
  799.  */
  800.  
  801. void bezerk_abort (stream)
  802.     MAILSTREAM *stream;
  803. {
  804.   long i;
  805.   if (LOCAL) {            /* only if a file is open */
  806.     if (LOCAL->host) fs_give ((void **) &LOCAL->host);
  807.     if (LOCAL->ld) {        /* have a mailbox lock? */
  808.       flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
  809.       close (LOCAL->ld);    /* close the lock file */
  810.       unlink (LOCAL->lname);    /* and delete it */
  811.     }
  812.     fs_give ((void **) &LOCAL->lname);
  813.     if (LOCAL->msgs) {        /* free local cache */
  814.       for (i = 0; i < stream->nmsgs; ++i) fs_give ((void **) &LOCAL->msgs[i]);
  815.       fs_give ((void **) &LOCAL->msgs);
  816.     }
  817.                 /* free local text buffers */
  818.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  819.     if (LOCAL->fname) fs_give ((void **) &LOCAL->fname);
  820.                 /* nuke the local data */
  821.     fs_give ((void **) &stream->local);
  822.     stream->dtb = NIL;        /* log out the DTB */
  823.   }
  824. }
  825.  
  826.  
  827. /* Berkeley mail generate file string
  828.  * Accepts: temporary buffer to write into
  829.  *        mailbox name string
  830.  * Returns: local file string
  831.  */
  832.  
  833.  
  834. char *bezerk_file (dst,name)
  835.     char *dst;
  836.     char *name;
  837. {
  838.   struct passwd *pwd;
  839.   char *uid;
  840.   strcpy (dst,name);        /* copy the mailbox name */
  841.   if (dst[0] != '/') {        /* pass absolute file paths */
  842.                 /* get user ID and directory */
  843.     uid = (pwd = getpwuid (geteuid ()))->pw_name;
  844.     if (strcmp (ucase (dst),"INBOX")) sprintf (dst,"%s/%s",pwd->pw_dir,name);
  845.                 /* INBOX becomes mail spool directory file */
  846.     else sprintf (dst,MAILFILE,uid);
  847.   }
  848.   return dst;
  849. }
  850.  
  851. /* Berkeley open and lock mailbox
  852.  * Accepts: file name to open/lock
  853.  *        file open mode
  854.  *        destination buffer for lock file name
  855.  *        type of locking operation (LOCK_SH or LOCK_EX)
  856.  */
  857.  
  858. int bezerk_lock (file,flags,mode,lock,op)
  859.     char *file;
  860.     int flags;
  861.     int mode;
  862.     char *lock;
  863.     int op;
  864. {
  865.   int fd,ld,j;
  866.   int i = LOCKTIMEOUT * 60 - 1;
  867.   char tmp[MAILTMPLEN];
  868.   struct timeval tp;
  869.   struct stat sb;
  870.                 /* build lock filename */
  871.   strcat (bezerk_file (lock,file),".lock");
  872.   do {                /* until OK or out of tries */
  873.     gettimeofday (&tp,NIL);    /* get the time now */
  874. #if T
  875.   /* SUN-OS had an NFS, As kludgy as an albatross;
  876.    * And everywhere that it was installed, It was a total loss.  -- MRC 9/25/91
  877.    */
  878.                 /* build hitching post file name */
  879.     sprintf (tmp,"%s.%d.%d.",lock,time (0),getpid ());
  880.     j = strlen (tmp);        /* append local host name */
  881.     gethostname (tmp + j,(MAILTMPLEN - j) - 1);
  882.                 /* try to get hitching-post file */
  883.     if ((ld = open (tmp,O_WRONLY|O_CREAT|O_EXCL,0666)) < 0) {
  884.                 /* prot fail & non-ex, don't use lock files */
  885.       if ((errno == EACCES) && (stat (tmp,&sb))) *lock = '\0';
  886.       else {            /* otherwise something strange is going on */
  887.     sprintf (tmp,"Error creating %s: %s",lock,strerror (errno));
  888.     mm_log (tmp,WARN);    /* this is probably not good */
  889.                 /* don't use lock files if not one of these */
  890.     if ((errno != EEXIST) && (errno != EACCES)) *lock = '\0';
  891.       }
  892.     }
  893.     else {            /* got a hitching-post */
  894.       chmod (tmp,0666);        /* make sure others can break the lock */
  895.       close (ld);        /* close the hitching-post */
  896.       link (tmp,lock);        /* tie hitching-post to lock, ignore failure */
  897.       stat (tmp,&sb);        /* get its data */
  898.       unlink (tmp);        /* flush hitching post */
  899.       /* If link count .ne. 2, hitch failed.  Set ld to -1 as if open() failed
  900.      so we try again.  If extant lock file and time now is .gt. file time
  901.      plus timeout interval, flush the lock so can win next time around. */
  902.       if ((ld = (sb.st_nlink != 2) ? -1 : 0) && (!stat (lock,&sb)) &&
  903.       (tp.tv_sec > sb.st_ctime + LOCKTIMEOUT * 60)) unlink (lock);
  904.     }
  905.  
  906. #else
  907.   /* This works on modern Unix systems which are not afflicted with NFS mail.
  908.    * "Modern" means that O_EXCL works.  I think that NFS mail is a terrible
  909.    * idea -- that's what IMAP is for -- but some people insist upon losing...
  910.    */
  911.                 /* try to get the lock */
  912.     if ((ld = open (lock,O_WRONLY|O_CREAT|O_EXCL,0666)) < 0) switch (errno) {
  913.     case EEXIST:        /* if extant and old, grab it for ourselves */
  914.       if ((!stat (lock,&sb)) && tp.tv_sec > sb.st_ctime + LOCKTIMEOUT * 60)
  915.     ld = open (lock,O_WRONLY|O_CREAT,0666);
  916.       break;
  917.     case EACCES:        /* protection fail, ignore if non-ex or old */
  918.       if (stat (lock,&sb) || (tp.tv_sec > sb.st_ctime + LOCKTIMEOUT * 60))
  919.     *lock = '\0';        /* assume no world write mail spool dir */
  920.       break;
  921.     default:            /* some other failure */
  922.       sprintf (tmp,"Error creating %s: %s",lock,strerror (errno));
  923.       mm_log (tmp,WARN);    /* this is probably not good */
  924.       *lock = '\0';        /* don't use lock files */
  925.       break;
  926.     }
  927.     if (ld >= 0) {        /* if made a lock file */
  928.       chmod (tmp,0666);        /* make sure others can break the lock */
  929.       close (ld);        /* close the lock file */
  930.     }
  931. #endif
  932.     if ((ld < 0) && *lock) {    /* if failed to make lock file and retry OK */
  933.       if (!(i%15)) {
  934.     sprintf (tmp,"Mailbox %s is locked, will override in %d seconds...",
  935.          file,i);
  936.     mm_log (tmp,WARN);
  937.       }
  938.       sleep (1);        /* wait 1 second before next try */
  939.     }
  940.   } while (i-- && ld < 0 && *lock);
  941.                 /* open file */
  942.   if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
  943.   else {            /* open failed */
  944.     j = errno;            /* preserve error code */
  945.     if (*lock) unlink (lock);    /* flush the lock file if any */
  946.     errno = j;            /* restore error code */
  947.   }
  948.   return fd;
  949. }
  950.  
  951. /* Berkeley unlock and close mailbox
  952.  * Accepts: file descriptor
  953.  *        (optional) mailbox stream to check atime/mtime
  954.  *        (optional) lock file name
  955.  */
  956.  
  957. void bezerk_unlock (fd,stream,lock)
  958.     int fd;
  959.     MAILSTREAM *stream;
  960.     char *lock;
  961. {
  962.   struct stat sbuf;
  963.   struct timeval tp[2];
  964.   fstat (fd,&sbuf);        /* get file times */
  965.                 /* if stream and csh would think new mail */
  966.   if (stream && (sbuf.st_atime <= sbuf.st_mtime)) {
  967.     gettimeofday (&tp[0],NIL);    /* set atime to now */
  968.                 /* set mtime to (now - 1) if necessary */
  969.     tp[1].tv_sec = tp[0].tv_sec > sbuf.st_mtime ? sbuf.st_mtime :
  970.       tp[0].tv_sec - 1;
  971.     tp[1].tv_usec = 0;        /* oh well */
  972.                 /* set the times, note change */
  973.     if (!utimes (stream->mailbox,tp)) LOCAL->filetime = tp[1].tv_sec;
  974.   }
  975.   flock (fd,LOCK_UN);        /* release flock'ers */
  976.   close (fd);            /* close the file */
  977.                 /* flush the lock file if any */
  978.   if (lock && *lock) unlink (lock);
  979. }
  980.  
  981. /* Berkeley mail parse and lock mailbox
  982.  * Accepts: MAIL stream
  983.  *        space to write lock file name
  984.  * Returns: file descriptor if parse OK, mailbox is locked shared
  985.  *        -1 if failure, stream aborted
  986.  */
  987.  
  988. int bezerk_parse (stream,lock)
  989.     MAILSTREAM *stream;
  990.     char *lock;
  991. {
  992.   int fd;
  993.   long delta,i,j,is,is1;
  994.   char *s,*s1,*t,*e;
  995.   int rn,sysv;
  996.   int first = T;
  997.   long nmsgs = stream->nmsgs;
  998.   long recent = stream->recent;
  999.   struct stat sbuf;
  1000.   MESSAGECACHE *elt;
  1001.   FILECACHE *m = NIL,*n;
  1002.   mail_lock (stream);        /* guard against recursion or pingers */
  1003.                 /* open and lock mailbox (shared OK) */
  1004.   if ((fd = bezerk_lock (LOCAL->fname,LOCAL->ld ? O_RDWR : O_RDONLY,NIL,
  1005.              lock,LOCK_SH)) < 0) {
  1006.                 /* failed, OK for non-ex file? */
  1007.     if ((errno != ENOENT) || LOCAL->filesize) {
  1008.       sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno));
  1009.       mm_log (LOCAL->buf,ERROR);
  1010.       bezerk_abort (stream);
  1011.     }
  1012.     else {            /* this is to allow for non-ex INBOX */
  1013.       mail_exists (stream,0);    /* make sure upper level sees this as empty */
  1014.       mail_recent (stream,0);
  1015.     }
  1016.     mail_unlock (stream);
  1017.     return -1;
  1018.   }
  1019.   fstat (fd,&sbuf);        /* get status */
  1020.                 /* calculate change in size */
  1021.   if ((delta = sbuf.st_size - LOCAL->filesize) < 0) {
  1022.     sprintf (LOCAL->buf,"Mailbox shrank from %d to %d bytes, aborted",
  1023.          LOCAL->filesize,sbuf.st_size);
  1024.     mm_log (LOCAL->buf,ERROR);    /* this is pretty bad */
  1025.     bezerk_unlock (fd,stream,lock);
  1026.     bezerk_abort (stream);
  1027.     mail_unlock (stream);
  1028.     return -1;
  1029.   }
  1030.  
  1031.   else if (delta) {        /* get to that position in the file */
  1032.     lseek (fd,LOCAL->filesize,L_SET);
  1033.     s = s1 = LOCAL->buf;    /* initial read-in location */
  1034.     i = 0;            /* initial unparsed read-in count */
  1035.     do {
  1036.       i = min (CHUNK,delta);    /* calculate read-in size */
  1037.                 /* increase the read-in buffer if necessary */
  1038.       if ((j = i + (s1 - s)) >= LOCAL->buflen) {
  1039.     is = s - LOCAL->buf;    /* note former start of message position */
  1040.     is1 = s1 - LOCAL->buf;    /* and start of new data position */
  1041.     if (s1 - s) fs_resize ((void **) &LOCAL->buf,(LOCAL->buflen = j) + 1);
  1042.     else {            /* fs_resize would do an unnecessary copy */
  1043.       fs_give ((void **) &LOCAL->buf);
  1044.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = j) + 1);
  1045.     }
  1046.     s = LOCAL->buf + is;    /* new start of message */
  1047.     s1 = LOCAL->buf + is1;    /* new start of new data */
  1048.       }
  1049.       s1[i] = '\0';        /* tie off chunk */
  1050.       if (read (fd,s1,i) < 0) {    /* read a chunk of new text */
  1051.     sprintf (LOCAL->buf,"Error reading mail file: %s",strerror (errno));
  1052.     mm_log (LOCAL->buf,ERROR);
  1053.     bezerk_unlock (fd,stream,lock);
  1054.     bezerk_abort (stream);
  1055.     mail_unlock (stream);
  1056.     return -1;
  1057.       }
  1058.       delta -= i;        /* account for data read in */
  1059.                 /* validate newly-appended data */
  1060.       if (first) {        /* only do this first time! */
  1061.     if (!((*s =='F') && VALID)) {
  1062.       mm_log ("Mailbox format invalidated (consult an expert), aborted",
  1063.           ERROR);
  1064.       bezerk_unlock (fd,stream,lock);
  1065.       bezerk_abort (stream);
  1066.       mail_unlock (stream);
  1067.       return -1;
  1068.     }
  1069.     first = NIL;        /* don't do this again */
  1070.     LOCAL->dirty = T;    /* note stream is now dirty */
  1071.       }
  1072.  
  1073.                 /* found end of message or end of data? */
  1074.       while ((e = bezerk_eom (s,s1,i)) || !delta) {
  1075.     nmsgs++;        /* yes, have a new message */
  1076.     if (e) j = (e - s) - 1;    /* calculate message length */
  1077.     else j = strlen (s) - 1;/* otherwise is remainder of data */
  1078.     if (m) {        /* new cache needed, have previous data? */
  1079.       n->header = (char *) fs_get (sizeof (FILECACHE) + j);
  1080.       n = (FILECACHE *) n->header;
  1081.     }
  1082.     else m = n = (FILECACHE *) fs_get (sizeof (FILECACHE) + j);
  1083.                 /* copy message data */
  1084.     strncpy (n->internal,s,j);
  1085.     n->internal[j] = '\0';
  1086.     n->header = NIL;    /* initially no link */
  1087.     n->headersize = j;    /* stash away buffer length */
  1088.     if (e) {        /* saw end of message? */
  1089.       i -= e - s1;        /* new unparsed data count */
  1090.       s = s1 = e;        /* advance to new message */
  1091.     }
  1092.     else break;        /* else punt this loop */
  1093.       }
  1094.       if (delta) {        /* end of message not found? */
  1095.     s1 += i;        /* end of unparsed data */
  1096.     if (s != LOCAL->buf){    /* message doesn't begin at buffer? */
  1097.       i = s1 - s;        /* length of message so far */
  1098.       memmove (LOCAL->buf,s,i);
  1099.       s = LOCAL->buf;    /* message now starts at buffer origin */
  1100.       s1 = s + i;        /* calculate new end of unparsed data */
  1101.     }
  1102.       }
  1103.     } while (delta);        /* until nothing more new to read */
  1104.   }
  1105.   else {            /* no change, don't babble if never got time */
  1106.     if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
  1107.       mm_log ("New mailbox modification time but apparently no changes",WARN);
  1108.   }
  1109.   mail_cache (stream,nmsgs);    /* expand the primary cache */
  1110.   if (LOCAL->msgs) {        /* have a cache yet? */
  1111.     if (nmsgs >= LOCAL->cachesize)
  1112.       fs_resize ((void **) &LOCAL->msgs,
  1113.          (LOCAL->cachesize += CACHEINCREMENT) * sizeof (FILECACHE *));
  1114.   }
  1115.   else LOCAL->msgs = (FILECACHE **)
  1116.     fs_get ((LOCAL->cachesize = nmsgs + CACHEINCREMENT) *sizeof (FILECACHE *));
  1117.  
  1118.   for (i = stream->nmsgs, n = m; i < nmsgs; i++) {
  1119.     LOCAL->msgs[i] = m = n;    /* set cache, and next cache pointer */
  1120.     n = (FILECACHE *) n->header;
  1121.     /* This is a bugtrap for bogons in the new message cache, which may happen
  1122.      * if memory is corrupted.  Note that in the case of a totally empty
  1123.      * message, a newline is appended and counts adjusted.
  1124.      */
  1125.     if ((!((s = m->internal) && VALID)) &&
  1126.     !(s && !strchr (s,'\n') && strcat (s,"\n") && VALID &&
  1127.       m->headersize++)) fatal ("Bogus entry in new cache list");
  1128.  
  1129.     if (!((s = m->internal) && VALID)) fatal ("Bogus entry in new cache list");
  1130.  
  1131.     m->header = s = ++t;    /* pointer to message header */
  1132.     m->headersize -= m->header - m->internal;
  1133.     m->body = NIL;        /* assume no body as yet */
  1134.     m->bodysize = 0;
  1135.     recent++;            /* assume recent by default */
  1136.     (elt = mail_elt (stream,i+1))->recent = T;
  1137.                 /* calculate initial Status/X-Status lines */
  1138.     bezerk_update_status (m->status,elt);
  1139.     t += rn + sysv - 21;    /* calculate start of date */
  1140.                 /* generate plausable IMAPish date string */
  1141.     /* This used to be an sprintf(), but it turns out that on certain
  1142.        cretinous C library implementations (e.g. Dynix) sprintf() is
  1143.        horribly slow */
  1144.     LOCAL->buf[2] = LOCAL->buf[6] = LOCAL->buf[18] = '-';
  1145.     LOCAL->buf[9] = ' ';
  1146.     LOCAL->buf[12] = LOCAL->buf[15] = ':';
  1147.                 /* dd */
  1148.     LOCAL->buf[0] = t[4]; LOCAL->buf[1] = t[5];
  1149.                 /* mmm */
  1150.     LOCAL->buf[3] = t[0]; LOCAL->buf[4] = t[1]; LOCAL->buf[5] = t[2];
  1151.                 /* yy */
  1152.     LOCAL->buf[7] = m->header[-3]; LOCAL->buf[8] = m->header[-2];
  1153.                 /* hh */
  1154.     LOCAL->buf[10] = t[7]; LOCAL->buf[11] = t[8];
  1155.                 /* mm */
  1156.     LOCAL->buf[13] = t[10]; LOCAL->buf[14] = t[11];
  1157.                 /* ss */
  1158.     LOCAL->buf[16] = sysv ? '0':t[13]; LOCAL->buf[17] = sysv ? '0':t[14];
  1159.                 /* zzz */
  1160.     t = rn ? t + 16 - sysv : "LCL";
  1161.     LOCAL->buf[19] = *t++; LOCAL->buf[20] = *t++; LOCAL->buf[21] = *t;
  1162.     LOCAL->buf[22] = '\0';
  1163.                 /* set internal date */
  1164.     elt->internal_date = cpystr (LOCAL->buf);
  1165.                 /* as well as parsed value */
  1166.     m->date = bezerk_date (LOCAL->buf);
  1167.  
  1168.     is = 0;            /* initialize newline count */
  1169.     e = NIL;            /* no status stuff yet */
  1170.     do switch (*(t = s)) {    /* look at header lines */
  1171.     case 'X':            /* possible X-Status: line */
  1172.       if (s[1] == '-' && s[2] == 'S') s += 2;
  1173.       else {
  1174.     is++;            /* count another newline */
  1175.     break;            /* this is uninteresting after all */
  1176.       }
  1177.     case 'S':            /* possible Status: line */
  1178.       if (s[1] == 't' && s[2] == 'a' && s[3] == 't' && s[4] == 'u' &&
  1179.       s[5] == 's' && s[6] == ':') {
  1180.     if (!e) e = t;        /* note deletion point */
  1181.     s += 6;            /* advance to status flags */
  1182.     do switch (*s++) {    /* parse flags */
  1183.     case 'R':        /* message read */
  1184.       elt->seen = T;
  1185.       break;
  1186.     case 'O':        /* message old */
  1187.       if (elt->recent) {    /* don't do this more than once! */
  1188.         elt->recent = NIL;
  1189.         recent--;        /* not recent any longer... */
  1190.       }
  1191.       break;
  1192.     case 'D':        /* message deleted */
  1193.       elt->deleted = T;
  1194.       break;
  1195.     case 'F':        /* message flagged */
  1196.       elt ->flagged = T;
  1197.       break;
  1198.     case 'A':        /* message answered */
  1199.       elt ->answered = T;
  1200.       break;
  1201.     default:        /* some other crap */
  1202.       break;
  1203.     } while (*s && *s != '\n');
  1204.                 /* recalculate Status/X-Status lines */
  1205.     bezerk_update_status (m->status,elt);
  1206.       }
  1207.       else is++;        /* otherwise random line */
  1208.       break;
  1209.  
  1210.     case '\n':            /* end of header */
  1211.       m->body = ++s;        /* start of body is here */
  1212.       j = m->body - m->header;    /* new header size */
  1213.                 /* calculate body size */
  1214.       m->bodysize = m->headersize - j;
  1215.       if (e) {            /* saw status poop? */
  1216.     *e++ = '\n';        /* patch in trailing newline */
  1217.     m->headersize = e - m->header;
  1218.       }
  1219.       else m->headersize = j;    /* set header size */
  1220.       s = NIL;            /* don't scan any further */
  1221.       is++;            /* count a newline */
  1222.       break;
  1223.     case '\0':            /* end of message */
  1224.       if (e) {            /* saw status poop? */
  1225.     *e++ = '\n';        /* patch in trailing newline */
  1226.     m->headersize = e - m->header;
  1227.       }
  1228.       is++;            /* count an extra newline here */
  1229.       break;
  1230.     default:            /* anything else is uninteresting */
  1231.       if (e) {            /* have status stuff to worry about? */
  1232.     j = s - e;        /* yuck!!  calculate size of delete area */
  1233.                 /* blat remaining number of bytes down */
  1234.     memmove (e,s,m->header + m->headersize - s);
  1235.     m->headersize -= j;    /* update for new size */
  1236.     e = NIL;        /* no more delete area */
  1237.                 /* tie off old cruft */
  1238.     *(m->header + m->headersize) = '\0';
  1239.       }
  1240.       is++;
  1241.       break;
  1242.     } while (s && (s = strchr (s,'\n')) && s++);
  1243.                 /* count newlines in body */
  1244.     if (s = m->body) while (*s) if (*s++ == '\n') is++;
  1245.     elt->rfc822_size = m->headersize + m->bodysize + is;
  1246.   }
  1247.   if (n) fatal ("Cache link-list inconsistency");
  1248.   while (i < LOCAL->cachesize) LOCAL->msgs[i++] = NIL;
  1249.                 /* update parsed file size and time */
  1250.   LOCAL->filesize = sbuf.st_size;
  1251.   LOCAL->filetime = sbuf.st_mtime;
  1252.   mail_exists (stream,nmsgs);    /* notify upper level of new mailbox size */
  1253.   mail_recent (stream,recent);    /* and of change in recent messages */
  1254.   return fd;            /* return the winnage */
  1255. }
  1256.  
  1257. /* Berkeley search for end of message
  1258.  * Accepts: start of message
  1259.  *        start of new data
  1260.  *        size of new data
  1261.  * Returns: pointer to start of new message if one found
  1262.  */
  1263.  
  1264. #define Word unsigned long
  1265.  
  1266. char *bezerk_eom (som,sod,i)
  1267.     char *som;
  1268.     char *sod;
  1269.     long i;
  1270. {
  1271.   char *s = sod;        /* find start of line in current message */
  1272.   char *t;
  1273.   int rn,sysv;
  1274.   union {
  1275.     unsigned long wd;
  1276.     char ch[9];
  1277.   } wdtest;
  1278.   strcpy (wdtest.ch,"AAAA1234");/* constant for word testing */
  1279.   while ((s > som) && *s-- != '\n');
  1280.   if (wdtest.wd != 0x41414141) {/* not a 32-bit word machine? */
  1281.     while (s = strstr (s,"\nFrom ")) if (s++ && VALID) return s;
  1282.   }
  1283.   else {            /* can do it faster this way */
  1284.     register Word m = 0x0a0a0a0a;
  1285.     while ((long) s & 3)    /* any characters before word boundary? */
  1286.       if ((*s++ == '\n') && s[0] == 'F' && VALID) return s;
  1287.     i = (sod + i) - s;        /* total number of tries */
  1288.     do {            /* fast search for newline */
  1289.       if (0x80808080 & (0x01010101 + (0x7f7f7f7f & ~(m ^ *(Word *) s)))) {
  1290.                 /* interesting word, check it closer */
  1291.     if (*s++ == '\n' && s[0] == 'F' && VALID) return s;
  1292.     else if (*s++ == '\n' && s[0] == 'F' && VALID) return s;
  1293.     else if (*s++ == '\n' && s[0] == 'F' && VALID) return s;
  1294.     else if (*s++ == '\n' && s[0] == 'F' && VALID) return s;
  1295.       }
  1296.       else s += 4;        /* try next word */
  1297.       i -= 4;            /* count a word checked */
  1298.     } while (i > 24);        /* continue until end of plausible string */
  1299.   }
  1300.   return NIL;
  1301. }
  1302.  
  1303. /* Berkeley extend mailbox to reserve worst-case space for expansion
  1304.  * Accepts: MAIL stream
  1305.  *        file descriptor
  1306.  *        error string
  1307.  * Returns: T if extend OK and have gone critical, NIL if should abort
  1308.  */
  1309.  
  1310. int bezerk_extend (stream,fd,error)
  1311.     MAILSTREAM *stream;
  1312.     int fd;
  1313.     char *error;
  1314. {
  1315.   struct stat sbuf;
  1316.   FILECACHE *m;
  1317.   char tmp[MAILTMPLEN];
  1318.   int i,ok;
  1319.   long f;
  1320.   char *s;
  1321.   int retry;
  1322.                 /* calculate estimated size of mailbox */
  1323.   for (i = 0,f = 0; i < stream->nmsgs; i++) {
  1324.     m = LOCAL->msgs[i];        /* get cache pointer */
  1325.     f += (m->header - m->internal) + m->headersize + sizeof (STATUS) +
  1326.       m->bodysize + 1;        /* update guesstimate */
  1327.     }
  1328.   mm_critical (stream);        /* go critical */
  1329.                 /* return now if file large enough */
  1330.   if (f <= LOCAL->filesize) return T;
  1331.   s = (char *) fs_get (f -= LOCAL->filesize);
  1332.   memset (s,0,f);        /* get a block of nulls */
  1333.                 /* get to end of file */
  1334.   lseek (fd,LOCAL->filesize,L_SET);
  1335.   do {
  1336.     retry = NIL;        /* no retry yet */
  1337.     if (!(ok = (write (fd,s,f) >= 0))) {
  1338.       i = errno;        /* note error before doing ftruncate */
  1339.                 /* restore prior file size */
  1340.       ftruncate (fd,LOCAL->filesize);
  1341.       fsync (fd);        /* is this necessary? */
  1342.       fstat (fd,&sbuf);        /* now get updated file time */
  1343.       LOCAL->filetime = sbuf.st_mtime;
  1344.                 /* punt if that's what main program wants */
  1345.       if (mm_diskerror (stream,i,NIL)) {
  1346.     mm_nocritical (stream);    /* exit critical */
  1347.     sprintf (tmp,"%s: %s",error,strerror (i));
  1348.     mm_notify (stream,tmp,WARN);
  1349.       }
  1350.       else retry = T;        /* set to retry */
  1351.     }
  1352.   } while (retry);        /* repeat if need to try again */
  1353.   fs_give ((void **) &s);    /* flush buffer of nulls */
  1354.   return ok;            /* return status */
  1355. }
  1356.  
  1357. /* Berkeley save mailbox
  1358.  * Accepts: MAIL stream
  1359.  *        mailbox file descriptor
  1360.  *
  1361.  * Mailbox must be readwrite and locked for exclusive access.
  1362.  */
  1363.  
  1364. void bezerk_save (stream,fd)
  1365.     MAILSTREAM *stream;
  1366.     int fd;
  1367. {
  1368.   struct stat sbuf;
  1369.   struct iovec iov[16];
  1370.   int iovc;
  1371.   long i;
  1372.   int e;
  1373.   int retry;
  1374.   do {                /* restart point if failure */
  1375.     retry = NIL;        /* no need to retry yet */
  1376.                 /* start at beginning of file */
  1377.     lseek (fd,LOCAL->filesize = 0,L_SET);
  1378.                 /* loop through all messages */
  1379.     for (i = 1,iovc = 0; i <= stream->nmsgs; i++) {
  1380.                 /* set up iov's for this message */
  1381.       bezerk_write_message (iov,&iovc,LOCAL->msgs[i-1]);
  1382.                 /* filled up iovec or end of messages? */
  1383.       if ((iovc == 16) || (i == stream->nmsgs)) {
  1384.                 /* write messages */
  1385.     if ((e = writev (fd,iov,iovc)) < 0) {
  1386.       sprintf (LOCAL->buf,"Unable to rewrite mailbox: %s",
  1387.            strerror (e = errno));
  1388.       mm_log (LOCAL->buf,WARN);
  1389.       mm_diskerror (stream,e,T);
  1390.       retry = T;        /* must retry */
  1391.       break;        /* abort this particular try */
  1392.     }
  1393.     else {            /* won */
  1394.       iovc = 0;        /* restart iovec */
  1395.       LOCAL->filesize += e;    /* count these bytes in data */
  1396.     }
  1397.       }
  1398.     }
  1399.   } while (retry);        /* repeat if need to try again */
  1400.   fsync (fd);            /* make sure the disk has the update */
  1401.                 /* nuke any cruft after that */
  1402.   ftruncate (fd,LOCAL->filesize);
  1403.   fsync (fd);            /* is this necessary? */
  1404.   fstat (fd,&sbuf);        /* now get updated file time */
  1405.   LOCAL->filetime = sbuf.st_mtime;
  1406.   LOCAL->dirty = NIL;        /* stream no longer dirty */
  1407.   mm_nocritical (stream);    /* exit critical */
  1408. }
  1409.  
  1410. /* Berkeley copy messages
  1411.  * Accepts: MAIL stream
  1412.  *        mailbox name
  1413.  * Returns: T if copy successful else NIL
  1414.  */
  1415.  
  1416. int bezerk_copy_messages (stream,mailbox)
  1417.     MAILSTREAM *stream;
  1418.     char *mailbox;
  1419. {
  1420.   char file[MAILTMPLEN];
  1421.   char lock[MAILTMPLEN];
  1422.   struct iovec iov[16];
  1423.   int iovc;
  1424.   struct stat sbuf;
  1425.   long i;
  1426.   int ok = T;
  1427.   int fd = bezerk_lock (bezerk_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT,
  1428.             S_IREAD|S_IWRITE,lock,LOCK_EX);
  1429.   if (fd < 0) {            /* got file? */
  1430.     sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
  1431.     mm_log (LOCAL->buf,ERROR);
  1432.     return NIL;
  1433.   }
  1434.   mm_critical (stream);        /* go critical */
  1435.   fstat (fd,&sbuf);        /* get current file size */
  1436.                 /* write all requested messages to mailbox */
  1437.   for (i = 1,iovc = 0; ok && i <= stream->nmsgs; i++) {
  1438.                 /* set up iov's if message selected */
  1439.     if (mail_elt (stream,i)->sequence)
  1440.       bezerk_write_message (iov,&iovc,LOCAL->msgs[i - 1]);
  1441.                 /* filled up iovec or end of messages? */
  1442.     if (iovc && ((iovc == 16) || (i == stream->nmsgs))) {
  1443.       if (ok = (writev (fd,iov,iovc) >= 0)) iovc = 0;
  1444.       else {
  1445.     sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
  1446.     mm_log (LOCAL->buf,ERROR);
  1447.     ftruncate (fd,sbuf.st_size);
  1448.     break;
  1449.       }
  1450.     }
  1451.   }
  1452.   fsync (fd);            /* force out the update */
  1453.   bezerk_unlock (fd,NIL,lock);    /* unlock and close mailbox */
  1454.   mm_nocritical (stream);    /* release critical */
  1455.   return ok;            /* return whether or not succeeded */
  1456. }
  1457.  
  1458. /* Berkeley write message to mailbox
  1459.  * Accepts: I/O vector
  1460.  *        I/O vector index
  1461.  *        local cache for this message
  1462.  *
  1463.  * This routine is the reason why the local cache has a copy of the status.
  1464.  * We can be called to dump out the mailbox as part of a stream recycle, since
  1465.  * we don't write out the mailbox when flags change and hence an update may be
  1466.  * needed.  However, at this point the elt has already become history, so we
  1467.  * can't use any information other than what is local to us.
  1468.  */
  1469.  
  1470. void bezerk_write_message (iov,i,m)
  1471.     struct iovec iov[];
  1472.     int *i;
  1473.     FILECACHE *m;
  1474. {
  1475.   iov[*i].iov_base =m->internal;/* pointer/counter to headers */
  1476.                 /* length of internal + message headers */
  1477.   iov[*i].iov_len = (m->header + m->headersize) - m->internal;
  1478.                 /* suppress extra newline if present */
  1479.   if ((iov[*i].iov_base)[iov[*i].iov_len - 2] == '\n') iov[(*i)++].iov_len--;
  1480.   else (*i)++;            /* unlikely but... */
  1481.   iov[*i].iov_base = m->status;    /* pointer/counter to status */
  1482.   iov[(*i)++].iov_len = strlen (m->status);
  1483.   iov[*i].iov_base = m->body;    /* pointer/counter to text body */
  1484.   iov[(*i)++].iov_len = m->bodysize;
  1485.   iov[*i].iov_base = "\n";    /* pointer/counter to extra newline */
  1486.   iov[(*i)++].iov_len = 1;
  1487. }
  1488.  
  1489. /* Berkeley update status string
  1490.  * Accepts: destination string to write
  1491.  *        message cache entry
  1492.  */
  1493.  
  1494. void bezerk_update_status (status,elt)
  1495.     char *status;
  1496.     MESSAGECACHE *elt;
  1497. {
  1498.   /* This used to be an sprintf(), but thanks to certain cretinous C libraries
  1499.      with horribly slow implementations of sprintf() I had to change it to this
  1500.      mess.  At least it should be fast. */
  1501.   char *t = status + 8;
  1502.   status[0] = 'S'; status[1] = 't'; status[2] = 'a'; status[3] = 't';
  1503.   status[4] = 'u'; status[5] = 's'; status[6] = ':';  status[7] = ' ';
  1504.   if (elt->seen) *t++ = 'R'; *t++ = 'O'; *t++ = '\n';
  1505.   *t++ = 'X'; *t++ = '-'; *t++ = 'S'; *t++ = 't'; *t++ = 'a'; *t++ = 't';
  1506.   *t++ = 'u'; *t++ = 's'; *t++ = ':'; *t++ = ' ';
  1507.   if (elt->deleted) *t++ = 'D'; if (elt->flagged) *t++ = 'F';
  1508.   if (elt->answered) *t++ = 'A';
  1509.   *t++ = '\n'; *t++ = '\n'; *t++ = '\0';
  1510. }
  1511.  
  1512. /* Parse flag list
  1513.  * Accepts: MAIL stream
  1514.  *        flag list as a character string
  1515.  * Returns: flag command list
  1516.  */
  1517.  
  1518.  
  1519. short bezerk_getflags (stream,flag)
  1520.     MAILSTREAM *stream;
  1521.     char *flag;
  1522. {
  1523.   char *t;
  1524.   short f = 0;
  1525.   short i,j;
  1526.   if (flag && *flag) {        /* no-op if no flag string */
  1527.                 /* check if a list and make sure valid */
  1528.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1529.       mm_log ("Bad flag list",ERROR);
  1530.       return NIL;
  1531.     }
  1532.                 /* copy the flag string w/o list construct */
  1533.     strncpy (LOCAL->buf,flag+i,(j = strlen (flag) - (2*i)));
  1534.     LOCAL->buf[j] = '\0';
  1535.     t = ucase (LOCAL->buf);    /* uppercase only from now on */
  1536.  
  1537.     while (*t) {        /* parse the flags */
  1538.       if (*t == '\\') {        /* system flag? */
  1539.     switch (*++t) {        /* dispatch based on first character */
  1540.     case 'S':        /* possible \Seen flag */
  1541.       if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  1542.       t += 4;        /* skip past flag name */
  1543.       break;
  1544.     case 'D':        /* possible \Deleted flag */
  1545.       if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1546.           t[5] == 'E' && t[6] == 'D') i = fDELETED;
  1547.       t += 7;        /* skip past flag name */
  1548.       break;
  1549.     case 'F':        /* possible \Flagged flag */
  1550.       if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1551.           t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  1552.       t += 7;        /* skip past flag name */
  1553.       break;
  1554.     case 'A':        /* possible \Answered flag */
  1555.       if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1556.           t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  1557.       t += 8;        /* skip past flag name */
  1558.       break;
  1559.     default:        /* unknown */
  1560.       i = 0;
  1561.       break;
  1562.     }
  1563.                 /* add flag to flags list */
  1564.     if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  1565.     else {            /* bitch about bogus flag */
  1566.       mm_log ("Unknown system flag",ERROR);
  1567.       return NIL;
  1568.     }
  1569.       }
  1570.       else {            /* no user flags yet */
  1571.     mm_log ("Unknown flag",ERROR);
  1572.     return NIL;
  1573.       }
  1574.     }
  1575.   }
  1576.   return f;
  1577. }
  1578.  
  1579. /* Search support routines
  1580.  * Accepts: MAIL stream
  1581.  *        message number
  1582.  *        pointer to additional data
  1583.  * Returns: T if search matches, else NIL
  1584.  */
  1585.  
  1586.  
  1587. char bezerk_search_all (stream,msgno,d,n)
  1588.     MAILSTREAM *stream;
  1589.     long msgno;
  1590.     char *d;
  1591.     long n;
  1592. {
  1593.   return T;            /* ALL always succeeds */
  1594. }
  1595.  
  1596.  
  1597. char bezerk_search_answered (stream,msgno,d,n)
  1598.     MAILSTREAM *stream;
  1599.     long msgno;
  1600.     char *d;
  1601.     long n;
  1602. {
  1603.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1604. }
  1605.  
  1606.  
  1607. char bezerk_search_deleted (stream,msgno,d,n)
  1608.     MAILSTREAM *stream;
  1609.     long msgno;
  1610.     char *d;
  1611.     long n;
  1612. {
  1613.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1614. }
  1615.  
  1616.  
  1617. char bezerk_search_flagged (stream,msgno,d,n)
  1618.     MAILSTREAM *stream;
  1619.     long msgno;
  1620.     char *d;
  1621.     long n;
  1622. {
  1623.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1624. }
  1625.  
  1626.  
  1627. char bezerk_search_keyword (stream,msgno,d,n)
  1628.     MAILSTREAM *stream;
  1629.     long msgno;
  1630.     char *d;
  1631.     long n;
  1632. {
  1633.   return NIL;            /* keywords not supported yet */
  1634. }
  1635.  
  1636.  
  1637. char bezerk_search_new (stream,msgno,d,n)
  1638.     MAILSTREAM *stream;
  1639.     long msgno;
  1640.     char *d;
  1641.     long n;
  1642. {
  1643.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1644.   return (elt->recent && !elt->seen) ? T : NIL;
  1645. }
  1646.  
  1647. char bezerk_search_old (stream,msgno,d,n)
  1648.     MAILSTREAM *stream;
  1649.     long msgno;
  1650.     char *d;
  1651.     long n;
  1652. {
  1653.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1654. }
  1655.  
  1656.  
  1657. char bezerk_search_recent (stream,msgno,d,n)
  1658.     MAILSTREAM *stream;
  1659.     long msgno;
  1660.     char *d;
  1661.     long n;
  1662. {
  1663.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1664. }
  1665.  
  1666.  
  1667. char bezerk_search_seen (stream,msgno,d,n)
  1668.     MAILSTREAM *stream;
  1669.     long msgno;
  1670.     char *d;
  1671.     long n;
  1672. {
  1673.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1674. }
  1675.  
  1676.  
  1677. char bezerk_search_unanswered (stream,msgno,d,n)
  1678.     MAILSTREAM *stream;
  1679.     long msgno;
  1680.     char *d;
  1681.     long n;
  1682. {
  1683.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1684. }
  1685.  
  1686.  
  1687. char bezerk_search_undeleted (stream,msgno,d,n)
  1688.     MAILSTREAM *stream;
  1689.     long msgno;
  1690.     char *d;
  1691.     long n;
  1692. {
  1693.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1694. }
  1695.  
  1696.  
  1697. char bezerk_search_unflagged (stream,msgno,d,n)
  1698.     MAILSTREAM *stream;
  1699.     long msgno;
  1700.     char *d;
  1701.     long n;
  1702. {
  1703.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1704. }
  1705.  
  1706.  
  1707. char bezerk_search_unkeyword (stream,msgno,d,n)
  1708.     MAILSTREAM *stream;
  1709.     long msgno;
  1710.     char *d;
  1711.     long n;
  1712. {
  1713.   return T;            /* keywords not supported yet */
  1714. }
  1715.  
  1716.  
  1717. char bezerk_search_unseen (stream,msgno,d,n)
  1718.     MAILSTREAM *stream;
  1719.     long msgno;
  1720.     char *d;
  1721.     long n;
  1722. {
  1723.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1724. }
  1725.  
  1726. char bezerk_search_before (stream,msgno,d,n)
  1727.     MAILSTREAM *stream;
  1728.     long msgno;
  1729.     char *d;
  1730.     long n;
  1731. {
  1732.   return (char) (LOCAL->msgs[msgno - 1]->date < n);
  1733. }
  1734.  
  1735.  
  1736. char bezerk_search_on (stream,msgno,d,n)
  1737.     MAILSTREAM *stream;
  1738.     long msgno;
  1739.     char *d;
  1740.     long n;
  1741. {
  1742.   return (char) (LOCAL->msgs[msgno - 1]->date == n);
  1743. }
  1744.  
  1745.  
  1746. char bezerk_search_since (stream,msgno,d,n)
  1747.     MAILSTREAM *stream;
  1748.     long msgno;
  1749.     char *d;
  1750.     long n;
  1751. {
  1752.                 /* everybody interprets "since" as .GE. */
  1753.   return (char) (LOCAL->msgs[msgno - 1]->date >= n);
  1754. }
  1755.  
  1756.  
  1757. char bezerk_search_body (stream,msgno,d,n)
  1758.     MAILSTREAM *stream;
  1759.     long msgno;
  1760.     char *d;
  1761.     long n;
  1762. {
  1763.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  1764.   return search (m->body,m->bodysize,d,n);
  1765. }
  1766.  
  1767.  
  1768. char bezerk_search_subject (stream,msgno,d,n)
  1769.     MAILSTREAM *stream;
  1770.     long msgno;
  1771.     char *d;
  1772.     long n;
  1773. {
  1774.   char *s = bezerk_fetchenvelope (stream,msgno)->subject;
  1775.   return s ? search (s,strlen (s),d,n) : NIL;
  1776. }
  1777.  
  1778.  
  1779. char bezerk_search_text (stream,msgno,d,n)
  1780.     MAILSTREAM *stream;
  1781.     long msgno;
  1782.     char *d;
  1783.     long n;
  1784. {
  1785.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  1786.   return search (m->header,m->headersize,d,n) ||
  1787.     bezerk_search_body (stream,msgno,d,n);
  1788. }
  1789.  
  1790. char bezerk_search_bcc (stream,msgno,d,n)
  1791.     MAILSTREAM *stream;
  1792.     long msgno;
  1793.     char *d;
  1794.     long n;
  1795. {
  1796.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1797.                 /* get text for address */
  1798.   rfc822_write_address (LOCAL->buf,bezerk_fetchenvelope (stream,msgno)->bcc);
  1799.   return search (LOCAL->buf,strlen (LOCAL->buf),d,n);
  1800. }
  1801.  
  1802.  
  1803. char bezerk_search_cc (stream,msgno,d,n)
  1804.     MAILSTREAM *stream;
  1805.     long msgno;
  1806.     char *d;
  1807.     long n;
  1808. {
  1809.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1810.                 /* get text for address */
  1811.   rfc822_write_address (LOCAL->buf,bezerk_fetchenvelope (stream,msgno)->cc);
  1812.   return search (LOCAL->buf,strlen (LOCAL->buf),d,n);
  1813. }
  1814.  
  1815.  
  1816. char bezerk_search_from (stream,msgno,d,n)
  1817.     MAILSTREAM *stream;
  1818.     long msgno;
  1819.     char *d;
  1820.     long n;
  1821. {
  1822.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1823.                 /* get text for address */
  1824.   rfc822_write_address (LOCAL->buf,bezerk_fetchenvelope (stream,msgno)->from);
  1825.   return search (LOCAL->buf,strlen (LOCAL->buf),d,n);
  1826. }
  1827.  
  1828.  
  1829. char bezerk_search_to (stream,msgno,d,n)
  1830.     MAILSTREAM *stream;
  1831.     long msgno;
  1832.     char *d;
  1833.     long n;
  1834. {
  1835.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1836.                 /* get text for address */
  1837.   rfc822_write_address (LOCAL->buf,bezerk_fetchenvelope (stream,msgno)->to);
  1838.   return search (LOCAL->buf,strlen (LOCAL->buf),d,n);
  1839. }
  1840.  
  1841. /* Search parsers */
  1842.  
  1843.  
  1844. /* Parse a date
  1845.  * Accepts: function to return
  1846.  *        pointer to date integer to return
  1847.  * Returns: function to return
  1848.  */
  1849.  
  1850. search_t bezerk_search_date (f,n)
  1851.     search_t f;
  1852.     long *n;
  1853. {
  1854.   long i;
  1855.   char *s;
  1856.                 /* parse the date and return fn if OK */
  1857.   return (bezerk_search_string (f,&s,&i) && (*n = bezerk_date (s))) ? f : NIL;
  1858. }
  1859.  
  1860.  
  1861. /* Actual date parser (routine)
  1862.     minimal and stupid routine;
  1863.  * Accepts: date to parse
  1864.  * Returns: date integer
  1865.  */
  1866.  
  1867. long bezerk_date (s)
  1868.     char *s;
  1869. {
  1870.   long ms;
  1871.   long d,m,y;
  1872.                 /* parse first number (probable month) */
  1873.   if (!(s && (m = strtol ((const char *) s,&s,10)))) return NIL;
  1874.   switch (*s) {            /* different parse based on delimiter */
  1875.   case '/':            /* mm/dd/yy format */
  1876.                 /* parse remainder of date */
  1877.     if (!((d = strtol ((const char *) ++s,&s,10)) && *s == '/' &&
  1878.       (y = strtol ((const char *) ++s,&s,10)) && *s == '\0'))
  1879.       return NIL;
  1880.     break;
  1881.  
  1882.   case '-':            /* dd-mmm-yy format */
  1883.     d = m;            /* so the number we got is a day */
  1884.                 /* make sure string is UC and long enough! */
  1885.     if (strlen (ucase (s)) < 5) return NIL;
  1886.                 /* slurp up the month string */
  1887.     ms = (((long) s[1]) << 16) + (((long) s[2]) << 8) + s[3];
  1888.     switch (ms) {        /* determine the month */
  1889.     case ('J' << 16) + ('A' << 8) + 'N':
  1890.       m = 1; break;
  1891.     case ('F' << 16) + ('E' << 8) + 'B':
  1892.       m = 2; break;
  1893.     case ('M' << 16) + ('A' << 8) + 'R':
  1894.       m = 3; break;
  1895.     case ('A' << 16) + ('P' << 8) + 'R':
  1896.       m = 4; break;
  1897.     case ('M' << 16) + ('A' << 8) + 'Y':
  1898.       m = 5; break;
  1899.     case ('J' << 16) + ('U' << 8) + 'N':
  1900.       m = 6; break;
  1901.     case ('J' << 16) + ('U' << 8) + 'L':
  1902.       m = 7; break;
  1903.     case ('A' << 16) + ('U' << 8) + 'G':
  1904.       m = 8; break;
  1905.     case ('S' << 16) + ('E' << 8) + 'P':
  1906.       m = 9; break;
  1907.     case ('O' << 16) + ('C' << 8) + 'T':
  1908.       m = 10; break;
  1909.     case ('N' << 16) + ('O' << 8) + 'V':
  1910.       m = 11; break;
  1911.     case ('D' << 16) + ('E' << 8) + 'C':
  1912.       m = 12; break;
  1913.     default:
  1914.       return NIL;
  1915.     }
  1916.                 /* parse the year */
  1917.     if (s[4] == '-' && (y = (int) strtol ((const char *) s+5,&s,10)) &&
  1918.     (*s == '\0' || *s == ' ')) break;
  1919.   default:            /* unknown format */
  1920.     return NIL;
  1921.   }
  1922.   y -= (y >= 1900) ? 1970 : 70;    /* the world began for Unix in 1970 */
  1923.                 /* minimal validity check of date */
  1924.   if (d < 1 || d > 31 || m < 1 || m > 12 || y < 0 || y >= 100) return NIL;
  1925.   return (y*12+m-1)*31+d-1;    /* calculate and return date value */
  1926. }
  1927.  
  1928. /* Parse a flag
  1929.  * Accepts: function to return
  1930.  *        pointer to string to return
  1931.  * Returns: function to return
  1932.  */
  1933.  
  1934. search_t bezerk_search_flag (f,d)
  1935.     search_t f;
  1936.     char **d;
  1937. {
  1938.                 /* get a keyword, return if OK */
  1939.   return (*d = strtok (NIL," ")) ? f : NIL;
  1940. }
  1941.  
  1942.  
  1943. /* Parse a string
  1944.  * Accepts: function to return
  1945.  *        pointer to string to return
  1946.  *        pointer to string length to return
  1947.  * Returns: function to return
  1948.  */
  1949.  
  1950.  
  1951. search_t bezerk_search_string (f,d,n)
  1952.     search_t f;
  1953.     char **d;
  1954.     long *n;
  1955. {
  1956.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1957.   if (c) {            /* better be an argument */
  1958.     switch (*c) {        /* see what the argument is */
  1959.     case '\0':            /* catch bogons */
  1960.     case ' ':
  1961.       return NIL;
  1962.     case '"':            /* quoted string */
  1963.       if (!(strchr (c+1,'"') && (*d = strtok (c,"\"")) && (*n = strlen (*d))))
  1964.     return NIL;
  1965.       break;
  1966.     case '{':            /* literal string */
  1967.       *n = strtol (c+1,&c,10);    /* get its length */
  1968.       if (*c++ != '}' || *c++ != '\015' || *c++ != '\012' ||
  1969.       *n > strlen (*d = c)) return NIL;
  1970.       c[*n] = '\255';        /* write new delimiter */
  1971.       strtok (c,"\255");    /* reset the strtok mechanism */
  1972.       break;
  1973.     default:            /* atomic string */
  1974.       *n = strlen (*d = strtok (c," "));
  1975.       break;
  1976.     }
  1977.     return f;
  1978.   }
  1979.   else return NIL;
  1980. }
  1981.