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

网络编程

开发平台:

Unix_Linux

  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: 1 November 1999
  14.  *
  15.  * Copyright 1999 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35. #include <ctype.h>
  36. #include <stdio.h>
  37. #include "mail.h"
  38. #include "osdep.h"
  39. #include <time.h>
  40. #include "misc.h"
  41. #include "rfc822.h"
  42. #include "utf8.h"
  43. #include "smtp.h"
  44. /* c-client global data */
  45. /* list of mail drivers */
  46. static DRIVER *maildrivers = NIL;
  47. /* list of authenticators */
  48. static AUTHENTICATOR *mailauthenticators = NIL;
  49. /* alternative network driver pointer */
  50. static NETDRIVER *mailaltdriver = NIL;
  51. /* alternative network driver name */
  52. static char *mailaltdrivername = NIL;
  53. /* pointer to alternate gets function */
  54. static mailgets_t mailgets = NIL;
  55. /* pointer to read progress function */
  56. static readprogress_t mailreadprogress = NIL;
  57. /* mail cache manipulation function */
  58. static mailcache_t mailcache = mm_cache;
  59. /* RFC-822 output generator */
  60. static rfc822out_t mail822out = NIL;
  61. /* SMTP verbose callback */
  62. static smtpverbose_t mailsmtpverbose = mm_dlog;
  63. /* proxy copy routine */
  64. static mailproxycopy_t mailproxycopy = NIL;
  65. /* RFC-822 external line parse */
  66. static parseline_t mailparseline = NIL;
  67. /* RFC-822 external phrase parser */
  68. static parsephrase_t mailparsephrase = NIL;
  69. /* sorted results callback */
  70. static sortresults_t mailsortresults = NIL;
  71. /* threaded results callback */
  72. static threadresults_t mailthreadresults = NIL;
  73. /* supported threaders */
  74. static THREADER mailthreadlist = {
  75.   "ORDEREDSUBJECT",
  76.   mail_thread_orderedsubject,
  77.   NIL
  78. };
  79. /* server name */
  80. static char *servicename = "unknown";
  81. static int expungeatping = T; /* mail_ping() may call mm_expunged() */
  82. static int tryaltfirst = NIL; /* always try alt driver first */
  83. static int notimezones = NIL; /* write timezones in "From " header */
  84. /* Default mail cache handler
  85.  * Accepts: pointer to cache handle
  86.  *     message number
  87.  *     caching function
  88.  * Returns: cache data
  89.  */
  90. void *mm_cache (MAILSTREAM *stream,unsigned long msgno,long op)
  91. {
  92.   size_t n;
  93.   void *ret = NIL;
  94.   unsigned long i;
  95.   switch ((int) op) { /* what function? */
  96.   case CH_INIT: /* initialize cache */
  97.     if (stream->cache) { /* flush old cache contents */
  98.       while (stream->cachesize) {
  99. mm_cache (stream,stream->cachesize,CH_FREE);
  100. mm_cache (stream,stream->cachesize--,CH_FREESORTCACHE);
  101.       }
  102.       fs_give ((void **) &stream->cache);
  103.       fs_give ((void **) &stream->sc);
  104.       stream->nmsgs = 0; /* can't have any messages now */
  105.     }
  106.     break;
  107.   case CH_SIZE: /* (re-)size the cache */
  108.     if (!stream->cache) { /* have a cache already? */
  109. /* no, create new cache */
  110.       n = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
  111.       stream->cache = (MESSAGECACHE **) memset (fs_get (n),0,n);
  112.       stream->sc = (SORTCACHE **) memset (fs_get (n),0,n);
  113.     }
  114. /* is existing cache size large neough */
  115.     else if (msgno > stream->cachesize) {
  116.       i = stream->cachesize; /* remember old size */
  117.       n = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
  118.       fs_resize ((void **) &stream->cache,n);
  119.       fs_resize ((void **) &stream->sc,n);
  120.       while (i < stream->cachesize) {
  121. stream->cache[i] = NIL;
  122. stream->sc[i++] = NIL;
  123.       }
  124.     }
  125.     break;
  126.   case CH_MAKEELT: /* return elt, make if necessary */
  127.     if (!stream->cache[msgno - 1])
  128.       stream->cache[msgno - 1] = mail_new_cache_elt (msgno);
  129. /* falls through */
  130.   case CH_ELT: /* return elt */
  131.     ret = (void *) stream->cache[msgno - 1];
  132.     break;
  133.   case CH_SORTCACHE: /* return sortcache entry, make if needed */
  134.     if (!stream->sc[msgno - 1]) stream->sc[msgno - 1] =
  135.       (SORTCACHE *) memset (fs_get (sizeof (SORTCACHE)),0,sizeof (SORTCACHE));
  136.     ret = (void *) stream->sc[msgno - 1];
  137.     break;
  138.   case CH_FREE: /* free elt */
  139.     mail_free_elt (&stream->cache[msgno - 1]);
  140.     break;
  141.   case CH_FREESORTCACHE:
  142.     if (stream->sc[msgno - 1]) {
  143.       if (stream->sc[msgno - 1]->from)
  144. fs_give ((void **) &stream->sc[msgno - 1]->from);
  145.       if (stream->sc[msgno - 1]->to)
  146. fs_give ((void **) &stream->sc[msgno - 1]->to);
  147.       if (stream->sc[msgno - 1]->cc)
  148. fs_give ((void **) &stream->sc[msgno - 1]->cc);
  149.       if (stream->sc[msgno - 1]->subject)
  150. fs_give ((void **) &stream->sc[msgno - 1]->subject);
  151.       fs_give ((void **) &stream->sc[msgno - 1]);
  152.     }
  153.     break;
  154.   case CH_EXPUNGE: /* expunge cache slot */
  155.     for (i = msgno - 1; msgno < stream->nmsgs; i++,msgno++) {
  156.       if (stream->cache[i] = stream->cache[msgno])
  157. stream->cache[i]->msgno = msgno;
  158.       stream->sc[i] = stream->sc[msgno];
  159.     }
  160.     stream->cache[i] = NIL; /* top of cache goes away */
  161.     stream->sc[i] = NIL;
  162.     break;
  163.   default:
  164.     fatal ("Bad mm_cache op");
  165.     break;
  166.   }
  167.   return ret;
  168. }
  169. /* Dummy string driver for complete in-memory strings */
  170. STRINGDRIVER mail_string = {
  171.   mail_string_init, /* initialize string structure */
  172.   mail_string_next, /* get next byte in string structure */
  173.   mail_string_setpos /* set position in string structure */
  174. };
  175. /* Initialize mail string structure for in-memory string
  176.  * Accepts: string structure
  177.  *     pointer to string
  178.  *     size of string
  179.  */
  180. void mail_string_init (STRING *s,void *data,unsigned long size)
  181. {
  182. /* set initial string pointers */
  183.   s->chunk = s->curpos = (char *) (s->data = data);
  184. /* and sizes */
  185.   s->size = s->chunksize = s->cursize = size;
  186.   s->data1 = s->offset = 0; /* never any offset */
  187. }
  188. /* Get next character from string
  189.  * Accepts: string structure
  190.  * Returns: character, string structure chunk refreshed
  191.  */
  192. char mail_string_next (STRING *s)
  193. {
  194.   return *s->curpos++; /* return the last byte */
  195. }
  196. /* Set string pointer position
  197.  * Accepts: string structure
  198.  *     new position
  199.  */
  200. void mail_string_setpos (STRING *s,unsigned long i)
  201. {
  202.   s->curpos = s->chunk + i; /* set new position */
  203.   s->cursize = s->chunksize - i;/* and new size */
  204. }
  205. /* Mail routines
  206.  *
  207.  *  mail_xxx routines are the interface between this module and the outside
  208.  * world.  Only these routines should be referenced by external callers.
  209.  *
  210.  *  Note that there is an important difference between a "sequence" and a
  211.  * "message #" (msgno).  A sequence is a string representing a sequence in
  212.  * {"n", "n:m", or combination separated by commas} format, whereas a msgno
  213.  * is a single integer.
  214.  *
  215.  */
  216. /* Mail link driver
  217.  * Accepts: driver to add to list
  218.  */
  219. void mail_link (DRIVER *driver)
  220. {
  221.   DRIVER **d = &maildrivers;
  222.   while (*d) d = &(*d)->next; /* find end of list of drivers */
  223.   *d = driver; /* put driver at the end */
  224.   driver->next = NIL; /* this driver is the end of the list */
  225. }
  226. /* Mail manipulate driver parameters
  227.  * Accepts: mail stream
  228.  *     function code
  229.  *     function-dependent value
  230.  * Returns: function-dependent return value
  231.  */
  232. void *mail_parameters (MAILSTREAM *stream,long function,void *value)
  233. {
  234.   void *r,*ret = NIL;
  235.   DRIVER *d;
  236.   AUTHENTICATOR *a;
  237.   switch ((int) function) {
  238.   case SET_THREADERS:
  239.     fatal ("SET_THREADERS not permitted");
  240.   case GET_THREADERS:
  241.     ret = (stream && stream->dtb) ?
  242.       (*stream->dtb->parameters) (function,stream) : (void *) &mailthreadlist;
  243.     break;
  244.   case SET_NAMESPACE:
  245.     fatal ("SET_NAMESPACE not permitted");
  246.   case GET_NAMESPACE:
  247.     ret = (stream && stream->dtb) ?
  248.       (*stream->dtb->parameters) (function,stream) :
  249. env_parameters (function,stream);
  250.     break;
  251.   case SET_DRIVERS:
  252.     fatal ("SET_DRIVERS not permitted");
  253.   case GET_DRIVERS:
  254.     ret = (void *) maildrivers;
  255.     break;
  256.   case SET_DRIVER:
  257.     fatal ("SET_DRIVER not permitted");
  258.   case GET_DRIVER:
  259.     for (d = maildrivers; d && strcmp (d->name,(char *) value); d = d->next);
  260.     ret = (void *) d;
  261.     break;
  262.   case ENABLE_DRIVER:
  263.     for (d = maildrivers; d && strcmp (d->name,(char *) value); d = d->next);
  264.     if (ret = (void *) d) d->flags &= ~DR_DISABLE;
  265.     break;
  266.   case DISABLE_DRIVER:
  267.     for (d = maildrivers; d && strcmp (d->name,(char *) value); d = d->next);
  268.     if (ret = (void *) d) d->flags |= DR_DISABLE;
  269.     break;
  270.   case ENABLE_AUTHENTICATOR: /* punt on this for the nonce */
  271.     fatal ("ENABLE_AUTHENTICATOR not permitted");
  272.   case DISABLE_AUTHENTICATOR:
  273.     for (a = mailauthenticators;/* scan authenticators */
  274.  a && strcmp (a->name,(char *) value); a = a->next);
  275.     if (a) { /* if authenticator name found */
  276.       a->client = NIL; /* blow it away */
  277.       a->server = NIL;
  278.     }
  279.     break;
  280.   case SET_GETS:
  281.     mailgets = (mailgets_t) value;
  282.   case GET_GETS:
  283.     ret = (void *) mailgets;
  284.     break;
  285.   case SET_READPROGRESS:
  286.     mailreadprogress = (readprogress_t) value;
  287.   case GET_READPROGRESS:
  288.     ret = (void *) mailreadprogress;
  289.     break;
  290.   case SET_CACHE:
  291.     mailcache = (mailcache_t) value;
  292.   case GET_CACHE:
  293.     ret = (void *) mailcache;
  294.     break;
  295.   case SET_RFC822OUTPUT:
  296.     mail822out = (rfc822out_t) value;
  297.   case GET_RFC822OUTPUT:
  298.     ret = (void *) mail822out;
  299.     break;
  300.   case SET_SMTPVERBOSE:
  301.     mailsmtpverbose = (smtpverbose_t) value;
  302.   case GET_SMTPVERBOSE:
  303.     ret = (void *) mailsmtpverbose;
  304.     break;
  305.   case SET_MAILPROXYCOPY:
  306.     mailproxycopy = (mailproxycopy_t) value;
  307.   case GET_MAILPROXYCOPY:
  308.     ret = (void *) mailproxycopy;
  309.     break;
  310.   case SET_PARSELINE:
  311.     mailparseline = (parseline_t) value;
  312.   case GET_PARSELINE:
  313.     ret = (void *) mailparseline;
  314.     break;
  315.   case SET_PARSEPHRASE:
  316.     mailparsephrase = (parsephrase_t) value;
  317.   case GET_PARSEPHRASE:
  318.     ret = (void *) mailparsephrase;
  319.     break;
  320.   case SET_SERVICENAME:
  321.     servicename = (char *) value;
  322.   case GET_SERVICENAME:
  323.     ret = (void *) servicename;
  324.     break;
  325.   case SET_EXPUNGEATPING:
  326.     expungeatping = (int) value;
  327.   case GET_EXPUNGEATPING:
  328.     value = (void *) expungeatping;
  329.     break;
  330.   case SET_SORTRESULTS:
  331.     mailsortresults = (sortresults_t) value;
  332.   case GET_SORTRESULTS:
  333.     ret = (void *) mailsortresults;
  334.     break;
  335.   case SET_THREADRESULTS:
  336.     mailthreadresults = (threadresults_t) value;
  337.   case GET_THREADRESULTS:
  338.     ret = (void *) mailthreadresults;
  339.     break;
  340.   case SET_ALTDRIVER:
  341.     mailaltdriver = (NETDRIVER *) value;
  342.   case GET_ALTDRIVER:
  343.     ret = (void *) mailaltdriver;
  344.     break;
  345.   case SET_ALTDRIVERNAME:
  346.     mailaltdrivername = (char *) value;
  347.   case GET_ALTDRIVERNAME:
  348.     ret = (void *) mailaltdrivername;
  349.     break;
  350.   case SET_TRYALTFIRST:
  351.     tryaltfirst = (int) value;
  352.   case GET_TRYALTFIRST:
  353.     ret = (void *) tryaltfirst;
  354.     break;
  355.   case SET_NOTIMEZONES:
  356.     notimezones = (int) value;
  357.   case GET_NOTIMEZONES:
  358.     ret = (void *) notimezones;
  359.     break;
  360.   default:
  361.     if (stream && stream->dtb) /* if have stream, do for its driver only */
  362.       ret = (*stream->dtb->parameters) (function,value);
  363. /* else do all drivers */
  364.     else for (d = maildrivers; d; d = d->next)
  365.       if (r = (d->parameters) (function,value)) ret = r;
  366. /* then do global values */
  367.     if (r = smtp_parameters (function,value)) ret = r;
  368.     if (r = env_parameters (function,value)) ret = r;
  369.     if (r = tcp_parameters (function,value)) ret = r;
  370.     break;
  371.   }
  372.   return ret;
  373. }
  374. /* Mail validate mailbox name
  375.  * Accepts: MAIL stream
  376.  *     mailbox name
  377.  *     purpose string for error message
  378.  * Return: driver factory on success, NIL on failure
  379.  */
  380. DRIVER *mail_valid (MAILSTREAM *stream,char *mailbox,char *purpose)
  381. {
  382.   char tmp[MAILTMPLEN];
  383.   DRIVER *factory = NIL;
  384.   if (strlen (mailbox) < (NETMAXHOST+NETMAXUSER+NETMAXMBX+NETMAXSRV+50))
  385.     for (factory = maildrivers; factory && 
  386.  ((factory->flags & DR_DISABLE) ||
  387.   ((factory->flags & DR_LOCAL) && (*mailbox == '{')) ||
  388.   !(*factory->valid) (mailbox));
  389.  factory = factory->next);
  390. /* must match stream if not dummy */
  391.   if (factory && stream && (stream->dtb != factory))
  392.     factory = strcmp (factory->name,"dummy") ? NIL : stream->dtb;
  393.   if (!factory && purpose) { /* if want an error message */
  394.     sprintf (tmp,"Can't %s %.80s: %s",purpose,mailbox,(*mailbox == '{') ?
  395.      "invalid remote specification" : "no such mailbox");
  396.     mm_log (tmp,ERROR);
  397.   }
  398.   return factory; /* return driver factory */
  399. }
  400. /* Mail validate network mailbox name
  401.  * Accepts: mailbox name
  402.  *     mailbox driver to validate against
  403.  *     pointer to where to return host name if non-NIL
  404.  *     pointer to where to return mailbox name if non-NIL
  405.  * Returns: driver on success, NIL on failure
  406.  */
  407. DRIVER *mail_valid_net (char *name,DRIVER *drv,char *host,char *mailbox)
  408. {
  409.   NETMBX mb;
  410.   if (!mail_valid_net_parse (name,&mb) || strcmp (mb.service,drv->name))
  411.     return NIL;
  412.   if (host) strcpy (host,mb.host);
  413.   if (mailbox) strcpy (mailbox,mb.mailbox);
  414.   return drv;
  415. }
  416. /* Mail validate network mailbox name
  417.  * Accepts: mailbox name
  418.  *     NETMBX structure to return values
  419.  * Returns: T on success, NIL on failure
  420.  */
  421. long mail_valid_net_parse (char *name,NETMBX *mb)
  422. {
  423.   int i,j;
  424.   char c,*s,*t,*v,tmp[MAILTMPLEN],arg[MAILTMPLEN];
  425. /* initialize structure */
  426.   memset (mb,'',sizeof (NETMBX));
  427. /* have host specification? */
  428.   if (!((*name++ == '{') && (v = strpbrk (name,"/:}")) && (i = v - name) &&
  429. (i < NETMAXHOST) && (t = strchr (v,'}')) && ((j = t - v)<MAILTMPLEN) &&
  430. (strlen (t+1) < (size_t) NETMAXMBX))) return NIL;
  431.   strncpy (mb->host,name,i); /* set host name */
  432.   strncpy (mb->orighost,name,i);
  433.   mb->host[i] = mb->orighost[i] = '';
  434.   strcpy (mb->mailbox,t+1); /* set mailbox name */
  435.   if (t - v) { /* any switches or port specification? */
  436.     strncpy (t = tmp,v,j); /* copy it */
  437.     tmp[j] = ''; /* tie it off */
  438.     c = *t++; /* get first delimiter */
  439.     do switch (c) { /* act based upon the character */
  440.     case ':': /* port specification */
  441.       if (mb->port || !(mb->port = strtoul (t,&t,10))) return NIL;
  442.       c = t ? *t++ : ''; /* get delimiter, advance pointer */
  443.       break;
  444.     case '/': /* switch */
  445. /* find delimiter */
  446.       if (t = strpbrk (s = t,"/:=")) {
  447. c = *t; /* remember delimiter for later */
  448. *t++ = ''; /* tie off switch name */
  449.       }
  450.       else c = ''; /* no delimiter */
  451.       lcase (s); /* coerce switch name to lower case */
  452.       if (c == '=') { /* parse switches which take arguments */
  453. if (*t == '"') { /* quoted string? */
  454.   for (v = arg,i = 0,++t; (c = *t++) != '"';) {
  455. /* quote next character */
  456.     if (c == '\') c = *t++;
  457.     arg[i++] = c;
  458.   }
  459.   c = *t++; /* remember delimiter for later */
  460.   arg[i] = ''; /* tie off argument */
  461. }
  462. else { /* non-quoted argument */
  463.   if (t = strpbrk (v = t,"/:")) {
  464.     c = *t; /* remember delimiter for later */
  465.     *t++ = ''; /* tie off switch name */
  466.   }
  467.   else c = ''; /* no delimiter */
  468.   i = strlen (v); /* length of argument */
  469. }
  470. if (!strcmp (s,"service") && (i < NETMAXSRV)) {
  471.   if (*mb->service) return NIL;
  472.   else strcpy (mb->service,lcase (v));
  473. }
  474. else if (!strcmp (s,"user") && (i < NETMAXUSER)) {
  475.   if (*mb->user) return NIL;
  476.   else strcpy (mb->user,v);
  477. }
  478. else return NIL; /* invalid argument switch */
  479.       }
  480.       else { /* non-argument switch */
  481. if (!strcmp (s,"anonymous")) mb->anoflag = T;
  482. else if (!strcmp (s,"debug")) mb->dbgflag = T;
  483. else if (!strcmp (s,"secure")) mb->secflag = T;
  484. else if (!strcmp (s,"alt")) mb->altflag = mailaltdriver ? T : NIL;
  485. else if (mailaltdriver && mailaltdrivername &&
  486.  !strcmp (s,mailaltdrivername)) mb->altflag = T;
  487. /* service switches below here */
  488. else if (*mb->service) return NIL;
  489. else if (!strncmp (s,"imap",4) &&
  490.  (!s[4] || !strcmp (s+4,"2") || !strcmp (s+4,"2bis") ||
  491.   !strcmp (s+4,"4") || !strcmp (s+4,"4rev1")))
  492.   strcpy (mb->service,"imap");
  493. else if (!strncmp (s,"pop",3) && (!s[3] || !strcmp (s+3,"3")))
  494.   strcpy (mb->service,"pop3");
  495. else if (!strcmp (s,"nntp") || !strcmp (s,"smtp"))
  496.   strcpy (mb->service,s);
  497. else return NIL; /* invalid non-argument switch */
  498.       }
  499.       break;
  500.     default: /* anything else is bogus */
  501.       return NIL;
  502.     } while (c); /* see if anything more to parse */
  503.   }
  504. /* default mailbox name */
  505.   if (!*mb->mailbox) strcpy (mb->mailbox,"INBOX");
  506. /* default service name */
  507.   if (!*mb->service) strcpy (mb->service,"imap");
  508.   return T;
  509. }
  510. /* Mail scan mailboxes for string
  511.  * Accepts: mail stream
  512.  *     reference
  513.  *     pattern to search
  514.  *     contents to search
  515.  */
  516. void mail_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
  517. {
  518.   int remote = ((*pat == '{') || (ref && *ref == '{'));
  519.   DRIVER *d;
  520.   if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */
  521.   if (stream) { /* if have a stream, do it for that stream */
  522.     if ((d = stream->dtb) && d->scan &&
  523. !(((d->flags & DR_LOCAL) && remote)))
  524.       (*d->scan) (stream,ref,pat,contents);
  525.   }
  526. /* otherwise do for all DTB's */
  527.   else for (d = maildrivers; d; d = d->next)
  528.     if (d->scan && !((d->flags & DR_DISABLE) ||
  529.      ((d->flags & DR_LOCAL) && remote)))
  530.       (d->scan) (NIL,ref,pat,contents);
  531. }
  532. /* Mail list mailboxes
  533.  * Accepts: mail stream
  534.  *     reference
  535.  *     pattern to search
  536.  */
  537. void mail_list (MAILSTREAM *stream,char *ref,char *pat)
  538. {
  539.   int remote = ((*pat == '{') || (ref && *ref == '{'));
  540.   DRIVER *d = maildrivers;
  541.   if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */
  542.   if (stream && stream->dtb) { /* if have a stream, do it for that stream */
  543.     if (!(((d = stream->dtb)->flags & DR_LOCAL) && remote))
  544.       (*d->list) (stream,ref,pat);
  545.   }
  546. /* otherwise do for all DTB's */
  547.   else do if (!((d->flags & DR_DISABLE) ||
  548. ((d->flags & DR_LOCAL) && remote)))
  549.     (d->list) (NIL,ref,pat);
  550.   while (d = d->next); /* until at the end */
  551. }
  552. /* Mail list subscribed mailboxes
  553.  * Accepts: mail stream
  554.  *     pattern to search
  555.  */
  556. void mail_lsub (MAILSTREAM *stream,char *ref,char *pat)
  557. {
  558.   int remote = ((*pat == '{') || (ref && *ref == '{'));
  559.   DRIVER *d = maildrivers;
  560.   if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */
  561.   if (stream && stream->dtb) { /* if have a stream, do it for that stream */
  562.     if (!(((d = stream->dtb)->flags & DR_LOCAL) && remote))
  563.       (*d->lsub) (stream,ref,pat);
  564.   }
  565. /* otherwise do for all DTB's */
  566.   else do if (!((d->flags & DR_DISABLE) ||
  567. ((d->flags & DR_LOCAL) && remote)))
  568.     (d->lsub) (NIL,ref,pat);
  569.   while (d = d->next); /* until at the end */
  570. }
  571. /* Mail subscribe to mailbox
  572.  * Accepts: mail stream
  573.  *     mailbox to add to subscription list
  574.  * Returns: T on success, NIL on failure
  575.  */
  576. long mail_subscribe (MAILSTREAM *stream,char *mailbox)
  577. {
  578.   DRIVER *factory = mail_valid (stream,mailbox,"subscribe to mailbox");
  579.   return factory ?
  580.     (factory->subscribe ?
  581.      (*factory->subscribe) (stream,mailbox) : sm_subscribe (mailbox)) : NIL;
  582. }
  583. /* Mail unsubscribe to mailbox
  584.  * Accepts: mail stream
  585.  *     mailbox to delete from subscription list
  586.  * Returns: T on success, NIL on failure
  587.  */
  588. long mail_unsubscribe (MAILSTREAM *stream,char *mailbox)
  589. {
  590.   DRIVER *factory = mail_valid (stream,mailbox,NIL);
  591.   return (factory && factory->unsubscribe) ?
  592.     (*factory->unsubscribe) (stream,mailbox) : sm_unsubscribe (mailbox);
  593. }
  594. /* Mail create mailbox
  595.  * Accepts: mail stream
  596.  *     mailbox name to create
  597.  * Returns: T on success, NIL on failure
  598.  */
  599. long mail_create (MAILSTREAM *stream,char *mailbox)
  600. {
  601.   MAILSTREAM *ts;
  602.   char *s,tmp[MAILTMPLEN];
  603.   DRIVER *d;
  604.   if (strlen (mailbox) >= (NETMAXHOST+NETMAXUSER+NETMAXMBX+NETMAXSRV+50)) {
  605.     sprintf (tmp,"Can't create %.80s: %s",mailbox,(*mailbox == '{') ?
  606.      "invalid remote specification" : "no such mailbox");
  607.     mm_log (tmp,ERROR);
  608.     return NIL;
  609.   }
  610. /* create of INBOX invalid */
  611.   if (!strcmp (lcase (strcpy (tmp,mailbox)),"inbox")) {
  612.     mm_log ("Can't create INBOX",ERROR);
  613.     return NIL;
  614.   }
  615.   for (s = mailbox; *s; s++) { /* make sure valid name */
  616.     if (*s & 0x80) { /* reserved for future use with UTF-8 */
  617.       mm_log ("Can't create mailbox name with 8-bit character",ERROR);
  618.       return NIL;
  619.     }
  620. /* validate modified UTF-7 */
  621.     else if (*s == '&') while (*++s != '-') switch (*s) {
  622.       case '':
  623. sprintf (tmp,"Can't create unterminated modified UTF-7 name: %.80s",
  624.  mailbox);
  625. mm_log (tmp,ERROR);
  626. return NIL;
  627.       default: /* must be alphanumeric */
  628. if (!isalnum (*s)) {
  629.   sprintf (tmp,"Can't create invalid modified UTF-7 name: %.80s",
  630.    mailbox);
  631.   mm_log (tmp,ERROR);
  632.   return NIL;
  633. }
  634.       case '+': /* valid modified BASE64 */
  635.       case ',':
  636. break; /* all OK so far */
  637.       }
  638.   }
  639. /* see if special driver hack */
  640.   if (!strncmp (tmp,"#driver.",8)) {
  641. /* tie off name at likely delimiter */
  642.     if (s = strpbrk (tmp+8,"/\:")) *s++ = '';
  643.     else {
  644.       sprintf (tmp,"Can't create mailbox %.80s: bad driver syntax",mailbox);
  645.       mm_log (tmp,ERROR);
  646.       return NIL;
  647.     }
  648.     for (d = maildrivers; d && strcmp (d->name,tmp+8); d = d->next);
  649.     if (d) mailbox += s - tmp; /* skip past driver specification */
  650.     else {
  651.       sprintf (tmp,"Can't create mailbox %.80s: unknown driver",mailbox);
  652.       mm_log (tmp,ERROR);
  653.       return NIL;
  654.     }
  655.   }
  656. /* use stream if one given or deterministic */
  657.   else if ((stream && stream->dtb) ||
  658.    (((*mailbox == '{') || (*mailbox == '#')) &&
  659.     (stream = mail_open (NIL,mailbox,OP_PROTOTYPE | OP_SILENT))))
  660.     d = stream->dtb;
  661.   else if ((*mailbox != '{') && (ts = default_proto (NIL))) d = ts->dtb;
  662.   else { /* failed utterly */
  663.     sprintf (tmp,"Can't create mailbox %.80s: indeterminate format",mailbox);
  664.     mm_log (tmp,ERROR);
  665.     return NIL;
  666.   }
  667.   return (*d->create) (stream,mailbox);
  668. }
  669. /* Mail delete mailbox
  670.  * Accepts: mail stream
  671.  *     mailbox name to delete
  672.  * Returns: T on success, NIL on failure
  673.  */
  674. long mail_delete (MAILSTREAM *stream,char *mailbox)
  675. {
  676.   long ret = NIL;
  677.   DRIVER *dtb = mail_valid (stream,mailbox,"delete mailbox");
  678.   if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
  679.       ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
  680.       ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
  681.       ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
  682.       ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5]) {
  683.     mm_log ("Can't delete INBOX",ERROR);
  684.   }
  685.   else SAFE_DISPATCH (dtb,ret,mbxdel,(stream,mailbox))
  686.   return ret;
  687. }
  688. /* Mail rename mailbox
  689.  * Accepts: mail stream
  690.  *     old mailbox name
  691.  *     new mailbox name
  692.  * Returns: T on success, NIL on failure
  693.  */
  694. long mail_rename (MAILSTREAM *stream,char *old,char *newname)
  695. {
  696.   long ret = NIL;
  697.   char tmp[MAILTMPLEN];
  698.   DRIVER *dtb = mail_valid (stream,old,"rename mailbox");
  699.   if ((*old != '{') && (*old != '#') && mail_valid (NIL,newname,NIL)) {
  700.     sprintf (tmp,"Can't rename to mailbox %.80s: mailbox already exists",
  701.      newname);
  702.     mm_log (tmp,ERROR);
  703.   }
  704.   else SAFE_DISPATCH (dtb,ret,mbxren,(stream,old,newname))
  705.   return ret;
  706. }
  707. /* Mail status of mailbox
  708.  * Accepts: mail stream
  709.  *     mailbox name
  710.  *     status flags
  711.  * Returns: T on success, NIL on failure
  712.  */
  713. long mail_status (MAILSTREAM *stream,char *mbx,long flags)
  714. {
  715.   long ret = NIL;
  716.   DRIVER *factory = mail_valid (stream,mbx,"get status of mailbox");
  717.   if (factory) { /* use driver's routine if have one */
  718.     if (factory->status) return (*factory->status) (stream,mbx,flags);
  719. /* foolish status of selected mbx? */
  720.     if (stream && !strcmp (mbx,stream->mailbox))
  721.       ret = mail_status_default (stream,mbx,flags);
  722.     else SAFE_FUNCTION (factory,ret,mail_status_default,(stream,mbx,flags))
  723.   }
  724.   return ret;
  725. }
  726. /* Mail status of mailbox default handler
  727.  * Accepts: mail stream
  728.  *     mailbox name
  729.  *     status flags
  730.  * Returns: T on success, NIL on failure
  731.  */
  732. long mail_status_default (MAILSTREAM *stream,char *mbx,long flags)
  733. {
  734.   MAILSTATUS status;
  735.   unsigned long i;
  736.   MAILSTREAM *tstream = NIL;
  737. /* make temporary stream (unless this mbx) */
  738.   if (!stream && !(stream = tstream =
  739.    mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
  740.   status.flags = flags; /* return status values */
  741.   status.messages = stream->nmsgs;
  742.   status.recent = stream->recent;
  743.   if (flags & SA_UNSEEN) /* must search to get unseen messages */
  744.     for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
  745.       if (!mail_elt (stream,i)->seen) status.unseen++;
  746.   status.uidnext = stream->uid_last + 1;
  747.   status.uidvalidity = stream->uid_validity;
  748. /* pass status to main program */
  749.   mm_status (stream,mbx,&status);
  750.   if (tstream) mail_close (tstream);
  751.   return T; /* success */
  752. }
  753. /* Mail open
  754.  * Accepts: candidate stream for recycling
  755.  *     mailbox name
  756.  *     open options
  757.  * Returns: stream to use on success, NIL on failure
  758.  */
  759. MAILSTREAM *mail_open (MAILSTREAM *stream,char *name,long options)
  760. {
  761.   int i;
  762.   char *s,tmp[MAILTMPLEN];
  763.   NETMBX mb;
  764.   DRIVER *d;
  765. /* see if special driver hack */
  766.   if ((options & OP_PROTOTYPE) && (name[0] == '#') &&
  767.       ((name[1] == 'D') || (name[1] == 'd')) &&
  768.       ((name[2] == 'R') || (name[2] == 'r')) &&
  769.       ((name[3] == 'I') || (name[3] == 'i')) &&
  770.       ((name[4] == 'V') || (name[4] == 'v')) &&
  771.       ((name[5] == 'E') || (name[5] == 'e')) &&
  772.       ((name[6] == 'R') || (name[6] == 'r')) && (name[7] == '.')) {
  773.     sprintf (tmp,"%.80s",name+8);
  774. /* tie off name at likely delimiter */
  775.     if (s = strpbrk (lcase (tmp),"/\:")) *s++ = '';
  776.     else {
  777.       sprintf (tmp,"Can't resolve mailbox %.80s: bad driver syntax",name);
  778.       mm_log (tmp,ERROR);
  779.       return NIL;
  780.     }
  781.     for (d = maildrivers; d && strcmp (d->name,tmp); d = d->next);
  782.     if (d) return (*d->open) (NIL);
  783.     else {
  784.       sprintf (tmp,"Can't resolve mailbox %.80s: unknown driver",name);
  785.       mm_log (tmp,ERROR);
  786.       return NIL;
  787.     }
  788.   }
  789.   else d = mail_valid (NIL,name,(options & OP_SILENT) ?
  790.        (char *) NIL : "open mailbox");
  791.   if (d) { /* must have a factory */
  792.     if (options & OP_PROTOTYPE) return (*d->open) (NIL);
  793.     if (stream) { /* recycling requested? */
  794. /* yes, recycleable stream? */
  795.       if ((stream->dtb == d) && (d->flags & DR_RECYCLE) &&
  796.   mail_usable_network_stream (stream,name)) {
  797. mail_free_cache(stream);/* yes, clean up stream */
  798. if (stream->mailbox) fs_give ((void **) &stream->mailbox);
  799. /* flush user flags */
  800. for (i = 0; i < NUSERFLAGS; i++)
  801.   if (stream->user_flags[i]) fs_give ((void **)&stream->user_flags[i]);
  802.       }
  803.       else { /* stream not recycleable, babble if net */
  804. if (!stream->silent && stream->dtb && !(stream->dtb->flags&DR_LOCAL) &&
  805.     mail_valid_net_parse (stream->mailbox,&mb)) {
  806.   sprintf (tmp,"Closing connection to %.80s",mb.host);
  807.   mm_log (tmp,(long) NIL);
  808. }
  809. /* flush the old stream */
  810. stream = mail_close (stream);
  811.       }
  812.     }
  813. /* instantiate new stream if not recycling */
  814.     if (!stream) (*mailcache) (stream = (MAILSTREAM *)
  815.        memset (fs_get (sizeof (MAILSTREAM)),0,
  816.        sizeof (MAILSTREAM)),(long) 0,CH_INIT);
  817.     stream->dtb = d; /* set dispatch */
  818. /* set mailbox name */
  819.     stream->mailbox = cpystr (name);
  820. /* initialize stream flags */
  821.     stream->inbox = stream->lock = NIL;
  822.     stream->debug = (options & OP_DEBUG) ? T : NIL;
  823.     stream->rdonly = (options & OP_READONLY) ? T : NIL;
  824.     stream->anonymous = (options & OP_ANONYMOUS) ? T : NIL;
  825.     stream->scache = (options & OP_SHORTCACHE) ? T : NIL;
  826.     stream->silent = (options & OP_SILENT) ? T : NIL;
  827.     stream->halfopen = (options & OP_HALFOPEN) ? T : NIL;
  828.     stream->secure = (options & OP_SECURE) ? T : NIL;
  829.     stream->tryalt = (options & OP_TRYALT) ? T : NIL;
  830.     stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
  831.       stream->perm_answered = stream->perm_draft = stream->kwd_create = NIL;
  832.     stream->uid_nosticky = (d->flags & DR_NOSTICKY) ? T : NIL;
  833.     stream->uid_last = 0; /* default UID validity */
  834.     stream->uid_validity = time (0);
  835. /* have driver open, flush if failed */
  836.     if (!(*d->open) (stream)) stream = mail_close (stream);
  837.   }
  838.   return stream; /* return the stream */
  839. }
  840. /* Mail close
  841.  * Accepts: mail stream
  842.  *     close options
  843.  * Returns: NIL, always
  844.  */
  845. MAILSTREAM *mail_close_full (MAILSTREAM *stream,long options)
  846. {
  847.   int i;
  848.   if (stream) { /* make sure argument given */
  849. /* do the driver's close action */
  850.     if (stream->dtb) (*stream->dtb->close) (stream,options);
  851.     if (stream->mailbox) fs_give ((void **) &stream->mailbox);
  852.     stream->sequence++; /* invalidate sequence */
  853. /* flush user flags */
  854.     for (i = 0; i < NUSERFLAGS; i++)
  855.       if (stream->user_flags[i]) fs_give ((void **) &stream->user_flags[i]);
  856.     mail_free_cache (stream); /* finally free the stream's storage */
  857.     if (!stream->use) fs_give ((void **) &stream);
  858.   }
  859.   return NIL;
  860. }
  861. /* Mail make handle
  862.  * Accepts: mail stream
  863.  * Returns: handle
  864.  *
  865.  *  Handles provide a way to have multiple pointers to a stream yet allow the
  866.  * stream's owner to nuke it or recycle it.
  867.  */
  868. MAILHANDLE *mail_makehandle (MAILSTREAM *stream)
  869. {
  870.   MAILHANDLE *handle = (MAILHANDLE *) fs_get (sizeof (MAILHANDLE));
  871.   handle->stream = stream; /* copy stream */
  872. /* and its sequence */
  873.   handle->sequence = stream->sequence;
  874.   stream->use++; /* let stream know another handle exists */
  875.   return handle;
  876. }
  877. /* Mail release handle
  878.  * Accepts: Mail handle
  879.  */
  880. void mail_free_handle (MAILHANDLE **handle)
  881. {
  882.   MAILSTREAM *s;
  883.   if (*handle) { /* only free if exists */
  884. /* resign stream, flush unreferenced zombies */
  885.     if ((!--(s = (*handle)->stream)->use) && !s->dtb) fs_give ((void **) &s);
  886.     fs_give ((void **) handle); /* now flush the handle */
  887.   }
  888. }
  889. /* Mail get stream handle
  890.  * Accepts: Mail handle
  891.  * Returns: mail stream or NIL if stream gone
  892.  */
  893. MAILSTREAM *mail_stream (MAILHANDLE *handle)
  894. {
  895.   MAILSTREAM *s = handle->stream;
  896.   return (s->dtb && (handle->sequence == s->sequence)) ? s : NIL;
  897. }
  898. /* Mail fetch cache element
  899.  * Accepts: mail stream
  900.  *     message # to fetch
  901.  * Returns: cache element of this message
  902.  * Can also be used to create cache elements for new messages.
  903.  */
  904. MESSAGECACHE *mail_elt (MAILSTREAM *stream,unsigned long msgno)
  905. {
  906.   if (msgno < 1 || msgno > stream->nmsgs) {
  907.     char tmp[MAILTMPLEN];
  908.     sprintf (tmp,"Bad msgno %lu in mail_elt, nmsgs = %lu",msgno,stream->nmsgs);
  909.     fatal (tmp);
  910.   }
  911.   return (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_MAKEELT);
  912. }
  913. /* Mail fetch fast information
  914.  * Accepts: mail stream
  915.  *     sequence
  916.  *     option flags
  917.  *
  918.  * Generally, mail_fetch_structure is preferred
  919.  */
  920. void mail_fetch_fast (MAILSTREAM *stream,char *sequence,long flags)
  921. {
  922.    /* do the driver's action */
  923.   if (stream->dtb && stream->dtb->fast)
  924.     (*stream->dtb->fast) (stream,sequence,flags);
  925. }
  926. /* Mail fetch flags
  927.  * Accepts: mail stream
  928.  *     sequence
  929.  *     option flags
  930.  */
  931. void mail_fetch_flags (MAILSTREAM *stream,char *sequence,long flags)
  932. {
  933.    /* do the driver's action */
  934.   if (stream->dtb && stream->dtb->msgflags)
  935.     (*stream->dtb->msgflags) (stream,sequence,flags);
  936. }
  937. /* Mail fetch message overview
  938.  * Accepts: mail stream
  939.  *     UID sequence to fetch
  940.  *     pointer to overview return function
  941.  */
  942. void mail_fetch_overview (MAILSTREAM *stream,char *sequence,overview_t ofn)
  943. {
  944.   if (stream->dtb && !(stream->dtb->overview &&
  945.        (*stream->dtb->overview) (stream,sequence,ofn)) &&
  946.       mail_uid_sequence (stream,sequence) && mail_ping (stream)) {
  947.     MESSAGECACHE *elt;
  948.     ENVELOPE *env;
  949.     OVERVIEW ov;
  950.     unsigned long i;
  951.     ov.optional.lines = 0;
  952.     ov.optional.xref = NIL;
  953.     for (i = 1; i <= stream->nmsgs; i++)
  954.       if (((elt = mail_elt (stream,i))->sequence) &&
  955.   (env = mail_fetch_structure (stream,i,NIL,NIL)) && ofn) {
  956. ov.subject = env->subject;
  957. ov.from = env->from;
  958. ov.date = env->date;
  959. ov.message_id = env->message_id;
  960. ov.references = env->references;
  961. ov.optional.octets = elt->rfc822_size;
  962. (*ofn) (stream,mail_uid (stream,i),&ov);
  963.       }
  964.   }
  965. }
  966. /* Mail fetch message structure
  967.  * Accepts: mail stream
  968.  *     message # to fetch
  969.  *     pointer to return body
  970.  *     option flags
  971.  * Returns: envelope of this message, body returned in body value
  972.  *
  973.  * Fetches the "fast" information as well
  974.  */
  975. ENVELOPE *mail_fetch_structure (MAILSTREAM *stream,unsigned long msgno,
  976. BODY **body,long flags)
  977. {
  978.   ENVELOPE **env;
  979.   BODY **b;
  980.   MESSAGECACHE *elt;
  981.   char c,*s,*hdr;
  982.   unsigned long hdrsize;
  983.   STRING bs;
  984. /* do the driver's action if specified */
  985.   if (stream->dtb && stream->dtb->structure)
  986.     return (*stream->dtb->structure) (stream,msgno,body,flags);
  987.   if (flags & FT_UID) { /* UID form of call */
  988.     if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
  989.     else return NIL; /* must get UID/msgno map first */
  990.   }
  991.   elt = mail_elt (stream,msgno);/* get elt for real message number */
  992.   if (stream->scache) { /* short caching */
  993.     if (msgno != stream->msgno){/* garbage collect if not same message */
  994.       mail_gc (stream,GC_ENV | GC_TEXTS);
  995.       stream->msgno = msgno; /* this is the current message now */
  996.     }
  997.     env = &stream->env; /* get pointers to envelope and body */
  998.     b = &stream->body;
  999.   }
  1000.   else { /* get pointers to elt envelope and body */
  1001.     env = &elt->private.msg.env;
  1002.     b = &elt->private.msg.body;
  1003.   }
  1004.   if (stream->dtb && ((body && !*b) || !*env || (*env)->incomplete)) {
  1005.     mail_free_envelope (env); /* flush old envelope and body */
  1006.     mail_free_body (b);
  1007. /* see if need to fetch the whole thing */
  1008.     if (body || !elt->rfc822_size) {
  1009.       s = (*stream->dtb->header) (stream,msgno,&hdrsize,flags & ~FT_INTERNAL);
  1010. /* make copy in case body fetch smashes it */
  1011.       hdr = (char *) memcpy (fs_get ((size_t) hdrsize+1),s,(size_t) hdrsize);
  1012.       hdr[hdrsize] = ''; /* tie off header */
  1013.       (*stream->dtb->text) (stream,msgno,&bs,(flags & ~FT_INTERNAL) | FT_PEEK);
  1014.       if (!elt->rfc822_size) elt->rfc822_size = hdrsize + SIZE (&bs);
  1015.       if (body) /* only parse body if requested */
  1016. rfc822_parse_msg (env,b,hdr,hdrsize,&bs,BADHOST,stream->dtb->flags);
  1017.       else
  1018. rfc822_parse_msg (env,NIL,hdr,hdrsize,NIL,BADHOST,stream->dtb->flags);
  1019.       fs_give ((void **) &hdr); /* flush header */
  1020.     }
  1021.     else { /* can save memory doing it this way */
  1022.       hdr = (*stream->dtb->header) (stream,msgno,&hdrsize,flags | FT_INTERNAL);
  1023.       c = hdr[hdrsize]; /* preserve what's there */
  1024.       hdr[hdrsize] = ''; /* tie off header */
  1025.       rfc822_parse_msg (env,NIL,hdr,hdrsize,NIL,BADHOST,stream->dtb->flags);
  1026.       hdr[hdrsize] = c; /* restore in case cached data */
  1027.     }
  1028.   }
  1029. /* if need date, have date in envelope? */
  1030.   if (!elt->day && *env && (*env)->date) mail_parse_date (elt,(*env)->date);
  1031. /* sigh, fill in bogus default */
  1032.   if (!elt->day) elt->day = elt->month = 1;
  1033.   if (body) *body = *b; /* return the body */
  1034.   return *env; /* return the envelope */
  1035. }
  1036. /* Mail mark single message (internal use only)
  1037.  * Accepts: mail stream
  1038.  *     elt to mark
  1039.  *     fetch flags
  1040.  */
  1041. static void markseen (MAILSTREAM *stream,MESSAGECACHE *elt,long flags)
  1042. {
  1043.   unsigned long i;
  1044.   char sequence[20];
  1045.   MESSAGECACHE *e;
  1046. /* non-peeking and needs to set Seen? */
  1047.   if (!(flags & FT_PEEK) && !elt->seen) {
  1048.     if (stream->dtb->flagmsg){ /* driver wants per-message call? */
  1049.       elt->valid = NIL; /* do pre-alteration driver call */
  1050.       (*stream->dtb->flagmsg) (stream,elt);
  1051. /* set seen, do post-alteration driver call */
  1052.       elt->seen = elt->valid = T;
  1053.       (*stream->dtb->flagmsg) (stream,elt);
  1054.     }
  1055.     if (stream->dtb->flag) { /* driver wants one-time call?  */
  1056. /* better safe than sorry, save seq bits */
  1057.       for (i = 1; i <= stream->nmsgs; i++) {
  1058. e = mail_elt (stream,i);
  1059. e->private.sequence = e->sequence;
  1060.       }
  1061. /* call driver to set the message */
  1062.       sprintf (sequence,"%lu",elt->msgno);
  1063.       (*stream->dtb->flag) (stream,sequence,"\Seen",ST_SET);
  1064. /* restore sequence bits */
  1065.       for (i = 1; i <= stream->nmsgs; i++) {
  1066. e = mail_elt (stream,i);
  1067. e->sequence = e->private.sequence;
  1068.       }
  1069.     }
  1070.     mm_flags(stream,elt->msgno);/* notify mail program of flag change */
  1071.   }
  1072. }
  1073. /* Mail fetch message
  1074.  * Accepts: mail stream
  1075.  *     message # to fetch
  1076.  *     pointer to returned length
  1077.  *     flags
  1078.  * Returns: message text
  1079.  */
  1080. char *mail_fetch_message (MAILSTREAM *stream,unsigned long msgno,
  1081.   unsigned long *len,long flags)
  1082. {
  1083.   GETS_DATA md;
  1084.   SIZEDTEXT *t;
  1085.   STRING bs;
  1086.   MESSAGECACHE *elt;
  1087.   char *s,*u;
  1088.   unsigned long i,j;
  1089.   if (len) *len = 0; /* default return size */
  1090.   if (flags & FT_UID) { /* UID form of call */
  1091.     if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
  1092.     else return ""; /* must get UID/msgno map first */
  1093.   }
  1094. /* initialize message data identifier */
  1095.   INIT_GETS (md,stream,msgno,"",0,0);
  1096. /* is data already cached? */
  1097.   if ((t = &(elt = mail_elt (stream,msgno))->private.msg.full.text)->data) {
  1098.     markseen (stream,elt,flags);/* mark message seen */
  1099.     return mail_fetch_text_return (&md,t,len);
  1100.   }
  1101.   if (!stream->dtb) return ""; /* not in cache, must have live driver */
  1102.   if (stream->dtb->msgdata) return
  1103.     ((*stream->dtb->msgdata) (stream,msgno,"",0,0,NIL,flags) && t->data) ?
  1104.       mail_fetch_text_return (&md,t,len) : "";
  1105. /* ugh, have to do this the crufty way */
  1106.   u = mail_fetch_header (stream,msgno,NIL,NIL,&i,flags);
  1107. /* copy in case text method stomps on it */
  1108.   s = (char *) memcpy (fs_get ((size_t) i),u,(size_t) i);
  1109.   if ((*stream->dtb->text) (stream,msgno,&bs,flags)) {
  1110.     t = &stream->text; /* build combined copy */
  1111.     if (t->data) fs_give ((void **) &t->data);
  1112.     t->data = (unsigned char *) fs_get ((t->size = i + SIZE (&bs)) + 1);
  1113.     memcpy (t->data,s,(size_t) i);
  1114.     for (j = i; j < t->size; j++) t->data[j] = SNX (&bs);
  1115.     t->data[j] = ''; /* tie off data */
  1116.     u = mail_fetch_text_return (&md,t,len);
  1117.   }
  1118.   else u = "";
  1119.   fs_give ((void **) &s); /* finished with copy of header */
  1120.   return u;
  1121. }
  1122. /* Mail fetch message header
  1123.  * Accepts: mail stream
  1124.  *     message # to fetch
  1125.  *     MIME section specifier (#.#.#...#)
  1126.  *     list of lines to fetch
  1127.  *     pointer to returned length
  1128.  *     flags
  1129.  * Returns: message header in RFC822 format
  1130.  *
  1131.  * Note: never calls a mailgets routine
  1132.  */
  1133. char *mail_fetch_header (MAILSTREAM *stream,unsigned long msgno,char *section,
  1134.  STRINGLIST *lines,unsigned long *len,long flags)
  1135. {
  1136.   STRING bs;
  1137.   BODY *b = NIL;
  1138.   SIZEDTEXT *t = NIL,rt;
  1139.   MESSAGE *m = NIL;
  1140.   MESSAGECACHE *elt;
  1141.   char tmp[MAILTMPLEN];
  1142.   if (len) *len = 0; /* default return size */
  1143.   if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
  1144.   if (flags & FT_UID) { /* UID form of call */
  1145.     if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
  1146.     else return ""; /* must get UID/msgno map first */
  1147.   }
  1148.   elt = mail_elt (stream,msgno);/* get cache data */
  1149.   if (section && *section) { /* nested body header wanted? */
  1150.     if (!((b = mail_body (stream,msgno,section)) &&
  1151.   (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
  1152.       return ""; /* lose if no body or not MESSAGE/RFC822 */
  1153.     m = b->nested.msg; /* point to nested message */
  1154.   }
  1155. /* else top-level message header wanted */
  1156.   else m = &elt->private.msg;
  1157.   if (m->header.text.data && mail_match_lines (lines,m->lines,flags)) {
  1158.     if (lines) textcpy (t = &stream->text,&m->header.text);
  1159.     else t = &m->header.text; /* in cache, and cache is valid */
  1160.     markseen (stream,elt,flags);/* mark message seen */
  1161.   }
  1162.   else if (stream->dtb) { /* not in cache, has live driver? */
  1163.     if (stream->dtb->msgdata) { /* has driver section fetch? */
  1164. /* build driver section specifier */
  1165.       if (section && *section) sprintf (tmp,"%s.HEADER",section);
  1166.       else strcpy (tmp,"HEADER");
  1167.       if ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,lines,flags)) {
  1168. t = &m->header.text; /* fetch data */
  1169. /* don't need to postprocess lines */
  1170. if (m->lines) lines = NIL;
  1171. else if (lines) textcpy (t = &stream->text,&m->header.text);
  1172.       }
  1173.     }
  1174.     else if (b) { /* nested body wanted? */
  1175.       if (stream->private.search.text) {
  1176. rt.data = (unsigned char *) stream->private.search.text +
  1177.   b->nested.msg->header.offset;
  1178. rt.size = b->nested.msg->header.text.size;
  1179. t = &rt;
  1180.       }
  1181.       else if ((*stream->dtb->text) (stream,msgno,&bs,flags & ~FT_INTERNAL)) {
  1182. if ((bs.dtb->next == mail_string_next) && !lines) {
  1183.   rt.data = (unsigned char *) bs.curpos + b->nested.msg->header.offset;
  1184.   rt.size = b->nested.msg->header.text.size;
  1185.   if (stream->private.search.string)
  1186.     stream->private.search.text = bs.curpos;
  1187.   t = &rt; /* special hack to avoid extra copy */
  1188. }
  1189. else textcpyoffstring (t = &stream->text,&bs,
  1190.        b->nested.msg->header.offset,
  1191.        b->nested.msg->header.text.size);
  1192.       }
  1193.     }
  1194.     else { /* top-level header fetch */
  1195. /* mark message seen */
  1196.       markseen (stream,elt,flags);
  1197.       if (rt.data = (unsigned char *)
  1198.   (*stream->dtb->header) (stream,msgno,&rt.size,flags)) {
  1199. /* make a safe copy if need to filter */
  1200. if (lines) textcpy (t = &stream->text,&rt);
  1201. else t = &rt; /* top level header */
  1202.       }
  1203.     }
  1204.   }
  1205.   if (!t || !t->data) return "";/* error if no string */
  1206. /* filter headers if requested */
  1207.   if (lines) t->size = mail_filter ((char *) t->data,t->size,lines,flags);
  1208.   if (len) *len = t->size; /* return size if requested */
  1209.   return (char *) t->data; /* and text */
  1210. }
  1211. /* Mail fetch message text
  1212.  * Accepts: mail stream
  1213.  *     message # to fetch
  1214.  *     MIME section specifier (#.#.#...#)
  1215.  *     pointer to returned length
  1216.  *     flags
  1217.  * Returns: message text
  1218.  */
  1219. char *mail_fetch_text (MAILSTREAM *stream,unsigned long msgno,char *section,
  1220.        unsigned long *len,long flags)
  1221. {
  1222.   GETS_DATA md;
  1223.   PARTTEXT *p;
  1224.   STRING bs;
  1225.   MESSAGECACHE *elt;
  1226.   BODY *b = NIL;
  1227.   char tmp[MAILTMPLEN];
  1228.   unsigned long i;
  1229.   if (len) *len = 0; /* default return size */
  1230.   if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
  1231.   if (flags & FT_UID) { /* UID form of call */
  1232.     if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
  1233.     else return ""; /* must get UID/msgno map first */
  1234.   }
  1235.   elt = mail_elt (stream,msgno);/* get cache data */
  1236.   if (section && *section) { /* nested body text wanted? */
  1237.     if (!((b = mail_body (stream,msgno,section)) &&
  1238.   (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
  1239.       return ""; /* lose if no body or not MESSAGE/RFC822 */
  1240.     p = &b->nested.msg->text; /* point at nested message */
  1241. /* build IMAP-format section specifier */
  1242.     sprintf (tmp,"%s.TEXT",section);
  1243.     flags &= ~FT_INTERNAL; /* can't win with this set */
  1244.   }
  1245.   else { /* top-level message text wanted */
  1246.     p = &elt->private.msg.text;
  1247.     strcpy (tmp,"TEXT");
  1248.   }
  1249. /* initialize message data identifier */
  1250.   INIT_GETS (md,stream,msgno,section,0,0);
  1251.   if (p->text.data) { /* is data already cached? */
  1252.     markseen (stream,elt,flags);/* mark message seen */
  1253.     return mail_fetch_text_return (&md,&p->text,len);
  1254.   }
  1255.   if (!stream->dtb) return ""; /* not in cache, must have live driver */
  1256.   if (stream->dtb->msgdata) return
  1257.     ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,NIL,flags) && p->text.data)?
  1258.       mail_fetch_text_return (&md,&p->text,len) : "";
  1259.   if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return "";
  1260.   if (section && *section) { /* nested is more complex */
  1261.     SETPOS (&bs,p->offset);
  1262.     i = p->text.size; /* just want this much */
  1263.   }
  1264.   else i = SIZE (&bs); /* want entire text */
  1265.   return mail_fetch_string_return (&md,&bs,i,len);
  1266. }
  1267. /* Mail fetch message body part MIME headers
  1268.  * Accepts: mail stream
  1269.  *     message # to fetch
  1270.  *     MIME section specifier (#.#.#...#)
  1271.  *     pointer to returned length
  1272.  *     flags
  1273.  * Returns: message text
  1274.  */
  1275. char *mail_fetch_mime (MAILSTREAM *stream,unsigned long msgno,char *section,
  1276.        unsigned long *len,long flags)
  1277. {
  1278.   PARTTEXT *p;
  1279.   STRING bs;
  1280.   BODY *b;
  1281.   char tmp[MAILTMPLEN];
  1282.   if (len) *len = 0; /* default return size */
  1283.   if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
  1284.   if (flags & FT_UID) { /* UID form of call */
  1285.     if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
  1286.     else return ""; /* must get UID/msgno map first */
  1287.   }
  1288.   flags &= ~FT_INTERNAL; /* can't win with this set */
  1289.   if (!(section && *section && (b = mail_body (stream,msgno,section))))
  1290.     return ""; /* not valid section */
  1291. /* in cache? */
  1292.   if ((p = &b->mime)->text.data) {
  1293. /* mark message seen */
  1294.     markseen (stream,mail_elt (stream,msgno),flags);
  1295.     if (len) *len = p->text.size;
  1296.     return (char *) p->text.data;
  1297.   }
  1298.   if (!stream->dtb) return ""; /* not in cache, must have live driver */
  1299.   if (stream->dtb->msgdata) { /* has driver fetch? */
  1300. /* build driver section specifier */
  1301.     sprintf (tmp,"%s.MIME",section);
  1302.     if ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,NIL,flags) &&
  1303. p->text.data) {
  1304.       if (len) *len = p->text.size;
  1305.       return (char *) p->text.data;
  1306.     }
  1307.     else return "";
  1308.   }
  1309.   if (len) *len = b->mime.text.size;
  1310.   if (!b->mime.text.size) { /* empty MIME header -- mark seen anyway */
  1311.     markseen (stream,mail_elt (stream,msgno),flags);
  1312.     return "";
  1313.   }
  1314. /* have to get it from offset */
  1315.   if (stream->private.search.text)
  1316.     return stream->private.search.text + b->mime.offset;
  1317.   if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) {
  1318.     if (len) *len = 0;
  1319.     return "";
  1320.   }
  1321.   if (bs.dtb->next == mail_string_next) {
  1322.     if (stream->private.search.string) stream->private.search.text = bs.curpos;
  1323.     return bs.curpos + b->mime.offset;
  1324.   }
  1325.   return textcpyoffstring (&stream->text,&bs,b->mime.offset,b->mime.text.size);
  1326. }
  1327. /* Mail fetch message body part
  1328.  * Accepts: mail stream
  1329.  *     message # to fetch
  1330.  *     MIME section specifier (#.#.#...#)
  1331.  *     pointer to returned length
  1332.  *     flags
  1333.  * Returns: message body
  1334.  */
  1335. char *mail_fetch_body (MAILSTREAM *stream,unsigned long msgno,char *section,
  1336.        unsigned long *len,long flags)
  1337. {
  1338.   GETS_DATA md;
  1339.   PARTTEXT *p;
  1340.   STRING bs;
  1341.   BODY *b;
  1342.   SIZEDTEXT *t;
  1343.   char *s,tmp[MAILTMPLEN];
  1344.   if (!(section && *section)) /* top-level text wanted? */
  1345.     return mail_fetch_message (stream,msgno,len,flags);
  1346.   else if (strlen (section) > (MAILTMPLEN - 20)) return "";
  1347.   flags &= ~FT_INTERNAL; /* can't win with this set */
  1348. /* initialize message data identifier */
  1349.   INIT_GETS (md,stream,msgno,section,0,0);
  1350. /* kludge for old section 0 header */
  1351.   if (!strcmp (s = strcpy (tmp,section),"0") ||
  1352.       ((s = strstr (tmp,".0")) && !s[2])) {
  1353.     SIZEDTEXT ht;
  1354.     *s = ''; /* tie off section */
  1355. /* this silly way so it does mailgets */
  1356.     ht.data = (unsigned char *) mail_fetch_header (stream,msgno,
  1357.    tmp[0] ? tmp : NIL,NIL,
  1358.    &ht.size,flags);
  1359. /* may have UIDs here */
  1360.     md.flags = (flags & FT_UID) ? MG_UID : NIL;
  1361.     return mail_fetch_text_return (&md,&ht,len);
  1362.   }
  1363.   if (len) *len = 0; /* default return size */
  1364.   if (flags & FT_UID) { /* UID form of call */
  1365.     if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
  1366.     else return ""; /* must get UID/msgno map first */
  1367.   }
  1368. /* must have body */
  1369.   if (!(b = mail_body (stream,msgno,section))) return "";
  1370. /* have cached text? */
  1371.   if ((t = &(p = &b->contents)->text)->data) {
  1372. /* mark message seen */
  1373.     markseen (stream,mail_elt (stream,msgno),flags);
  1374.     return mail_fetch_text_return (&md,t,len);
  1375.   }
  1376.   if (!stream->dtb) return ""; /* not in cache, must have live driver */
  1377.   if (stream->dtb->msgdata) return
  1378.     ((*stream->dtb->msgdata)(stream,msgno,section,0,0,NIL,flags) && t->data) ?
  1379.       mail_fetch_text_return (&md,t,len) : "";
  1380.   if (len) *len = t->size;
  1381.   if (!t->size) { /* empty body part -- mark seen anyway */
  1382.     markseen (stream,mail_elt (stream,msgno),flags);
  1383.     return "";
  1384.   }
  1385. /* copy body from stringstruct offset */
  1386.   if (stream->private.search.text)
  1387.     return stream->private.search.text + p->offset;
  1388.   if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) {
  1389.     if (len) *len = 0;
  1390.     return "";
  1391.   }
  1392.   if (bs.dtb->next == mail_string_next) {
  1393.     if (stream->private.search.string) stream->private.search.text = bs.curpos;
  1394.     return bs.curpos + p->offset;
  1395.   }
  1396.   SETPOS (&bs,p->offset);
  1397.   return mail_fetch_string_return (&md,&bs,t->size,len);
  1398. }
  1399. /* Mail fetch partial message text
  1400.  * Accepts: mail stream
  1401.  *     message # to fetch
  1402.  *     MIME section specifier (#.#.#...#)
  1403.  *     offset of first designed byte or 0 to start at beginning
  1404.  *     maximum number of bytes or 0 for all bytes
  1405.  *     flags
  1406.  * Returns: T if successful, else NIL
  1407.  */
  1408. long mail_partial_text (MAILSTREAM *stream,unsigned long msgno,char *section,
  1409. unsigned long first,unsigned long last,long flags)
  1410. {
  1411.   GETS_DATA md;
  1412.   PARTTEXT *p = NIL;
  1413.   MESSAGECACHE *elt;
  1414.   STRING bs;
  1415.   BODY *b;
  1416.   char tmp[MAILTMPLEN];
  1417.   unsigned long i;
  1418.   if (!mailgets) fatal ("mail_partial_text() called without a mailgets!");
  1419.   if (section && (strlen (section) > (MAILTMPLEN - 20))) return NIL;
  1420.   if (flags & FT_UID) { /* UID form of call */
  1421.     if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
  1422.     else return NIL; /* must get UID/msgno map first */
  1423.   }
  1424.   elt = mail_elt (stream,msgno);/* get cache data */
  1425.   flags &= ~FT_INTERNAL; /* bogus if this is set */
  1426.   if (section && *section) { /* nested body text wanted? */
  1427.     if (!((b = mail_body (stream,msgno,section)) &&
  1428.   (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
  1429.       return NIL; /* lose if no body or not MESSAGE/RFC822 */
  1430.     p = &b->nested.msg->text; /* point at nested message */
  1431. /* build IMAP-format section specifier */
  1432.     sprintf (tmp,"%s.TEXT",section);
  1433.   }
  1434.   else { /* else top-level message text wanted */
  1435.     p = &elt->private.msg.text;
  1436.     strcpy (tmp,"TEXT");
  1437.   }
  1438. /* initialize message data identifier */
  1439.   INIT_GETS (md,stream,msgno,tmp,first,last);
  1440.   if (p->text.data) { /* is data already cached? */
  1441.     INIT (&bs,mail_string,p->text.data,i = p->text.size);
  1442.     markseen (stream,elt,flags);/* mark message seen */
  1443.   }
  1444.   else { /* else get data from driver */
  1445.     if (!stream->dtb) return NIL;
  1446.     if (stream->dtb->msgdata) /* driver will handle this */
  1447.       return (*stream->dtb->msgdata) (stream,msgno,tmp,first,last,NIL,flags);
  1448.     if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return NIL;
  1449.     if (section && *section) { /* nexted if more complex */
  1450.       SETPOS (&bs,p->offset); /* offset stringstruct to data */
  1451.       i = p->text.size; /* maximum size of data */
  1452.     }
  1453.     else i = SIZE (&bs); /* just want this much */
  1454.   }
  1455.   if (i <= first) i = first = 0;/* first byte is beyond end of text */
  1456. /* truncate as needed */
  1457.   else { /* offset and truncate */
  1458.     SETPOS (&bs,first + GETPOS (&bs));
  1459.     i -= first; /* reduced size */
  1460.     if (last && (i > last)) i = last;
  1461.   }
  1462. /* do the mailgets thing */
  1463.   (*mailgets) (mail_read,&bs,i,&md);
  1464.   return T; /* success */
  1465. }
  1466. /* Mail fetch partial message body part
  1467.  * Accepts: mail stream
  1468.  *     message # to fetch
  1469.  *     MIME section specifier (#.#.#...#)
  1470.  *     offset of first designed byte or 0 to start at beginning
  1471.  *     maximum number of bytes or 0 for all bytes
  1472.  *     flags
  1473.  * Returns: T if successful, else NIL
  1474.  */
  1475. long mail_partial_body (MAILSTREAM *stream,unsigned long msgno,char *section,
  1476. unsigned long first,unsigned long last,long flags)
  1477. {
  1478.   GETS_DATA md;
  1479.   PARTTEXT *p;
  1480.   STRING bs;
  1481.   BODY *b;
  1482.   SIZEDTEXT *t;
  1483.   unsigned long i;
  1484.   if (!(section && *section)) /* top-level text wanted? */
  1485.     return mail_partial_text (stream,msgno,NIL,first,last,flags);
  1486.   if (!mailgets) fatal ("mail_partial_body() called without a mailgets!");
  1487.   if (flags & FT_UID) { /* UID form of call */
  1488.     if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
  1489.     else return NIL; /* must get UID/msgno map first */
  1490.   }
  1491. /* must have body */
  1492.   if (!(b = mail_body (stream,msgno,section))) return NIL;
  1493.   flags &= ~FT_INTERNAL; /* bogus if this is set */
  1494. /* initialize message data identifier */
  1495.   INIT_GETS (md,stream,msgno,section,first,last);
  1496. /* have cached text? */
  1497.   if ((t = &(p = &b->contents)->text)->data) {
  1498. /* mark message seen */
  1499.     markseen (stream,mail_elt (stream,msgno),flags);
  1500.     INIT (&bs,mail_string,t->data,i = t->size);
  1501.   }
  1502.   else { /* else get data from driver */
  1503.     if (!stream->dtb) return NIL;
  1504.     if (stream->dtb->msgdata) /* driver will handle this */
  1505.       return (*stream->dtb->msgdata) (stream,msgno,section,first,last,NIL,
  1506.       flags);
  1507.     if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return NIL;
  1508.     if (section && *section) { /* nexted if more complex */
  1509.       SETPOS (&bs,p->offset); /* offset stringstruct to data */
  1510.       i = t->size; /* maximum size of data */
  1511.     }
  1512.     else i = SIZE (&bs); /* just want this much */
  1513.   }
  1514.   if (i <= first) i = first = 0;/* first byte is beyond end of text */
  1515.   else { /* offset and truncate */
  1516.     SETPOS (&bs,first + GETPOS (&bs));
  1517.     i -= first; /* reduced size */
  1518.     if (last && (i > last)) i = last;
  1519.   }
  1520. /* do the mailgets thing */
  1521.   (*mailgets) (mail_read,&bs,i,&md);
  1522.   return T; /* success */
  1523. }
  1524. /* Mail return message text
  1525.  * Accepts: identifier data
  1526.  *     sized text
  1527.  *     pointer to returned length
  1528.  * Returns: text
  1529.  */
  1530. char *mail_fetch_text_return (GETS_DATA *md,SIZEDTEXT *t,unsigned long *len)
  1531. {
  1532.   STRING bs;
  1533.   if (len) *len = t->size; /* return size */
  1534.   if (t->size && mailgets) { /* have to do the mailgets thing? */
  1535. /* silly but do it anyway for consistency */
  1536.     INIT (&bs,mail_string,t->data,t->size);
  1537.     return (*mailgets) (mail_read,&bs,t->size,md);
  1538.   }
  1539.   return t->size ? (char *) t->data : "";
  1540. }
  1541. /* Mail return message string
  1542.  * Accepts: identifier data
  1543.  *     stringstruct
  1544.  *     text length
  1545.  *     pointer to returned length
  1546.  * Returns: text
  1547.  */
  1548. char *mail_fetch_string_return (GETS_DATA *md,STRING *bs,unsigned long i,
  1549. unsigned long *len)
  1550. {
  1551.   if (len) *len = i; /* return size */
  1552. /* have to do the mailgets thing? */
  1553.   if (mailgets) return (*mailgets) (mail_read,bs,i,md);
  1554. /* special hack to avoid extra copy */
  1555.   if (bs->dtb->next == mail_string_next) return bs->curpos;
  1556. /* make string copy in memory */
  1557.   return textcpyoffstring (&md->stream->text,bs,GETPOS (bs),i);
  1558. }
  1559. /* Read data from stringstruct
  1560.  * Accepts: stringstruct
  1561.  *     size of data to read
  1562.  *     buffer to read into
  1563.  * Returns: T, always, stringstruct updated
  1564.  */
  1565. long mail_read (void *stream,unsigned long size,char *buffer)
  1566. {
  1567.   STRING *s = (STRING *) stream;
  1568.   while (size--) *buffer++ = SNX (s);
  1569.   return T;
  1570. }
  1571. /* Mail fetch UID
  1572.  * Accepts: mail stream
  1573.  *     message number
  1574.  * Returns: UID or zero if dead stream
  1575.  */
  1576. unsigned long mail_uid (MAILSTREAM *stream,unsigned long msgno)
  1577. {
  1578.   unsigned long uid = mail_elt (stream,msgno)->private.uid;
  1579.   return uid ? uid :
  1580.     (stream->dtb && stream->dtb->uid) ? (*stream->dtb->uid) (stream,msgno) : 0;
  1581. }
  1582. /* Mail fetch msgno from UID (for internal use only)
  1583.  * Accepts: mail stream
  1584.  *     UID
  1585.  * Returns: msgno or zero if failed
  1586.  */
  1587. unsigned long mail_msgno (MAILSTREAM *stream,unsigned long uid)
  1588. {
  1589.   unsigned long msgno;
  1590. /* scan cache for UID */
  1591.   for (msgno = 1; msgno <= stream->nmsgs; msgno++)
  1592.     if (mail_elt (stream,msgno)->private.uid == uid) return msgno;
  1593.   if (stream->dtb) { /* else get it from driver if possible */
  1594. /* direct way */
  1595.     if (stream->dtb->msgno) return (*stream->dtb->msgno) (stream,uid);
  1596.     if (stream->dtb->uid) /* indirect way */
  1597.       for (msgno = 1; msgno <= stream->nmsgs; msgno++)
  1598. if ((*stream->dtb->uid) (stream,msgno) == uid) return msgno;
  1599.   }
  1600.   return 0; /* didn't find the UID anywhere */
  1601. }
  1602. /* Mail fetch From string for menu
  1603.  * Accepts: destination string
  1604.  *     mail stream
  1605.  *     message # to fetch
  1606.  *     desired string length
  1607.  * Returns: string of requested length
  1608.  */
  1609. void mail_fetchfrom (char *s,MAILSTREAM *stream,unsigned long msgno,
  1610.      long length)
  1611. {
  1612.   char *t;
  1613.   char tmp[MAILTMPLEN];
  1614.   ENVELOPE *env = mail_fetchenvelope (stream,msgno);
  1615.   ADDRESS *adr = env ? env->from : NIL;
  1616.   memset (s,' ',(size_t)length);/* fill it with spaces */
  1617.   s[length] = ''; /* tie off with null */
  1618. /* get first from address from envelope */
  1619.   while (adr && !adr->host) adr = adr->next;
  1620.   if (adr) { /* if a personal name exists use it */
  1621.     if (!(t = adr->personal))
  1622.       sprintf (t = tmp,"%.256s@%.256s",adr->mailbox,adr->host);
  1623.     memcpy (s,t,(size_t) min (length,(long) strlen (t)));
  1624.   }
  1625. }
  1626. /* Mail fetch Subject string for menu
  1627.  * Accepts: destination string
  1628.  *     mail stream
  1629.  *     message # to fetch
  1630.  *     desired string length
  1631.  * Returns: string of no more than requested length
  1632.  */
  1633. void mail_fetchsubject (char *s,MAILSTREAM *stream,unsigned long msgno,
  1634. long length)
  1635. {
  1636.   ENVELOPE *env = mail_fetchenvelope (stream,msgno);
  1637.   memset (s,'',(size_t) length+1);
  1638. /* copy subject from envelope */
  1639.   if (env && env->subject) strncpy (s,env->subject,(size_t) length);
  1640.   else *s = ' '; /* if no subject then just a space */
  1641. }
  1642. /* Mail modify flags
  1643.  * Accepts: mail stream
  1644.  *     sequence
  1645.  *     flag(s)
  1646.  *     option flags
  1647.  */
  1648. void mail_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
  1649. {
  1650.   MESSAGECACHE *elt;
  1651.   unsigned long i,uf;
  1652.   long f;
  1653.   short nf;
  1654.   if (!stream->dtb) return; /* no-op if no stream */
  1655.   if ((stream->dtb->flagmsg || !stream->dtb->flag) &&
  1656.       ((f = mail_parse_flags (stream,flag,&uf)) || uf) &&
  1657.       ((flags & ST_UID) ? mail_uid_sequence (stream,sequence) :
  1658.        mail_sequence (stream,sequence)))
  1659.     for (i = 1,nf = (flags & ST_SET) ? T : NIL; i <= stream->nmsgs; i++)
  1660.       if ((elt = mail_elt (stream,i))->sequence) {
  1661. struct { /* old flags */
  1662.   unsigned int valid : 1;
  1663.   unsigned int seen : 1;
  1664.   unsigned int deleted : 1;
  1665.   unsigned int flagged : 1;
  1666.   unsigned int answered : 1;
  1667.   unsigned int draft : 1;
  1668.   unsigned long user_flags;
  1669. } old;
  1670. old.valid = elt->valid; old.seen = elt->seen;
  1671. old.deleted = elt->deleted; old.flagged = elt->flagged;
  1672. old.answered = elt->answered; old.draft = elt->draft;
  1673. old.user_flags = elt->user_flags;
  1674. elt->valid = NIL; /* prepare for flag alteration */
  1675. if (stream->dtb->flagmsg) (*stream->dtb->flagmsg) (stream,elt);
  1676. if (f&fSEEN) elt->seen = nf;
  1677. if (f&fDELETED) elt->deleted = nf;
  1678. if (f&fFLAGGED) elt->flagged = nf;
  1679. if (f&fANSWERED) elt->answered = nf;
  1680. if (f&fDRAFT) elt->draft = nf;
  1681. /* user flags */
  1682. if (flags & ST_SET) elt->user_flags |= uf;
  1683. else elt->user_flags &= ~uf;
  1684. elt->valid = T; /* flags now altered */
  1685. if ((old.valid != elt->valid) || (old.seen != elt->seen) ||
  1686.     (old.deleted != elt->deleted) || (old.flagged != elt->flagged) ||
  1687.     (old.answered != elt->answered) || (old.draft != elt->draft) ||
  1688.     (old.user_flags != elt->user_flags)) mm_flags(stream,elt->msgno);
  1689. if (stream->dtb->flagmsg) (*stream->dtb->flagmsg) (stream,elt);
  1690.       }
  1691. /* call driver once */
  1692.   if (stream->dtb->flag) (*stream->dtb->flag) (stream,sequence,flag,flags);
  1693. }
  1694. /* Mail search for messages
  1695.  * Accepts: mail stream
  1696.  *     character set
  1697.  *     search program
  1698.  *     option flags
  1699.  */
  1700. void mail_search_full (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
  1701.        long flags)
  1702. {
  1703.   unsigned long i;
  1704.   if (!(flags & SE_RETAIN)) /* clear search vector unless retaining */
  1705.     for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = NIL;
  1706.   if (pgm && stream->dtb) { /* must have a search program and driver */
  1707. /* do the driver's action if requested */
  1708.     if (!(flags & SO_NOSERVER) && stream->dtb->search)
  1709.       (*stream->dtb->search) (stream,charset,pgm,flags);
  1710.     else mail_search_default (stream,charset,pgm,flags);
  1711.   }
  1712. /* flush search program if requested */
  1713.   if (flags & SE_FREE) mail_free_searchpgm (&pgm);
  1714. }
  1715. /* Mail search for messages default handler
  1716.  * Accepts: mail stream
  1717.  *     character set
  1718.  *     search program
  1719.  *     option flags
  1720.  */
  1721. void mail_search_default (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
  1722.   long flags)
  1723. {
  1724.   unsigned long i;
  1725.   if (charset && *charset && /* convert if charset not US-ASCII or UTF-8 */
  1726.       !(((charset[0] == 'U') || (charset[0] == 'u')) &&
  1727. ((((charset[1] == 'S') || (charset[1] == 's')) &&
  1728.   (charset[2] == '-') &&
  1729.   ((charset[3] == 'A') || (charset[3] == 'a')) &&
  1730.   ((charset[4] == 'S') || (charset[4] == 's')) &&
  1731.   ((charset[5] == 'C') || (charset[5] == 'c')) &&
  1732.   ((charset[6] == 'I') || (charset[6] == 'i')) &&
  1733.   ((charset[7] == 'I') || (charset[7] == 'i')) && !charset[8]) ||
  1734.  (((charset[1] == 'T') || (charset[1] == 't')) &&
  1735.   ((charset[2] == 'F') || (charset[2] == 'f')) &&
  1736.   (charset[3] == '-') && (charset[4] == '8') && !charset[5])))) {
  1737.     if (utf8_text (NIL,charset,NIL,T)) utf8_searchpgm (pgm,charset);
  1738.     else return; /* charset unknown */
  1739.   }
  1740.   for (i = 1; i <= stream->nmsgs; ++i) if (mail_search_msg (stream,i,NIL,pgm)){
  1741.     if (flags & SE_UID) mm_searched (stream,mail_uid (stream,i));
  1742.     else { /* mark as searched, notify mail program */
  1743.       mail_elt (stream,i)->searched = T;
  1744.       if (!stream->silent) mm_searched (stream,i);
  1745.     }
  1746.   }
  1747. }
  1748. /* Mail ping mailbox
  1749.  * Accepts: mail stream
  1750.  * Returns: stream if still open else NIL
  1751.  */
  1752. long mail_ping (MAILSTREAM *stream)
  1753. {
  1754.    /* do the driver's action */
  1755.   return stream->dtb ? (*stream->dtb->ping) (stream) : NIL;
  1756. }
  1757. /* Mail check mailbox
  1758.  * Accepts: mail stream
  1759.  */
  1760. void mail_check (MAILSTREAM *stream)
  1761. {
  1762.    /* do the driver's action */
  1763.   if (stream->dtb) (*stream->dtb->check) (stream);
  1764. }
  1765. /* Mail expunge mailbox
  1766.  * Accepts: mail stream
  1767.  */
  1768. void mail_expunge (MAILSTREAM *stream)
  1769. {
  1770.    /* do the driver's action */
  1771.   if (stream->dtb) (*stream->dtb->expunge) (stream);
  1772. }
  1773. /* Mail copy message(s)
  1774.  * Accepts: mail stream
  1775.  *     sequence
  1776.  *     destination mailbox
  1777.  *     flags
  1778.  */
  1779. long mail_copy_full (MAILSTREAM *stream,char *sequence,char *mailbox,
  1780.      long options)
  1781. {
  1782.   long ret = NIL;
  1783.   SAFE_DISPATCH (stream->dtb,ret,copy,(stream,sequence,mailbox,options))
  1784.   return ret;
  1785. }
  1786. /* Mail append message string
  1787.  * Accepts: mail stream
  1788.  *     destination mailbox
  1789.  *     initial flags
  1790.  *     message internal date
  1791.  *     stringstruct of message to append
  1792.  * Returns: T on success, NIL on failure
  1793.  */
  1794. long mail_append_full (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  1795.        STRING *message)
  1796. {
  1797.   long ret = NIL;
  1798.   char *s,tmp[MAILTMPLEN];
  1799.   DRIVER *d = NIL;
  1800.   if (strlen (mailbox) >= (NETMAXHOST+NETMAXUSER+NETMAXMBX+NETMAXSRV+50)) {
  1801.     sprintf (tmp,"Can't append %.80s: %s",mailbox,(*mailbox == '{') ?
  1802.      "invalid remote specification" : "no such mailbox");
  1803.     mm_log (tmp,ERROR);
  1804.     return NIL;
  1805.   }
  1806. /* see if special driver hack */
  1807.   if (strncmp (lcase (strcpy (tmp,mailbox)),"#driver.",8))
  1808.     d = mail_valid (stream,mailbox,NIL);
  1809.   else { /* it is, tie off name at likely delimiter */
  1810.     if (!(s = strpbrk (tmp+8,"/\:"))) {
  1811.       sprintf (tmp,"Can't append to mailbox %.80s: bad driver syntax",mailbox);
  1812.       mm_log (tmp,ERROR);
  1813.     }
  1814.     else { /* found delimiter */
  1815.       *s++ = '';
  1816.       for (d = maildrivers; d && strcmp (d->name,tmp+8); d = d->next);
  1817.       if (d) mailbox += s - tmp;/* skip past driver specification */
  1818.       else {
  1819. sprintf (tmp,"Can't append to mailbox %.80s: unknown driver",mailbox);
  1820. mm_log (tmp,ERROR);
  1821.       }
  1822.     }
  1823.   }
  1824. /* do append if have a driver */
  1825.   SAFE_DISPATCH (d,ret,append,(stream,mailbox,flags,date,message))
  1826.   else { /* failed, try for TRYCREATE if no stream */
  1827.     if (!stream && (stream = default_proto (T)))
  1828.       SAFE_FUNCTION (stream->dtb,ret,(*stream->dtb->append),
  1829.      (stream,mailbox,flags,date,message))
  1830. /* timing race? */
  1831.     if (ret) mm_notify (stream,"Append validity confusion",WARN);
  1832. /* now generate error message */
  1833.     else mail_valid (stream,mailbox,"append to mailbox");
  1834.   }
  1835.   return ret;
  1836. }
  1837. /* Mail garbage collect stream
  1838.  * Accepts: mail stream
  1839.  *     garbage collection flags
  1840.  */
  1841. void mail_gc (MAILSTREAM *stream,long gcflags)
  1842. {
  1843.   MESSAGECACHE *elt;
  1844.   unsigned long i;
  1845.    /* do the driver's action first */
  1846.   if (stream->dtb && stream->dtb->gc) (*stream->dtb->gc) (stream,gcflags);
  1847.   stream->msgno = 0; /* nothing cached now */
  1848.   if (gcflags & GC_ENV) { /* garbage collect envelopes? */
  1849.     if (stream->env) mail_free_envelope (&stream->env);
  1850.     if (stream->body) mail_free_body (&stream->body);
  1851.   }
  1852.   if (gcflags & GC_TEXTS) { /* free texts */
  1853.     if (stream->text.data) fs_give ((void **) &stream->text.data);
  1854.     stream->text.size = 0;
  1855.   }
  1856. /* garbage collect per-message stuff */
  1857.   for (i = 1; i <= stream->nmsgs; i++) 
  1858.     if (elt = (MESSAGECACHE *) (*mailcache) (stream,i,CH_ELT))
  1859.       mail_gc_msg (&elt->private.msg,gcflags);
  1860. }
  1861. /* Mail garbage collect message
  1862.  * Accepts: message structure
  1863.  *     garbage collection flags
  1864.  */
  1865. void mail_gc_msg (MESSAGE *msg,long gcflags)
  1866. {
  1867.   if (gcflags & GC_ENV) { /* garbage collect envelopes? */
  1868.     mail_free_envelope (&msg->env);
  1869.     mail_free_body (&msg->body);
  1870.   }
  1871.   if (gcflags & GC_TEXTS) { /* garbage collect texts */
  1872.     if (msg->full.text.data) fs_give ((void **) &msg->full.text.data);
  1873.     if (msg->header.text.data) {
  1874.       mail_free_stringlist (&msg->lines);
  1875.       fs_give ((void **) &msg->header.text.data);
  1876.     }
  1877.     if (msg->text.text.data) fs_give ((void **) &msg->text.text.data);
  1878. /* now GC all body components */
  1879.     if (msg->body) mail_gc_body (msg->body);
  1880.   }
  1881. }
  1882. /* Mail garbage collect texts in BODY structure
  1883.  * Accepts: BODY structure
  1884.  */
  1885. void mail_gc_body (BODY *body)
  1886. {
  1887.   PART *part;
  1888.   switch (body->type) { /* free contents */
  1889.   case TYPEMULTIPART: /* multiple part */
  1890.     for (part = body->nested.part; part; part = part->next)
  1891.       mail_gc_body (&part->body);
  1892.     break;
  1893.   case TYPEMESSAGE: /* encapsulated message */
  1894.     if (body->subtype && !strcmp (body->subtype,"RFC822")) {
  1895.       mail_free_stringlist (&body->nested.msg->lines);
  1896.       mail_gc_msg (body->nested.msg,GC_TEXTS);
  1897.     }
  1898.     break;
  1899.   default:
  1900.     break;
  1901.   }
  1902.   if (body->mime.text.data) fs_give ((void **) &body->mime.text.data);
  1903.   if (body->contents.text.data) fs_give ((void **) &body->contents.text.data);
  1904. }
  1905. /* Mail get body part
  1906.  * Accepts: mail stream
  1907.  *     message number
  1908.  *     section specifier
  1909.  * Returns: pointer to body
  1910.  */
  1911. BODY *mail_body (MAILSTREAM *stream,unsigned long msgno,char *section)
  1912. {
  1913.   BODY *b;
  1914.   PART *pt;
  1915.   unsigned long i;
  1916.   char tmp[MAILTMPLEN];
  1917. /* make sure have a body */
  1918.   if (!(section && *section && (strlen (section) < (MAILTMPLEN - 20)) &&
  1919. mail_fetchstructure (stream,msgno,&b) && b)) return NIL;
  1920. /* find desired section */
  1921.   if (section) for (section = ucase (strcpy (tmp,section)); *section;) {
  1922.     if (isdigit (*section)) { /* get section specifier */
  1923. /* make sure what follows is valid */
  1924.       if (!(i = strtoul (section,&section,10)) ||
  1925.   (*section && ((*section++ != '.') || !*section))) return NIL;
  1926. /* multipart content? */
  1927.       if (b->type == TYPEMULTIPART) {
  1928. /* yes, find desired part */
  1929. if (pt = b->nested.part) while (--i && (pt = pt->next));
  1930. if (!pt) return NIL; /* bad specifier */
  1931. b = &pt->body; /* note new body */
  1932.       }
  1933. /* otherwise must be section 1 */
  1934.       else if (i != 1) return NIL;
  1935. /* need to go down further? */
  1936.       if (*section) switch (b->type) {
  1937.       case TYPEMULTIPART: /* multipart */
  1938. break;
  1939.       case TYPEMESSAGE: /* embedded message */
  1940. if (!strcmp (b->subtype,"RFC822")) {
  1941.   b = b->nested.msg->body;
  1942.   break;
  1943. }
  1944.       default: /* bogus subpart specification */
  1945. return NIL;
  1946.       }
  1947.     }
  1948.     else return NIL; /* unknown section specifier */
  1949.   }
  1950.   return b;
  1951. }  
  1952. /* Mail output date from elt fields
  1953.  * Accepts: character string to write into
  1954.  *     elt to get data data from
  1955.  * Returns: the character string
  1956.  */
  1957. const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  1958. const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1959. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  1960. char *mail_date (char *string,MESSAGECACHE *elt)
  1961. {
  1962.   const char *s = (elt->month && elt->month < 13) ?
  1963.     months[elt->month - 1] : (const char *) "???";
  1964.   sprintf (string,"%2d-%s-%d %02d:%02d:%02d %c%02d%02d",
  1965.    elt->day,s,elt->year + BASEYEAR,
  1966.    elt->hours,elt->minutes,elt->seconds,
  1967.    elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes);
  1968.   return string;
  1969. }
  1970. /* Mail output cdate format date from elt fields
  1971.  * Accepts: character string to write into
  1972.  *     elt to get data data from
  1973.  * Returns: the character string
  1974.  */
  1975. char *mail_cdate (char *string,MESSAGECACHE *elt)
  1976. {
  1977.   char *fmt = "%s %s %2d %02d:%02d:%02d %4d %s%02d%02dn";
  1978.   const char *s = (elt->month && elt->month < 13) ?
  1979.     months[elt->month - 1] : (const char *) "???";
  1980.   int m = elt->month;
  1981.   int y = elt->year + BASEYEAR;
  1982.   if (elt->month <= 2) { /* if before March, */
  1983.     m = elt->month + 9; /* January = month 10 of previous year */
  1984.     y--;
  1985.   }
  1986.   else m = elt->month - 3; /* March is month 0 */
  1987.   sprintf (string,fmt,days[(int)(elt->day+2+((7+31*m)/12)+y+(y/4)
  1988. #ifndef USEORTHODOXCALENDAR /* Gregorian calendar */
  1989.       +(y / 400) - (y / 100)
  1990. #ifdef Y4KBUGFIX
  1991.       - (y / 4000)
  1992. #endif
  1993. #else /* Orthodox calendar */
  1994.       +(2*(y/900))+((y%900) >= 200)+((y%900) >= 600) - (y/100)
  1995. #endif
  1996.       ) % 7],s,
  1997.    elt->day,elt->hours,elt->minutes,elt->seconds,elt->year + BASEYEAR,
  1998.    elt->zoccident ? "-" : "+",elt->zhours,elt->zminutes);
  1999.   return string;
  2000. }
  2001. /* Mail parse date into elt fields
  2002.  * Accepts: elt to write into
  2003.  *     date string to parse
  2004.  * Returns: T if parse successful, else NIL 
  2005.  * This routine parses dates as follows:
  2006.  * . leading three alphas followed by comma and space are ignored
  2007.  * . date accepted in format: mm/dd/yy, mm/dd/yyyy, dd-mmm-yy, dd-mmm-yyyy,
  2008.  *    dd mmm yy, dd mmm yyyy
  2009.  * . space or end of string required
  2010.  * . time accepted in format hh:mm:ss or hh:mm
  2011.  * . end of string accepted
  2012.  * . timezone accepted: hyphen followed by symbolic timezone, or space
  2013.  *    followed by signed numeric timezone or symbolic timezone
  2014.  * Examples of normal input:
  2015.  * . IMAP date-only (SEARCH): dd-mmm-yy, dd-mmm-yyyy, mm/dd/yy, mm/dd/yyyy
  2016.  * . IMAP date-time (INTERNALDATE):
  2017.  *    dd-mmm-yy hh:mm:ss-zzz
  2018.  *    dd-mmm-yyyy hh:mm:ss +zzzz
  2019.  * . RFC-822:
  2020.  *    www, dd mmm yy hh:mm:ss zzz
  2021.  *    www, dd mmm yyyy hh:mm:ss +zzzz
  2022.  */
  2023. long mail_parse_date (MESSAGECACHE *elt,char *s)
  2024. {
  2025.   unsigned long d,m,y;
  2026.   int mi,ms;
  2027.   struct tm *t;
  2028.   time_t tn;
  2029.   char tmp[MAILTMPLEN];
  2030. /* clear elt */
  2031.   elt->zoccident = elt->zhours = elt->zminutes =
  2032.     elt->hours = elt->minutes = elt->seconds =
  2033.       elt->day = elt->month = elt->year = 0;
  2034. /* make a writeable uppercase copy */
  2035.   if (s && *s && (strlen (s) < (size_t)MAILTMPLEN)) s = ucase (strcpy (tmp,s));
  2036.   else return NIL;
  2037. /* skip over possible day of week */
  2038.   if (isalpha (*s) && isalpha (s[1]) && isalpha (s[2]) && (s[3] == ',') &&
  2039.       (s[4] == ' ')) s += 5;
  2040.   while (*s == ' ') s++; /* parse first number (probable month) */
  2041.   if (!(m = strtoul ((const char *) s,&s,10))) return NIL;
  2042.   switch (*s) { /* different parse based on delimiter */
  2043.   case '/': /* mm/dd/yy format */
  2044.     if (!((d = strtoul ((const char *) ++s,&s,10)) && *s == '/' &&
  2045.   (y = strtoul ((const char *) ++s,&s,10)) && *s == '')) return NIL;
  2046.     break;
  2047.   case ' ': /* dd mmm yy format */
  2048.     while (s[1] == ' ') s++; /* slurp extra whitespace */
  2049.   case '-': /* dd-mmm-yy format */
  2050.     d = m; /* so the number we got is a day */
  2051. /* make sure string long enough! */
  2052.     if (strlen (s) < (size_t) 5) return NIL;
  2053.     /* Some compilers don't allow `<<' and/or longs in case statements. */
  2054. /* slurp up the month string */
  2055.     ms = ((s[1] - 'A') * 1024) + ((s[2] - 'A') * 32) + (s[3] - 'A');
  2056.     switch (ms) { /* determine the month */
  2057.     case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
  2058.     case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
  2059.     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
  2060.     case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
  2061.     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
  2062.     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
  2063.     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
  2064.     case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
  2065.     case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
  2066.     case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10; break;
  2067.     case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11; break;
  2068.     case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12; break;
  2069.     default: return NIL; /* unknown month */
  2070.     }
  2071.     if (s[4] == *s) s += 5; /* advance to year */
  2072.     else { /* first three were OK, possibly full name */
  2073.       mi = *s; /* note delimiter, skip alphas */
  2074.       for (s += 4; isalpha (*s); s++);
  2075. /* error if delimiter not here */
  2076.       if (mi != *s++) return NIL;
  2077.     }
  2078.     if ((y = strtoul ((const char *) s,&s,10)) && (*s == '' || *s == ' '))
  2079.       break; /* successfully parsed year */
  2080.   default: return NIL; /* unknown date format */
  2081.   }
  2082. /* minimal validity check of date */
  2083.   if ((d > 31) || (m > 12)) return NIL; 
  2084. /* two digit year */
  2085.   if (y < 100) y += (y >= (BASEYEAR - 1900)) ? 1900 : 2000;
  2086. /* set values in elt */
  2087.   elt->day = d; elt->month = m; elt->year = y - BASEYEAR;
  2088.   ms = ''; /* initially no time zone string */
  2089.   if (*s) { /* time specification present? */
  2090. /* parse time */
  2091.     d = strtoul ((const char *) s+1,&s,10);
  2092.     if (*s != ':') return NIL;
  2093.     m = strtoul ((const char *) ++s,&s,10);
  2094.     y = (*s == ':') ? strtoul ((const char *) ++s,&s,10) : 0;
  2095. /* validity check time */
  2096.     if ((d > 23) || (m > 59) || (y > 59)) return NIL; 
  2097. /* set values in elt */
  2098.     elt->hours = d; elt->minutes = m; elt->seconds = y;
  2099.     switch (*s) { /* time zone specifier? */
  2100.     case ' ': /* numeric time zone */
  2101.       while (s[1] == ' ') s++; /* slurp extra whitespace */
  2102.       if (!isalpha (s[1])) { /* treat as '-' case if alphabetic */
  2103. /* test for sign character */
  2104. if ((elt->zoccident = (*++s == '-')) || (*s == '+')) s++;
  2105. /* validate proper timezone */
  2106. if (isdigit(*s) && isdigit(s[1]) && isdigit(s[2]) && isdigit(s[3])) {
  2107.   elt->zhours = (*s - '0') * 10 + (s[1] - '0');
  2108.   elt->zminutes = (s[2] - '0') * 10 + (s[3] - '0');
  2109. }
  2110. return T; /* all done! */
  2111.       }
  2112. /* falls through */
  2113.     case '-': /* symbolic time zone */
  2114.       if (!(ms = *++s)) ms = 'Z';
  2115.       else if (*++s) { /* multi-character? */
  2116. ms -= 'A'; ms *= 1024; /* yes, make compressed three-byte form */
  2117. ms += ((*s++ - 'A') * 32);
  2118. if (*s) ms += *s++ - 'A';
  2119. if (*s) ms = ''; /* more than three characters */
  2120.       }
  2121.     default: /* ignore anything else */
  2122.       break;
  2123.     }
  2124.   }
  2125.   /*  This is not intended to be a comprehensive list of all possible
  2126.    * timezone strings.  Such a list would be impractical.  Rather, this
  2127.    * listing is intended to incorporate all military, North American, and
  2128.    * a few special cases such as Japan and the major European zone names,
  2129.    * such as what might be expected to be found in a Tenex format mailbox
  2130.    * and spewed from an IMAP server.  The trend is to migrate to numeric
  2131.    * timezones which lack the flavor but also the ambiguity of the names.
  2132.    *
  2133.    *  RFC-822 only recognizes UT, GMT, 1-letter military timezones, and the
  2134.    * 4 CONUS timezones and their summer time variants.  [Sorry, Canadian
  2135.    * Atlantic Provinces, Alaska, and Hawaii.]
  2136.    */
  2137.   switch (ms) { /* determine the timezone */
  2138. /* Universal */
  2139.   case (('U'-'A')*1024)+(('T'-'A')*32):
  2140. #ifndef STRICT_RFC822_TIMEZONES
  2141.   case (('U'-'A')*1024)+(('T'-'A')*32)+'C'-'A':
  2142. #endif
  2143. /* Greenwich */
  2144.   case (('G'-'A')*1024)+(('M'-'A')*32)+'T'-'A':
  2145.   case 'Z': elt->zhours = 0; break;
  2146.     /* oriental (from Greenwich) timezones */
  2147. #ifndef STRICT_RFC822_TIMEZONES
  2148. /* Middle Europe */
  2149.   case (('M'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
  2150. #endif
  2151. #ifdef BRITISH_SUMMER_TIME
  2152. /* British Summer */
  2153.   case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  2154. #endif
  2155.   case 'A': elt->zhours = 1; break;
  2156. #ifndef STRICT_RFC822_TIMEZONES
  2157. /* Eastern Europe */
  2158.   case (('E'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
  2159. #endif
  2160.   case 'B': elt->zhours = 2; break;
  2161.   case 'C': elt->zhours = 3; break;
  2162.   case 'D': elt->zhours = 4; break;
  2163.   case 'E': elt->zhours = 5; break;
  2164.   case 'F': elt->zhours = 6; break;
  2165.   case 'G': elt->zhours = 7; break;
  2166.   case 'H': elt->zhours = 8; break;
  2167. #ifndef STRICT_RFC822_TIMEZONES
  2168. /* Japan */
  2169.   case (('J'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  2170. #endif
  2171.   case 'I': elt->zhours = 9; break;
  2172.   case 'K': elt->zhours = 10; break;
  2173.   case 'L': elt->zhours = 11; break;
  2174.   case 'M': elt->zhours = 12; break;
  2175. /* occidental (from Greenwich) timezones */
  2176.   case 'N': elt->zoccident = 1; elt->zhours = 1; break;
  2177.   case 'O': elt->zoccident = 1; elt->zhours = 2; break;
  2178. #ifndef STRICT_RFC822_TIMEZONES
  2179.   case (('A'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  2180. #endif
  2181.   case 'P': elt->zoccident = 1; elt->zhours = 3; break;
  2182. #ifdef NEWFOUNDLAND_STANDARD_TIME
  2183. /* Newfoundland */
  2184.   case (('N'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  2185.     elt->zoccident = 1; elt->zhours = 3; elt->zminutes = 30; break;
  2186. #endif
  2187. #ifndef STRICT_RFC822_TIMEZONES
  2188. /* Atlantic */
  2189.   case (('A'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  2190. #endif
  2191. /* CONUS */
  2192.   case (('E'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  2193.   case 'Q': elt->zoccident = 1; elt->zhours = 4; break;
  2194. /* Eastern */
  2195.   case (('E'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  2196.   case (('C'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  2197.   case 'R': elt->zoccident = 1; elt->zhours = 5; break;
  2198. /* Central */
  2199.   case (('C'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  2200.   case (('M'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  2201.   case 'S': elt->zoccident = 1; elt->zhours = 6; break;
  2202. /* Mountain */
  2203.   case (('M'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  2204.   case (('P'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  2205.   case 'T': elt->zoccident = 1; elt->zhours = 7; break;
  2206. /* Pacific */
  2207.   case (('P'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  2208. #ifndef STRICT_RFC822_TIMEZONES
  2209.   case (('Y'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  2210. #endif
  2211.   case 'U': elt->zoccident = 1; elt->zhours = 8; break;
  2212. #ifndef STRICT_RFC822_TIMEZONES
  2213. /* Yukon */
  2214.   case (('Y'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  2215. #endif
  2216.   case 'V': elt->zoccident = 1; elt->zhours = 9; break;
  2217. #ifndef STRICT_RFC822_TIMEZONES
  2218. /* Hawaii */
  2219.   case (('H'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  2220. #endif
  2221.   case 'W': elt->zoccident = 1; elt->zhours = 10; break;
  2222. /* Nome/Bering/Samoa */
  2223. #ifndef NOME_STANDARD_TIME
  2224.   case (('N'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  2225. #endif
  2226. #ifndef BERING_STANDARD_TIME
  2227.   case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  2228. #endif
  2229. #ifndef SAMOA_STANDARD_TIME
  2230.   case (('S'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  2231. #endif
  2232.   case 'X': elt->zoccident = 1; elt->zhours = 11; break;
  2233.   case 'Y': elt->zoccident = 1; elt->zhours = 12; break;
  2234.   default: /* unknown time zones treated as local */
  2235.     tn = time (0); /* time now... */
  2236.     t = localtime (&tn); /* get local minutes since midnight */
  2237.     mi = t->tm_hour * 60 + t->tm_min;
  2238.     ms = t->tm_yday; /* note Julian day */
  2239.     if (t = gmtime (&tn)) { /* minus UTC minutes since midnight */
  2240.       mi -= t->tm_hour * 60 + t->tm_min;
  2241. /* ms can be one of:
  2242.  *  36x  local time is December 31, UTC is January 1, offset -24 hours
  2243.  *    1  local time is 1 day ahead of UTC, offset +24 hours
  2244.  *    0  local time is same day as UTC, no offset
  2245.  *   -1  local time is 1 day behind UTC, offset -24 hours
  2246.  * -36x  local time is January 1, UTC is December 31, offset +24 hours
  2247.  */
  2248.       if (ms -= t->tm_yday) /* correct offset if different Julian day */
  2249. mi += ((ms < 0) == (abs (ms) == 1)) ? -24*60 : 24*60;
  2250.       if (mi < 0) { /* occidental? */
  2251. mi = abs (mi); /* yup, make positive number */
  2252. elt->zoccident = 1; /* and note west of UTC */
  2253.       }
  2254.       elt->zhours = mi / 60; /* now break into hours and minutes */
  2255.       elt->zminutes = mi % 60;
  2256.     }
  2257.     break;
  2258.   }
  2259.   return T;
  2260. }
  2261. /* Mail n messages exist
  2262.  * Accepts: mail stream
  2263.  *     number of messages
  2264.  */
  2265. void mail_exists (MAILSTREAM *stream,unsigned long nmsgs)
  2266. {
  2267. /* make sure cache is large enough */
  2268.   (*mailcache) (stream,nmsgs,CH_SIZE);
  2269.   stream->nmsgs = nmsgs; /* update stream status */
  2270. /* notify main program of change */
  2271.   if (!stream->silent) mm_exists (stream,nmsgs);
  2272. }
  2273. /* Mail n messages are recent
  2274.  * Accepts: mail stream
  2275.  *     number of recent messages
  2276.  */
  2277. void mail_recent (MAILSTREAM *stream,unsigned long recent)
  2278. {
  2279.   stream->recent = recent; /* update stream status */