home *** CD-ROM | disk | FTP | other *** search
- /*
- * Program: Tenex mail 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 May 1990
- * Last Edited: 1 June 1992
- *
- * Copyright 1992 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 <stdio.h>
- #include <ctype.h>
- #include <pwd.h>
- #include <netdb.h>
- #include <errno.h>
- extern int errno; /* just in case */
- #include <sys/types.h>
- #include <sys/file.h>
- #include <sys/stat.h>
- #include <sys/time.h>
- #include <sys/uio.h>
- #include "osdep.h"
- #include "mail.h"
- #include "tenex.h"
- #include "rfc822.h"
- #include "misc.h"
-
- /* Tenex mail routines */
-
-
- /* Driver dispatch used by MAIL */
-
- DRIVER tenexdriver = {
- (DRIVER *) NIL, /* next driver */
- tenex_valid, /* mailbox is valid for us */
- tenex_find, /* find mailboxes */
- tenex_find_bboards, /* find bboards */
- tenex_open, /* open mailbox */
- tenex_close, /* close mailbox */
- tenex_fetchfast, /* fetch message "fast" attributes */
- tenex_fetchflags, /* fetch message flags */
- tenex_fetchenvelope, /* fetch message envelopes */
- tenex_fetchheader, /* fetch message header only */
- tenex_fetchtext, /* fetch message body only */
- tenex_fetchbody, /* fetch message body section */
- tenex_setflag, /* set message flag */
- tenex_clearflag, /* clear message flag */
- tenex_search, /* search for message based on criteria */
- tenex_ping, /* ping mailbox to see if still alive */
- tenex_check, /* check for new messages */
- tenex_expunge, /* expunge deleted messages */
- tenex_copy, /* copy messages to another mailbox */
- tenex_move, /* move messages to another mailbox */
- tenex_gc /* garbage collect stream */
- };
-
- /* Tenex mail validate mailbox
- * Accepts: mailbox name
- * Returns: our driver if name is valid, otherwise calls valid in next driver
- */
-
- DRIVER *tenex_valid (name)
- char *name;
- {
- return tenex_isvalid (name) ? &tenexdriver :
- (tenexdriver.next ? (*tenexdriver.next->valid) (name) : NIL);
- }
-
-
- /* Tenex mail test for valid mailbox
- * Accepts: mailbox name
- * Returns: T if valid, NIL otherwise
- */
-
- int tenex_isvalid (name)
- char *name;
- {
- int fd;
- char tmp[MAILTMPLEN];
- struct stat sbuf;
- /* if file, get its status */
- if (*name != '{' && (stat (tenex_file (tmp,name),&sbuf) == 0)) {
- if (sbuf.st_size != 0) { /* if non-empty file */
- if ((fd = open (tmp,O_RDONLY,NIL)) >= 0 && read (fd,tmp,23) >= 0) {
- close (fd); /* close the file */
- /* must begin with dd-mmm-yy hh:mm:ss" */
- if (tmp[2] == '-' && tmp[6] == '-' && tmp[9] == ' ' &&
- tmp[12] == ':' && tmp[15] == ':') return T;
- }
- }
- /* allow empty if a ".txt" file */
- else if (strstr (tmp,".txt")) return T;
- }
- return NIL; /* failed miserably */
- }
-
- /* Tenex mail find list of mailboxes
- * Accepts: mail stream
- * pattern to search
- */
-
- void tenex_find (stream,pat)
- MAILSTREAM *stream;
- char *pat;
- {
- int fd;
- char tmp[MAILTMPLEN];
- char *s,*t;
- struct stat sbuf;
- /* make file name */
- sprintf (tmp,"%s/.mailboxlist",getpwuid (geteuid ())->pw_dir);
- if ((fd = open (tmp,O_RDONLY,NIL)) >= 0) {
- fstat (fd,&sbuf); /* get file size and read data */
- read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
- close (fd); /* close file */
- s[sbuf.st_size] = '\0'; /* tie off string */
- if (t = strtok (s,"\n")) /* get first mailbox name */
- do if ((*t != '{') && strcmp (t,"INBOX") && pmatch (t,pat) &&
- tenex_isvalid (t)) mm_mailbox (t);
- /* for each mailbox */
- while (t = strtok (NIL,"\n"));
- }
- }
-
-
- /* Tenex mail find list of bboards
- * Accepts: mail stream
- * pattern to search
- */
-
- void tenex_find_bboards (stream,pat)
- MAILSTREAM *stream;
- char *pat;
- {
- }
-
- /* Tenex mail open
- * Accepts: stream to open
- * Returns: stream on success, NIL on failure
- */
-
- MAILSTREAM *tenex_open (stream)
- MAILSTREAM *stream;
- {
- long i;
- int fd;
- char *s,*t,*k;
- char tmp[MAILTMPLEN];
- struct stat sbuf;
- struct hostent *host_name;
- if (LOCAL) { /* close old file if stream being recycled */
- tenex_close (stream); /* dump and save the changes */
- stream->dtb = &tenexdriver; /* reattach this driver */
- /* clean up cache (again) */
- mail_free_cache (&stream->cache,&stream->cachesize);
- }
- else { /* flush old flagstring and flags if any */
- if (stream->flagstring) fs_give ((void **) &stream->flagstring);
- for (i = 0; i < NUSERFLAGS; ++i) stream->user_flags[i] = NIL;
- /* open .imapinit or .mminit file */
- if ((fd = open (strcat (strcpy (tmp,getpwuid (geteuid ())->pw_dir),
- "/.imapinit"),O_RDONLY,NIL)) < 0)
- fd = open (strcat (strcpy (tmp,getpwuid (geteuid ())->pw_dir),
- "/.mminit"),O_RDONLY,NIL);
- if (fd >= 0) { /* got an init file? */
- fstat (fd,&sbuf); /* yes, get size */
- read (fd,(s = stream->flagstring = (char *) fs_get (sbuf.st_size + 1)),
- sbuf.st_size); /* make readin area and read the file */
- close (fd); /* don't need the file any more */
- s[sbuf.st_size] = '\0'; /* tie it off */
- /* parse init file */
- while (*s && (t = strchr (s,'\n'))) {
- *t = '\0'; /* tie off line, find second space */
- if ((k = strchr (s,' ')) && (k = strchr (++k,' '))) {
- *k = '\0'; /* tie off two words, is it "set keywords"? */
- if (!strcmp (lcase (s),"set keywords")) {
- /* yes, get first keyword */
- k = strtok (++k,", ");
- /* until parsed all keywords or empty */
- for (i = 0; k && i < NUSERFLAGS; ++i) {
- /* set keyword, get next one */
- stream->user_flags[i] = k;
- k = strtok (NIL,", ");
- }
- break; /* don't need to look at rest of file */
- }
- }
- s = ++t; /* try next line */
- }
- }
- }
-
- if ((fd = open (tenex_file (tmp,stream->mailbox),O_RDWR,NIL)) < 0) {
- if ((fd = open (tenex_file (tmp,stream->mailbox),O_RDONLY,NIL)) < 0) {
- sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
- mm_log (tmp,ERROR);
- return NIL;
- }
- else { /* got it, but readonly */
- mm_log ("Can't get write access to mailbox, access is readonly",WARN);
- stream->readonly = T;
- }
- }
- stream->local = fs_get (sizeof (TENEXLOCAL));
- /* note if an INBOX or not */
- LOCAL->inbox = !strcmp (ucase (stream->mailbox),"INBOX");
- /* canonicalize the stream mailbox name */
- fs_give ((void **) &stream->mailbox);
- stream->mailbox = cpystr (tmp);
- gethostname (tmp,MAILTMPLEN); /* get local host name */
- LOCAL->host = cpystr ((host_name = gethostbyname (tmp)) ?
- host_name->h_name : tmp);
- /* bind and lock the file */
- flock (LOCAL->fd = fd,LOCK_SH);
- LOCAL->filesize = 0; /* initialize parsed file size */
- LOCAL->msgs = NIL; /* no cache yet */
- LOCAL->cachesize = 0;
- LOCAL->text = NIL; /* no text yet */
- LOCAL->textsize = LOCAL->textend = 0;
- LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1);
- LOCAL->buflen = MAXMESSAGESIZE;
- stream->sequence++; /* bump sequence number */
- /* parse mailbox */
- stream->nmsgs = stream->recent = 0;
- if (tenex_ping (stream) && !stream->nmsgs) mm_log ("Mailbox is empty",NIL);
- return stream; /* return stream to caller */
- }
-
- /* Tenex mail close
- * Accepts: MAIL stream
- */
-
- void tenex_close (stream)
- MAILSTREAM *stream;
- {
- long i;
- if (stream && LOCAL) { /* only if a file is open */
- if (LOCAL->host) fs_give ((void **) &LOCAL->host);
- flock (LOCAL->fd,LOCK_UN); /* unlock local file */
- close (LOCAL->fd); /* close the local file */
- /* free local cache */
- for (i = 0; i < stream->nmsgs; ++i)
- if (LOCAL->msgs[i]) fs_give ((void **) &LOCAL->msgs[i]);
- fs_give ((void **) &LOCAL->msgs);
- /* free local text buffer */
- if (LOCAL->text) fs_give ((void **) &LOCAL->text);
- if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
- /* nuke the local data */
- fs_give ((void **) &stream->local);
- stream->dtb = NIL; /* log out the DTB */
- }
- }
-
-
- /* Tenex mail fetch fast information
- * Accepts: MAIL stream
- * sequence
- */
-
- void tenex_fetchfast (stream,sequence)
- MAILSTREAM *stream;
- char *sequence;
- {
- return; /* no-op for local mail */
- }
-
-
- /* Tenex mail fetch flags
- * Accepts: MAIL stream
- * sequence
- */
-
- void tenex_fetchflags (stream,sequence)
- MAILSTREAM *stream;
- char *sequence;
- {
- return; /* no-op for local mail */
- }
-
- /* Tenex mail fetch envelope
- * Accepts: MAIL stream
- * message # to fetch
- * Returns: envelope of this message
- *
- * Fetches the "fast" information as well
- */
-
- ENVELOPE *tenex_fetchenvelope (stream,msgno)
- MAILSTREAM *stream;
- long msgno;
- {
- FILECACHE *m = LOCAL->msgs[msgno - 1];
- long i = max (m->headersize,m->bodysize);
- MESSAGECACHE *elt = mail_elt (stream,msgno);
- if (i > LOCAL->buflen) { /* make sure buffer can hold the text */
- /* fs_resize does an unnecesary copy */
- fs_give ((void **) &LOCAL->buf);
- LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
- }
- if (!elt->env) /* make envelope now if don't have one */
- rfc822_parse_msg (&elt->env,&elt->body,
- LOCAL->text + m->header,m->headersize,
- LOCAL->text + m->body,m->bodysize,
- LOCAL->host,LOCAL->buf);
- return elt->env; /* return the envelope */
- }
-
- /* Tenex mail fetch message header
- * Accepts: MAIL stream
- * message # to fetch
- * Returns: message header in RFC822 format
- */
-
- char *tenex_fetchheader (stream,msgno)
- MAILSTREAM *stream;
- long msgno;
- {
- FILECACHE *m = LOCAL->msgs[msgno-1];
- /* copy the string */
- return strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,
- LOCAL->text + m->header,m->headersize);
- }
-
-
- /* Tenex mail fetch message text (only)
- body only;
- * Accepts: MAIL stream
- * message # to fetch
- * Returns: message text in RFC822 format
- */
-
- char *tenex_fetchtext (stream,msgno)
- MAILSTREAM *stream;
- long msgno;
- {
- MESSAGECACHE *elt = mail_elt (stream,msgno);
- FILECACHE *m = LOCAL->msgs[msgno-1];
- elt->seen = T; /* mark message as seen */
- /* recalculate status */
- tenex_update_status (stream,msgno,T);
- return strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,
- LOCAL->text + m->body,m->bodysize);
- }
-
- /* Tenex fetch message body as a structure
- * Accepts: Mail stream
- * message # to fetch
- * section specifier
- * pointer to length
- * Returns: pointer to section of message body
- */
-
- char *tenex_fetchbody (stream,m,s,len)
- MAILSTREAM *stream;
- long m;
- char *s;
- unsigned long *len;
- {
- BODY *b;
- PART *pt;
- unsigned long i;
- char *base = LOCAL->text + LOCAL->msgs[m-1]->body;
- unsigned long offset = 0;
- MESSAGECACHE *elt = mail_elt (stream,m);
- /* make sure have a body */
- if (!(tenex_fetchenvelope (stream,m) && (b = mail_elt (stream,m)->body) &&
- s && *s && ((i = strtol (s,&s,10)) > 0))) return NIL;
- do { /* until find desired body part */
- /* multipart content? */
- if (b->type == TYPEMULTIPART) {
- pt = b->contents.part; /* yes, find desired part */
- while (--i && (pt = pt->next));
- if (!pt) return NIL; /* bad specifier */
- /* note new body, check valid nesting */
- if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
- base += offset; /* calculate new base */
- offset = pt->offset; /* and its offset */
- }
- else if (i != 1) return NIL;/* otherwise must be section 1 */
- /* need to go down further? */
- if (i = *s) switch (b->type) {
- case TYPEMESSAGE: /* embedded message, calculate new base */
- base += offset + b->contents.msg.offset;
- offset = 0; /* no offset any more */
- b = b->contents.msg.body; /* get its body, drop into multipart case */
- case TYPEMULTIPART: /* multipart, get next section */
- if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
- default: /* bogus subpart specification */
- return NIL;
- }
- } while (i);
- /* lose if body bogus */
- if ((!b) || b->type == TYPEMULTIPART) return NIL;
- elt->seen = T; /* mark message as seen */
- /* recalculate status */
- tenex_update_status (stream,m,T);
- return rfc822_contents (&LOCAL->buf,&LOCAL->buflen,len,base + offset,
- b->size.ibytes,b->encoding);
- }
-
- /* Tenex mail set flag
- * Accepts: MAIL stream
- * sequence
- * flag(s)
- */
-
- void tenex_setflag (stream,sequence,flag)
- MAILSTREAM *stream;
- char *sequence;
- char *flag;
- {
- MESSAGECACHE *elt;
- long i;
- long uf;
- short f;
- /* no-op if no flags to modify */
- if (!((f = tenex_getflags (stream,flag,&uf)) || uf)) return;
- /* get sequence and loop on it */
- if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
- if ((elt = tenex_elt (stream,i))->sequence) {
- /* set all requested flags */
- if (f&fSEEN) elt->seen = T;
- if (f&fDELETED) elt->deleted = T;
- if (f&fFLAGGED) elt->flagged = T;
- if (f&fANSWERED) elt->answered = T;
- elt->user_flags |= uf;
- /* recalculate status */
- tenex_update_status (stream,i,NIL);
- }
- /* make sure the update takes */
- if (!stream->readonly) fsync (LOCAL->fd);
- }
-
- /* Tenex mail clear flag
- * Accepts: MAIL stream
- * sequence
- * flag(s)
- */
-
- void tenex_clearflag (stream,sequence,flag)
- MAILSTREAM *stream;
- char *sequence;
- char *flag;
- {
- MESSAGECACHE *elt;
- long i;
- long uf;
- short f;
- /* no-op if no flags to modify */
- if (!((f = tenex_getflags (stream,flag,&uf)) || uf)) return;
- /* get sequence and loop on it */
- if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
- if ((elt = tenex_elt (stream,i))->sequence) {
- /* clear all requested flags */
- if (f&fSEEN) elt->seen = NIL;
- if (f&fDELETED) elt->deleted = NIL;
- if (f&fFLAGGED) elt->flagged = NIL;
- if (f&fANSWERED) elt->answered = NIL;
- elt->user_flags &= ~uf;
- /* recalculate status */
- tenex_update_status (stream,i,NIL);
- }
- /* make sure the update takes */
- if (!stream->readonly) fsync (LOCAL->fd);
- }
-
- /* Tenex mail search for messages
- * Accepts: MAIL stream
- * search criteria
- */
-
- void tenex_search (stream,criteria)
- MAILSTREAM *stream;
- char *criteria;
- {
- long i,n;
- char *d;
- search_t f;
- /* initially all searched */
- for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
- /* get first criterion */
- if (criteria && (criteria = strtok (criteria," "))) {
- /* for each criterion */
- for (; criteria; (criteria = strtok (NIL," "))) {
- f = NIL; d = NIL; n = 0; /* init then scan the criterion */
- switch (*ucase (criteria)) {
- case 'A': /* possible ALL, ANSWERED */
- if (!strcmp (criteria+1,"LL")) f = tenex_search_all;
- else if (!strcmp (criteria+1,"NSWERED")) f = tenex_search_answered;
- break;
- case 'B': /* possible BCC, BEFORE, BODY */
- if (!strcmp (criteria+1,"CC"))
- f = tenex_search_string (tenex_search_bcc,&d,&n);
- else if (!strcmp (criteria+1,"EFORE"))
- f = tenex_search_date (tenex_search_before,&n);
- else if (!strcmp (criteria+1,"ODY"))
- f = tenex_search_string (tenex_search_body,&d,&n);
- break;
- case 'C': /* possible CC */
- if (!strcmp (criteria+1,"C"))
- f = tenex_search_string (tenex_search_cc,&d,&n);
- break;
- case 'D': /* possible DELETED */
- if (!strcmp (criteria+1,"ELETED")) f = tenex_search_deleted;
- break;
- case 'F': /* possible FLAGGED, FROM */
- if (!strcmp (criteria+1,"LAGGED")) f = tenex_search_flagged;
- else if (!strcmp (criteria+1,"ROM"))
- f = tenex_search_string (tenex_search_from,&d,&n);
- break;
- case 'K': /* possible KEYWORD */
- if (!strcmp (criteria+1,"EYWORD"))
- f = tenex_search_flag (tenex_search_keyword,&n,stream);
- break;
- case 'N': /* possible NEW */
- if (!strcmp (criteria+1,"EW")) f = tenex_search_new;
- break;
-
- case 'O': /* possible OLD, ON */
- if (!strcmp (criteria+1,"LD")) f = tenex_search_old;
- else if (!strcmp (criteria+1,"N"))
- f = tenex_search_date (tenex_search_on,&n);
- break;
- case 'R': /* possible RECENT */
- if (!strcmp (criteria+1,"ECENT")) f = tenex_search_recent;
- break;
- case 'S': /* possible SEEN, SINCE, SUBJECT */
- if (!strcmp (criteria+1,"EEN")) f = tenex_search_seen;
- else if (!strcmp (criteria+1,"INCE"))
- f = tenex_search_date (tenex_search_since,&n);
- else if (!strcmp (criteria+1,"UBJECT"))
- f = tenex_search_string (tenex_search_subject,&d,&n);
- break;
- case 'T': /* possible TEXT, TO */
- if (!strcmp (criteria+1,"EXT"))
- f = tenex_search_string (tenex_search_text,&d,&n);
- else if (!strcmp (criteria+1,"O"))
- f = tenex_search_string (tenex_search_to,&d,&n);
- break;
- case 'U': /* possible UN* */
- if (criteria[1] == 'N') {
- if (!strcmp (criteria+2,"ANSWERED")) f = tenex_search_unanswered;
- else if (!strcmp (criteria+2,"DELETED")) f = tenex_search_undeleted;
- else if (!strcmp (criteria+2,"FLAGGED")) f = tenex_search_unflagged;
- else if (!strcmp (criteria+2,"KEYWORD"))
- f = tenex_search_flag (tenex_search_unkeyword,&n,stream);
- else if (!strcmp (criteria+2,"SEEN")) f = tenex_search_unseen;
- }
- break;
- default: /* we will barf below */
- break;
- }
- if (!f) { /* if can't determine any criteria */
- sprintf (LOCAL->buf,"Unknown search criterion: %.80s",criteria);
- mm_log (LOCAL->buf,ERROR);
- return;
- }
- /* run the search criterion */
- for (i = 1; i <= stream->nmsgs; ++i)
- if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
- mail_elt (stream,i)->searched = NIL;
- }
- /* report search results to main program */
- for (i = 1; i <= stream->nmsgs; ++i)
- if (mail_elt (stream,i)->searched) mail_searched (stream,i);
- }
- }
-
- /* Tenex mail ping mailbox
- * Accepts: MAIL stream
- * Returns: T if stream still alive, NIL if not
- */
-
- long tenex_ping (stream)
- MAILSTREAM *stream;
- {
- long i = 0;
- long r,j;
- struct stat sbuf;
- struct iovec iov[2];
- MAILSTREAM *bezerk = NIL;
- /* punt if stream no longer alive */
- if (!(stream && LOCAL)) return NIL;
- #if unix
- /* only if this is a read-write inbox */
- if (LOCAL->inbox && !stream->readonly) {
- stream->silent = T; /* avoid blabber in initial parse */
- /* do initial parse if necessary */
- r = (!LOCAL->filesize) && (!tenex_parse (stream));
- stream->silent = NIL; /* allow things to continue */
- if (r) return NIL; /* oops */
-
- mm_critical (stream); /* go critical */
- /* calculate name of bezerk file */
- sprintf (LOCAL->buf,MAILFILE,getpwuid (geteuid ())->pw_name);
- stat (LOCAL->buf,&sbuf); /* see if anything there */
- /* non-empty and we can lock our file? */
- if (sbuf.st_size && !flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
- fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
- /* sizes match and can get bezerk mailbox? */
- if ((sbuf.st_size == LOCAL->filesize) &&
- (bezerk = mail_open (bezerk,LOCAL->buf,36)) &&
- (r = bezerk->nmsgs)) {
- /* yes, go to end of file in our mailbox */
- lseek (LOCAL->fd,sbuf.st_size,L_SET);
- /* for each message in bezerk mailbox */
- while (r && (++i <= bezerk->nmsgs)) {
- /* snarf message from Berkeley mailbox */
- iov[1].iov_base = bezerk_snarf (bezerk,i,&j);
- /* calculate header line */
- sprintf ((iov[0].iov_base = LOCAL->buf),"%s,%d;000000000000\n",
- mail_elt (bezerk,i)->internal_date,iov[1].iov_len = j);
- iov[0].iov_len = strlen (iov[0].iov_base);
- /* copy message to new mailbox */
- if (writev (LOCAL->fd,iov,2) < 0) {
- sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
- mm_log (LOCAL->buf,ERROR);
- ftruncate (LOCAL->fd,sbuf.st_size);
- r = 0; /* flag that we have lost big */
- }
- }
- if (r) { /* delete all the messages we copied */
- for (i = 1; i <= r; i++) mail_elt (bezerk,i)->deleted = T;
- mail_expunge (bezerk);/* now expunge all those messages */
- }
- }
- if (bezerk) mail_close (bezerk);
- }
- flock (LOCAL->fd,LOCK_SH); /* back to shared access */
- mm_nocritical (stream); /* release critical */
- }
- #endif
- /* parse mailbox, punt if parse dies */
- return (tenex_parse (stream)) ? T : NIL;
- }
-
- /* Tenex mail check mailbox (too)
- reparses status too;
- * Accepts: MAIL stream
- */
-
- void tenex_check (stream)
- MAILSTREAM *stream;
- {
- long i = 1;
- if (tenex_ping (stream)) { /* ping mailbox */
- /* get new message status */
- while (i <= stream->nmsgs) tenex_elt (stream,i++);
- mm_log ("Check completed",NIL);
- }
- }
-
-
- /* Tenex mail expunge mailbox
- * Accepts: MAIL stream
- */
-
- void tenex_expunge (stream)
- MAILSTREAM *stream;
- {
- long j;
- long i = 1;
- long n = 0;
- long delta = 0;
- unsigned long recent;
- MESSAGECACHE *elt;
- FILECACHE *m;
- /* do nothing if stream dead */
- if (!tenex_ping (stream)) return;
- if (stream->readonly) { /* won't do on readonly files! */
- mm_log ("Expunge ignored on readonly mailbox",WARN);
- return;
- }
- /* get exclusive access */
- if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
- flock (LOCAL->fd,LOCK_SH); /* recover previous lock */
- mm_log ("Expunge rejected: mailbox locked",ERROR);
- return;
- }
-
- recent = stream->recent; /* get recent now that pinged and locked */
- while (i <= stream->nmsgs) { /* for each message */
- m = LOCAL->msgs[i-1]; /* get file cache */
- /* if deleted */
- if ((elt = tenex_elt (stream,i))->deleted) {
- if (elt->recent) --recent;/* if recent, note one less recent message */
- /* one more message to flush */
- delta += m->headersize + m->bodysize + m->header - m->internal;
- fs_give ((void **) &m); /* flush local cache entry */
- for (j = i; j < stream->nmsgs; j++) LOCAL->msgs[j-1] = LOCAL->msgs[j];
- LOCAL->msgs[stream->nmsgs - 1] = NIL;
- mail_expunged (stream,i); /* notify upper levels */
- n++; /* count up one more expunged message */
- }
- else if (i++ && delta) { /* if undeleted message needs moving */
- /* blat it down to new position */
- memmove (LOCAL->text + m->internal - delta,LOCAL->text + m->internal,
- m->headersize + m->bodysize + m->header - m->internal);
- m->internal -= delta; /* relocate pointers */
- m->header -= delta;
- m->body -= delta;
- }
- }
- if (n) { /* expunged anything? */
- /* yes, calculate new file/text size */
- if ((LOCAL->textend -= delta) != (LOCAL->filesize -= delta) ||
- (LOCAL->filesize < 0)) fatal ("File/text pointers screwed up!");
- while (T) { /* modal to ensure it won */
- lseek (LOCAL->fd,0,L_SET);/* seek to start of file */
- if (write (LOCAL->fd,LOCAL->text,LOCAL->textend) >= 0) break;
- sprintf (LOCAL->buf,"Unable to rewrite mailbox: %s",strerror (i =errno));
- mm_log (LOCAL->buf,WARN);
- mm_diskerror (stream,i,T);/* report to top level */
- }
- /* nuke any remaining cruft */
- ftruncate (LOCAL->fd,LOCAL->filesize);
- sprintf (LOCAL->buf,"Expunged %d messages",n);
- mm_log (LOCAL->buf,NIL); /* output the news */
- }
- else mm_log ("No messages deleted, so no update needed",NIL);
- /* notify upper level of new mailbox size */
- mail_exists (stream,stream->nmsgs);
- mail_recent (stream,recent);
- flock (LOCAL->fd,LOCK_SH); /* allow sharers again */
- }
-
- /* Tenex mail copy message(s)
- s;
- * Accepts: MAIL stream
- * sequence
- * destination mailbox
- * Returns: T if success, NIL if failed
- */
-
- long tenex_copy (stream,sequence,mailbox)
- MAILSTREAM *stream;
- char *sequence;
- char *mailbox;
- {
- /* copy the messages */
- return (mail_sequence (stream,sequence)) ?
- tenex_copy_messages (stream,mailbox) : NIL;
- }
-
-
- /* Tenex mail move message(s)
- s;
- * Accepts: MAIL stream
- * sequence
- * destination mailbox
- * Returns: T if success, NIL if failed
- */
-
- long tenex_move (stream,sequence,mailbox)
- MAILSTREAM *stream;
- char *sequence;
- char *mailbox;
- {
- long i;
- MESSAGECACHE *elt;
- if (!(mail_sequence (stream,sequence) &&
- tenex_copy_messages (stream,mailbox))) return NIL;
- /* delete all requested messages */
- for (i = 1; i <= stream->nmsgs; i++)
- if ((elt = tenex_elt (stream,i))->sequence) {
- elt->deleted = T; /* mark message deleted */
- /* recalculate status */
- tenex_update_status (stream,i,NIL);
- }
- /* make sure the update takes */
- if (!stream->readonly) fsync (LOCAL->fd);
- return T;
- }
-
-
- /* Tenex garbage collect stream
- * Accepts: Mail stream
- * garbage collection flags
- */
-
- void tenex_gc (stream,gcflags)
- MAILSTREAM *stream;
- long gcflags;
- {
- /* nothing here for now */
- }
-
- /* Internal routines */
-
-
- /* Tenex mail generate file string
- * Accepts: temporary buffer to write into
- * mailbox name string
- * Returns: local file string
- */
-
- char *tenex_file (dst,name)
- char *dst;
- char *name;
- {
- char *home = getpwuid (geteuid ())->pw_dir;
- strcpy (dst,name); /* copy the mailbox name */
- if (*dst != '/') { /* pass absolute file paths */
- if (strcmp (ucase (dst),"INBOX")) sprintf (dst,"%s/%s",home,name);
- /* INBOX becomes ~USER/mail.txt file */
- else sprintf (dst,"%s/mail.txt",home);
- }
- return dst;
- }
-
-
- /* Parse flag list
- * Accepts: MAIL stream
- * flag list as a character string
- * pointer to user flags to return
- * Returns: system flags
- */
-
- int tenex_getflags (stream,flag,uf)
- MAILSTREAM *stream;
- char *flag;
- long *uf;
- {
- char key[MAILTMPLEN];
- char *t,*s;
- short f = 0;
- long i;
- short j;
- *uf = 0; /* initially no user flags */
- if (flag && *flag) { /* no-op if no flag string */
- /* check if a list and make sure valid */
- if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
- mm_log ("Bad flag list",ERROR);
- return NIL;
- }
- /* copy the flag string w/o list construct */
- strncpy (LOCAL->buf,flag+i,(j = strlen (flag) - (2*i)));
- LOCAL->buf[j] = '\0'; /* tie off tail */
-
- /* make uppercase, find first, parse */
- if (t = strtok (ucase (LOCAL->buf)," ")) do {
- i = 0; /* no flag yet */
- /* system flag, dispatch on first character */
- if (*t == '\\') switch (*++t) {
- case 'S': /* possible \Seen flag */
- if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N' && t[4] == '\0')
- f |= i = fSEEN;
- break;
- case 'D': /* possible \Deleted flag */
- if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
- t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fDELETED;
- break;
- case 'F': /* possible \Flagged flag */
- if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
- t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fFLAGGED;
- break;
- case 'A': /* possible \Answered flag */
- if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
- t[5] == 'R' && t[6] == 'E' && t[7] == 'D' && t[8] == '\0')
- f |= i = fANSWERED;
- break;
- default: /* unknown */
- break;
- }
- else { /* user flag, search through table */
- for (j = 0; !i && j < NUSERFLAGS && (s = stream->user_flags[j]); ++j)
- if (!strcmp (t,ucase (strcpy (key,s)))) *uf |= i = 1 << j;
- }
- if (!i) { /* didn't find a matching flag? */
- sprintf (key,"Unknown flag: %.80s",t);
- mm_log (key,ERROR);
- *uf = NIL; /* be sure no user flags returned */
- return NIL; /* return no system flags */
- }
- /* parse next flag */
- } while (t = strtok (NIL," "));
- }
- return f;
- }
-
- /* Tenex mail parse mailbox
- * Accepts: MAIL stream
- * Returns: T if parse OK
- * NIL if failure, stream aborted
- */
-
- #define PAD(s) LOCAL->textsize = (s | 0x7fff) + 0x8001
- #define Word unsigned long
-
- int tenex_parse (stream)
- MAILSTREAM *stream;
- {
- struct stat sbuf;
- MESSAGECACHE *elt = NIL;
- FILECACHE *m = NIL;
- char *end;
- char c,*s,*t,*v;
- long i,j,k,delta;
- long nmsgs = stream->nmsgs;
- long recent = stream->recent;
- union {
- unsigned long wd;
- char ch[9];
- } wdtest;
- strcpy (wdtest.ch,"AAAA1234");/* constant for word testing */
- fstat (LOCAL->fd,&sbuf); /* get status */
- /* calculate change in size */
- if ((delta = sbuf.st_size - LOCAL->filesize) < 0) {
- sprintf (LOCAL->buf,"Mailbox shrank from %d to %d bytes, aborted",
- LOCAL->filesize,sbuf.st_size);
- mm_log (LOCAL->buf,ERROR); /* this is pretty bad */
- tenex_close (stream);
- return NIL;
- }
- if (delta > 0) { /* apparent change in the file */
- if (!LOCAL->text) /* create buffer if first time through */
- LOCAL->text = (char *) fs_get (PAD (sbuf.st_size));
- /* else resize if need more space */
- else if (LOCAL->textsize < (i = delta + LOCAL->textend))
- fs_resize ((void **) &LOCAL->text,PAD (i));
- /* get to that position in the file */
- lseek (LOCAL->fd,LOCAL->filesize,L_SET);
- /* read the new text */
- if (read (LOCAL->fd,(s = LOCAL->text+LOCAL->textend),delta) < 0) {
- sprintf (LOCAL->buf,"I/O error reading mail file: %s",strerror (errno));
- mm_log (LOCAL->buf,ERROR);
- tenex_close (stream);
- return NIL;
- }
-
- /* must begin with dd-mmm-yy hh:mm:ss" */
- if (s[2] != '-' || s[6] != '-' || s[9] != ' ' || s[12] != ':' ||
- s[15] != ':') {
- mm_log ("Mailbox invalidated by some other process, aborted",ERROR);
- tenex_close (stream);
- return NIL;
- }
- /* update parsed file size */
- LOCAL->filesize = sbuf.st_size;
- /* calculate and tie off end of text */
- *(end = (LOCAL->text + (LOCAL->textend += delta))) = '\0';
- if (!LOCAL->msgs) { /* if no cache yet */
- /* calculate initial cache size */
- for (t = s,i = 0; (t = strchr (t,',')) && (j = strtol (++t,&t,10)) &&
- (*t == ';') && (t = strchr (t,'\n')) && ((t += j) < end); i++);
- mail_cache (stream,i); /* instantiate caches */
- LOCAL->msgs = (FILECACHE **)
- fs_get ((LOCAL->cachesize = i + CACHEINCREMENT) * sizeof(FILECACHE *));
- for (i = 0; i < LOCAL->cachesize; i++) LOCAL->msgs[i] = NIL;
- }
- while (s < end) { /* mailbox parse loop */
- v = s; /* save pointer to internal header */
- /* parse header */
- if ((t = strchr (s,'\n')) && ((i = t - s) < LOCAL->buflen) &&
- strncpy (LOCAL->buf,s,i) && (!(LOCAL->buf[i] = '\0')) &&
- (s = t + 1) && strtok (LOCAL->buf,",") && (t = strtok (NIL,";")) &&
- (i = strtol (t,&t,10)) && (!(t && *t)) && (t = strtok (NIL," \r")) &&
- (strlen (t) == 12)) { /* create new cache entries if valid */
- /* validate mailbox not too big */
- /* increase cache if necessary */
- if (nmsgs >= LOCAL->cachesize) {
- fs_resize ((void **) &LOCAL->msgs,
- (LOCAL->cachesize += CACHEINCREMENT) * sizeof(FILECACHE));
- for (j = nmsgs; j < LOCAL->cachesize; j++) LOCAL->msgs[j] = NIL;
- }
- m = LOCAL->msgs[nmsgs] = (FILECACHE *) fs_get (sizeof (FILECACHE));
- elt = mail_elt (stream,++nmsgs);
- /* note internal and message headers */
- m->internal = v - LOCAL->text;
- m->header = s - LOCAL->text;
- c = s[i]; /* save start of next message */
- s[i] = '\0'; /* tie off message temporarily */
-
- /* fast scan for body text on 32-bit machine */
- if (wdtest.wd == 0x41414141) {
- m->body = NIL; /* not found yet */
- k = 0; /* not CRLF */
- /* any characters before word boundary? */
- if (j = (long) (v = s) & 3) {
- j = 4 - j; /* calculate how many */
- v += j; /* start at new word */
- j = i - (5 + j);
- }
- else j = i - 5; /* j is total # of tries */
- do { /* fast search for string */
- if (0x80808080&(0x01010101+(0x7f7f7f7f&~(0x0a0a0a0a^*(Word *)v)))){
- if (v[0] == '\n' && ((v[1] == '\n') ||
- (k = (v[-1] == '\r') && (v[1] == '\r') &&
- (v[2] == '\n')))) {
- m->body = (v + 2 + k) - LOCAL->text;
- break;
- }
- if (v[1] == '\n' && ((v[2] == '\n') ||
- (k = (v[0] == '\r') && (v[2] == '\r') &&
- (v[3] == '\n')))) {
- m->body = (v + 3 + k) - LOCAL->text;
- break;
- }
- if (v[2] == '\n' && ((v[3] == '\n') ||
- (k = (v[1] == '\r') && (v[3] == '\r') &&
- (v[4] == '\n')))) {
- m->body = (v + 4 + k) - LOCAL->text;
- break;
- }
- if (v[3] == '\n' && ((v[4] == '\n') ||
- (k = (v[2] == '\r') && (v[4] == '\r') &&
- (v[5] == '\n')))) {
- m->body = (v + 5 + k) - LOCAL->text;
- break;
- }
- }
- v += 4,j -= 4; /* try next word */
- } while (j > 0); /* continue until end of string */
- }
- else { /* do it the (possibly slower) way */
- m->body = (v = strstr (s,"\n\n")) ? (v + 2) - LOCAL->text :
- ((v = strstr (s,"\r\n\r\n")) ? (v + 4) - LOCAL->text : NIL);
- }
-
- s[i] = c; /* restore next message character */
- /* compute header and body sizes */
- m->headersize = m->body ? m->body - m->header : i;
- m->bodysize = i - m->headersize;
- if (stream->nmsgs) { /* if not first time through */
- elt->recent = T; /* this is a recent message */
- recent++; /* count up a new recent message */
- }
- elt->rfc822_size = i; /* note message size */
- for (j = 0; j < i; j++) if (s[j] == '\n') elt->rfc822_size++;
- /* set internal date */
- elt->internal_date = cpystr (LOCAL->buf);
- /* as well as parsed value */
- m->date = tenex_date (LOCAL->buf);
- s += i; /* advance to next message */
- /* calculate system flags */
- if ((i = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
- if (i & fDELETED) elt->deleted = T;
- if (i & fFLAGGED) elt->flagged = T;
- if (i & fANSWERED) elt->answered = T;
- t[10] = '\0'; /* tie off flags */
- j = strtol (t,NIL,8); /* get user flags value */
- /* set up all valid user flags (reversed!) */
- while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
- stream->user_flags[i]) elt->user_flags |= 1 << i;
- }
- else { /* header parse failed! */
- sprintf (LOCAL->buf,"Bad format in message %d",nmsgs);
- mm_log (LOCAL->buf,ERROR);
- tenex_close (stream);
- return NIL;
- }
- }
- }
- mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
- mail_recent (stream,recent); /* and of change in recent messages */
- return T; /* return the winnage */
- }
-
- /* Tenex copy messages
- * Accepts: MAIL stream
- * mailbox copy vector
- * mailbox name
- * Returns: T if success, NIL if failed
- */
-
- int tenex_copy_messages (stream,mailbox)
- MAILSTREAM *stream;
- char *mailbox;
- {
- struct iovec iov[2];
- struct stat sbuf;
- MESSAGECACHE *elt;
- FILECACHE *m;
- long j = NIL;
- long i;
- int fd = open (tenex_file (LOCAL->buf,mailbox),O_WRONLY|O_APPEND|O_CREAT,
- S_IREAD|S_IWRITE);
- if (fd < 0) { /* got file? */
- sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
- mm_log (LOCAL->buf,ERROR);
- return NIL;
- }
- mm_critical (stream); /* go critical */
- /* This used to be LOCK_EX, but this would cause a hang if the destination
- * mailbox was open. This could put MailManager, etc. into a deadlock.
- * Something will need to be done about interlocking multiple appends.
- */
- flock (fd,LOCK_SH); /* lock out expunges */
- fstat (fd,&sbuf); /* get current file size */
- /* for each requested message */
- for (i = 1; i <= stream->nmsgs; i++)
- if ((elt =mail_elt (stream,i))->sequence) {
- m = LOCAL->msgs[i - 1]; /* internal message pointers */
- /* pointer to text of message */
- iov[1].iov_base = LOCAL->text + m->header;
- iov[1].iov_len = m->headersize + m->bodysize;
- sprintf ((iov[0].iov_base = LOCAL->buf),"%s,%d;000000000000\n",
- elt->internal_date,iov[1].iov_len);
- /* new internal header */
- iov[0].iov_len = strlen (iov[0].iov_base);
- /* write the message */
- if (j = (writev (fd,iov,2) < 0)) {
- sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
- mm_log (LOCAL->buf,ERROR);
- ftruncate (fd,sbuf.st_size);
- break;
- }
- }
- fsync (fd); /* force out the update */
- flock (fd,LOCK_UN); /* unlock mailbox */
- close (fd); /* close the file */
- mm_nocritical (stream); /* release critical */
- return !j;
- }
-
- /* Tenex get cache element with status updating from file
- * Accepts: MAIL stream
- * message number
- * Returns: cache element
- */
-
- MESSAGECACHE *tenex_elt (stream,msgno)
- MAILSTREAM *stream;
- long msgno;
- {
- long i = 0,j;
- char c;
- char *s;
- MESSAGECACHE *elt = mail_elt (stream,msgno);
- FILECACHE *m = LOCAL->msgs[msgno-1];
- /* locate flags in header */
- if (!(s = strchr (LOCAL->text + m->internal,';')) ||
- ((m->header - 1 - (i = ++s - LOCAL->text)) < 12))
- fatal ("Bad mailbox flag syntax!");
- /* set the seek pointer */
- lseek (LOCAL->fd,(off_t) i,L_SET);
- /* read the new flags */
- if (read (LOCAL->fd,s,12) < 0) {
- sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
- fatal (LOCAL->buf);
- }
- /* calculate system flags */
- elt->seen = (i = ((s[10]-'0') * 8) + s[11]-'0') & fSEEN ? T : NIL;
- elt->deleted = i & fDELETED ? T : NIL;
- elt->flagged = i & fFLAGGED ? T : NIL;
- elt->answered = i & fANSWERED ? T : NIL;
- c = s[10]; /* remember first system flags byte */
- s[10] = '\0'; /* tie off flags */
- j = strtol (s,NIL,8); /* get user flags value */
- s[10] = c; /* restore first system flags byte */
- /* set up all valid user flags (reversed!) */
- while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
- stream->user_flags[i]) elt->user_flags |= 1 << i;
- return elt;
- }
-
- /* Tenex update status string
- * Accepts: MAIL stream
- * message number
- * flag saying whether or not to sync
- */
-
- void tenex_update_status (stream,msgno,syncflag)
- MAILSTREAM *stream;
- long msgno;
- int syncflag;
- {
- long i = 0,j;
- long k = 0;
- char *s;
- MESSAGECACHE *elt = mail_elt (stream,msgno);
- FILECACHE *m = LOCAL->msgs[msgno-1];
- if (!stream->readonly) { /* not if readonly you don't */
- /* locate flags in header */
- if (!(s = strchr (LOCAL->text + m->internal,';')) ||
- ((m->header - 1 - (i = ++s - LOCAL->text)) < 12))
- fatal ("Bad mailbox flag syntax!");
- j = elt->user_flags; /* get user flags */
- /* reverse bits (dontcha wish we had CIRC?) */
- while (j) k |= 1 << 29 - find_rightmost_bit (&j);
- /* print new flag string */
- sprintf (LOCAL->buf,"%010lo%02o",k,
- (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
- (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
- memcpy (s,LOCAL->buf,12); /* set the new flags w/o a NUL */
- /* set the seek pointer */
- while (T) { /* write the new flags */
- lseek (LOCAL->fd,(off_t) i,L_SET);
- if (write (LOCAL->fd,s,12) == 12) break;
- sprintf (LOCAL->buf,"Unable to write status: %s",strerror (j = errno));
- mm_log (LOCAL->buf,WARN);
- mm_diskerror (stream,j,T);
- }
- /* sync if requested */
- if (syncflag) fsync (LOCAL->fd);
- }
- }
-
- /* Search support routines
- * Accepts: MAIL stream
- * message number
- * pointer to additional data
- * Returns: T if search matches, else NIL
- */
-
-
- char tenex_search_all (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return T; /* ALL always succeeds */
- }
-
-
- char tenex_search_answered (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return mail_elt (stream,msgno)->answered ? T : NIL;
- }
-
-
- char tenex_search_deleted (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return mail_elt (stream,msgno)->deleted ? T : NIL;
- }
-
-
- char tenex_search_flagged (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return mail_elt (stream,msgno)->flagged ? T : NIL;
- }
-
-
- char tenex_search_keyword (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return mail_elt (stream,msgno)->user_flags & n ? T : NIL;
- }
-
-
- char tenex_search_new (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- MESSAGECACHE *elt = mail_elt (stream,msgno);
- return (elt->recent && !elt->seen) ? T : NIL;
- }
-
- char tenex_search_old (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return mail_elt (stream,msgno)->recent ? NIL : T;
- }
-
-
- char tenex_search_recent (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return mail_elt (stream,msgno)->recent ? T : NIL;
- }
-
-
- char tenex_search_seen (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return mail_elt (stream,msgno)->seen ? T : NIL;
- }
-
-
- char tenex_search_unanswered (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return mail_elt (stream,msgno)->answered ? NIL : T;
- }
-
-
- char tenex_search_undeleted (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return mail_elt (stream,msgno)->deleted ? NIL : T;
- }
-
-
- char tenex_search_unflagged (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return mail_elt (stream,msgno)->flagged ? NIL : T;
- }
-
-
- char tenex_search_unkeyword (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return mail_elt (stream,msgno)->user_flags & n ? NIL : T;
- }
-
-
- char tenex_search_unseen (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return mail_elt (stream,msgno)->seen ? NIL : T;
- }
-
- char tenex_search_before (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return (char) (LOCAL->msgs[msgno-1]->date < n);
- }
-
-
- char tenex_search_on (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- return (char) (LOCAL->msgs[msgno-1]->date == n);
- }
-
-
- char tenex_search_since (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- /* everybody interprets "since" as .GE. */
- return (char) (LOCAL->msgs[msgno-1]->date >= n);
- }
-
-
- char tenex_search_body (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- FILECACHE *m = LOCAL->msgs[msgno-1];
- return search (LOCAL->text + m->body,m->bodysize,d,n);
- }
-
-
- char tenex_search_subject (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- char *s = tenex_fetchenvelope (stream,msgno)->subject;
- return s ? search (s,strlen (s),d,n) : NIL;
- }
-
-
- char tenex_search_text (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- FILECACHE *m = LOCAL->msgs[msgno-1];
- return search (LOCAL->text + m->header,m->headersize,d,n) ||
- tenex_search_body (stream,msgno,d,n);
- }
-
- char tenex_search_bcc (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- LOCAL->buf[0] = '\0'; /* initially empty string */
- /* get text for address */
- rfc822_write_address (LOCAL->buf,tenex_fetchenvelope (stream,msgno)->bcc);
- return search (LOCAL->buf,strlen (LOCAL->buf),d,n);
- }
-
-
- char tenex_search_cc (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- LOCAL->buf[0] = '\0'; /* initially empty string */
- /* get text for address */
- rfc822_write_address (LOCAL->buf,tenex_fetchenvelope (stream,msgno)->cc);
- return search (LOCAL->buf,strlen (LOCAL->buf),d,n);
- }
-
-
- char tenex_search_from (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- LOCAL->buf[0] = '\0'; /* initially empty string */
- /* get text for address */
- rfc822_write_address (LOCAL->buf,tenex_fetchenvelope (stream,msgno)->from);
- return search (LOCAL->buf,strlen (LOCAL->buf),d,n);
- }
-
-
- char tenex_search_to (stream,msgno,d,n)
- MAILSTREAM *stream;
- long msgno;
- char *d;
- long n;
- {
- LOCAL->buf[0] = '\0'; /* initially empty string */
- /* get text for address */
- rfc822_write_address (LOCAL->buf,tenex_fetchenvelope (stream,msgno)->to);
- return search (LOCAL->buf,strlen (LOCAL->buf),d,n);
- }
-
- /* Search parsers */
-
-
- /* Parse a date
- * Accepts: function to return
- * pointer to date integer to return
- * Returns: function to return
- */
-
- search_t tenex_search_date (f,n)
- search_t f;
- long *n;
- {
- long i;
- char *s;
- /* parse the date and return fn if OK */
- return (tenex_search_string (f,&s,&i) && (*n = tenex_date (s))) ? f : NIL;
- }
-
-
- /* Actual date parser (routine)
- minimal and stupid routine;
- * Accepts: date to parse
- * Returns: date integer
- */
-
- long tenex_date (s)
- char *s;
- {
- long ms;
- long d,m,y;
- /* parse first number (probable month) */
- if (!(s && (m = strtol ((const char *) s,&s,10)))) return NIL;
- switch (*s) { /* different parse based on delimiter */
- case '/': /* mm/dd/yy format */
- /* parse remainder of date */
- if (!((d = strtol ((const char *) ++s,&s,10)) && *s == '/' &&
- (y = strtol ((const char *) ++s,&s,10)) && *s == '\0'))
- return NIL;
- break;
-
- case '-': /* dd-mmm-yy format */
- d = m; /* so the number we got is a day */
- /* make sure string is UC and long enough! */
- if (strlen (ucase (s)) < 5) return NIL;
- /* slurp up the month string */
- ms = (((long) s[1]) << 16) + (((long) s[2]) << 8) + s[3];
- switch (ms) { /* determine the month */
- case ('J' << 16) + ('A' << 8) + 'N':
- m = 1; break;
- case ('F' << 16) + ('E' << 8) + 'B':
- m = 2; break;
- case ('M' << 16) + ('A' << 8) + 'R':
- m = 3; break;
- case ('A' << 16) + ('P' << 8) + 'R':
- m = 4; break;
- case ('M' << 16) + ('A' << 8) + 'Y':
- m = 5; break;
- case ('J' << 16) + ('U' << 8) + 'N':
- m = 6; break;
- case ('J' << 16) + ('U' << 8) + 'L':
- m = 7; break;
- case ('A' << 16) + ('U' << 8) + 'G':
- m = 8; break;
- case ('S' << 16) + ('E' << 8) + 'P':
- m = 9; break;
- case ('O' << 16) + ('C' << 8) + 'T':
- m = 10; break;
- case ('N' << 16) + ('O' << 8) + 'V':
- m = 11; break;
- case ('D' << 16) + ('E' << 8) + 'C':
- m = 12; break;
- default:
- return NIL;
- }
- /* parse the year */
- if (s[4] == '-' && (y = (int) strtol ((const char *) s+5,&s,10)) &&
- (*s == '\0' || *s == ' ')) break;
- default: /* unknown format */
- return NIL;
- }
- y += (y < 64) ? 2000 : 0; /* the PDP-10 world began in 1964 */
- y -= (y >= 1964) ? 1964 : 64;
- /* minimal validity check of date */
- if (d < 1 || d > 31 || m < 1 || m > 12 || y < 0 || y >= 100) return NIL;
- return (y*12+m-1)*31+d-1; /* calculate and return date value */
- }
-
- /* Parse a flag
- * Accepts: function to return
- * pointer to keyword integer to return
- * MAIL stream
- * Returns: function to return
- */
-
- search_t tenex_search_flag (f,n,stream)
- search_t f;
- long *n;
- MAILSTREAM *stream;
- {
- short i;
- char *s,*t;
- if (t = strtok (NIL," ")) { /* get a keyword */
- ucase (t); /* get uppercase form of flag */
- for (i = 0; i < NUSERFLAGS && (s = stream->user_flags[i]); ++i)
- if (!strcmp (t,ucase (strcpy (LOCAL->buf,s))) && (*n = 1 << i)) return f;
- }
- return NIL; /* couldn't find keyword */
- }
-
- /* Parse a string
- * Accepts: function to return
- * pointer to string to return
- * pointer to string length to return
- * Returns: function to return
- */
-
-
- search_t tenex_search_string (f,d,n)
- search_t f;
- char **d;
- long *n;
- {
- char *c = strtok (NIL,""); /* remainder of criteria */
- if (c) { /* better be an argument */
- switch (*c) { /* see what the argument is */
- case '\0': /* catch bogons */
- case ' ':
- return NIL;
- case '"': /* quoted string */
- if (!(strchr (c+1,'"') && (*d = strtok (c,"\"")) && (*n = strlen (*d))))
- return NIL;
- break;
- case '{': /* literal string */
- *n = strtol (c+1,&c,10); /* get its length */
- if (*c++ != '}' || *c++ != '\015' || *c++ != '\012' ||
- *n > strlen (*d = c)) return NIL;
- c[*n] = '\255'; /* write new delimiter */
- strtok (c,"\255"); /* reset the strtok mechanism */
- break;
- default: /* atomic string */
- *n = strlen (*d = strtok (c," "));
- break;
- }
- return f;
- }
- else return NIL;
- }
-