mail.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:153k
- /*
- * Program: Mailbox Access routines
- *
- * Author: Mark Crispin
- * Networks and Distributed Computing
- * Computing & Communications
- * University of Washington
- * Administration Building, AG-44
- * Seattle, WA 98195
- * Internet: MRC@CAC.Washington.EDU
- *
- * Date: 22 November 1989
- * Last Edited: 1 November 1999
- *
- * Copyright 1999 by the University of Washington
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee is hereby granted, provided
- * that the above copyright notice appears in all copies and that both the
- * above copyright notice and this permission notice appear in supporting
- * documentation, and that the name of the University of Washington not be
- * used in advertising or publicity pertaining to distribution of the software
- * without specific, written prior permission. This software is made
- * available "as is", and
- * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
- * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
- * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
- * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
- * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
- #include <ctype.h>
- #include <stdio.h>
- #include "mail.h"
- #include "osdep.h"
- #include <time.h>
- #include "misc.h"
- #include "rfc822.h"
- #include "utf8.h"
- #include "smtp.h"
- /* c-client global data */
- /* list of mail drivers */
- static DRIVER *maildrivers = NIL;
- /* list of authenticators */
- static AUTHENTICATOR *mailauthenticators = NIL;
- /* alternative network driver pointer */
- static NETDRIVER *mailaltdriver = NIL;
- /* alternative network driver name */
- static char *mailaltdrivername = NIL;
- /* pointer to alternate gets function */
- static mailgets_t mailgets = NIL;
- /* pointer to read progress function */
- static readprogress_t mailreadprogress = NIL;
- /* mail cache manipulation function */
- static mailcache_t mailcache = mm_cache;
- /* RFC-822 output generator */
- static rfc822out_t mail822out = NIL;
- /* SMTP verbose callback */
- static smtpverbose_t mailsmtpverbose = mm_dlog;
- /* proxy copy routine */
- static mailproxycopy_t mailproxycopy = NIL;
- /* RFC-822 external line parse */
- static parseline_t mailparseline = NIL;
- /* RFC-822 external phrase parser */
- static parsephrase_t mailparsephrase = NIL;
- /* sorted results callback */
- static sortresults_t mailsortresults = NIL;
- /* threaded results callback */
- static threadresults_t mailthreadresults = NIL;
- /* supported threaders */
- static THREADER mailthreadlist = {
- "ORDEREDSUBJECT",
- mail_thread_orderedsubject,
- NIL
- };
- /* server name */
- static char *servicename = "unknown";
- static int expungeatping = T; /* mail_ping() may call mm_expunged() */
- static int tryaltfirst = NIL; /* always try alt driver first */
- static int notimezones = NIL; /* write timezones in "From " header */
- /* Default mail cache handler
- * Accepts: pointer to cache handle
- * message number
- * caching function
- * Returns: cache data
- */
- void *mm_cache (MAILSTREAM *stream,unsigned long msgno,long op)
- {
- size_t n;
- void *ret = NIL;
- unsigned long i;
- switch ((int) op) { /* what function? */
- case CH_INIT: /* initialize cache */
- if (stream->cache) { /* flush old cache contents */
- while (stream->cachesize) {
- mm_cache (stream,stream->cachesize,CH_FREE);
- mm_cache (stream,stream->cachesize--,CH_FREESORTCACHE);
- }
- fs_give ((void **) &stream->cache);
- fs_give ((void **) &stream->sc);
- stream->nmsgs = 0; /* can't have any messages now */
- }
- break;
- case CH_SIZE: /* (re-)size the cache */
- if (!stream->cache) { /* have a cache already? */
- /* no, create new cache */
- n = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
- stream->cache = (MESSAGECACHE **) memset (fs_get (n),0,n);
- stream->sc = (SORTCACHE **) memset (fs_get (n),0,n);
- }
- /* is existing cache size large neough */
- else if (msgno > stream->cachesize) {
- i = stream->cachesize; /* remember old size */
- n = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
- fs_resize ((void **) &stream->cache,n);
- fs_resize ((void **) &stream->sc,n);
- while (i < stream->cachesize) {
- stream->cache[i] = NIL;
- stream->sc[i++] = NIL;
- }
- }
- break;
- case CH_MAKEELT: /* return elt, make if necessary */
- if (!stream->cache[msgno - 1])
- stream->cache[msgno - 1] = mail_new_cache_elt (msgno);
- /* falls through */
- case CH_ELT: /* return elt */
- ret = (void *) stream->cache[msgno - 1];
- break;
- case CH_SORTCACHE: /* return sortcache entry, make if needed */
- if (!stream->sc[msgno - 1]) stream->sc[msgno - 1] =
- (SORTCACHE *) memset (fs_get (sizeof (SORTCACHE)),0,sizeof (SORTCACHE));
- ret = (void *) stream->sc[msgno - 1];
- break;
- case CH_FREE: /* free elt */
- mail_free_elt (&stream->cache[msgno - 1]);
- break;
- case CH_FREESORTCACHE:
- if (stream->sc[msgno - 1]) {
- if (stream->sc[msgno - 1]->from)
- fs_give ((void **) &stream->sc[msgno - 1]->from);
- if (stream->sc[msgno - 1]->to)
- fs_give ((void **) &stream->sc[msgno - 1]->to);
- if (stream->sc[msgno - 1]->cc)
- fs_give ((void **) &stream->sc[msgno - 1]->cc);
- if (stream->sc[msgno - 1]->subject)
- fs_give ((void **) &stream->sc[msgno - 1]->subject);
- fs_give ((void **) &stream->sc[msgno - 1]);
- }
- break;
- case CH_EXPUNGE: /* expunge cache slot */
- for (i = msgno - 1; msgno < stream->nmsgs; i++,msgno++) {
- if (stream->cache[i] = stream->cache[msgno])
- stream->cache[i]->msgno = msgno;
- stream->sc[i] = stream->sc[msgno];
- }
- stream->cache[i] = NIL; /* top of cache goes away */
- stream->sc[i] = NIL;
- break;
- default:
- fatal ("Bad mm_cache op");
- break;
- }
- return ret;
- }
- /* Dummy string driver for complete in-memory strings */
- STRINGDRIVER mail_string = {
- mail_string_init, /* initialize string structure */
- mail_string_next, /* get next byte in string structure */
- mail_string_setpos /* set position in string structure */
- };
- /* Initialize mail string structure for in-memory string
- * Accepts: string structure
- * pointer to string
- * size of string
- */
- void mail_string_init (STRING *s,void *data,unsigned long size)
- {
- /* set initial string pointers */
- s->chunk = s->curpos = (char *) (s->data = data);
- /* and sizes */
- s->size = s->chunksize = s->cursize = size;
- s->data1 = s->offset = 0; /* never any offset */
- }
- /* Get next character from string
- * Accepts: string structure
- * Returns: character, string structure chunk refreshed
- */
- char mail_string_next (STRING *s)
- {
- return *s->curpos++; /* return the last byte */
- }
- /* Set string pointer position
- * Accepts: string structure
- * new position
- */
- void mail_string_setpos (STRING *s,unsigned long i)
- {
- s->curpos = s->chunk + i; /* set new position */
- s->cursize = s->chunksize - i;/* and new size */
- }
- /* Mail routines
- *
- * mail_xxx routines are the interface between this module and the outside
- * world. Only these routines should be referenced by external callers.
- *
- * Note that there is an important difference between a "sequence" and a
- * "message #" (msgno). A sequence is a string representing a sequence in
- * {"n", "n:m", or combination separated by commas} format, whereas a msgno
- * is a single integer.
- *
- */
- /* Mail link driver
- * Accepts: driver to add to list
- */
- void mail_link (DRIVER *driver)
- {
- DRIVER **d = &maildrivers;
- while (*d) d = &(*d)->next; /* find end of list of drivers */
- *d = driver; /* put driver at the end */
- driver->next = NIL; /* this driver is the end of the list */
- }
- /* Mail manipulate driver parameters
- * Accepts: mail stream
- * function code
- * function-dependent value
- * Returns: function-dependent return value
- */
- void *mail_parameters (MAILSTREAM *stream,long function,void *value)
- {
- void *r,*ret = NIL;
- DRIVER *d;
- AUTHENTICATOR *a;
- switch ((int) function) {
- case SET_THREADERS:
- fatal ("SET_THREADERS not permitted");
- case GET_THREADERS:
- ret = (stream && stream->dtb) ?
- (*stream->dtb->parameters) (function,stream) : (void *) &mailthreadlist;
- break;
- case SET_NAMESPACE:
- fatal ("SET_NAMESPACE not permitted");
- case GET_NAMESPACE:
- ret = (stream && stream->dtb) ?
- (*stream->dtb->parameters) (function,stream) :
- env_parameters (function,stream);
- break;
- case SET_DRIVERS:
- fatal ("SET_DRIVERS not permitted");
- case GET_DRIVERS:
- ret = (void *) maildrivers;
- break;
- case SET_DRIVER:
- fatal ("SET_DRIVER not permitted");
- case GET_DRIVER:
- for (d = maildrivers; d && strcmp (d->name,(char *) value); d = d->next);
- ret = (void *) d;
- break;
- case ENABLE_DRIVER:
- for (d = maildrivers; d && strcmp (d->name,(char *) value); d = d->next);
- if (ret = (void *) d) d->flags &= ~DR_DISABLE;
- break;
- case DISABLE_DRIVER:
- for (d = maildrivers; d && strcmp (d->name,(char *) value); d = d->next);
- if (ret = (void *) d) d->flags |= DR_DISABLE;
- break;
- case ENABLE_AUTHENTICATOR: /* punt on this for the nonce */
- fatal ("ENABLE_AUTHENTICATOR not permitted");
- case DISABLE_AUTHENTICATOR:
- for (a = mailauthenticators;/* scan authenticators */
- a && strcmp (a->name,(char *) value); a = a->next);
- if (a) { /* if authenticator name found */
- a->client = NIL; /* blow it away */
- a->server = NIL;
- }
- break;
- case SET_GETS:
- mailgets = (mailgets_t) value;
- case GET_GETS:
- ret = (void *) mailgets;
- break;
- case SET_READPROGRESS:
- mailreadprogress = (readprogress_t) value;
- case GET_READPROGRESS:
- ret = (void *) mailreadprogress;
- break;
- case SET_CACHE:
- mailcache = (mailcache_t) value;
- case GET_CACHE:
- ret = (void *) mailcache;
- break;
- case SET_RFC822OUTPUT:
- mail822out = (rfc822out_t) value;
- case GET_RFC822OUTPUT:
- ret = (void *) mail822out;
- break;
- case SET_SMTPVERBOSE:
- mailsmtpverbose = (smtpverbose_t) value;
- case GET_SMTPVERBOSE:
- ret = (void *) mailsmtpverbose;
- break;
- case SET_MAILPROXYCOPY:
- mailproxycopy = (mailproxycopy_t) value;
- case GET_MAILPROXYCOPY:
- ret = (void *) mailproxycopy;
- break;
- case SET_PARSELINE:
- mailparseline = (parseline_t) value;
- case GET_PARSELINE:
- ret = (void *) mailparseline;
- break;
- case SET_PARSEPHRASE:
- mailparsephrase = (parsephrase_t) value;
- case GET_PARSEPHRASE:
- ret = (void *) mailparsephrase;
- break;
- case SET_SERVICENAME:
- servicename = (char *) value;
- case GET_SERVICENAME:
- ret = (void *) servicename;
- break;
- case SET_EXPUNGEATPING:
- expungeatping = (int) value;
- case GET_EXPUNGEATPING:
- value = (void *) expungeatping;
- break;
- case SET_SORTRESULTS:
- mailsortresults = (sortresults_t) value;
- case GET_SORTRESULTS:
- ret = (void *) mailsortresults;
- break;
- case SET_THREADRESULTS:
- mailthreadresults = (threadresults_t) value;
- case GET_THREADRESULTS:
- ret = (void *) mailthreadresults;
- break;
- case SET_ALTDRIVER:
- mailaltdriver = (NETDRIVER *) value;
- case GET_ALTDRIVER:
- ret = (void *) mailaltdriver;
- break;
- case SET_ALTDRIVERNAME:
- mailaltdrivername = (char *) value;
- case GET_ALTDRIVERNAME:
- ret = (void *) mailaltdrivername;
- break;
- case SET_TRYALTFIRST:
- tryaltfirst = (int) value;
- case GET_TRYALTFIRST:
- ret = (void *) tryaltfirst;
- break;
- case SET_NOTIMEZONES:
- notimezones = (int) value;
- case GET_NOTIMEZONES:
- ret = (void *) notimezones;
- break;
- default:
- if (stream && stream->dtb) /* if have stream, do for its driver only */
- ret = (*stream->dtb->parameters) (function,value);
- /* else do all drivers */
- else for (d = maildrivers; d; d = d->next)
- if (r = (d->parameters) (function,value)) ret = r;
- /* then do global values */
- if (r = smtp_parameters (function,value)) ret = r;
- if (r = env_parameters (function,value)) ret = r;
- if (r = tcp_parameters (function,value)) ret = r;
- break;
- }
- return ret;
- }
- /* Mail validate mailbox name
- * Accepts: MAIL stream
- * mailbox name
- * purpose string for error message
- * Return: driver factory on success, NIL on failure
- */
- DRIVER *mail_valid (MAILSTREAM *stream,char *mailbox,char *purpose)
- {
- char tmp[MAILTMPLEN];
- DRIVER *factory = NIL;
- if (strlen (mailbox) < (NETMAXHOST+NETMAXUSER+NETMAXMBX+NETMAXSRV+50))
- for (factory = maildrivers; factory &&
- ((factory->flags & DR_DISABLE) ||
- ((factory->flags & DR_LOCAL) && (*mailbox == '{')) ||
- !(*factory->valid) (mailbox));
- factory = factory->next);
- /* must match stream if not dummy */
- if (factory && stream && (stream->dtb != factory))
- factory = strcmp (factory->name,"dummy") ? NIL : stream->dtb;
- if (!factory && purpose) { /* if want an error message */
- sprintf (tmp,"Can't %s %.80s: %s",purpose,mailbox,(*mailbox == '{') ?
- "invalid remote specification" : "no such mailbox");
- mm_log (tmp,ERROR);
- }
- return factory; /* return driver factory */
- }
- /* Mail validate network mailbox name
- * Accepts: mailbox name
- * mailbox driver to validate against
- * pointer to where to return host name if non-NIL
- * pointer to where to return mailbox name if non-NIL
- * Returns: driver on success, NIL on failure
- */
- DRIVER *mail_valid_net (char *name,DRIVER *drv,char *host,char *mailbox)
- {
- NETMBX mb;
- if (!mail_valid_net_parse (name,&mb) || strcmp (mb.service,drv->name))
- return NIL;
- if (host) strcpy (host,mb.host);
- if (mailbox) strcpy (mailbox,mb.mailbox);
- return drv;
- }
- /* Mail validate network mailbox name
- * Accepts: mailbox name
- * NETMBX structure to return values
- * Returns: T on success, NIL on failure
- */
- long mail_valid_net_parse (char *name,NETMBX *mb)
- {
- int i,j;
- char c,*s,*t,*v,tmp[MAILTMPLEN],arg[MAILTMPLEN];
- /* initialize structure */
- memset (mb,' ',sizeof (NETMBX));
- /* have host specification? */
- if (!((*name++ == '{') && (v = strpbrk (name,"/:}")) && (i = v - name) &&
- (i < NETMAXHOST) && (t = strchr (v,'}')) && ((j = t - v)<MAILTMPLEN) &&
- (strlen (t+1) < (size_t) NETMAXMBX))) return NIL;
- strncpy (mb->host,name,i); /* set host name */
- strncpy (mb->orighost,name,i);
- mb->host[i] = mb->orighost[i] = ' ';
- strcpy (mb->mailbox,t+1); /* set mailbox name */
- if (t - v) { /* any switches or port specification? */
- strncpy (t = tmp,v,j); /* copy it */
- tmp[j] = ' '; /* tie it off */
- c = *t++; /* get first delimiter */
- do switch (c) { /* act based upon the character */
- case ':': /* port specification */
- if (mb->port || !(mb->port = strtoul (t,&t,10))) return NIL;
- c = t ? *t++ : ' '; /* get delimiter, advance pointer */
- break;
- case '/': /* switch */
- /* find delimiter */
- if (t = strpbrk (s = t,"/:=")) {
- c = *t; /* remember delimiter for later */
- *t++ = ' '; /* tie off switch name */
- }
- else c = ' '; /* no delimiter */
- lcase (s); /* coerce switch name to lower case */
- if (c == '=') { /* parse switches which take arguments */
- if (*t == '"') { /* quoted string? */
- for (v = arg,i = 0,++t; (c = *t++) != '"';) {
- /* quote next character */
- if (c == '\') c = *t++;
- arg[i++] = c;
- }
- c = *t++; /* remember delimiter for later */
- arg[i] = ' '; /* tie off argument */
- }
- else { /* non-quoted argument */
- if (t = strpbrk (v = t,"/:")) {
- c = *t; /* remember delimiter for later */
- *t++ = ' '; /* tie off switch name */
- }
- else c = ' '; /* no delimiter */
- i = strlen (v); /* length of argument */
- }
- if (!strcmp (s,"service") && (i < NETMAXSRV)) {
- if (*mb->service) return NIL;
- else strcpy (mb->service,lcase (v));
- }
- else if (!strcmp (s,"user") && (i < NETMAXUSER)) {
- if (*mb->user) return NIL;
- else strcpy (mb->user,v);
- }
- else return NIL; /* invalid argument switch */
- }
- else { /* non-argument switch */
- if (!strcmp (s,"anonymous")) mb->anoflag = T;
- else if (!strcmp (s,"debug")) mb->dbgflag = T;
- else if (!strcmp (s,"secure")) mb->secflag = T;
- else if (!strcmp (s,"alt")) mb->altflag = mailaltdriver ? T : NIL;
- else if (mailaltdriver && mailaltdrivername &&
- !strcmp (s,mailaltdrivername)) mb->altflag = T;
- /* service switches below here */
- else if (*mb->service) return NIL;
- else if (!strncmp (s,"imap",4) &&
- (!s[4] || !strcmp (s+4,"2") || !strcmp (s+4,"2bis") ||
- !strcmp (s+4,"4") || !strcmp (s+4,"4rev1")))
- strcpy (mb->service,"imap");
- else if (!strncmp (s,"pop",3) && (!s[3] || !strcmp (s+3,"3")))
- strcpy (mb->service,"pop3");
- else if (!strcmp (s,"nntp") || !strcmp (s,"smtp"))
- strcpy (mb->service,s);
- else return NIL; /* invalid non-argument switch */
- }
- break;
- default: /* anything else is bogus */
- return NIL;
- } while (c); /* see if anything more to parse */
- }
- /* default mailbox name */
- if (!*mb->mailbox) strcpy (mb->mailbox,"INBOX");
- /* default service name */
- if (!*mb->service) strcpy (mb->service,"imap");
- return T;
- }
- /* Mail scan mailboxes for string
- * Accepts: mail stream
- * reference
- * pattern to search
- * contents to search
- */
- void mail_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
- {
- int remote = ((*pat == '{') || (ref && *ref == '{'));
- DRIVER *d;
- if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */
- if (stream) { /* if have a stream, do it for that stream */
- if ((d = stream->dtb) && d->scan &&
- !(((d->flags & DR_LOCAL) && remote)))
- (*d->scan) (stream,ref,pat,contents);
- }
- /* otherwise do for all DTB's */
- else for (d = maildrivers; d; d = d->next)
- if (d->scan && !((d->flags & DR_DISABLE) ||
- ((d->flags & DR_LOCAL) && remote)))
- (d->scan) (NIL,ref,pat,contents);
- }
- /* Mail list mailboxes
- * Accepts: mail stream
- * reference
- * pattern to search
- */
- void mail_list (MAILSTREAM *stream,char *ref,char *pat)
- {
- int remote = ((*pat == '{') || (ref && *ref == '{'));
- DRIVER *d = maildrivers;
- if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */
- if (stream && stream->dtb) { /* if have a stream, do it for that stream */
- if (!(((d = stream->dtb)->flags & DR_LOCAL) && remote))
- (*d->list) (stream,ref,pat);
- }
- /* otherwise do for all DTB's */
- else do if (!((d->flags & DR_DISABLE) ||
- ((d->flags & DR_LOCAL) && remote)))
- (d->list) (NIL,ref,pat);
- while (d = d->next); /* until at the end */
- }
- /* Mail list subscribed mailboxes
- * Accepts: mail stream
- * pattern to search
- */
- void mail_lsub (MAILSTREAM *stream,char *ref,char *pat)
- {
- int remote = ((*pat == '{') || (ref && *ref == '{'));
- DRIVER *d = maildrivers;
- if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */
- if (stream && stream->dtb) { /* if have a stream, do it for that stream */
- if (!(((d = stream->dtb)->flags & DR_LOCAL) && remote))
- (*d->lsub) (stream,ref,pat);
- }
- /* otherwise do for all DTB's */
- else do if (!((d->flags & DR_DISABLE) ||
- ((d->flags & DR_LOCAL) && remote)))
- (d->lsub) (NIL,ref,pat);
- while (d = d->next); /* until at the end */
- }
- /* Mail subscribe to mailbox
- * Accepts: mail stream
- * mailbox to add to subscription list
- * Returns: T on success, NIL on failure
- */
- long mail_subscribe (MAILSTREAM *stream,char *mailbox)
- {
- DRIVER *factory = mail_valid (stream,mailbox,"subscribe to mailbox");
- return factory ?
- (factory->subscribe ?
- (*factory->subscribe) (stream,mailbox) : sm_subscribe (mailbox)) : NIL;
- }
- /* Mail unsubscribe to mailbox
- * Accepts: mail stream
- * mailbox to delete from subscription list
- * Returns: T on success, NIL on failure
- */
- long mail_unsubscribe (MAILSTREAM *stream,char *mailbox)
- {
- DRIVER *factory = mail_valid (stream,mailbox,NIL);
- return (factory && factory->unsubscribe) ?
- (*factory->unsubscribe) (stream,mailbox) : sm_unsubscribe (mailbox);
- }
- /* Mail create mailbox
- * Accepts: mail stream
- * mailbox name to create
- * Returns: T on success, NIL on failure
- */
- long mail_create (MAILSTREAM *stream,char *mailbox)
- {
- MAILSTREAM *ts;
- char *s,tmp[MAILTMPLEN];
- DRIVER *d;
- if (strlen (mailbox) >= (NETMAXHOST+NETMAXUSER+NETMAXMBX+NETMAXSRV+50)) {
- sprintf (tmp,"Can't create %.80s: %s",mailbox,(*mailbox == '{') ?
- "invalid remote specification" : "no such mailbox");
- mm_log (tmp,ERROR);
- return NIL;
- }
- /* create of INBOX invalid */
- if (!strcmp (lcase (strcpy (tmp,mailbox)),"inbox")) {
- mm_log ("Can't create INBOX",ERROR);
- return NIL;
- }
- for (s = mailbox; *s; s++) { /* make sure valid name */
- if (*s & 0x80) { /* reserved for future use with UTF-8 */
- mm_log ("Can't create mailbox name with 8-bit character",ERROR);
- return NIL;
- }
- /* validate modified UTF-7 */
- else if (*s == '&') while (*++s != '-') switch (*s) {
- case ' ':
- sprintf (tmp,"Can't create unterminated modified UTF-7 name: %.80s",
- mailbox);
- mm_log (tmp,ERROR);
- return NIL;
- default: /* must be alphanumeric */
- if (!isalnum (*s)) {
- sprintf (tmp,"Can't create invalid modified UTF-7 name: %.80s",
- mailbox);
- mm_log (tmp,ERROR);
- return NIL;
- }
- case '+': /* valid modified BASE64 */
- case ',':
- break; /* all OK so far */
- }
- }
- /* see if special driver hack */
- if (!strncmp (tmp,"#driver.",8)) {
- /* tie off name at likely delimiter */
- if (s = strpbrk (tmp+8,"/\:")) *s++ = ' ';
- else {
- sprintf (tmp,"Can't create mailbox %.80s: bad driver syntax",mailbox);
- mm_log (tmp,ERROR);
- return NIL;
- }
- for (d = maildrivers; d && strcmp (d->name,tmp+8); d = d->next);
- if (d) mailbox += s - tmp; /* skip past driver specification */
- else {
- sprintf (tmp,"Can't create mailbox %.80s: unknown driver",mailbox);
- mm_log (tmp,ERROR);
- return NIL;
- }
- }
- /* use stream if one given or deterministic */
- else if ((stream && stream->dtb) ||
- (((*mailbox == '{') || (*mailbox == '#')) &&
- (stream = mail_open (NIL,mailbox,OP_PROTOTYPE | OP_SILENT))))
- d = stream->dtb;
- else if ((*mailbox != '{') && (ts = default_proto (NIL))) d = ts->dtb;
- else { /* failed utterly */
- sprintf (tmp,"Can't create mailbox %.80s: indeterminate format",mailbox);
- mm_log (tmp,ERROR);
- return NIL;
- }
- return (*d->create) (stream,mailbox);
- }
- /* Mail delete mailbox
- * Accepts: mail stream
- * mailbox name to delete
- * Returns: T on success, NIL on failure
- */
- long mail_delete (MAILSTREAM *stream,char *mailbox)
- {
- long ret = NIL;
- DRIVER *dtb = mail_valid (stream,mailbox,"delete mailbox");
- if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
- ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
- ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
- ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
- ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5]) {
- mm_log ("Can't delete INBOX",ERROR);
- }
- else SAFE_DISPATCH (dtb,ret,mbxdel,(stream,mailbox))
- return ret;
- }
- /* Mail rename mailbox
- * Accepts: mail stream
- * old mailbox name
- * new mailbox name
- * Returns: T on success, NIL on failure
- */
- long mail_rename (MAILSTREAM *stream,char *old,char *newname)
- {
- long ret = NIL;
- char tmp[MAILTMPLEN];
- DRIVER *dtb = mail_valid (stream,old,"rename mailbox");
- if ((*old != '{') && (*old != '#') && mail_valid (NIL,newname,NIL)) {
- sprintf (tmp,"Can't rename to mailbox %.80s: mailbox already exists",
- newname);
- mm_log (tmp,ERROR);
- }
- else SAFE_DISPATCH (dtb,ret,mbxren,(stream,old,newname))
- return ret;
- }
- /* Mail status of mailbox
- * Accepts: mail stream
- * mailbox name
- * status flags
- * Returns: T on success, NIL on failure
- */
- long mail_status (MAILSTREAM *stream,char *mbx,long flags)
- {
- long ret = NIL;
- DRIVER *factory = mail_valid (stream,mbx,"get status of mailbox");
- if (factory) { /* use driver's routine if have one */
- if (factory->status) return (*factory->status) (stream,mbx,flags);
- /* foolish status of selected mbx? */
- if (stream && !strcmp (mbx,stream->mailbox))
- ret = mail_status_default (stream,mbx,flags);
- else SAFE_FUNCTION (factory,ret,mail_status_default,(stream,mbx,flags))
- }
- return ret;
- }
- /* Mail status of mailbox default handler
- * Accepts: mail stream
- * mailbox name
- * status flags
- * Returns: T on success, NIL on failure
- */
- long mail_status_default (MAILSTREAM *stream,char *mbx,long flags)
- {
- MAILSTATUS status;
- unsigned long i;
- MAILSTREAM *tstream = NIL;
- /* make temporary stream (unless this mbx) */
- if (!stream && !(stream = tstream =
- mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
- status.flags = flags; /* return status values */
- status.messages = stream->nmsgs;
- status.recent = stream->recent;
- if (flags & SA_UNSEEN) /* must search to get unseen messages */
- for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
- if (!mail_elt (stream,i)->seen) status.unseen++;
- status.uidnext = stream->uid_last + 1;
- status.uidvalidity = stream->uid_validity;
- /* pass status to main program */
- mm_status (stream,mbx,&status);
- if (tstream) mail_close (tstream);
- return T; /* success */
- }
- /* Mail open
- * Accepts: candidate stream for recycling
- * mailbox name
- * open options
- * Returns: stream to use on success, NIL on failure
- */
- MAILSTREAM *mail_open (MAILSTREAM *stream,char *name,long options)
- {
- int i;
- char *s,tmp[MAILTMPLEN];
- NETMBX mb;
- DRIVER *d;
- /* see if special driver hack */
- if ((options & OP_PROTOTYPE) && (name[0] == '#') &&
- ((name[1] == 'D') || (name[1] == 'd')) &&
- ((name[2] == 'R') || (name[2] == 'r')) &&
- ((name[3] == 'I') || (name[3] == 'i')) &&
- ((name[4] == 'V') || (name[4] == 'v')) &&
- ((name[5] == 'E') || (name[5] == 'e')) &&
- ((name[6] == 'R') || (name[6] == 'r')) && (name[7] == '.')) {
- sprintf (tmp,"%.80s",name+8);
- /* tie off name at likely delimiter */
- if (s = strpbrk (lcase (tmp),"/\:")) *s++ = ' ';
- else {
- sprintf (tmp,"Can't resolve mailbox %.80s: bad driver syntax",name);
- mm_log (tmp,ERROR);
- return NIL;
- }
- for (d = maildrivers; d && strcmp (d->name,tmp); d = d->next);
- if (d) return (*d->open) (NIL);
- else {
- sprintf (tmp,"Can't resolve mailbox %.80s: unknown driver",name);
- mm_log (tmp,ERROR);
- return NIL;
- }
- }
- else d = mail_valid (NIL,name,(options & OP_SILENT) ?
- (char *) NIL : "open mailbox");
- if (d) { /* must have a factory */
- if (options & OP_PROTOTYPE) return (*d->open) (NIL);
- if (stream) { /* recycling requested? */
- /* yes, recycleable stream? */
- if ((stream->dtb == d) && (d->flags & DR_RECYCLE) &&
- mail_usable_network_stream (stream,name)) {
- mail_free_cache(stream);/* yes, clean up stream */
- if (stream->mailbox) fs_give ((void **) &stream->mailbox);
- /* flush user flags */
- for (i = 0; i < NUSERFLAGS; i++)
- if (stream->user_flags[i]) fs_give ((void **)&stream->user_flags[i]);
- }
- else { /* stream not recycleable, babble if net */
- if (!stream->silent && stream->dtb && !(stream->dtb->flags&DR_LOCAL) &&
- mail_valid_net_parse (stream->mailbox,&mb)) {
- sprintf (tmp,"Closing connection to %.80s",mb.host);
- mm_log (tmp,(long) NIL);
- }
- /* flush the old stream */
- stream = mail_close (stream);
- }
- }
- /* instantiate new stream if not recycling */
- if (!stream) (*mailcache) (stream = (MAILSTREAM *)
- memset (fs_get (sizeof (MAILSTREAM)),0,
- sizeof (MAILSTREAM)),(long) 0,CH_INIT);
- stream->dtb = d; /* set dispatch */
- /* set mailbox name */
- stream->mailbox = cpystr (name);
- /* initialize stream flags */
- stream->inbox = stream->lock = NIL;
- stream->debug = (options & OP_DEBUG) ? T : NIL;
- stream->rdonly = (options & OP_READONLY) ? T : NIL;
- stream->anonymous = (options & OP_ANONYMOUS) ? T : NIL;
- stream->scache = (options & OP_SHORTCACHE) ? T : NIL;
- stream->silent = (options & OP_SILENT) ? T : NIL;
- stream->halfopen = (options & OP_HALFOPEN) ? T : NIL;
- stream->secure = (options & OP_SECURE) ? T : NIL;
- stream->tryalt = (options & OP_TRYALT) ? T : NIL;
- stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
- stream->perm_answered = stream->perm_draft = stream->kwd_create = NIL;
- stream->uid_nosticky = (d->flags & DR_NOSTICKY) ? T : NIL;
- stream->uid_last = 0; /* default UID validity */
- stream->uid_validity = time (0);
- /* have driver open, flush if failed */
- if (!(*d->open) (stream)) stream = mail_close (stream);
- }
- return stream; /* return the stream */
- }
- /* Mail close
- * Accepts: mail stream
- * close options
- * Returns: NIL, always
- */
- MAILSTREAM *mail_close_full (MAILSTREAM *stream,long options)
- {
- int i;
- if (stream) { /* make sure argument given */
- /* do the driver's close action */
- if (stream->dtb) (*stream->dtb->close) (stream,options);
- if (stream->mailbox) fs_give ((void **) &stream->mailbox);
- stream->sequence++; /* invalidate sequence */
- /* flush user flags */
- for (i = 0; i < NUSERFLAGS; i++)
- if (stream->user_flags[i]) fs_give ((void **) &stream->user_flags[i]);
- mail_free_cache (stream); /* finally free the stream's storage */
- if (!stream->use) fs_give ((void **) &stream);
- }
- return NIL;
- }
- /* Mail make handle
- * Accepts: mail stream
- * Returns: handle
- *
- * Handles provide a way to have multiple pointers to a stream yet allow the
- * stream's owner to nuke it or recycle it.
- */
- MAILHANDLE *mail_makehandle (MAILSTREAM *stream)
- {
- MAILHANDLE *handle = (MAILHANDLE *) fs_get (sizeof (MAILHANDLE));
- handle->stream = stream; /* copy stream */
- /* and its sequence */
- handle->sequence = stream->sequence;
- stream->use++; /* let stream know another handle exists */
- return handle;
- }
- /* Mail release handle
- * Accepts: Mail handle
- */
- void mail_free_handle (MAILHANDLE **handle)
- {
- MAILSTREAM *s;
- if (*handle) { /* only free if exists */
- /* resign stream, flush unreferenced zombies */
- if ((!--(s = (*handle)->stream)->use) && !s->dtb) fs_give ((void **) &s);
- fs_give ((void **) handle); /* now flush the handle */
- }
- }
- /* Mail get stream handle
- * Accepts: Mail handle
- * Returns: mail stream or NIL if stream gone
- */
- MAILSTREAM *mail_stream (MAILHANDLE *handle)
- {
- MAILSTREAM *s = handle->stream;
- return (s->dtb && (handle->sequence == s->sequence)) ? s : NIL;
- }
- /* Mail fetch cache element
- * Accepts: mail stream
- * message # to fetch
- * Returns: cache element of this message
- * Can also be used to create cache elements for new messages.
- */
- MESSAGECACHE *mail_elt (MAILSTREAM *stream,unsigned long msgno)
- {
- if (msgno < 1 || msgno > stream->nmsgs) {
- char tmp[MAILTMPLEN];
- sprintf (tmp,"Bad msgno %lu in mail_elt, nmsgs = %lu",msgno,stream->nmsgs);
- fatal (tmp);
- }
- return (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_MAKEELT);
- }
- /* Mail fetch fast information
- * Accepts: mail stream
- * sequence
- * option flags
- *
- * Generally, mail_fetch_structure is preferred
- */
- void mail_fetch_fast (MAILSTREAM *stream,char *sequence,long flags)
- {
- /* do the driver's action */
- if (stream->dtb && stream->dtb->fast)
- (*stream->dtb->fast) (stream,sequence,flags);
- }
- /* Mail fetch flags
- * Accepts: mail stream
- * sequence
- * option flags
- */
- void mail_fetch_flags (MAILSTREAM *stream,char *sequence,long flags)
- {
- /* do the driver's action */
- if (stream->dtb && stream->dtb->msgflags)
- (*stream->dtb->msgflags) (stream,sequence,flags);
- }
- /* Mail fetch message overview
- * Accepts: mail stream
- * UID sequence to fetch
- * pointer to overview return function
- */
- void mail_fetch_overview (MAILSTREAM *stream,char *sequence,overview_t ofn)
- {
- if (stream->dtb && !(stream->dtb->overview &&
- (*stream->dtb->overview) (stream,sequence,ofn)) &&
- mail_uid_sequence (stream,sequence) && mail_ping (stream)) {
- MESSAGECACHE *elt;
- ENVELOPE *env;
- OVERVIEW ov;
- unsigned long i;
- ov.optional.lines = 0;
- ov.optional.xref = NIL;
- for (i = 1; i <= stream->nmsgs; i++)
- if (((elt = mail_elt (stream,i))->sequence) &&
- (env = mail_fetch_structure (stream,i,NIL,NIL)) && ofn) {
- ov.subject = env->subject;
- ov.from = env->from;
- ov.date = env->date;
- ov.message_id = env->message_id;
- ov.references = env->references;
- ov.optional.octets = elt->rfc822_size;
- (*ofn) (stream,mail_uid (stream,i),&ov);
- }
- }
- }
- /* Mail fetch message structure
- * Accepts: mail stream
- * message # to fetch
- * pointer to return body
- * option flags
- * Returns: envelope of this message, body returned in body value
- *
- * Fetches the "fast" information as well
- */
- ENVELOPE *mail_fetch_structure (MAILSTREAM *stream,unsigned long msgno,
- BODY **body,long flags)
- {
- ENVELOPE **env;
- BODY **b;
- MESSAGECACHE *elt;
- char c,*s,*hdr;
- unsigned long hdrsize;
- STRING bs;
- /* do the driver's action if specified */
- if (stream->dtb && stream->dtb->structure)
- return (*stream->dtb->structure) (stream,msgno,body,flags);
- if (flags & FT_UID) { /* UID form of call */
- if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
- else return NIL; /* must get UID/msgno map first */
- }
- elt = mail_elt (stream,msgno);/* get elt for real message number */
- if (stream->scache) { /* short caching */
- if (msgno != stream->msgno){/* garbage collect if not same message */
- mail_gc (stream,GC_ENV | GC_TEXTS);
- stream->msgno = msgno; /* this is the current message now */
- }
- env = &stream->env; /* get pointers to envelope and body */
- b = &stream->body;
- }
- else { /* get pointers to elt envelope and body */
- env = &elt->private.msg.env;
- b = &elt->private.msg.body;
- }
- if (stream->dtb && ((body && !*b) || !*env || (*env)->incomplete)) {
- mail_free_envelope (env); /* flush old envelope and body */
- mail_free_body (b);
- /* see if need to fetch the whole thing */
- if (body || !elt->rfc822_size) {
- s = (*stream->dtb->header) (stream,msgno,&hdrsize,flags & ~FT_INTERNAL);
- /* make copy in case body fetch smashes it */
- hdr = (char *) memcpy (fs_get ((size_t) hdrsize+1),s,(size_t) hdrsize);
- hdr[hdrsize] = ' '; /* tie off header */
- (*stream->dtb->text) (stream,msgno,&bs,(flags & ~FT_INTERNAL) | FT_PEEK);
- if (!elt->rfc822_size) elt->rfc822_size = hdrsize + SIZE (&bs);
- if (body) /* only parse body if requested */
- rfc822_parse_msg (env,b,hdr,hdrsize,&bs,BADHOST,stream->dtb->flags);
- else
- rfc822_parse_msg (env,NIL,hdr,hdrsize,NIL,BADHOST,stream->dtb->flags);
- fs_give ((void **) &hdr); /* flush header */
- }
- else { /* can save memory doing it this way */
- hdr = (*stream->dtb->header) (stream,msgno,&hdrsize,flags | FT_INTERNAL);
- c = hdr[hdrsize]; /* preserve what's there */
- hdr[hdrsize] = ' '; /* tie off header */
- rfc822_parse_msg (env,NIL,hdr,hdrsize,NIL,BADHOST,stream->dtb->flags);
- hdr[hdrsize] = c; /* restore in case cached data */
- }
- }
- /* if need date, have date in envelope? */
- if (!elt->day && *env && (*env)->date) mail_parse_date (elt,(*env)->date);
- /* sigh, fill in bogus default */
- if (!elt->day) elt->day = elt->month = 1;
- if (body) *body = *b; /* return the body */
- return *env; /* return the envelope */
- }
- /* Mail mark single message (internal use only)
- * Accepts: mail stream
- * elt to mark
- * fetch flags
- */
- static void markseen (MAILSTREAM *stream,MESSAGECACHE *elt,long flags)
- {
- unsigned long i;
- char sequence[20];
- MESSAGECACHE *e;
- /* non-peeking and needs to set Seen? */
- if (!(flags & FT_PEEK) && !elt->seen) {
- if (stream->dtb->flagmsg){ /* driver wants per-message call? */
- elt->valid = NIL; /* do pre-alteration driver call */
- (*stream->dtb->flagmsg) (stream,elt);
- /* set seen, do post-alteration driver call */
- elt->seen = elt->valid = T;
- (*stream->dtb->flagmsg) (stream,elt);
- }
- if (stream->dtb->flag) { /* driver wants one-time call? */
- /* better safe than sorry, save seq bits */
- for (i = 1; i <= stream->nmsgs; i++) {
- e = mail_elt (stream,i);
- e->private.sequence = e->sequence;
- }
- /* call driver to set the message */
- sprintf (sequence,"%lu",elt->msgno);
- (*stream->dtb->flag) (stream,sequence,"\Seen",ST_SET);
- /* restore sequence bits */
- for (i = 1; i <= stream->nmsgs; i++) {
- e = mail_elt (stream,i);
- e->sequence = e->private.sequence;
- }
- }
- mm_flags(stream,elt->msgno);/* notify mail program of flag change */
- }
- }
- /* Mail fetch message
- * Accepts: mail stream
- * message # to fetch
- * pointer to returned length
- * flags
- * Returns: message text
- */
- char *mail_fetch_message (MAILSTREAM *stream,unsigned long msgno,
- unsigned long *len,long flags)
- {
- GETS_DATA md;
- SIZEDTEXT *t;
- STRING bs;
- MESSAGECACHE *elt;
- char *s,*u;
- unsigned long i,j;
- if (len) *len = 0; /* default return size */
- if (flags & FT_UID) { /* UID form of call */
- if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
- else return ""; /* must get UID/msgno map first */
- }
- /* initialize message data identifier */
- INIT_GETS (md,stream,msgno,"",0,0);
- /* is data already cached? */
- if ((t = &(elt = mail_elt (stream,msgno))->private.msg.full.text)->data) {
- markseen (stream,elt,flags);/* mark message seen */
- return mail_fetch_text_return (&md,t,len);
- }
- if (!stream->dtb) return ""; /* not in cache, must have live driver */
- if (stream->dtb->msgdata) return
- ((*stream->dtb->msgdata) (stream,msgno,"",0,0,NIL,flags) && t->data) ?
- mail_fetch_text_return (&md,t,len) : "";
- /* ugh, have to do this the crufty way */
- u = mail_fetch_header (stream,msgno,NIL,NIL,&i,flags);
- /* copy in case text method stomps on it */
- s = (char *) memcpy (fs_get ((size_t) i),u,(size_t) i);
- if ((*stream->dtb->text) (stream,msgno,&bs,flags)) {
- t = &stream->text; /* build combined copy */
- if (t->data) fs_give ((void **) &t->data);
- t->data = (unsigned char *) fs_get ((t->size = i + SIZE (&bs)) + 1);
- memcpy (t->data,s,(size_t) i);
- for (j = i; j < t->size; j++) t->data[j] = SNX (&bs);
- t->data[j] = ' '; /* tie off data */
- u = mail_fetch_text_return (&md,t,len);
- }
- else u = "";
- fs_give ((void **) &s); /* finished with copy of header */
- return u;
- }
- /* Mail fetch message header
- * Accepts: mail stream
- * message # to fetch
- * MIME section specifier (#.#.#...#)
- * list of lines to fetch
- * pointer to returned length
- * flags
- * Returns: message header in RFC822 format
- *
- * Note: never calls a mailgets routine
- */
- char *mail_fetch_header (MAILSTREAM *stream,unsigned long msgno,char *section,
- STRINGLIST *lines,unsigned long *len,long flags)
- {
- STRING bs;
- BODY *b = NIL;
- SIZEDTEXT *t = NIL,rt;
- MESSAGE *m = NIL;
- MESSAGECACHE *elt;
- char tmp[MAILTMPLEN];
- if (len) *len = 0; /* default return size */
- if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
- if (flags & FT_UID) { /* UID form of call */
- if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
- else return ""; /* must get UID/msgno map first */
- }
- elt = mail_elt (stream,msgno);/* get cache data */
- if (section && *section) { /* nested body header wanted? */
- if (!((b = mail_body (stream,msgno,section)) &&
- (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
- return ""; /* lose if no body or not MESSAGE/RFC822 */
- m = b->nested.msg; /* point to nested message */
- }
- /* else top-level message header wanted */
- else m = &elt->private.msg;
- if (m->header.text.data && mail_match_lines (lines,m->lines,flags)) {
- if (lines) textcpy (t = &stream->text,&m->header.text);
- else t = &m->header.text; /* in cache, and cache is valid */
- markseen (stream,elt,flags);/* mark message seen */
- }
- else if (stream->dtb) { /* not in cache, has live driver? */
- if (stream->dtb->msgdata) { /* has driver section fetch? */
- /* build driver section specifier */
- if (section && *section) sprintf (tmp,"%s.HEADER",section);
- else strcpy (tmp,"HEADER");
- if ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,lines,flags)) {
- t = &m->header.text; /* fetch data */
- /* don't need to postprocess lines */
- if (m->lines) lines = NIL;
- else if (lines) textcpy (t = &stream->text,&m->header.text);
- }
- }
- else if (b) { /* nested body wanted? */
- if (stream->private.search.text) {
- rt.data = (unsigned char *) stream->private.search.text +
- b->nested.msg->header.offset;
- rt.size = b->nested.msg->header.text.size;
- t = &rt;
- }
- else if ((*stream->dtb->text) (stream,msgno,&bs,flags & ~FT_INTERNAL)) {
- if ((bs.dtb->next == mail_string_next) && !lines) {
- rt.data = (unsigned char *) bs.curpos + b->nested.msg->header.offset;
- rt.size = b->nested.msg->header.text.size;
- if (stream->private.search.string)
- stream->private.search.text = bs.curpos;
- t = &rt; /* special hack to avoid extra copy */
- }
- else textcpyoffstring (t = &stream->text,&bs,
- b->nested.msg->header.offset,
- b->nested.msg->header.text.size);
- }
- }
- else { /* top-level header fetch */
- /* mark message seen */
- markseen (stream,elt,flags);
- if (rt.data = (unsigned char *)
- (*stream->dtb->header) (stream,msgno,&rt.size,flags)) {
- /* make a safe copy if need to filter */
- if (lines) textcpy (t = &stream->text,&rt);
- else t = &rt; /* top level header */
- }
- }
- }
- if (!t || !t->data) return "";/* error if no string */
- /* filter headers if requested */
- if (lines) t->size = mail_filter ((char *) t->data,t->size,lines,flags);
- if (len) *len = t->size; /* return size if requested */
- return (char *) t->data; /* and text */
- }
- /* Mail fetch message text
- * Accepts: mail stream
- * message # to fetch
- * MIME section specifier (#.#.#...#)
- * pointer to returned length
- * flags
- * Returns: message text
- */
- char *mail_fetch_text (MAILSTREAM *stream,unsigned long msgno,char *section,
- unsigned long *len,long flags)
- {
- GETS_DATA md;
- PARTTEXT *p;
- STRING bs;
- MESSAGECACHE *elt;
- BODY *b = NIL;
- char tmp[MAILTMPLEN];
- unsigned long i;
- if (len) *len = 0; /* default return size */
- if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
- if (flags & FT_UID) { /* UID form of call */
- if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
- else return ""; /* must get UID/msgno map first */
- }
- elt = mail_elt (stream,msgno);/* get cache data */
- if (section && *section) { /* nested body text wanted? */
- if (!((b = mail_body (stream,msgno,section)) &&
- (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
- return ""; /* lose if no body or not MESSAGE/RFC822 */
- p = &b->nested.msg->text; /* point at nested message */
- /* build IMAP-format section specifier */
- sprintf (tmp,"%s.TEXT",section);
- flags &= ~FT_INTERNAL; /* can't win with this set */
- }
- else { /* top-level message text wanted */
- p = &elt->private.msg.text;
- strcpy (tmp,"TEXT");
- }
- /* initialize message data identifier */
- INIT_GETS (md,stream,msgno,section,0,0);
- if (p->text.data) { /* is data already cached? */
- markseen (stream,elt,flags);/* mark message seen */
- return mail_fetch_text_return (&md,&p->text,len);
- }
- if (!stream->dtb) return ""; /* not in cache, must have live driver */
- if (stream->dtb->msgdata) return
- ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,NIL,flags) && p->text.data)?
- mail_fetch_text_return (&md,&p->text,len) : "";
- if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return "";
- if (section && *section) { /* nested is more complex */
- SETPOS (&bs,p->offset);
- i = p->text.size; /* just want this much */
- }
- else i = SIZE (&bs); /* want entire text */
- return mail_fetch_string_return (&md,&bs,i,len);
- }
- /* Mail fetch message body part MIME headers
- * Accepts: mail stream
- * message # to fetch
- * MIME section specifier (#.#.#...#)
- * pointer to returned length
- * flags
- * Returns: message text
- */
- char *mail_fetch_mime (MAILSTREAM *stream,unsigned long msgno,char *section,
- unsigned long *len,long flags)
- {
- PARTTEXT *p;
- STRING bs;
- BODY *b;
- char tmp[MAILTMPLEN];
- if (len) *len = 0; /* default return size */
- if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
- if (flags & FT_UID) { /* UID form of call */
- if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
- else return ""; /* must get UID/msgno map first */
- }
- flags &= ~FT_INTERNAL; /* can't win with this set */
- if (!(section && *section && (b = mail_body (stream,msgno,section))))
- return ""; /* not valid section */
- /* in cache? */
- if ((p = &b->mime)->text.data) {
- /* mark message seen */
- markseen (stream,mail_elt (stream,msgno),flags);
- if (len) *len = p->text.size;
- return (char *) p->text.data;
- }
- if (!stream->dtb) return ""; /* not in cache, must have live driver */
- if (stream->dtb->msgdata) { /* has driver fetch? */
- /* build driver section specifier */
- sprintf (tmp,"%s.MIME",section);
- if ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,NIL,flags) &&
- p->text.data) {
- if (len) *len = p->text.size;
- return (char *) p->text.data;
- }
- else return "";
- }
- if (len) *len = b->mime.text.size;
- if (!b->mime.text.size) { /* empty MIME header -- mark seen anyway */
- markseen (stream,mail_elt (stream,msgno),flags);
- return "";
- }
- /* have to get it from offset */
- if (stream->private.search.text)
- return stream->private.search.text + b->mime.offset;
- if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) {
- if (len) *len = 0;
- return "";
- }
- if (bs.dtb->next == mail_string_next) {
- if (stream->private.search.string) stream->private.search.text = bs.curpos;
- return bs.curpos + b->mime.offset;
- }
- return textcpyoffstring (&stream->text,&bs,b->mime.offset,b->mime.text.size);
- }
- /* Mail fetch message body part
- * Accepts: mail stream
- * message # to fetch
- * MIME section specifier (#.#.#...#)
- * pointer to returned length
- * flags
- * Returns: message body
- */
- char *mail_fetch_body (MAILSTREAM *stream,unsigned long msgno,char *section,
- unsigned long *len,long flags)
- {
- GETS_DATA md;
- PARTTEXT *p;
- STRING bs;
- BODY *b;
- SIZEDTEXT *t;
- char *s,tmp[MAILTMPLEN];
- if (!(section && *section)) /* top-level text wanted? */
- return mail_fetch_message (stream,msgno,len,flags);
- else if (strlen (section) > (MAILTMPLEN - 20)) return "";
- flags &= ~FT_INTERNAL; /* can't win with this set */
- /* initialize message data identifier */
- INIT_GETS (md,stream,msgno,section,0,0);
- /* kludge for old section 0 header */
- if (!strcmp (s = strcpy (tmp,section),"0") ||
- ((s = strstr (tmp,".0")) && !s[2])) {
- SIZEDTEXT ht;
- *s = ' '; /* tie off section */
- /* this silly way so it does mailgets */
- ht.data = (unsigned char *) mail_fetch_header (stream,msgno,
- tmp[0] ? tmp : NIL,NIL,
- &ht.size,flags);
- /* may have UIDs here */
- md.flags = (flags & FT_UID) ? MG_UID : NIL;
- return mail_fetch_text_return (&md,&ht,len);
- }
- if (len) *len = 0; /* default return size */
- if (flags & FT_UID) { /* UID form of call */
- if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
- else return ""; /* must get UID/msgno map first */
- }
- /* must have body */
- if (!(b = mail_body (stream,msgno,section))) return "";
- /* have cached text? */
- if ((t = &(p = &b->contents)->text)->data) {
- /* mark message seen */
- markseen (stream,mail_elt (stream,msgno),flags);
- return mail_fetch_text_return (&md,t,len);
- }
- if (!stream->dtb) return ""; /* not in cache, must have live driver */
- if (stream->dtb->msgdata) return
- ((*stream->dtb->msgdata)(stream,msgno,section,0,0,NIL,flags) && t->data) ?
- mail_fetch_text_return (&md,t,len) : "";
- if (len) *len = t->size;
- if (!t->size) { /* empty body part -- mark seen anyway */
- markseen (stream,mail_elt (stream,msgno),flags);
- return "";
- }
- /* copy body from stringstruct offset */
- if (stream->private.search.text)
- return stream->private.search.text + p->offset;
- if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) {
- if (len) *len = 0;
- return "";
- }
- if (bs.dtb->next == mail_string_next) {
- if (stream->private.search.string) stream->private.search.text = bs.curpos;
- return bs.curpos + p->offset;
- }
- SETPOS (&bs,p->offset);
- return mail_fetch_string_return (&md,&bs,t->size,len);
- }
- /* Mail fetch partial message text
- * Accepts: mail stream
- * message # to fetch
- * MIME section specifier (#.#.#...#)
- * offset of first designed byte or 0 to start at beginning
- * maximum number of bytes or 0 for all bytes
- * flags
- * Returns: T if successful, else NIL
- */
- long mail_partial_text (MAILSTREAM *stream,unsigned long msgno,char *section,
- unsigned long first,unsigned long last,long flags)
- {
- GETS_DATA md;
- PARTTEXT *p = NIL;
- MESSAGECACHE *elt;
- STRING bs;
- BODY *b;
- char tmp[MAILTMPLEN];
- unsigned long i;
- if (!mailgets) fatal ("mail_partial_text() called without a mailgets!");
- if (section && (strlen (section) > (MAILTMPLEN - 20))) return NIL;
- if (flags & FT_UID) { /* UID form of call */
- if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
- else return NIL; /* must get UID/msgno map first */
- }
- elt = mail_elt (stream,msgno);/* get cache data */
- flags &= ~FT_INTERNAL; /* bogus if this is set */
- if (section && *section) { /* nested body text wanted? */
- if (!((b = mail_body (stream,msgno,section)) &&
- (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
- return NIL; /* lose if no body or not MESSAGE/RFC822 */
- p = &b->nested.msg->text; /* point at nested message */
- /* build IMAP-format section specifier */
- sprintf (tmp,"%s.TEXT",section);
- }
- else { /* else top-level message text wanted */
- p = &elt->private.msg.text;
- strcpy (tmp,"TEXT");
- }
- /* initialize message data identifier */
- INIT_GETS (md,stream,msgno,tmp,first,last);
- if (p->text.data) { /* is data already cached? */
- INIT (&bs,mail_string,p->text.data,i = p->text.size);
- markseen (stream,elt,flags);/* mark message seen */
- }
- else { /* else get data from driver */
- if (!stream->dtb) return NIL;
- if (stream->dtb->msgdata) /* driver will handle this */
- return (*stream->dtb->msgdata) (stream,msgno,tmp,first,last,NIL,flags);
- if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return NIL;
- if (section && *section) { /* nexted if more complex */
- SETPOS (&bs,p->offset); /* offset stringstruct to data */
- i = p->text.size; /* maximum size of data */
- }
- else i = SIZE (&bs); /* just want this much */
- }
- if (i <= first) i = first = 0;/* first byte is beyond end of text */
- /* truncate as needed */
- else { /* offset and truncate */
- SETPOS (&bs,first + GETPOS (&bs));
- i -= first; /* reduced size */
- if (last && (i > last)) i = last;
- }
- /* do the mailgets thing */
- (*mailgets) (mail_read,&bs,i,&md);
- return T; /* success */
- }
- /* Mail fetch partial message body part
- * Accepts: mail stream
- * message # to fetch
- * MIME section specifier (#.#.#...#)
- * offset of first designed byte or 0 to start at beginning
- * maximum number of bytes or 0 for all bytes
- * flags
- * Returns: T if successful, else NIL
- */
- long mail_partial_body (MAILSTREAM *stream,unsigned long msgno,char *section,
- unsigned long first,unsigned long last,long flags)
- {
- GETS_DATA md;
- PARTTEXT *p;
- STRING bs;
- BODY *b;
- SIZEDTEXT *t;
- unsigned long i;
- if (!(section && *section)) /* top-level text wanted? */
- return mail_partial_text (stream,msgno,NIL,first,last,flags);
- if (!mailgets) fatal ("mail_partial_body() called without a mailgets!");
- if (flags & FT_UID) { /* UID form of call */
- if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
- else return NIL; /* must get UID/msgno map first */
- }
- /* must have body */
- if (!(b = mail_body (stream,msgno,section))) return NIL;
- flags &= ~FT_INTERNAL; /* bogus if this is set */
- /* initialize message data identifier */
- INIT_GETS (md,stream,msgno,section,first,last);
- /* have cached text? */
- if ((t = &(p = &b->contents)->text)->data) {
- /* mark message seen */
- markseen (stream,mail_elt (stream,msgno),flags);
- INIT (&bs,mail_string,t->data,i = t->size);
- }
- else { /* else get data from driver */
- if (!stream->dtb) return NIL;
- if (stream->dtb->msgdata) /* driver will handle this */
- return (*stream->dtb->msgdata) (stream,msgno,section,first,last,NIL,
- flags);
- if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return NIL;
- if (section && *section) { /* nexted if more complex */
- SETPOS (&bs,p->offset); /* offset stringstruct to data */
- i = t->size; /* maximum size of data */
- }
- else i = SIZE (&bs); /* just want this much */
- }
- if (i <= first) i = first = 0;/* first byte is beyond end of text */
- else { /* offset and truncate */
- SETPOS (&bs,first + GETPOS (&bs));
- i -= first; /* reduced size */
- if (last && (i > last)) i = last;
- }
- /* do the mailgets thing */
- (*mailgets) (mail_read,&bs,i,&md);
- return T; /* success */
- }
- /* Mail return message text
- * Accepts: identifier data
- * sized text
- * pointer to returned length
- * Returns: text
- */
- char *mail_fetch_text_return (GETS_DATA *md,SIZEDTEXT *t,unsigned long *len)
- {
- STRING bs;
- if (len) *len = t->size; /* return size */
- if (t->size && mailgets) { /* have to do the mailgets thing? */
- /* silly but do it anyway for consistency */
- INIT (&bs,mail_string,t->data,t->size);
- return (*mailgets) (mail_read,&bs,t->size,md);
- }
- return t->size ? (char *) t->data : "";
- }
- /* Mail return message string
- * Accepts: identifier data
- * stringstruct
- * text length
- * pointer to returned length
- * Returns: text
- */
- char *mail_fetch_string_return (GETS_DATA *md,STRING *bs,unsigned long i,
- unsigned long *len)
- {
- if (len) *len = i; /* return size */
- /* have to do the mailgets thing? */
- if (mailgets) return (*mailgets) (mail_read,bs,i,md);
- /* special hack to avoid extra copy */
- if (bs->dtb->next == mail_string_next) return bs->curpos;
- /* make string copy in memory */
- return textcpyoffstring (&md->stream->text,bs,GETPOS (bs),i);
- }
- /* Read data from stringstruct
- * Accepts: stringstruct
- * size of data to read
- * buffer to read into
- * Returns: T, always, stringstruct updated
- */
- long mail_read (void *stream,unsigned long size,char *buffer)
- {
- STRING *s = (STRING *) stream;
- while (size--) *buffer++ = SNX (s);
- return T;
- }
- /* Mail fetch UID
- * Accepts: mail stream
- * message number
- * Returns: UID or zero if dead stream
- */
- unsigned long mail_uid (MAILSTREAM *stream,unsigned long msgno)
- {
- unsigned long uid = mail_elt (stream,msgno)->private.uid;
- return uid ? uid :
- (stream->dtb && stream->dtb->uid) ? (*stream->dtb->uid) (stream,msgno) : 0;
- }
- /* Mail fetch msgno from UID (for internal use only)
- * Accepts: mail stream
- * UID
- * Returns: msgno or zero if failed
- */
- unsigned long mail_msgno (MAILSTREAM *stream,unsigned long uid)
- {
- unsigned long msgno;
- /* scan cache for UID */
- for (msgno = 1; msgno <= stream->nmsgs; msgno++)
- if (mail_elt (stream,msgno)->private.uid == uid) return msgno;
- if (stream->dtb) { /* else get it from driver if possible */
- /* direct way */
- if (stream->dtb->msgno) return (*stream->dtb->msgno) (stream,uid);
- if (stream->dtb->uid) /* indirect way */
- for (msgno = 1; msgno <= stream->nmsgs; msgno++)
- if ((*stream->dtb->uid) (stream,msgno) == uid) return msgno;
- }
- return 0; /* didn't find the UID anywhere */
- }
- /* Mail fetch From string for menu
- * Accepts: destination string
- * mail stream
- * message # to fetch
- * desired string length
- * Returns: string of requested length
- */
- void mail_fetchfrom (char *s,MAILSTREAM *stream,unsigned long msgno,
- long length)
- {
- char *t;
- char tmp[MAILTMPLEN];
- ENVELOPE *env = mail_fetchenvelope (stream,msgno);
- ADDRESS *adr = env ? env->from : NIL;
- memset (s,' ',(size_t)length);/* fill it with spaces */
- s[length] = ' '; /* tie off with null */
- /* get first from address from envelope */
- while (adr && !adr->host) adr = adr->next;
- if (adr) { /* if a personal name exists use it */
- if (!(t = adr->personal))
- sprintf (t = tmp,"%.256s@%.256s",adr->mailbox,adr->host);
- memcpy (s,t,(size_t) min (length,(long) strlen (t)));
- }
- }
- /* Mail fetch Subject string for menu
- * Accepts: destination string
- * mail stream
- * message # to fetch
- * desired string length
- * Returns: string of no more than requested length
- */
- void mail_fetchsubject (char *s,MAILSTREAM *stream,unsigned long msgno,
- long length)
- {
- ENVELOPE *env = mail_fetchenvelope (stream,msgno);
- memset (s,' ',(size_t) length+1);
- /* copy subject from envelope */
- if (env && env->subject) strncpy (s,env->subject,(size_t) length);
- else *s = ' '; /* if no subject then just a space */
- }
- /* Mail modify flags
- * Accepts: mail stream
- * sequence
- * flag(s)
- * option flags
- */
- void mail_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
- {
- MESSAGECACHE *elt;
- unsigned long i,uf;
- long f;
- short nf;
- if (!stream->dtb) return; /* no-op if no stream */
- if ((stream->dtb->flagmsg || !stream->dtb->flag) &&
- ((f = mail_parse_flags (stream,flag,&uf)) || uf) &&
- ((flags & ST_UID) ? mail_uid_sequence (stream,sequence) :
- mail_sequence (stream,sequence)))
- for (i = 1,nf = (flags & ST_SET) ? T : NIL; i <= stream->nmsgs; i++)
- if ((elt = mail_elt (stream,i))->sequence) {
- struct { /* old flags */
- unsigned int valid : 1;
- unsigned int seen : 1;
- unsigned int deleted : 1;
- unsigned int flagged : 1;
- unsigned int answered : 1;
- unsigned int draft : 1;
- unsigned long user_flags;
- } old;
- old.valid = elt->valid; old.seen = elt->seen;
- old.deleted = elt->deleted; old.flagged = elt->flagged;
- old.answered = elt->answered; old.draft = elt->draft;
- old.user_flags = elt->user_flags;
- elt->valid = NIL; /* prepare for flag alteration */
- if (stream->dtb->flagmsg) (*stream->dtb->flagmsg) (stream,elt);
- if (f&fSEEN) elt->seen = nf;
- if (f&fDELETED) elt->deleted = nf;
- if (f&fFLAGGED) elt->flagged = nf;
- if (f&fANSWERED) elt->answered = nf;
- if (f&fDRAFT) elt->draft = nf;
- /* user flags */
- if (flags & ST_SET) elt->user_flags |= uf;
- else elt->user_flags &= ~uf;
- elt->valid = T; /* flags now altered */
- if ((old.valid != elt->valid) || (old.seen != elt->seen) ||
- (old.deleted != elt->deleted) || (old.flagged != elt->flagged) ||
- (old.answered != elt->answered) || (old.draft != elt->draft) ||
- (old.user_flags != elt->user_flags)) mm_flags(stream,elt->msgno);
- if (stream->dtb->flagmsg) (*stream->dtb->flagmsg) (stream,elt);
- }
- /* call driver once */
- if (stream->dtb->flag) (*stream->dtb->flag) (stream,sequence,flag,flags);
- }
- /* Mail search for messages
- * Accepts: mail stream
- * character set
- * search program
- * option flags
- */
- void mail_search_full (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
- long flags)
- {
- unsigned long i;
- if (!(flags & SE_RETAIN)) /* clear search vector unless retaining */
- for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = NIL;
- if (pgm && stream->dtb) { /* must have a search program and driver */
- /* do the driver's action if requested */
- if (!(flags & SO_NOSERVER) && stream->dtb->search)
- (*stream->dtb->search) (stream,charset,pgm,flags);
- else mail_search_default (stream,charset,pgm,flags);
- }
- /* flush search program if requested */
- if (flags & SE_FREE) mail_free_searchpgm (&pgm);
- }
- /* Mail search for messages default handler
- * Accepts: mail stream
- * character set
- * search program
- * option flags
- */
- void mail_search_default (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
- long flags)
- {
- unsigned long i;
- if (charset && *charset && /* convert if charset not US-ASCII or UTF-8 */
- !(((charset[0] == 'U') || (charset[0] == 'u')) &&
- ((((charset[1] == 'S') || (charset[1] == 's')) &&
- (charset[2] == '-') &&
- ((charset[3] == 'A') || (charset[3] == 'a')) &&
- ((charset[4] == 'S') || (charset[4] == 's')) &&
- ((charset[5] == 'C') || (charset[5] == 'c')) &&
- ((charset[6] == 'I') || (charset[6] == 'i')) &&
- ((charset[7] == 'I') || (charset[7] == 'i')) && !charset[8]) ||
- (((charset[1] == 'T') || (charset[1] == 't')) &&
- ((charset[2] == 'F') || (charset[2] == 'f')) &&
- (charset[3] == '-') && (charset[4] == '8') && !charset[5])))) {
- if (utf8_text (NIL,charset,NIL,T)) utf8_searchpgm (pgm,charset);
- else return; /* charset unknown */
- }
- for (i = 1; i <= stream->nmsgs; ++i) if (mail_search_msg (stream,i,NIL,pgm)){
- if (flags & SE_UID) mm_searched (stream,mail_uid (stream,i));
- else { /* mark as searched, notify mail program */
- mail_elt (stream,i)->searched = T;
- if (!stream->silent) mm_searched (stream,i);
- }
- }
- }
- /* Mail ping mailbox
- * Accepts: mail stream
- * Returns: stream if still open else NIL
- */
- long mail_ping (MAILSTREAM *stream)
- {
- /* do the driver's action */
- return stream->dtb ? (*stream->dtb->ping) (stream) : NIL;
- }
- /* Mail check mailbox
- * Accepts: mail stream
- */
- void mail_check (MAILSTREAM *stream)
- {
- /* do the driver's action */
- if (stream->dtb) (*stream->dtb->check) (stream);
- }
- /* Mail expunge mailbox
- * Accepts: mail stream
- */
- void mail_expunge (MAILSTREAM *stream)
- {
- /* do the driver's action */
- if (stream->dtb) (*stream->dtb->expunge) (stream);
- }
- /* Mail copy message(s)
- * Accepts: mail stream
- * sequence
- * destination mailbox
- * flags
- */
- long mail_copy_full (MAILSTREAM *stream,char *sequence,char *mailbox,
- long options)
- {
- long ret = NIL;
- SAFE_DISPATCH (stream->dtb,ret,copy,(stream,sequence,mailbox,options))
- return ret;
- }
- /* Mail append message string
- * Accepts: mail stream
- * destination mailbox
- * initial flags
- * message internal date
- * stringstruct of message to append
- * Returns: T on success, NIL on failure
- */
- long mail_append_full (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
- STRING *message)
- {
- long ret = NIL;
- char *s,tmp[MAILTMPLEN];
- DRIVER *d = NIL;
- if (strlen (mailbox) >= (NETMAXHOST+NETMAXUSER+NETMAXMBX+NETMAXSRV+50)) {
- sprintf (tmp,"Can't append %.80s: %s",mailbox,(*mailbox == '{') ?
- "invalid remote specification" : "no such mailbox");
- mm_log (tmp,ERROR);
- return NIL;
- }
- /* see if special driver hack */
- if (strncmp (lcase (strcpy (tmp,mailbox)),"#driver.",8))
- d = mail_valid (stream,mailbox,NIL);
- else { /* it is, tie off name at likely delimiter */
- if (!(s = strpbrk (tmp+8,"/\:"))) {
- sprintf (tmp,"Can't append to mailbox %.80s: bad driver syntax",mailbox);
- mm_log (tmp,ERROR);
- }
- else { /* found delimiter */
- *s++ = ' ';
- for (d = maildrivers; d && strcmp (d->name,tmp+8); d = d->next);
- if (d) mailbox += s - tmp;/* skip past driver specification */
- else {
- sprintf (tmp,"Can't append to mailbox %.80s: unknown driver",mailbox);
- mm_log (tmp,ERROR);
- }
- }
- }
- /* do append if have a driver */
- SAFE_DISPATCH (d,ret,append,(stream,mailbox,flags,date,message))
- else { /* failed, try for TRYCREATE if no stream */
- if (!stream && (stream = default_proto (T)))
- SAFE_FUNCTION (stream->dtb,ret,(*stream->dtb->append),
- (stream,mailbox,flags,date,message))
- /* timing race? */
- if (ret) mm_notify (stream,"Append validity confusion",WARN);
- /* now generate error message */
- else mail_valid (stream,mailbox,"append to mailbox");
- }
- return ret;
- }
- /* Mail garbage collect stream
- * Accepts: mail stream
- * garbage collection flags
- */
- void mail_gc (MAILSTREAM *stream,long gcflags)
- {
- MESSAGECACHE *elt;
- unsigned long i;
- /* do the driver's action first */
- if (stream->dtb && stream->dtb->gc) (*stream->dtb->gc) (stream,gcflags);
- stream->msgno = 0; /* nothing cached now */
- if (gcflags & GC_ENV) { /* garbage collect envelopes? */
- if (stream->env) mail_free_envelope (&stream->env);
- if (stream->body) mail_free_body (&stream->body);
- }
- if (gcflags & GC_TEXTS) { /* free texts */
- if (stream->text.data) fs_give ((void **) &stream->text.data);
- stream->text.size = 0;
- }
- /* garbage collect per-message stuff */
- for (i = 1; i <= stream->nmsgs; i++)
- if (elt = (MESSAGECACHE *) (*mailcache) (stream,i,CH_ELT))
- mail_gc_msg (&elt->private.msg,gcflags);
- }
- /* Mail garbage collect message
- * Accepts: message structure
- * garbage collection flags
- */
- void mail_gc_msg (MESSAGE *msg,long gcflags)
- {
- if (gcflags & GC_ENV) { /* garbage collect envelopes? */
- mail_free_envelope (&msg->env);
- mail_free_body (&msg->body);
- }
- if (gcflags & GC_TEXTS) { /* garbage collect texts */
- if (msg->full.text.data) fs_give ((void **) &msg->full.text.data);
- if (msg->header.text.data) {
- mail_free_stringlist (&msg->lines);
- fs_give ((void **) &msg->header.text.data);
- }
- if (msg->text.text.data) fs_give ((void **) &msg->text.text.data);
- /* now GC all body components */
- if (msg->body) mail_gc_body (msg->body);
- }
- }
- /* Mail garbage collect texts in BODY structure
- * Accepts: BODY structure
- */
- void mail_gc_body (BODY *body)
- {
- PART *part;
- switch (body->type) { /* free contents */
- case TYPEMULTIPART: /* multiple part */
- for (part = body->nested.part; part; part = part->next)
- mail_gc_body (&part->body);
- break;
- case TYPEMESSAGE: /* encapsulated message */
- if (body->subtype && !strcmp (body->subtype,"RFC822")) {
- mail_free_stringlist (&body->nested.msg->lines);
- mail_gc_msg (body->nested.msg,GC_TEXTS);
- }
- break;
- default:
- break;
- }
- if (body->mime.text.data) fs_give ((void **) &body->mime.text.data);
- if (body->contents.text.data) fs_give ((void **) &body->contents.text.data);
- }
- /* Mail get body part
- * Accepts: mail stream
- * message number
- * section specifier
- * Returns: pointer to body
- */
- BODY *mail_body (MAILSTREAM *stream,unsigned long msgno,char *section)
- {
- BODY *b;
- PART *pt;
- unsigned long i;
- char tmp[MAILTMPLEN];
- /* make sure have a body */
- if (!(section && *section && (strlen (section) < (MAILTMPLEN - 20)) &&
- mail_fetchstructure (stream,msgno,&b) && b)) return NIL;
- /* find desired section */
- if (section) for (section = ucase (strcpy (tmp,section)); *section;) {
- if (isdigit (*section)) { /* get section specifier */
- /* make sure what follows is valid */
- if (!(i = strtoul (section,§ion,10)) ||
- (*section && ((*section++ != '.') || !*section))) return NIL;
- /* multipart content? */
- if (b->type == TYPEMULTIPART) {
- /* yes, find desired part */
- if (pt = b->nested.part) while (--i && (pt = pt->next));
- if (!pt) return NIL; /* bad specifier */
- b = &pt->body; /* note new body */
- }
- /* otherwise must be section 1 */
- else if (i != 1) return NIL;
- /* need to go down further? */
- if (*section) switch (b->type) {
- case TYPEMULTIPART: /* multipart */
- break;
- case TYPEMESSAGE: /* embedded message */
- if (!strcmp (b->subtype,"RFC822")) {
- b = b->nested.msg->body;
- break;
- }
- default: /* bogus subpart specification */
- return NIL;
- }
- }
- else return NIL; /* unknown section specifier */
- }
- return b;
- }
- /* Mail output date from elt fields
- * Accepts: character string to write into
- * elt to get data data from
- * Returns: the character string
- */
- const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
- const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
- char *mail_date (char *string,MESSAGECACHE *elt)
- {
- const char *s = (elt->month && elt->month < 13) ?
- months[elt->month - 1] : (const char *) "???";
- sprintf (string,"%2d-%s-%d %02d:%02d:%02d %c%02d%02d",
- elt->day,s,elt->year + BASEYEAR,
- elt->hours,elt->minutes,elt->seconds,
- elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes);
- return string;
- }
- /* Mail output cdate format date from elt fields
- * Accepts: character string to write into
- * elt to get data data from
- * Returns: the character string
- */
- char *mail_cdate (char *string,MESSAGECACHE *elt)
- {
- char *fmt = "%s %s %2d %02d:%02d:%02d %4d %s%02d%02dn";
- const char *s = (elt->month && elt->month < 13) ?
- months[elt->month - 1] : (const char *) "???";
- int m = elt->month;
- int y = elt->year + BASEYEAR;
- if (elt->month <= 2) { /* if before March, */
- m = elt->month + 9; /* January = month 10 of previous year */
- y--;
- }
- else m = elt->month - 3; /* March is month 0 */
- sprintf (string,fmt,days[(int)(elt->day+2+((7+31*m)/12)+y+(y/4)
- #ifndef USEORTHODOXCALENDAR /* Gregorian calendar */
- +(y / 400) - (y / 100)
- #ifdef Y4KBUGFIX
- - (y / 4000)
- #endif
- #else /* Orthodox calendar */
- +(2*(y/900))+((y%900) >= 200)+((y%900) >= 600) - (y/100)
- #endif
- ) % 7],s,
- elt->day,elt->hours,elt->minutes,elt->seconds,elt->year + BASEYEAR,
- elt->zoccident ? "-" : "+",elt->zhours,elt->zminutes);
- return string;
- }
- /* Mail parse date into elt fields
- * Accepts: elt to write into
- * date string to parse
- * Returns: T if parse successful, else NIL
- * This routine parses dates as follows:
- * . leading three alphas followed by comma and space are ignored
- * . date accepted in format: mm/dd/yy, mm/dd/yyyy, dd-mmm-yy, dd-mmm-yyyy,
- * dd mmm yy, dd mmm yyyy
- * . space or end of string required
- * . time accepted in format hh:mm:ss or hh:mm
- * . end of string accepted
- * . timezone accepted: hyphen followed by symbolic timezone, or space
- * followed by signed numeric timezone or symbolic timezone
- * Examples of normal input:
- * . IMAP date-only (SEARCH): dd-mmm-yy, dd-mmm-yyyy, mm/dd/yy, mm/dd/yyyy
- * . IMAP date-time (INTERNALDATE):
- * dd-mmm-yy hh:mm:ss-zzz
- * dd-mmm-yyyy hh:mm:ss +zzzz
- * . RFC-822:
- * www, dd mmm yy hh:mm:ss zzz
- * www, dd mmm yyyy hh:mm:ss +zzzz
- */
- long mail_parse_date (MESSAGECACHE *elt,char *s)
- {
- unsigned long d,m,y;
- int mi,ms;
- struct tm *t;
- time_t tn;
- char tmp[MAILTMPLEN];
- /* clear elt */
- elt->zoccident = elt->zhours = elt->zminutes =
- elt->hours = elt->minutes = elt->seconds =
- elt->day = elt->month = elt->year = 0;
- /* make a writeable uppercase copy */
- if (s && *s && (strlen (s) < (size_t)MAILTMPLEN)) s = ucase (strcpy (tmp,s));
- else return NIL;
- /* skip over possible day of week */
- if (isalpha (*s) && isalpha (s[1]) && isalpha (s[2]) && (s[3] == ',') &&
- (s[4] == ' ')) s += 5;
- while (*s == ' ') s++; /* parse first number (probable month) */
- if (!(m = strtoul ((const char *) s,&s,10))) return NIL;
- switch (*s) { /* different parse based on delimiter */
- case '/': /* mm/dd/yy format */
- if (!((d = strtoul ((const char *) ++s,&s,10)) && *s == '/' &&
- (y = strtoul ((const char *) ++s,&s,10)) && *s == ' ')) return NIL;
- break;
- case ' ': /* dd mmm yy format */
- while (s[1] == ' ') s++; /* slurp extra whitespace */
- case '-': /* dd-mmm-yy format */
- d = m; /* so the number we got is a day */
- /* make sure string long enough! */
- if (strlen (s) < (size_t) 5) return NIL;
- /* Some compilers don't allow `<<' and/or longs in case statements. */
- /* slurp up the month string */
- ms = ((s[1] - 'A') * 1024) + ((s[2] - 'A') * 32) + (s[3] - 'A');
- switch (ms) { /* determine the month */
- case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
- case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
- case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
- case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
- case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
- case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
- case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
- case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
- case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
- case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10; break;
- case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11; break;
- case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12; break;
- default: return NIL; /* unknown month */
- }
- if (s[4] == *s) s += 5; /* advance to year */
- else { /* first three were OK, possibly full name */
- mi = *s; /* note delimiter, skip alphas */
- for (s += 4; isalpha (*s); s++);
- /* error if delimiter not here */
- if (mi != *s++) return NIL;
- }
- if ((y = strtoul ((const char *) s,&s,10)) && (*s == ' ' || *s == ' '))
- break; /* successfully parsed year */
- default: return NIL; /* unknown date format */
- }
- /* minimal validity check of date */
- if ((d > 31) || (m > 12)) return NIL;
- /* two digit year */
- if (y < 100) y += (y >= (BASEYEAR - 1900)) ? 1900 : 2000;
- /* set values in elt */
- elt->day = d; elt->month = m; elt->year = y - BASEYEAR;
- ms = ' '; /* initially no time zone string */
- if (*s) { /* time specification present? */
- /* parse time */
- d = strtoul ((const char *) s+1,&s,10);
- if (*s != ':') return NIL;
- m = strtoul ((const char *) ++s,&s,10);
- y = (*s == ':') ? strtoul ((const char *) ++s,&s,10) : 0;
- /* validity check time */
- if ((d > 23) || (m > 59) || (y > 59)) return NIL;
- /* set values in elt */
- elt->hours = d; elt->minutes = m; elt->seconds = y;
- switch (*s) { /* time zone specifier? */
- case ' ': /* numeric time zone */
- while (s[1] == ' ') s++; /* slurp extra whitespace */
- if (!isalpha (s[1])) { /* treat as '-' case if alphabetic */
- /* test for sign character */
- if ((elt->zoccident = (*++s == '-')) || (*s == '+')) s++;
- /* validate proper timezone */
- if (isdigit(*s) && isdigit(s[1]) && isdigit(s[2]) && isdigit(s[3])) {
- elt->zhours = (*s - '0') * 10 + (s[1] - '0');
- elt->zminutes = (s[2] - '0') * 10 + (s[3] - '0');
- }
- return T; /* all done! */
- }
- /* falls through */
- case '-': /* symbolic time zone */
- if (!(ms = *++s)) ms = 'Z';
- else if (*++s) { /* multi-character? */
- ms -= 'A'; ms *= 1024; /* yes, make compressed three-byte form */
- ms += ((*s++ - 'A') * 32);
- if (*s) ms += *s++ - 'A';
- if (*s) ms = ' '; /* more than three characters */
- }
- default: /* ignore anything else */
- break;
- }
- }
- /* This is not intended to be a comprehensive list of all possible
- * timezone strings. Such a list would be impractical. Rather, this
- * listing is intended to incorporate all military, North American, and
- * a few special cases such as Japan and the major European zone names,
- * such as what might be expected to be found in a Tenex format mailbox
- * and spewed from an IMAP server. The trend is to migrate to numeric
- * timezones which lack the flavor but also the ambiguity of the names.
- *
- * RFC-822 only recognizes UT, GMT, 1-letter military timezones, and the
- * 4 CONUS timezones and their summer time variants. [Sorry, Canadian
- * Atlantic Provinces, Alaska, and Hawaii.]
- */
- switch (ms) { /* determine the timezone */
- /* Universal */
- case (('U'-'A')*1024)+(('T'-'A')*32):
- #ifndef STRICT_RFC822_TIMEZONES
- case (('U'-'A')*1024)+(('T'-'A')*32)+'C'-'A':
- #endif
- /* Greenwich */
- case (('G'-'A')*1024)+(('M'-'A')*32)+'T'-'A':
- case 'Z': elt->zhours = 0; break;
- /* oriental (from Greenwich) timezones */
- #ifndef STRICT_RFC822_TIMEZONES
- /* Middle Europe */
- case (('M'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
- #endif
- #ifdef BRITISH_SUMMER_TIME
- /* British Summer */
- case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
- #endif
- case 'A': elt->zhours = 1; break;
- #ifndef STRICT_RFC822_TIMEZONES
- /* Eastern Europe */
- case (('E'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
- #endif
- case 'B': elt->zhours = 2; break;
- case 'C': elt->zhours = 3; break;
- case 'D': elt->zhours = 4; break;
- case 'E': elt->zhours = 5; break;
- case 'F': elt->zhours = 6; break;
- case 'G': elt->zhours = 7; break;
- case 'H': elt->zhours = 8; break;
- #ifndef STRICT_RFC822_TIMEZONES
- /* Japan */
- case (('J'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
- #endif
- case 'I': elt->zhours = 9; break;
- case 'K': elt->zhours = 10; break;
- case 'L': elt->zhours = 11; break;
- case 'M': elt->zhours = 12; break;
- /* occidental (from Greenwich) timezones */
- case 'N': elt->zoccident = 1; elt->zhours = 1; break;
- case 'O': elt->zoccident = 1; elt->zhours = 2; break;
- #ifndef STRICT_RFC822_TIMEZONES
- case (('A'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
- #endif
- case 'P': elt->zoccident = 1; elt->zhours = 3; break;
- #ifdef NEWFOUNDLAND_STANDARD_TIME
- /* Newfoundland */
- case (('N'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
- elt->zoccident = 1; elt->zhours = 3; elt->zminutes = 30; break;
- #endif
- #ifndef STRICT_RFC822_TIMEZONES
- /* Atlantic */
- case (('A'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
- #endif
- /* CONUS */
- case (('E'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
- case 'Q': elt->zoccident = 1; elt->zhours = 4; break;
- /* Eastern */
- case (('E'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
- case (('C'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
- case 'R': elt->zoccident = 1; elt->zhours = 5; break;
- /* Central */
- case (('C'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
- case (('M'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
- case 'S': elt->zoccident = 1; elt->zhours = 6; break;
- /* Mountain */
- case (('M'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
- case (('P'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
- case 'T': elt->zoccident = 1; elt->zhours = 7; break;
- /* Pacific */
- case (('P'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
- #ifndef STRICT_RFC822_TIMEZONES
- case (('Y'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
- #endif
- case 'U': elt->zoccident = 1; elt->zhours = 8; break;
- #ifndef STRICT_RFC822_TIMEZONES
- /* Yukon */
- case (('Y'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
- #endif
- case 'V': elt->zoccident = 1; elt->zhours = 9; break;
- #ifndef STRICT_RFC822_TIMEZONES
- /* Hawaii */
- case (('H'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
- #endif
- case 'W': elt->zoccident = 1; elt->zhours = 10; break;
- /* Nome/Bering/Samoa */
- #ifndef NOME_STANDARD_TIME
- case (('N'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
- #endif
- #ifndef BERING_STANDARD_TIME
- case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
- #endif
- #ifndef SAMOA_STANDARD_TIME
- case (('S'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
- #endif
- case 'X': elt->zoccident = 1; elt->zhours = 11; break;
- case 'Y': elt->zoccident = 1; elt->zhours = 12; break;
- default: /* unknown time zones treated as local */
- tn = time (0); /* time now... */
- t = localtime (&tn); /* get local minutes since midnight */
- mi = t->tm_hour * 60 + t->tm_min;
- ms = t->tm_yday; /* note Julian day */
- if (t = gmtime (&tn)) { /* minus UTC minutes since midnight */
- mi -= t->tm_hour * 60 + t->tm_min;
- /* ms can be one of:
- * 36x local time is December 31, UTC is January 1, offset -24 hours
- * 1 local time is 1 day ahead of UTC, offset +24 hours
- * 0 local time is same day as UTC, no offset
- * -1 local time is 1 day behind UTC, offset -24 hours
- * -36x local time is January 1, UTC is December 31, offset +24 hours
- */
- if (ms -= t->tm_yday) /* correct offset if different Julian day */
- mi += ((ms < 0) == (abs (ms) == 1)) ? -24*60 : 24*60;
- if (mi < 0) { /* occidental? */
- mi = abs (mi); /* yup, make positive number */
- elt->zoccident = 1; /* and note west of UTC */
- }
- elt->zhours = mi / 60; /* now break into hours and minutes */
- elt->zminutes = mi % 60;
- }
- break;
- }
- return T;
- }
- /* Mail n messages exist
- * Accepts: mail stream
- * number of messages
- */
- void mail_exists (MAILSTREAM *stream,unsigned long nmsgs)
- {
- /* make sure cache is large enough */
- (*mailcache) (stream,nmsgs,CH_SIZE);
- stream->nmsgs = nmsgs; /* update stream status */
- /* notify main program of change */
- if (!stream->silent) mm_exists (stream,nmsgs);
- }
- /* Mail n messages are recent
- * Accepts: mail stream
- * number of recent messages
- */
- void mail_recent (MAILSTREAM *stream,unsigned long recent)
- {
- stream->recent = recent; /* update stream status */