home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / pine / c-client / mail.c < prev    next >
C/C++ Source or Header  |  1994-02-20  |  50KB  |  1,740 lines

  1. /*
  2.  * Program:    Mailbox Access 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 November 1989
  13.  * Last Edited:    21 October 1993
  14.  *
  15.  * Copyright 1993 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <ctype.h>
  38. #include <stdio.h>
  39. /* VMS - the following two lines were transposed */
  40. #include "osdep.h"
  41. #include "mail.h"
  42. #include <time.h>
  43. #include "misc.h"
  44.  
  45.  
  46. /* c-client global data */
  47.  
  48. DRIVER *maildrivers = NIL;    /* list of mail drivers */
  49. DRIVER *inboxdriver = NIL;    /* INBOX mail driver */
  50. char *lhostn = NIL;        /* local host name */
  51. mailgets_t mailgets = NIL;    /* pointer to alternate gets function */
  52.                 /* mail cache manipulation function */
  53. mailcache_t mailcache = mm_cache;
  54.  
  55. /* Default limited get string
  56.  * Accepts: readin function pointer
  57.  *        stream to use
  58.  *        number of bytes
  59.  * Returns: string read in, truncated if necessary
  60.  *
  61.  * This is a sample mailgets routine.  It simply truncates any data larger
  62.  * than MAXMESSAGESIZE.  On most systems, you generally don't use a mailgets
  63.  * routine at all, but on some systems (e.g. DOS) it's required to prevent the
  64.  * application from crashing.  This one is filled in by the os driver for any
  65.  * OS that requires a mailgets routine and the main program has not already
  66.  * supplied one, generally in tcp_open().
  67.  */
  68.  
  69. char *mm_gets (f,stream,size)
  70.     readfn_t f;
  71.     void *stream;
  72.     unsigned long size;
  73. {
  74.   char *s;
  75.   char tmp[MAILTMPLEN+1];
  76.   unsigned long i,j = 0;
  77.                 /* truncate? */
  78.   if (i = (size > MAXMESSAGESIZE)) {
  79.     sprintf (tmp,"%ld character literal truncated to %ld characters",
  80.          size,MAXMESSAGESIZE);
  81.     mm_log (tmp,WARN);        /* warn user */
  82.     i = size - MAXMESSAGESIZE;    /* number of bytes of slop */
  83.     size = MAXMESSAGESIZE;    /* maximum length string we can read */
  84.   }
  85.   s = (char *) fs_get (size + 1);
  86.   *s = s[size] = '\0';        /* init in case getbuffer fails */
  87.   (*f) (stream,size,s);        /* get the literal */
  88.                 /* toss out everything after that */
  89.   while (i -= j) (*f) (stream,j = min ((long) MAILTMPLEN,i),tmp);
  90.   return s;
  91. }
  92.  
  93. /* Default mail cache handler
  94.  * Accepts: pointer to cache handle
  95.  *        message number
  96.  *        caching function
  97.  * Returns: cache data
  98.  */
  99.  
  100. void *mm_cache (stream,msgno,op)
  101.     MAILSTREAM *stream;
  102.     long msgno;
  103.     long op;
  104. {
  105.   size_t new;
  106.   void *ret = NIL;
  107.   long i = msgno - 1;
  108.   unsigned long j = stream->cachesize;
  109.   switch ((int) op) {        /* what function? */
  110.   case CH_INIT:            /* initialize cache */
  111.     if (stream->cachesize) {    /* flush old cache contents */
  112.       while (stream->cachesize) mm_cache (stream,stream->cachesize--,CH_FREE);
  113.       fs_give ((void **) &stream->cache.c);
  114.       stream->nmsgs = 0;    /* can't have any messages now */
  115.     }
  116.     break;
  117.   case CH_SIZE:            /* (re-)size the cache */
  118.     if (msgno > j) {        /* do nothing if size adequate */
  119.       new = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
  120.       if (stream->cache.c) fs_resize ((void **) &stream->cache.c,new);
  121.       else stream->cache.c = (void **) fs_get (new);
  122.                 /* init cache */
  123.       while (j < stream->cachesize) stream->cache.c[j++] = NIL;
  124.     }
  125.     break;
  126.   case CH_MAKELELT:        /* return long elt, make if necessary */
  127.     if (!stream->cache.c[i]) {    /* have one already? */
  128.                 /* no, instantiate it */
  129.       stream->cache.l[i] = (LONGCACHE *) fs_get (sizeof (LONGCACHE));
  130.       memset (&stream->cache.l[i]->elt,0,sizeof (MESSAGECACHE));
  131.       stream->cache.l[i]->elt.lockcount = 1;
  132.       stream->cache.l[i]->elt.msgno = msgno;
  133.       stream->cache.l[i]->env = NIL;
  134.       stream->cache.l[i]->body = NIL;
  135.     }
  136.                 /* drop in to CH_LELT */
  137.   case CH_LELT:            /* return long elt */
  138.     ret = stream->cache.c[i];    /* return void version */
  139.     break;
  140.  
  141.   case CH_MAKEELT:        /* return short elt, make if necessary */
  142.     if (!stream->cache.c[i]) {    /* have one already? */
  143.       if (stream->scache) {    /* short cache? */
  144.     stream->cache.s[i] = (MESSAGECACHE *) fs_get (sizeof(MESSAGECACHE));
  145.     memset (stream->cache.s[i],0,sizeof (MESSAGECACHE));
  146.     stream->cache.s[i]->lockcount = 1;
  147.     stream->cache.s[i]->msgno = msgno;
  148.       }
  149.       else mm_cache (stream,msgno,CH_MAKELELT);
  150.     }
  151.                 /* drop in to CH_ELT */
  152.   case CH_ELT:            /* return short elt */
  153.     ret = stream->cache.c[i] && !stream->scache ?
  154.       (void *) &stream->cache.l[i]->elt : stream->cache.c[i];
  155.     break;
  156.   case CH_FREE:            /* free (l)elt */
  157.     if (stream->scache) mail_free_elt (&stream->cache.s[i]);
  158.     else mail_free_lelt (&stream->cache.l[i]);
  159.     break;
  160.   case CH_EXPUNGE:        /* expunge cache slot */
  161.                 /* slide down remainder of cache */
  162.     for (i = msgno; i < stream->nmsgs; ++i)
  163.       if (stream->cache.c[i-1] = stream->cache.c[i])
  164.     ((MESSAGECACHE *) mm_cache (stream,i,CH_ELT))->msgno = i;
  165.     stream->cache.c[stream->nmsgs-1] = NIL;
  166.     break;
  167.   default:
  168.     fatal ("Bad mm_cache op");
  169.     break;
  170.   }
  171.   return ret;
  172. }
  173.  
  174. /* Dummy string driver for complete in-memory strings */
  175.  
  176. STRINGDRIVER mail_string = {
  177.   mail_string_init,        /* initialize string structure */
  178.   mail_string_next,        /* get next byte in string structure */
  179.   mail_string_setpos        /* set position in string structure */
  180. };
  181.  
  182.  
  183. /* Initialize mail string structure for in-memory string
  184.  * Accepts: string structure
  185.  *        pointer to string
  186.  *        size of string
  187.  */
  188.  
  189. void mail_string_init (s,data,size)
  190.     STRING *s;
  191.     void *data;
  192.     unsigned long size;
  193. {
  194.                 /* set initial string pointers */
  195.   s->chunk = s->curpos = (char *) (s->data = data);
  196.                 /* and sizes */
  197.   s->size = s->chunksize = s->cursize = size;
  198.   s->data1 = s->offset = 0;    /* never any offset */
  199. }
  200.  
  201. /* Get next character from string
  202.  * Accepts: string structure
  203.  * Returns: character, string structure chunk refreshed
  204.  */
  205.  
  206. char mail_string_next (s)
  207.     STRING *s;
  208. {
  209.   return *s->curpos++;        /* return the last byte */
  210. }
  211.  
  212.  
  213. /* Set string pointer position
  214.  * Accepts: string structure
  215.  *        new position
  216.  */
  217.  
  218. void mail_string_setpos (s,i)
  219.     STRING *s;
  220.     unsigned long i;
  221. {
  222.   s->curpos = s->chunk + i;    /* set new position */
  223.   s->cursize = s->chunksize - i;/* and new size */
  224. }
  225.  
  226. /* Mail routines
  227.  *
  228.  *  mail_xxx routines are the interface between this module and the outside
  229.  * world.  Only these routines should be referenced by external callers.
  230.  *
  231.  *  Note that there is an important difference between a "sequence" and a
  232.  * "message #" (msgno).  A sequence is a string representing a sequence in
  233.  * {"n", "n:m", or combination separated by commas} format, whereas a msgno
  234.  * is a single integer.
  235.  *
  236.  */
  237.  
  238. /* Mail link driver
  239.  * Accepts: driver to add to list
  240.  */
  241.  
  242. void mail_link (driver)
  243.     DRIVER *driver;
  244. {
  245.   DRIVER **d = &maildrivers;
  246.   while (*d) d = &(*d)->next;    /* find end of list of drivers */
  247.   *d = driver;            /* put driver at the end */
  248.   driver->next = NIL;        /* this driver is the end of the list */
  249. }
  250.  
  251.  
  252. /* Mail manipulate driver parameters
  253.  * Accepts: mail stream
  254.  *        function code
  255.  *        function-dependent value
  256.  * Returns: function-dependent return value
  257.  */
  258.  
  259. void *mail_parameters (stream,function,value)
  260.     MAILSTREAM *stream;
  261.     long function;
  262.     void *value;
  263. {
  264.   return stream->dtb ? (stream->dtb->parameters) (function,value) : NIL;
  265. }
  266.  
  267. /* Mail find list of subscribed mailboxes
  268.  * Accepts: mail stream
  269.  *        pattern to search
  270.  */
  271.  
  272. void mail_find (stream,pat)
  273.     MAILSTREAM *stream;
  274.     char *pat;
  275. {
  276.   DRIVER *d = maildrivers;
  277.                 /* if have a stream, do it for that stream */
  278.   if (stream && stream->dtb) (*stream->dtb->find) (stream,pat);
  279.                 /* otherwise do for all DTB's */
  280.   else do (d->find) (NIL,pat);
  281.   while (d = d->next);        /* until at the end */
  282. }
  283.  
  284.  
  285. /* Mail find list of subscribed bboards
  286.  * Accepts: mail stream
  287.  *        pattern to search
  288.  */
  289.  
  290. void mail_find_bboards (stream,pat)
  291.     MAILSTREAM *stream;
  292.     char *pat;
  293. {
  294.   DRIVER *d = maildrivers;
  295.   if (stream && stream->dtb) (*stream->dtb->find_bboard) (stream,pat);
  296.   else do (d->find_bboard) (NIL,pat);
  297.   while (d = d->next);        /* until at the end */
  298. }
  299.  
  300. /* Mail find list of all mailboxes
  301.  * Accepts: mail stream
  302.  *        pattern to search
  303.  */
  304.  
  305. void mail_find_all (stream,pat)
  306.     MAILSTREAM *stream;
  307.     char *pat;
  308. {
  309.   DRIVER *d = maildrivers;
  310.                 /* if have a stream, do it for that stream */
  311.   if (stream && stream->dtb) (*stream->dtb->find_all) (stream,pat);
  312.                 /* otherwise do for all DTB's */
  313.   else do (d->find_all) (NIL,pat);
  314.   while (d = d->next);        /* until at the end */
  315. }
  316.  
  317.  
  318. /* Mail find list of all bboards
  319.  * Accepts: mail stream
  320.  *        pattern to search
  321.  */
  322.  
  323. void mail_find_all_bboard (stream,pat)
  324.     MAILSTREAM *stream;
  325.     char *pat;
  326. {
  327.   DRIVER *d = maildrivers;
  328.                 /* if have a stream, do it for that stream */
  329.   if (stream && stream->dtb) (*stream->dtb->find_all_bboard) (stream,pat);
  330.                 /* otherwise do for all DTB's */
  331.   else do (d->find_all_bboard) (NIL,pat);
  332.   while (d = d->next);        /* until at the end */
  333. }
  334.  
  335. /* Mail validate mailbox name
  336.  * Accepts: MAIL stream
  337.  *        mailbox name
  338.  *        purpose string for error message
  339.  *        flag indicating this is not an open
  340.  * Return: driver factory on success, NIL on failure
  341.  */
  342.  
  343. DRIVER *mail_valid (stream,mailbox,purpose,nopen)
  344.     MAILSTREAM *stream;
  345.     char *mailbox;
  346.     char *purpose;
  347.     long nopen;
  348. {
  349.   char tmp[MAILTMPLEN];
  350.   DRIVER *factory;
  351.   for (factory = maildrivers; factory && !(*factory->valid) (mailbox);
  352.        factory = factory->next);
  353.                 /* must match stream and not be dummy */
  354.   if (factory && ((stream && (stream->dtb != factory)) ||
  355.           (nopen && !strcmp (factory->name,"dummy")))) factory = NIL;
  356.   if (!factory && purpose) {    /* if want an error message */
  357.     sprintf (tmp,"Can't %s %s: no such mailbox",purpose,mailbox);
  358.     mm_log (tmp,ERROR);
  359.   }
  360.   return factory;        /* return driver factory */
  361. }
  362.  
  363.  
  364. /* Mail validate network mailbox name
  365.  * Accepts: mailbox name
  366.  *        mailbox driver to validate against
  367.  *        pointer to where to return host name if non-NIL
  368.  *        pointer to where to return mailbox name if non-NIL
  369.  * Returns: driver on success, NIL on failure
  370.  */
  371.  
  372. DRIVER *mail_valid_net (name,drv,host,mailbox)
  373.     char *name;
  374.     DRIVER *drv;
  375.     char *host;
  376.     char *mailbox;
  377. {
  378.   NETMBX mb;
  379.   if (!mail_valid_net_parse (name,&mb) || strcmp (mb.service,drv->name))
  380.     return NIL;
  381.   if (host) strcpy (host,mb.host);
  382.   if (mailbox) strcpy (mailbox,mb.mailbox);
  383.   return drv;
  384. }
  385.  
  386. /* Mail validate network mailbox name
  387.  * Accepts: mailbox name
  388.  *        NETMBX structure to return values
  389.  * Returns: T on success, NIL on failure
  390.  */
  391.  
  392. long mail_valid_net_parse (name,mb)
  393.     char *name;
  394.     NETMBX *mb;
  395. {
  396.   long i;
  397.   char c,*s,*t,*v;
  398.   mb->port = 0;            /* initialize structure */
  399.   *mb->host = *mb->mailbox = *mb->service = '\0';
  400.   mb->anoflag = NIL;        /* not (yet) anonymous */
  401.                 /* check if bboard */
  402.   if (mb->bbdflag = (*name == '*') ? T : NIL) name++;
  403.                 /* have host specification? */
  404.   if (!(*name == '{' && (t = strchr (s = name+1,'}')) && (i = t - s)))
  405.     return NIL;            /* not valid host specification */
  406.   strncpy (mb->host,s,i);    /* set host name */
  407.   mb->host[i] = '\0';        /* tie it off */
  408.   strcpy (mb->mailbox,t+1);    /* set mailbox name */
  409.                 /* any switches or port specification? */
  410.   if (t = strpbrk (mb->host,"/:")) {
  411.     c = *t;            /* yes, remember delimiter */
  412.     *t++ = '\0';        /* tie off host name */
  413.     lcase (t);            /* coerce remaining stuff to lowercase */
  414.     do switch (c) {        /* act based upon the character */
  415.     case ':':            /* port specification */
  416.       if (mb->port || ((mb->port = strtol (t,&t,10)) <= 0)) return NIL;
  417.       c = t ? *t++ : '\0';    /* get delimiter, advance pointer */
  418.       break;
  419.  
  420.     case '/':            /* switch */
  421.                 /* find delimiter */
  422.       if (t = strpbrk (s = t,"/:=")) {
  423.     c = *t;            /* remember delimiter for later */
  424.     *t++ = '\0';        /* tie off switch name */
  425.       }
  426.       else c = '\0';        /* no delimiter */
  427.       if (c == '=') {        /* parse switches which take arguments */
  428.     if (t = strpbrk (v = t,"/:")) {
  429.       c = *t;        /* remember delimiter for later */
  430.       *t++ = '\0';        /* tie off switch name */
  431.     }
  432.     else c = '\0';        /* no delimiter */
  433.     if (!strcmp (s,"service")) {
  434.       if (*mb->service) return NIL;
  435.       else strcpy (mb->service,v);
  436.     }
  437.     else return NIL;    /* invalid argument switch */
  438.       }
  439.       else {            /* non-argument switch */
  440.     if (!strcmp (s,"anonymous")) mb->anoflag = T;
  441.     else if (!strcmp (s,"imap") || !strcmp (s,"imap2") ||
  442.          !strcmp (s,"nntp")) {
  443.       if (*mb->service) return NIL;
  444.       else strcpy (mb->service,s);
  445.     }
  446.     else return NIL;    /* invalid non-argument switch */
  447.       }
  448.       break;
  449.     default:            /* anything else is bogus */
  450.       return NIL;
  451.     } while (c);        /* see if anything more to parse */
  452.   }
  453.                 /* default mailbox name */
  454.   if (!*mb->mailbox) strcpy (mb->mailbox,mb->bbdflag ? "general" : "INBOX");
  455.                 /* default service name */
  456.   if (!(*mb->service && strcmp (mb->service,"imap")))
  457.     strcpy (mb->service,"imap2");
  458.   return T;
  459. }
  460.  
  461. /* Mail subscribe to mailbox
  462.  * Accepts: mail stream
  463.  *        mailbox to add to subscription list
  464.  * Returns: T on success, NIL on failure
  465.  */
  466.  
  467. long mail_subscribe (stream,mailbox)
  468.     MAILSTREAM *stream;
  469.     char *mailbox;
  470. {
  471.   DRIVER *factory = mail_valid (stream,mailbox,"subscribe to mailbox",LONGT);
  472.   return factory ? (*factory->subscribe) (stream,mailbox) : NIL;
  473. }
  474.  
  475.  
  476. /* Mail unsubscribe to mailbox
  477.  * Accepts: mail stream
  478.  *        mailbox to delete from subscription list
  479.  * Returns: T on success, NIL on failure
  480.  */
  481.  
  482. long mail_unsubscribe (stream,mailbox)
  483.     MAILSTREAM *stream;
  484.     char *mailbox;
  485. {
  486.  DRIVER *factory = mail_valid (stream,mailbox,"unsubscribe to mailbox",LONGT);
  487.   return factory ? (*factory->unsubscribe) (stream,mailbox) : NIL;
  488. }
  489.  
  490.  
  491. /* Mail subscribe to bboard
  492.  * Accepts: mail stream
  493.  *        bboard to add to subscription list
  494.  * Returns: T on success, NIL on failure
  495.  */
  496.  
  497. long mail_subscribe_bboard (stream,mailbox)
  498.     MAILSTREAM *stream;
  499.     char *mailbox;
  500. {
  501.   char tmp[MAILTMPLEN];
  502.   DRIVER *factory;
  503.   sprintf (tmp,"*%s",mailbox);
  504.   return (factory = mail_valid (stream,tmp,"subscribe to bboard",LONGT)) ?
  505.     (*factory->subscribe_bboard) (stream,mailbox) : NIL;
  506. }
  507.  
  508.  
  509. /* Mail unsubscribe to bboard
  510.  * Accepts: mail stream
  511.  *        bboard to delete from subscription list
  512.  * Returns: T on success, NIL on failure
  513.  */
  514.  
  515. long mail_unsubscribe_bboard (stream,mailbox)
  516.     MAILSTREAM *stream;
  517.     char *mailbox;
  518. {
  519.   char tmp[MAILTMPLEN];
  520.   DRIVER *factory;
  521.   sprintf (tmp,"*%s",mailbox);
  522.   return (factory = mail_valid (stream,tmp,"unsubscribe to bboard",LONGT)) ?
  523.     (*factory->unsubscribe_bboard) (stream,mailbox) : NIL;
  524. }
  525.  
  526. /* Mail create mailbox
  527.  * Accepts: mail stream
  528.  *        mailbox name to create
  529.  * Returns: T on success, NIL on failure
  530.  */
  531.  
  532. long mail_create (stream,mailbox)
  533.     MAILSTREAM *stream;
  534.     char *mailbox;
  535. {
  536.   int localp = *mailbox != '{';
  537.   char tmp[MAILTMPLEN];
  538.   if (!stream) {        /* guess at driver if stream not specified */
  539.     if (localp) {        /* if local */
  540.       if (!(stream = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
  541.     stream = mailstd_proto;    /* default to standard if no INBOX */
  542.     }
  543.     else stream = mail_open (NIL,mailbox,OP_PROTOTYPE);
  544.   }
  545.   if (!stream) {        /* damn, still no prototype! */
  546.     sprintf (tmp,"Can't create mailbox %s: indeterminate format",mailbox);
  547.     mm_log (tmp,ERROR);
  548.     return NIL;
  549.   }
  550.                 /* must not already exist if local */
  551.   if (localp && mail_valid (stream,mailbox,NIL,LONGT)) {
  552.     sprintf (tmp,"Can't create mailbox %s: mailbox already exists",mailbox);
  553.     mm_log (tmp,ERROR);
  554.     return NIL;
  555.   }
  556.   return stream->dtb ? (*stream->dtb->create) (stream,mailbox) : NIL;
  557. }
  558.  
  559. /* Mail delete mailbox
  560.  * Accepts: mail stream
  561.  *        mailbox name to delete
  562.  * Returns: T on success, NIL on failure
  563.  */
  564.  
  565. long mail_delete (stream,mailbox)
  566.     MAILSTREAM *stream;
  567.     char *mailbox;
  568. {
  569.   DRIVER *factory = mail_valid (stream,mailbox,"delete mailbox",LONGT);
  570.   return factory ? (*factory->delete) (stream,mailbox) : NIL;
  571. }
  572.  
  573.  
  574. /* Mail rename mailbox
  575.  * Accepts: mail stream
  576.  *        old mailbox name
  577.  *        new mailbox name
  578.  * Returns: T on success, NIL on failure
  579.  */
  580.  
  581. long mail_rename (stream,old,new)
  582.     MAILSTREAM *stream;
  583.     char *old;
  584.     char *new;
  585. {
  586.   char tmp[MAILTMPLEN];
  587.   DRIVER *factory = mail_valid (stream,old,"rename mailbox",LONGT);
  588.   if ((*old != '{') && mail_valid (NIL,new,NIL,LONGT)) {
  589.     sprintf (tmp,"Can't rename to mailbox %s: mailbox already exists",new);
  590.     mm_log (tmp,ERROR);
  591.     return NIL;
  592.   }
  593.   return factory ? (*factory->rename) (stream,old,new) : NIL;
  594. }
  595.  
  596. /* Mail open
  597.  * Accepts: candidate stream for recycling
  598.  *        mailbox name
  599.  *        open options
  600.  * Returns: stream to use on success, NIL on failure
  601.  */
  602.  
  603. MAILSTREAM *mail_open (stream,name,options)
  604.     MAILSTREAM *stream;
  605.     char *name;
  606.     long options;
  607. {
  608.   DRIVER *factory = mail_valid (NIL,name,(!(stream && stream->silent)) ?
  609.                 "open mailbox" : NIL,(long) NIL);
  610.   if (factory) {        /* must have a factory */
  611.     if (!stream) {        /* instantiate stream if none to recycle */
  612.       if (options & OP_PROTOTYPE) return (*factory->open) (NIL);
  613.       stream = (MAILSTREAM *) fs_get (sizeof (MAILSTREAM));
  614.                 /* initialize stream */
  615.       memset ((void *) stream,0,sizeof (MAILSTREAM));
  616.       stream->dtb = factory;    /* set dispatch */
  617.                 /* set mailbox name */
  618.       stream->mailbox = cpystr (name);
  619.                 /* initialize cache */
  620.       (*mailcache) (stream,(long) 0,CH_INIT);
  621.     }
  622.     else {            /* close driver if different from factory */
  623.       if (stream->dtb != factory) {
  624.     if (stream->dtb) (*stream->dtb->close) (stream);
  625.     stream->dtb = factory;    /* establish factory as our driver */
  626.     stream->local = NIL;    /* flush old driver's local data */
  627.     mail_free_cache (stream);
  628.       }
  629.                 /* clean up old mailbox name for recycling */
  630.       if (stream->mailbox) fs_give ((void **) &stream->mailbox);
  631.       stream->mailbox = cpystr (name);
  632.     }
  633.     stream->lock = NIL;        /* initialize lock and options */
  634.     stream->debug = (options & OP_DEBUG) ? T : NIL;
  635.     stream->readonly = (options & OP_READONLY) ? T : NIL;
  636.     stream->anonymous = (options & OP_ANONYMOUS) ? T : NIL;
  637.     stream->scache = (options & OP_SHORTCACHE) ? T : NIL;
  638.     stream->silent = (options & OP_SILENT) ? T : NIL;
  639.     stream->halfopen = (options & OP_HALFOPEN) ? T : NIL;
  640.                 /* have driver open, flush if failed */
  641.     if (!(*factory->open) (stream)) stream = mail_close (stream);
  642.   }
  643.   return stream;        /* return the stream */
  644. }
  645.  
  646. /* Mail close
  647.  * Accepts: mail stream
  648.  * Returns: NIL
  649.  */
  650.  
  651. MAILSTREAM *mail_close (stream)
  652.     MAILSTREAM *stream;
  653. {
  654.   if (stream) {            /* make sure argument given */
  655.                 /* do the driver's close action */
  656.     if (stream->dtb) (*stream->dtb->close) (stream);
  657.     if (stream->mailbox) fs_give ((void **) &stream->mailbox);
  658.     stream->sequence++;        /* invalidate sequence */
  659.     if (stream->flagstring) fs_give ((void **) &stream->flagstring);
  660.     mail_free_cache (stream);    /* finally free the stream's storage */
  661.     if (!stream->use) fs_give ((void **) &stream);
  662.   }
  663.   return NIL;
  664. }
  665.  
  666. /* Mail make handle
  667.  * Accepts: mail stream
  668.  * Returns: handle
  669.  *
  670.  *  Handles provide a way to have multiple pointers to a stream yet allow the
  671.  * stream's owner to nuke it or recycle it.
  672.  */
  673.  
  674. MAILHANDLE *mail_makehandle (stream)
  675.     MAILSTREAM *stream;
  676. {
  677.   MAILHANDLE *handle = (MAILHANDLE *) fs_get (sizeof (MAILHANDLE));
  678.   handle->stream = stream;    /* copy stream */
  679.                 /* and its sequence */
  680.   handle->sequence = stream->sequence;
  681.   stream->use++;        /* let stream know another handle exists */
  682.   return handle;
  683. }
  684.  
  685.  
  686. /* Mail release handle
  687.  * Accepts: Mail handle
  688.  */
  689.  
  690. void mail_free_handle (handle)
  691.     MAILHANDLE **handle;
  692. {
  693.   MAILSTREAM *s;
  694.   if (*handle) {        /* only free if exists */
  695.                 /* resign stream, flush unreferenced zombies */
  696.     if ((!--(s = (*handle)->stream)->use) && !s->dtb) fs_give ((void **) &s);
  697.     fs_give ((void **) handle);    /* now flush the handle */
  698.   }
  699. }
  700.  
  701.  
  702. /* Mail get stream handle
  703.  * Accepts: Mail handle
  704.  * Returns: mail stream or NIL if stream gone
  705.  */
  706.  
  707. MAILSTREAM *mail_stream (handle)
  708.     MAILHANDLE *handle;
  709. {
  710.   MAILSTREAM *s = handle->stream;
  711.   return (s->dtb && (handle->sequence == s->sequence)) ? s : NIL;
  712. }
  713.  
  714. /* Mail fetch long cache element
  715.  * Accepts: mail stream
  716.  *        message # to fetch
  717.  * Returns: long cache element of this message
  718.  * Can also be used to create cache elements for new messages.
  719.  */
  720.  
  721. LONGCACHE *mail_lelt (stream,msgno)
  722.     MAILSTREAM *stream;
  723.     long msgno;
  724. {
  725.   if (stream->scache) fatal ("Short cache in mail_lelt");
  726.                 /* be sure it the cache is large enough */
  727.   (*mailcache) (stream,msgno,CH_SIZE);
  728.   return (LONGCACHE *) (*mailcache) (stream,msgno,CH_MAKELELT);
  729. }
  730.  
  731.  
  732. /* Mail fetch cache element
  733.  * Accepts: mail stream
  734.  *        message # to fetch
  735.  * Returns: cache element of this message
  736.  * Can also be used to create cache elements for new messages.
  737.  */
  738.  
  739. MESSAGECACHE *mail_elt (stream,msgno)
  740.     MAILSTREAM *stream;
  741.     long msgno;
  742. {
  743.   if (msgno < 1) fatal ("Bad msgno in mail_elt");
  744.                 /* be sure it the cache is large enough */
  745.   (*mailcache) (stream,msgno,CH_SIZE);
  746.   return (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_MAKEELT);
  747. }
  748.  
  749. /* Mail fetch fast information
  750.  * Accepts: mail stream
  751.  *        sequence
  752.  *
  753.  * Generally, mail_fetchstructure is preferred
  754.  */
  755.  
  756. void mail_fetchfast (stream,sequence)
  757.     MAILSTREAM *stream;
  758.     char *sequence;
  759. {
  760.                   /* do the driver's action */
  761.   if (stream->dtb) (*stream->dtb->fetchfast) (stream,sequence);
  762. }
  763.  
  764.  
  765. /* Mail fetch flags
  766.  * Accepts: mail stream
  767.  *        sequence
  768.  */
  769.  
  770. void mail_fetchflags (stream,sequence)
  771.     MAILSTREAM *stream;
  772.     char *sequence;
  773. {
  774.                   /* do the driver's action */
  775.   if (stream->dtb) (*stream->dtb->fetchflags) (stream,sequence);
  776. }
  777.  
  778.  
  779. /* Mail fetch message structure
  780.  * Accepts: mail stream
  781.  *        message # to fetch
  782.  *        pointer to return body
  783.  * Returns: envelope of this message, body returned in body value
  784.  *
  785.  * Fetches the "fast" information as well
  786.  */
  787.  
  788. ENVELOPE *mail_fetchstructure (stream,msgno,body)
  789.     MAILSTREAM *stream;
  790.     long msgno;
  791.     BODY **body;
  792. {
  793.   if (msgno < 1 || msgno > stream->nmsgs)
  794.     fatal ("Bad msgno in mail_fetchstructure");
  795.                   /* do the driver's action */
  796.   return stream->dtb ? (*stream->dtb->fetchstructure) (stream,msgno,body) :NIL;
  797. }
  798.  
  799. /* Mail fetch message header
  800.  * Accepts: mail stream
  801.  *        message # to fetch
  802.  * Returns: message header in RFC822 format
  803.  */
  804.  
  805. char *mail_fetchheader (stream,msgno)
  806.     MAILSTREAM *stream;
  807.     long msgno;
  808. {
  809.   if (msgno < 1 || msgno > stream->nmsgs)
  810.     fatal ("Bad msgno in mail_fetchheader");
  811.                   /* do the driver's action */
  812.   return stream->dtb ? (*stream->dtb->fetchheader) (stream,msgno) : "";
  813. }
  814.  
  815.  
  816. /* Mail fetch message text (only)
  817.     body only;
  818.  * Accepts: mail stream
  819.  *        message # to fetch
  820.  * Returns: message text in RFC822 format
  821.  */
  822.  
  823. char *mail_fetchtext (stream,msgno)
  824.     MAILSTREAM *stream;
  825.     long msgno;
  826. {
  827.   if (msgno < 1 || msgno > stream->nmsgs)
  828.     fatal ("Bad msgno in mail_fetchtext");
  829.                   /* do the driver's action */
  830.   return stream->dtb ? (*stream->dtb->fetchtext) (stream,msgno) : "";
  831. }
  832.  
  833.  
  834. /* Mail fetch message body part text
  835.  * Accepts: mail stream
  836.  *        message # to fetch
  837.  *        section specifier (#.#.#...#)
  838.  *        pointer to returned length
  839.  * Returns: pointer to section of message body
  840.  */
  841.  
  842. char *mail_fetchbody (stream,m,sec,len)
  843.     MAILSTREAM *stream;
  844.     long m;
  845.     char *sec;
  846.     unsigned long *len;
  847. {
  848.   if (m < 1 || m > stream->nmsgs) fatal ("Bad msgno in mail_fetchbody");
  849.                   /* do the driver's action */
  850.   return stream->dtb ? (*stream->dtb->fetchbody) (stream,m,sec,len) : "";
  851. }
  852.  
  853. /* Mail fetch From string for menu
  854.  * Accepts: destination string
  855.  *        mail stream
  856.  *        message # to fetch
  857.  *        desired string length
  858.  * Returns: string of requested length
  859.  */
  860.  
  861. void mail_fetchfrom (s,stream,msgno,length)
  862.     char *s;
  863.     MAILSTREAM *stream;
  864.     long msgno;
  865.     long length;
  866. {
  867.   char *t;
  868.   char tmp[MAILTMPLEN];
  869.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  870.   ADDRESS *adr = env ? env->from : NIL;
  871.   memset (s,' ',length);    /* fill it with spaces */
  872.   s[length] = '\0';        /* tie off with null */
  873.                 /* get first from address from envelope */
  874.   while (adr && !adr->host) adr = adr->next;
  875.   if (adr) {            /* if a personal name exists use it */
  876.     if (!(t = adr->personal)) sprintf (t = tmp,"%s@%s",adr->mailbox,adr->host);
  877.     memcpy (s,t,min (length,(long) strlen (t)));
  878.   }
  879. }
  880.  
  881.  
  882. /* Mail fetch Subject string for menu
  883.  * Accepts: destination string
  884.  *        mail stream
  885.  *        message # to fetch
  886.  *        desired string length
  887.  * Returns: string of no more than requested length
  888.  */
  889.  
  890. void mail_fetchsubject (s,stream,msgno,length)
  891.     char *s;
  892.     MAILSTREAM *stream;
  893.     long msgno;
  894.     long length;
  895. {
  896.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  897.   memset (s,'\0',length+1);
  898.                 /* copy subject from envelope */
  899.   if (env && env->subject) strncpy (s,env->subject,length);
  900.   else *s = ' ';        /* if no subject then just a space */
  901. }
  902.  
  903. /* Mail set flag
  904.  * Accepts: mail stream
  905.  *        sequence
  906.  *        flag(s)
  907.  */
  908.  
  909. void mail_setflag (stream,sequence,flag)
  910.     MAILSTREAM *stream;
  911.     char *sequence;
  912.     char *flag;
  913. {
  914.                   /* do the driver's action */
  915.   if (stream->dtb) (*stream->dtb->setflag) (stream,sequence,flag);
  916. }
  917.  
  918.  
  919. /* Mail clear flag
  920.  * Accepts: mail stream
  921.  *        sequence
  922.  *        flag(s)
  923.  */
  924.  
  925. void mail_clearflag (stream,sequence,flag)
  926.     MAILSTREAM *stream;
  927.     char *sequence;
  928.     char *flag;
  929. {
  930.                   /* do the driver's action */
  931.   if (stream->dtb) (*stream->dtb->clearflag) (stream,sequence,flag);
  932. }
  933.  
  934.  
  935. /* Mail search for messages
  936.  * Accepts: mail stream
  937.  *        search criteria
  938.  */
  939.  
  940. void mail_search (stream,criteria)
  941.     MAILSTREAM *stream;
  942.     char *criteria;
  943. {
  944.   long i = 1;
  945.   while (i <= stream->nmsgs) mail_elt (stream,i++)->searched = NIL;
  946.                   /* do the driver's action */
  947.   if (stream->dtb) (*stream->dtb->search) (stream,criteria);
  948. }
  949.  
  950.  
  951. /* Mail ping mailbox
  952.  * Accepts: mail stream
  953.  * Returns: stream if still open else NIL
  954.  */
  955.  
  956. long mail_ping (stream)
  957.     MAILSTREAM *stream;
  958. {
  959.                   /* do the driver's action */
  960.   return stream->dtb ? (*stream->dtb->ping) (stream) : NIL;
  961. }
  962.  
  963. /* Mail check mailbox
  964.  * Accepts: mail stream
  965.  */
  966.  
  967. void mail_check (stream)
  968.     MAILSTREAM *stream;
  969. {
  970.                   /* do the driver's action */
  971.   if (stream->dtb) (*stream->dtb->check) (stream);
  972. }
  973.  
  974.  
  975. /* Mail expunge mailbox
  976.  * Accepts: mail stream
  977.  */
  978.  
  979. void mail_expunge (stream)
  980.     MAILSTREAM *stream;
  981. {
  982.                   /* do the driver's action */
  983.   if (stream->dtb) (*stream->dtb->expunge) (stream);
  984. }
  985.  
  986. /* Mail copy message(s)
  987.     s;
  988.  * Accepts: mail stream
  989.  *        sequence
  990.  *        destination mailbox
  991.  */
  992.  
  993. long mail_copy (stream,sequence,mailbox)
  994.     MAILSTREAM *stream;
  995.     char *sequence;
  996.     char *mailbox;
  997. {
  998.                   /* do the driver's action */
  999.   return stream->dtb ? (*stream->dtb->copy) (stream,sequence,mailbox) : NIL;
  1000. }
  1001.  
  1002.  
  1003. /* Mail move message(s)
  1004.     s;
  1005.  * Accepts: mail stream
  1006.  *        sequence
  1007.  *        destination mailbox
  1008.  */
  1009.  
  1010. long mail_move (stream,sequence,mailbox)
  1011.     MAILSTREAM *stream;
  1012.     char *sequence;
  1013.     char *mailbox;
  1014. {
  1015.                   /* do the driver's action */
  1016.   return stream->dtb ? (*stream->dtb->move) (stream,sequence,mailbox) : NIL;
  1017. }
  1018.  
  1019.  
  1020. /* Mail append message string
  1021.  * Accepts: mail stream
  1022.  *        destination mailbox
  1023.  *        stringstruct of message to append
  1024.  * Returns: T on success, NIL on failure
  1025.  */
  1026.  
  1027. long mail_append (stream,mailbox,message)
  1028.     MAILSTREAM *stream;
  1029.     char *mailbox;
  1030.     STRING *message;
  1031. {
  1032.   DRIVER *factory = mail_valid (stream,mailbox,"append to mailbox",(long) NIL);
  1033.                 /* do the driver's action */
  1034.   return factory ? (factory->append) (stream,mailbox,message) : NIL;
  1035. }
  1036.  
  1037. /* Mail garbage collect stream
  1038.  * Accepts: mail stream
  1039.  *        garbage collection flags
  1040.  */
  1041.  
  1042. void mail_gc (stream,gcflags)
  1043.     MAILSTREAM *stream;
  1044.     long gcflags;
  1045. {
  1046.   unsigned long i = 1;
  1047.   LONGCACHE *lelt;
  1048.                   /* do the driver's action first */
  1049.   if (stream->dtb) (*stream->dtb->gc) (stream,gcflags);
  1050.   if (gcflags & GC_ENV) {    /* garbage collect envelopes? */
  1051.                 /* yes, free long cache if in use */
  1052.     if (!stream->scache) while (i <= stream->nmsgs)
  1053.       if (lelt = (LONGCACHE *) (*mailcache) (stream,i++,CH_LELT)) {
  1054.     mail_free_envelope (&lelt->env);
  1055.     mail_free_body (&lelt->body);
  1056.       }
  1057.     stream->msgno = 0;        /* free this cruft too */
  1058.     mail_free_envelope (&stream->env);
  1059.     mail_free_body (&stream->body);
  1060.   }
  1061.                 /* free text if any */
  1062.   if ((gcflags & GC_TEXTS) && (stream->text)) fs_give ((void **)&stream->text);
  1063. }
  1064.  
  1065. /* Mail output date from elt fields
  1066.  * Accepts: character string to write into
  1067.  *        elt to get data data from
  1068.  * Returns: the character string
  1069.  */
  1070.  
  1071. const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1072.             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  1073.  
  1074. char *mail_date (string,elt)
  1075.     char *string;
  1076.     MESSAGECACHE *elt;
  1077. {
  1078.   const char *s = (elt->month && elt->month < 13) ?
  1079.     months[elt->month - 1] : (const char *) "???";
  1080.   sprintf (string,"%2d-%s-%d %02d:%02d:%02d %c%02d%02d",
  1081.        elt->day,s,elt->year + BASEYEAR,
  1082.        elt->hours,elt->minutes,elt->seconds,
  1083.        elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes);
  1084.   return string;
  1085. }
  1086.  
  1087.  
  1088. /* Mail output cdate format date from elt fields
  1089.  * Accepts: character string to write into
  1090.  *        elt to get data data from
  1091.  * Returns: the character string
  1092.  */
  1093.  
  1094. const char *cdays[] = {"Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Mon"};
  1095.  
  1096. char *mail_cdate (string,elt)
  1097.     char *string;
  1098.     MESSAGECACHE *elt;
  1099. {
  1100.   const char *s = (elt->month && elt->month < 13) ?
  1101.     months[elt->month - 1] : (const char *) "???";
  1102.   int m = elt->month;
  1103.   int y = elt->year + BASEYEAR;
  1104.   if (elt->month <= 2) {    /* if before March, */
  1105.     m = elt->month + 9;        /* January = month 10 of previous year */
  1106.     y--;
  1107.   }
  1108.   else m = elt->month - 3;    /* March is month 0 */
  1109.   sprintf (string,"%s %s %2d %02d:%02d:%02d %4d\n",
  1110.        cdays[(int)(elt->day+((7+31*m)/12)+y+(y/4)+(y/400)-(y/100)) % 7],s,
  1111.        elt->day,elt->hours,elt->minutes,elt->seconds,elt->year + BASEYEAR);
  1112.   return string;
  1113. }
  1114.  
  1115. /* Mail parse date into elt fields
  1116.  * Accepts: elt to write into
  1117.  *        date string to parse
  1118.  * Returns: T if parse successful, else NIL
  1119.  * This routine accepts mm/dd/yy format for the date as well
  1120.  */
  1121.  
  1122. long mail_parse_date (elt,s)
  1123.     MESSAGECACHE *elt;
  1124.     char *s;
  1125. {
  1126.   long d,m,y;
  1127.   int ms;
  1128.   struct tm *t;
  1129.   time_t tn;
  1130.  
  1131.                 /* skip over possible day of week */
  1132.   if (isalpha (*s) && s[3] == ',') s += 5;
  1133.                 /* parse first number (probable month) */
  1134.   if (!(s && (m = strtol ((const char *) s,&s,10)))) return NIL;
  1135.   switch (*s) {            /* different parse based on delimiter */
  1136.   case '/':            /* mm/dd/yy format */
  1137.     if (!((d = strtol ((const char *) ++s,&s,10)) && *s == '/' &&
  1138.       (y = strtol ((const char *) ++s,&s,10)) && *s == '\0')) return NIL;
  1139.     break;
  1140.   case ' ':            /* dd mmm yy format */
  1141.   case '-':            /* dd-mmm-yy format */
  1142.     d = m;            /* so the number we got is a day */
  1143.                 /* make sure string is UC and long enough! */
  1144.     if (strlen (ucase (s)) < 5) return NIL;
  1145.     /* Some compilers don't allow `<<' and/or longs in case statements. */
  1146.                 /* slurp up the month string */
  1147.     ms = ((s[1] - 'A') * 1024) + ((s[2] - 'A') * 32) + (s[3] - 'A');
  1148.     switch (ms) {        /* determine the month */
  1149.     case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
  1150.     case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
  1151.     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
  1152.     case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
  1153.     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
  1154.     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
  1155.     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
  1156.     case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
  1157.     case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
  1158.     case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10; break;
  1159.     case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11; break;
  1160.     case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12; break;
  1161.     default: return NIL;
  1162.     }
  1163.     if (((s[4] == '-') || s[4] == ' ') &&
  1164.     (y = strtol ((const char *) s+5,&s,10)) && (*s == '\0' || *s == ' '))
  1165.       break;            /* parse the year */
  1166.   default: return NIL;        /* unknown date format */
  1167.   }
  1168.                 /* minimal validity check of date */
  1169.   if (d < 1 || d > 31 || m < 1 || m > 12 || y < 0) return NIL;
  1170.                 /* Tenex/ARPAnet began in 1969 */
  1171.   if (y < 100) y += (y >= (BASEYEAR - 1900)) ? 1900 : 2000;
  1172.                 /* set values in elt */
  1173.   elt->day = d; elt->month = m; elt->year = y - BASEYEAR;
  1174.  
  1175.   if (*s) {            /* time specification present? */
  1176.                 /* parse time */
  1177.     d = strtol ((const char *) s,&s,10);
  1178.     if (*s != ':') return NIL;
  1179.     m = strtol ((const char *) ++s,&s,10);
  1180.     if (*s != ':') return NIL;
  1181.     y = strtol ((const char *) ++s,&s,10);
  1182.                 /* minimal validity check of time */
  1183.     if (d < 0 || d > 23 || m < 0 || m > 59 || y < 0 || y > 59) return NIL;
  1184.                 /* set values in elt */
  1185.     elt->hours = d; elt->minutes = m; elt->seconds = y;
  1186.     switch (*s) {        /* time zone specifier? */
  1187.     case '-':            /* symbolic time zone */
  1188.       if (!(ms = *++s)) return NIL;
  1189.       if (*++s) {        /* multi-character? */
  1190.     ms -= 'A'; ms *= 1024;    /* yes, make compressed three-byte form */
  1191.     ms += ((*s++ - 'A') * 32);
  1192.     if (*s) ms += *s++ - 'A';
  1193.     if (*s) return NIL;    /* more than three characters */
  1194.       }
  1195.       /* This is not intended to be a comprehensive list of all possible
  1196.        * timezone strings.  Such a list would be impractical.  Rather, this
  1197.        * listing is intended to incorporate all military, north American, and
  1198.        * a few special cases such as Japan and the major European zone names,
  1199.        * such as what might be expected to be found in a Tenex format mailbox
  1200.        * and spewed from an IMAP server.  The trend is to migrate to numeric
  1201.        * timezones which lack the flavor but also the ambiguity of the names.
  1202.        */
  1203.       switch (ms) {        /* determine the timezone */
  1204.                 /* Middle Europe */
  1205.       case (('M'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
  1206.       case 'A': elt->zhours = 1; break;
  1207.                 /* Eastern Europe */
  1208.       case (('E'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
  1209.       case 'B': elt->zhours = 2; break;
  1210.       case 'C': elt->zhours = 3; break;
  1211.       case 'D': elt->zhours = 4; break;
  1212.       case 'E': elt->zhours = 5; break;
  1213.       case 'F': elt->zhours = 6; break;
  1214.       case 'G': elt->zhours = 7; break;
  1215.       case 'H': elt->zhours = 8; break;
  1216.                 /* Japan */
  1217.       case (('J'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1218.       case 'I': elt->zhours = 9; break;
  1219.       case 'K': elt->zhours = 10; break;
  1220.       case 'L': elt->zhours = 11; break;
  1221.       case 'M': elt->zhours = 12; break;
  1222.  
  1223.       case 'N': elt->zoccident = 1; elt->zhours = 1; break;
  1224.       case 'O': elt->zoccident = 1; elt->zhours = 2; break;
  1225.       case (('A'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1226.       case 'P': elt->zoccident = 1; elt->zhours = 3; break;
  1227.                 /* Atlantic */
  1228.       case (('A'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1229.       case (('E'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1230.       case 'Q': elt->zoccident = 1; elt->zhours = 4; break;
  1231.                 /* Eastern */
  1232.       case (('E'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1233.       case (('C'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1234.       case 'R': elt->zoccident = 1; elt->zhours = 5; break;
  1235.                 /* Central */
  1236.       case (('C'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1237.       case (('M'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1238.       case 'S': elt->zoccident = 1; elt->zhours = 6; break;
  1239.                 /* Mountain */
  1240.       case (('M'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1241.       case (('P'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1242.       case 'T': elt->zoccident = 1; elt->zhours = 7; break;
  1243.                 /* Pacific */
  1244.       case (('P'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1245.       case (('Y'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1246.       case 'U': elt->zoccident = 1; elt->zhours = 8; break;
  1247.                 /* Yukon */
  1248.       case (('Y'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1249.       case (('H'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1250.       case 'V': elt->zoccident = 1; elt->zhours = 9; break;
  1251.                 /* Hawaii */
  1252.       case (('H'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1253.       case (('B'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1254.       case 'W': elt->zoccident = 1; elt->zhours = 10; break;
  1255.                 /* Bering */
  1256.       case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1257.       case 'X': elt->zoccident = 1; elt->zhours = 11; break;
  1258.       case 'Y': elt->zoccident = 1; elt->zhours = 12; break;
  1259.                 /* Universal */
  1260.       case (('U'-'A')*1024)+(('T'-'A')*32):
  1261.       case (('G'-'A')*1024)+(('M'-'A')*32)+'T'-'A':
  1262.       case 'Z': elt->zhours = 0; break;
  1263.  
  1264.       case (('L'-'A')*1024) + (('C'-'A')*32)+'L'-'A':
  1265.     /* I knew I was going to regret that idiotic `LCL' stuff someday!! */
  1266.     tn = time (0);        /* time now... */
  1267.     t = localtime (&tn);    /* get local minutes since midnight */
  1268.     m = t->tm_hour * 60 + t->tm_min;
  1269.     t = gmtime (&tn);    /* minus UTC minutes since midnight */
  1270.                 /* add a day if too far past */
  1271.     if ((m -= t->tm_hour * 60 + t->tm_min) < -720) m += 1440;
  1272.     if (m > 720) m -= 1440;    /* subtract a day if too far ahead */
  1273.     if (m < 0) {        /* occidental? */
  1274.       m = abs (m);        /* yup, make positive number */
  1275.       elt->zoccident = 1;    /* and note west of UTC */
  1276.     }
  1277.     elt->zhours = m / 60;    /* now break into hours and minutes */
  1278.     elt->zminutes = m % 60;
  1279.     break;
  1280.       default:            /* unknown time zone name */
  1281.     return NIL;
  1282.       }
  1283.       elt->zminutes = 0;    /* never a fractional hour */
  1284.       break;
  1285.     case '\0':            /* no time zone */
  1286.       break;
  1287.     case ' ':            /* numeric time zone */
  1288.                 /* test for sign character */
  1289.       if ((elt->zoccident = (*++s == '-')) || (*s == '+')) s++;
  1290.       if (!(isdigit (*s) && isdigit (s[1]) && isdigit (s[2]) && isdigit (s[3]))
  1291.        || s[4]) return NIL;    /* proper timezone */
  1292.       elt->zhours = (*s - '0') * 10 + (s[1] - '0');
  1293.       elt->zminutes = (s[2] - '0') * 10 + (s[3] - '0');
  1294.       break;
  1295.     default:
  1296.       return NIL;
  1297.     }
  1298.   }
  1299.   return T;
  1300. }
  1301.  
  1302. /* Mail messages have been searched out
  1303.  * Accepts: mail stream
  1304.  *        message number
  1305.  *
  1306.  * Calls external "mm_searched" function to notify main program
  1307.  */
  1308.  
  1309. void mail_searched (stream,msgno)
  1310.     MAILSTREAM *stream;
  1311.     long msgno;
  1312. {
  1313.                 /* mark as searched */
  1314.   mail_elt (stream,msgno)->searched = T;
  1315.   mm_searched (stream,msgno);    /* notify main program */
  1316. }
  1317.  
  1318.  
  1319. /* Mail n messages exist
  1320.  * Accepts: mail stream
  1321.  *        number of messages
  1322.  *
  1323.  * Calls external "mm_exists" function that notifies main program prior
  1324.  * to updating the stream
  1325.  */
  1326.  
  1327. void mail_exists (stream,nmsgs)
  1328.     MAILSTREAM *stream;
  1329.     long nmsgs;
  1330. {
  1331.                 /* make sure cache is large enough */
  1332.   (*mailcache) (stream,nmsgs,CH_SIZE);
  1333.                 /* notify main program of change */
  1334.   if (!stream->silent) mm_exists (stream,nmsgs);
  1335.   stream->nmsgs = nmsgs;    /* update stream status */
  1336. }
  1337.  
  1338. /* Mail n messages are recent
  1339.  * Accepts: mail stream
  1340.  *        number of recent messages
  1341.  */
  1342.  
  1343. void mail_recent (stream,recent)
  1344.     MAILSTREAM *stream;
  1345.     long recent;
  1346. {
  1347.   stream->recent = recent;    /* update stream status */
  1348. }
  1349.  
  1350.  
  1351. /* Mail message n is expunged
  1352.  * Accepts: mail stream
  1353.  *        message #
  1354.  *
  1355.  * Calls external "mm_expunged" function that notifies main program prior
  1356.  * to updating the stream
  1357.  */
  1358.  
  1359. void mail_expunged (stream,msgno)
  1360.     MAILSTREAM *stream;
  1361.     long msgno;
  1362. {
  1363.   long i = msgno - 1;
  1364.   MESSAGECACHE *elt = (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_ELT);
  1365.   if (elt) {            /* if an element is there */
  1366.     elt->msgno = 0;        /* invalidate its message number and free */
  1367.     (*mailcache) (stream,msgno,CH_FREE);
  1368.   }
  1369.                 /* expunge the slot */
  1370.   (*mailcache) (stream,msgno,CH_EXPUNGE);
  1371.   --stream->nmsgs;        /* update stream status */
  1372.   stream->msgno = 0;        /* nuke the short cache too */
  1373.   mail_free_envelope (&stream->env);
  1374.   mail_free_body (&stream->body);
  1375.                 /* notify main program of change */
  1376.   if (!stream->silent) mm_expunged (stream,msgno);
  1377. }
  1378.  
  1379. /* mail stream status routines */
  1380.  
  1381.  
  1382. /* Mail lock stream
  1383.  * Accepts: mail stream
  1384.  */
  1385.  
  1386. void mail_lock (stream)
  1387.     MAILSTREAM *stream;
  1388. {
  1389.   if (stream->lock) fatal ("Lock when already locked");
  1390.   else stream->lock = T;    /* lock stream */
  1391. }
  1392.  
  1393.  
  1394. /* Mail unlock stream
  1395.  * Accepts: mail stream
  1396.  */
  1397.  
  1398. void mail_unlock (stream)
  1399.     MAILSTREAM *stream;
  1400. {
  1401.   if (!stream->lock) fatal ("Unlock when not locked");
  1402.   else stream->lock = NIL;    /* unlock stream */
  1403. }
  1404.  
  1405.  
  1406. /* Mail turn on debugging telemetry
  1407.  * Accepts: mail stream
  1408.  */
  1409.  
  1410. void mail_debug (stream)
  1411.     MAILSTREAM *stream;
  1412. {
  1413.   stream->debug = T;        /* turn on debugging telemetry */
  1414. }
  1415.  
  1416.  
  1417. /* Mail turn off debugging telemetry
  1418.  * Accepts: mail stream
  1419.  */
  1420.  
  1421. void mail_nodebug (stream)
  1422.     MAILSTREAM *stream;
  1423. {
  1424.   stream->debug = NIL;        /* turn off debugging telemetry */
  1425. }
  1426.  
  1427. /* Mail parse sequence
  1428.  * Accepts: mail stream
  1429.  *        sequence to parse
  1430.  * Returns: T if parse successful, else NIL
  1431.  */
  1432.  
  1433. long mail_sequence (stream,sequence)
  1434.     MAILSTREAM *stream;
  1435.     char *sequence;
  1436. {
  1437.   long i,j,x;
  1438.   for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
  1439.   while (*sequence) {        /* while there is something to parse */
  1440.                 /* parse and validate message number */
  1441.     if (((i = (int) strtol ((const char *) sequence,&sequence,10)) < 1) ||
  1442.     (i > stream->nmsgs)) {
  1443.       mm_log ("Sequence invalid",ERROR);
  1444.       return NIL;
  1445.     }
  1446.     switch (*sequence) {    /* see what the delimiter is */
  1447.     case ':':            /* sequence range */
  1448.                 /* parse end of range */
  1449.       if (((j = (int) strtol ((const char *) ++sequence,&sequence,10)) < 1) ||
  1450.       (j > stream->nmsgs) || (*sequence && *sequence++ != ',')) {
  1451.     mm_log ("Sequence range invalid",ERROR);
  1452.     return NIL;
  1453.       }
  1454.       if (i > j) {        /* swap the range if backwards */
  1455.     x = i; i = j; j = x;
  1456.       }
  1457.                 /* mark each item in the sequence */
  1458.       while (i <= j) mail_elt (stream,j--)->sequence = T;
  1459.       break;
  1460.     case ',':            /* single message */
  1461.       ++sequence;        /* skip the delimiter, fall into end case */
  1462.     case '\0':            /* end of sequence, mark this message */
  1463.       mail_elt (stream,i)->sequence = T;
  1464.       break;
  1465.     default:            /* anything else is a syntax error! */
  1466.       mm_log ("Syntax error in sequence",ERROR);
  1467.       return NIL;
  1468.     }
  1469.   }
  1470.   return T;            /* successfully parsed sequence */
  1471. }
  1472.  
  1473. /* Mail data structure instantiation routines */
  1474.  
  1475.  
  1476. /* Mail instantiate envelope
  1477.  * Returns: new envelope
  1478.  */
  1479.  
  1480. ENVELOPE *mail_newenvelope ()
  1481. {
  1482.   ENVELOPE *env = (ENVELOPE *) fs_get (sizeof (ENVELOPE));
  1483.   env->remail = NIL;        /* initialize all fields */
  1484.   env->return_path = NIL;
  1485.   env->date = NIL;
  1486.   env->subject = NIL;
  1487.   env->from = env->sender = env->reply_to = env->to = env->cc = env->bcc = NIL;
  1488.   env->in_reply_to = env->message_id = env->newsgroups = NIL;
  1489.   return env;
  1490. }
  1491.  
  1492.  
  1493. /* Mail instantiate address
  1494.  * Returns: new address
  1495.  */
  1496.  
  1497. ADDRESS *mail_newaddr ()
  1498. {
  1499.   ADDRESS *adr = (ADDRESS *) fs_get (sizeof (ADDRESS));
  1500.                 /* initialize all fields */
  1501.   adr->personal = adr->adl = adr->mailbox = adr->host = adr->error = NIL;
  1502.   adr->next = NIL;
  1503.   return adr;
  1504. }
  1505.  
  1506.  
  1507. /* Mail instantiate body
  1508.  * Returns: new body
  1509.  */
  1510.  
  1511. BODY *mail_newbody ()
  1512. {
  1513.   return mail_initbody ((BODY *) fs_get (sizeof (BODY)));
  1514. }
  1515.  
  1516. /* Mail initialize body
  1517.  * Accepts: body
  1518.  * Returns: body
  1519.  */
  1520.  
  1521. BODY *mail_initbody (body)
  1522.     BODY *body;
  1523. {
  1524.   body->type = TYPETEXT;    /* content type */
  1525.   body->encoding = ENC7BIT;    /* content encoding */
  1526.   body->subtype = body->id = body->description = NIL;
  1527.   body->parameter = NIL;
  1528.   body->contents.text = NIL;    /* no contents yet */
  1529.   body->contents.binary = NIL;
  1530.   body->contents.part = NIL;
  1531.   body->contents.msg.env = NIL;
  1532.   body->contents.msg.body = NIL;
  1533.   body->contents.msg.text = NIL;
  1534.   body->size.lines = body->size.bytes = body->size.ibytes = 0;
  1535.   return body;
  1536. }
  1537.  
  1538.  
  1539. /* Mail instantiate body parameter
  1540.  * Returns: new body part
  1541.  */
  1542.  
  1543. PARAMETER *mail_newbody_parameter ()
  1544. {
  1545.   PARAMETER *parameter = (PARAMETER *) fs_get (sizeof (PARAMETER));
  1546.   parameter->attribute = parameter->value = NIL;
  1547.   parameter->next = NIL;    /* no next yet */
  1548.   return parameter;
  1549. }
  1550.  
  1551.  
  1552. /* Mail instantiate body part
  1553.  * Returns: new body part
  1554.  */
  1555.  
  1556. PART *mail_newbody_part ()
  1557. {
  1558.   PART *part = (PART *) fs_get (sizeof (PART));
  1559.   mail_initbody (&part->body);    /* initialize the body */
  1560.   part->offset = 0;        /* no offset yet */
  1561.   part->next = NIL;        /* no next yet */
  1562.   return part;
  1563. }
  1564.  
  1565. /* Mail garbage collection routines */
  1566.  
  1567.  
  1568. /* Mail garbage collect body
  1569.  * Accepts: pointer to body pointer
  1570.  */
  1571.  
  1572. void mail_free_body (body)
  1573.     BODY **body;
  1574. {
  1575.   if (*body) {            /* only free if exists */
  1576.     mail_free_body_data (*body);/* free its data */
  1577.     fs_give ((void **) body);    /* return body to free storage */
  1578.   }
  1579. }
  1580.  
  1581.  
  1582. /* Mail garbage collect body data
  1583.  * Accepts: body pointer
  1584.  */
  1585.  
  1586. void mail_free_body_data (body)
  1587.     BODY *body;
  1588. {
  1589.   if (body->subtype) fs_give ((void **) &body->subtype);
  1590.   mail_free_body_parameter (&body->parameter);
  1591.   if (body->id) fs_give ((void **) &body->id);
  1592.   if (body->description) fs_give ((void **) &body->description);
  1593.   switch (body->type) {        /* free contents */
  1594.   case TYPETEXT:        /* unformatted text */
  1595.     if (body->contents.text) fs_give ((void **) &body->contents.text);
  1596.     break;
  1597.   case TYPEMULTIPART:        /* multiple part */
  1598.     mail_free_body_part (&body->contents.part);
  1599.     break;
  1600.   case TYPEMESSAGE:        /* encapsulated message */
  1601.     mail_free_envelope (&body->contents.msg.env);
  1602.     mail_free_body (&body->contents.msg.body);
  1603.     if (body->contents.msg.text)
  1604.       fs_give ((void **) &body->contents.msg.text);
  1605.     break;
  1606.   case TYPEAPPLICATION:        /* application data */
  1607.   case TYPEAUDIO:        /* audio */
  1608.   case TYPEIMAGE:        /* static image */
  1609.   case TYPEVIDEO:        /* video */
  1610.     if (body->contents.binary) fs_give (&body->contents.binary);
  1611.     break;
  1612.   default:
  1613.     break;
  1614.   }
  1615. }
  1616.  
  1617. /* Mail garbage collect body parameter
  1618.  * Accepts: pointer to body parameter pointer
  1619.  */
  1620.  
  1621. void mail_free_body_parameter (parameter)
  1622.     PARAMETER **parameter;
  1623. {
  1624.   if (*parameter) {        /* only free if exists */
  1625.     if ((*parameter)->attribute) fs_give ((void **) &(*parameter)->attribute);
  1626.     if ((*parameter)->value) fs_give ((void **) &(*parameter)->value);
  1627.                 /* run down the list as necessary */
  1628.     mail_free_body_parameter (&(*parameter)->next);
  1629.                 /* return body part to free storage */
  1630.     fs_give ((void **) parameter);
  1631.   }
  1632. }
  1633.  
  1634.  
  1635. /* Mail garbage collect body part
  1636.  * Accepts: pointer to body part pointer
  1637.  */
  1638.  
  1639. void mail_free_body_part (part)
  1640.     PART **part;
  1641. {
  1642.   if (*part) {            /* only free if exists */
  1643.     mail_free_body_data (&(*part)->body);
  1644.                 /* run down the list as necessary */
  1645.     mail_free_body_part (&(*part)->next);
  1646.     fs_give ((void **) part);    /* return body part to free storage */
  1647.   }
  1648. }
  1649.  
  1650. /* Mail garbage collect message cache
  1651.  * Accepts: mail stream
  1652.  *
  1653.  * The message cache is set to NIL when this function finishes.
  1654.  */
  1655.  
  1656. void mail_free_cache (stream)
  1657.     MAILSTREAM *stream;
  1658. {
  1659.                 /* flush the cache */
  1660.   (*mailcache) (stream,(long) 0,CH_INIT);
  1661.   stream->msgno = 0;        /* free this cruft too */
  1662.   mail_free_envelope (&stream->env);
  1663.   mail_free_body (&stream->body);
  1664.   if (stream->text) fs_give ((void **) &stream->text);
  1665. }
  1666.  
  1667.  
  1668. /* Mail garbage collect cache element
  1669.  * Accepts: pointer to cache element pointer
  1670.  */
  1671.  
  1672. void mail_free_elt (elt)
  1673.     MESSAGECACHE **elt;
  1674. {
  1675.                 /* only free if exists and no sharers */
  1676.   if (*elt && !--(*elt)->lockcount) fs_give ((void **) elt);
  1677.   else *elt = NIL;        /* else simply drop pointer */
  1678. }
  1679.  
  1680.  
  1681. /* Mail garbage collect long cache element
  1682.  * Accepts: pointer to long cache element pointer
  1683.  */
  1684.  
  1685. void mail_free_lelt (lelt)
  1686.     LONGCACHE **lelt;
  1687. {
  1688.                 /* only free if exists and no sharers */
  1689.   if (*lelt && !--(*lelt)->elt.lockcount) {
  1690.     mail_free_envelope (&(*lelt)->env);
  1691.     mail_free_body (&(*lelt)->body);
  1692.     fs_give ((void **) lelt);    /* return cache element to free storage */
  1693.   }
  1694.   else *lelt = NIL;        /* else simply drop pointer */
  1695. }
  1696.  
  1697. /* Mail garbage collect envelope
  1698.  * Accepts: pointer to envelope pointer
  1699.  */
  1700.  
  1701. void mail_free_envelope (env)
  1702.     ENVELOPE **env;
  1703. {
  1704.   if (*env) {            /* only free if exists */
  1705.     if ((*env)->remail) fs_give ((void **) &(*env)->remail);
  1706.     mail_free_address (&(*env)->return_path);
  1707.     if ((*env)->date) fs_give ((void **) &(*env)->date);
  1708.     mail_free_address (&(*env)->from);
  1709.     mail_free_address (&(*env)->sender);
  1710.     mail_free_address (&(*env)->reply_to);
  1711.     if ((*env)->subject) fs_give ((void **) &(*env)->subject);
  1712.     mail_free_address (&(*env)->to);
  1713.     mail_free_address (&(*env)->cc);
  1714.     mail_free_address (&(*env)->bcc);
  1715.     if ((*env)->in_reply_to) fs_give ((void **) &(*env)->in_reply_to);
  1716.     if ((*env)->message_id) fs_give ((void **) &(*env)->message_id);
  1717.     if ((*env)->newsgroups) fs_give ((void **) &(*env)->newsgroups);
  1718.     fs_give ((void **) env);    /* return envelope to free storage */
  1719.   }
  1720. }
  1721.  
  1722.  
  1723. /* Mail garbage collect address
  1724.  * Accepts: pointer to address pointer
  1725.  */
  1726.  
  1727. void mail_free_address (address)
  1728.     ADDRESS **address;
  1729. {
  1730.   if (*address) {        /* only free if exists */
  1731.     if ((*address)->personal) fs_give ((void **) &(*address)->personal);
  1732.     if ((*address)->adl) fs_give ((void **) &(*address)->adl);
  1733.     if ((*address)->mailbox) fs_give ((void **) &(*address)->mailbox);
  1734.     if ((*address)->host) fs_give ((void **) &(*address)->host);
  1735.     if ((*address)->error) fs_give ((void **) &(*address)->error);
  1736.     mail_free_address (&(*address)->next);
  1737.     fs_give ((void **) address);/* return address to free storage */
  1738.   }
  1739. }
  1740.