home *** CD-ROM | disk | FTP | other *** search
- /*
- * Program: Distributed Electronic Mail Manager (Utilities)
- *
- * 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: 9 May 1989
- * Last Edited: 25 June 1993
- *
- * Copyright 1993 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.
- *
- */
-
-
- #import "MailManager.h"
-
- // Global variables from preferences panel
-
- const char *defaultbcc; // default bcc list
- const char *defaultcc; // default cc list
- const char *hostlist[] = { // server list for sending
- NIL, // preferred SMTP server
- NIL, // domain name (for sending)
- "mailhost","localhost",NIL}; // last chance entries
- const char *outbox; // saved messages file
- const char *personalname; // personal name (for sending)
- const char *repository; // preferred repository
- const char *username; // preferred user name (for sending)
- BOOL autoexpunge = NIL; // automatic expunge when mailbox closed
- BOOL autologin = NIL; // automatic login with previous password
- BOOL autoopen = NIL; // automatic open
- BOOL autoread = NIL; // automatic read when opening mailbox
- BOOL autozoom = NIL; // automatic zoom to new messages
- BOOL closefinalkill = NIL; // close read window on final kill in sequence
- BOOL literaldisplay = NIL; // literal display
- BOOL nolinebreaking = NIL; // don't line break in send windows
- BOOL nopersonal = NIL; // don't show personal names in recipient lists
- BOOL readbboards = NIL; // mdd: enable/disable bboards
- BOOL lockedopen = NIL; // (internal) single locked mailbox windows
- BOOL lockedread = NIL; // (internal) single locked read windows
- short debug = NIL; // debugging state
- short format = MBOX; // default format is mbox
- double interval = 0; // number of seconds between wakeups
-
-
- // Global variables
-
- char *localhost; // local host name
- char *localuser; // local user name
- char *localpersonal; // local personal name
- char *lastlogin = NIL; // last login username
- Font *defaultfont; // default font for all text
-
- // Build header for message
- // Accepts: pointer to text
- // MAIL stream or NIL to return a prototype header
- // message cache element
-
- void headerline (char *text,MAILSTREAM *stream,MESSAGECACHE *elt)
- {
- char from[FROMLEN];
- char flgs[FLAGSLEN];
- char sub[TMPLEN];
- char date[TMPLEN];
- long i = 999999;
- if (stream && elt) { // want real header?
- // yes, do fetchfrom now so elt is set up
- mail_fetchfrom (from,stream,elt->msgno,FROMLEN-1);
- // compute flag string
- flgs[0] = elt->recent ? (elt->seen ? 'R' : 'N') : (elt->seen ? ' ' : 'U');
- flgs[1] = elt->flagged ? 'F' : ' ';
- flgs[2] = elt->answered ? 'A' : ' ';
- flgs[3] = elt->deleted ? 'D' : ' ';
- flgs[4] = '\0';
- mail_date (date,elt); // set date
- if (i = elt->user_flags) { // first stash user flags into subject
- sub[0] = '{'; // open brace for keywords
- sub[1] = '\0'; // tie off so strcat starts off right
- // output each keyword
- do strcat (sub,stream->user_flags[find_rightmost_bit (&i)]);
- while (i && strcat (sub," "));
- strcat (sub,"} "); // close brace and space before subject text
- }
- else sub[0] = '\0'; // no user flags in front of subject
- // append the subject
- mail_fetchsubject (sub + strlen (sub),stream,elt->msgno,SUBJECTLEN-1);
- sub[SUBJECTLEN-1] = '\0'; // truncate as necessary
- i = elt->rfc822_size; // get message size
- }
- else { // want prototype text
- strcpy (date,DATE); // dummy date
- strcpy (from,FROM); // dummy from
- strcpy (flgs,FLAGS); // dummy flags
- strcpy (sub,SUBJECT); // dummy subject
- }
- // output what we got
- sprintf (text,"%s%.6s %s %s (%ld chars)",flgs,date,from,sub,i);
- }
-
- // Append a selection string
- // Accepts: string to append to
- // selection name
- // selection argument
-
- void selstr (char *str,char *name,const char *arg)
- {
- char tmp[TMPLEN];
- char c;
- char *s = (char *) arg;
- strcat (str,name); // append the start of the string
- // do it this way if need to be literal
- while (c = *s++) if (c == '"' || c == '\015' || c == '\012') {
- sprintf (tmp," {%lu}\015\012",strlen (arg));
- strcat (str,tmp);
- strcat (str,arg);
- return;
- }
- strcat (str," \""); // else output as quoted string
- strcat (str,arg);
- strcat (str,"\"");
- return;
- }
-
- // Address list management
-
-
- // Copy address list for reply
- // Accepts: MAP address list
- // optional MTP address list to append to
- // Returns: MTP address list
-
-
- ADDRESS *copy_adr (ADDRESS *adr,ADDRESS *ret)
- {
- char tmp[TMPLEN],mymbx[TMPLEN],mydom[TMPLEN];
- char *s;
- ADDRESS *dadr,*prev;
- if (!adr) return ret; // no-op if empty list
- // make uppercase copy of my mailbox name
- ucase (strcpy (mymbx,username));
- // and uppercase copy of my domain name
- ucase (strcpy (mydom,domainname));
- // run down previous list until the end
- if (prev = ret) while (prev->next) prev = prev->next;
- // don't copy if it matches me
- do if (adr->host && (strcmp (mymbx,ucase (strcpy (tmp,adr->mailbox))) ||
- (strcmp (mydom,ucase (strcpy (tmp,adr->host))) &&
- (s = strchr (tmp,'.')) && strcmp (mydom,s+1)))) {
- dadr = mail_newaddr (); // instantiate new address
- if (!ret) ret = dadr; // set return if new address list
- // tie on to the end of any previous
- if (prev) prev->next = dadr;
- dadr->personal = cpystr (adr->personal);
- dadr->adl = cpystr (adr->adl);
- dadr->mailbox = cpystr (adr->mailbox);
- dadr->host = cpystr (adr->host);
- dadr->error = NIL; // initially no error
- dadr->next = NIL; // no next pointer yet
- prev = dadr; // this is now the previous
- } while (adr = adr->next); // go to next address in list
- return (ret); // return the MTP address list
- }
-
- // C Client event notification
-
-
- // Message matches a search
- // Accepts: MAIL stream
- // message number
-
- void mm_searched (MAILSTREAM *stream,long msgno)
- {
- [(getstreamprop (stream))->window searched:msgno];
- }
-
-
- // Message exists (i.e. there are that many mesages in the mailbox)
- // Accepts: MAIL stream
- // message number
-
- void mm_exists (MAILSTREAM *stream,long msgno)
- {
- STREAMPROP *s = getstreamprop (stream);
- s->nmsgs = msgno; // note number of messages
- [s->window exists:msgno]; // let us know about the message
- }
-
-
- // Message expunged
- // Accepts: MAIL stream
- // message number
-
- void mm_expunged (MAILSTREAM *stream,long msgno)
- {
- [(getstreamprop (stream))->window expunged:msgno];
- }
-
-
- // Notification event
- // Accepts: MAIL stream
- // string to log
- // error flag
-
- void mm_notify (MAILSTREAM *stream,char *string,long errflg)
- {
- mm_log (string,errflg); // not doing anything special yet
- }
-
- // Mailbox found
- // Accepts: mailbox name
-
- void mm_mailbox (char *string)
- {
- // add this to the browser
- [NXApp addMailboxToBrowser:string];
- }
-
-
- // BBoard found
- // Accepts: BBoard name
-
- void mm_bboard (char *string)
- {
- //char tmp[MAILTMPLEN];
- //sprintf (tmp,"*%s",string); // need something better than this
- //[NXApp addMailboxToBrowser:tmp];
- }
-
- // Log an event for the user to see
- // Accepts: string to log
- // error flag
-
- void mm_log (char *string,long errflg)
- {
- char *tmp;
- struct tm *t;
- struct timeb tb;
- switch (errflg) {
- case NIL: // no error
- case PARSE: // parse glitch
- case WARN: // warning only
- tmp = fs_get (16+strlen (string));
- ftime (&tb); // get time and timezone poop
- t = localtime (&tb.time); // convert to individual items
- sprintf (tmp,"%02d:%02d %s",t->tm_hour,t->tm_min,string);
- [NXApp telemetry:tmp];
- fs_give ((void **) &tmp);
- break;
- case ERROR: // error that causes a punt
- default:
- NXRunAlertPanel ("Error",string,NIL,NIL,NIL);
- break;
- }
- }
-
-
- // Log an event to debugging telemetry
- // Accepts: string to log
-
- void mm_dlog (char *string)
- {
- char *tmp;
- struct tm *t;
- struct timeb tb;
- tmp = fs_get (16+strlen (string));
- ftime (&tb); // get time and timezone poop
- t = localtime (&tb.time); // convert to individual items
- sprintf (tmp,"%02d:%02d:%02d %s",t->tm_hour,t->tm_min,t->tm_sec,string);
- [NXApp telemetry:tmp];
- fs_give ((void **) &tmp);
- }
-
- // Get user name and password for this host
- // Accepts: host name
- // where to return user name
- // where to return password
- // trial number of this login
-
- void mm_login (char *host,char *username,char *password,long trial)
- {
- // do the login
- [NXApp login:host user:username password:password force:trial];
- }
-
-
- // Notification that the c-client has gone critical
- // Accepts: MAIL stream
-
- void mm_critical (MAILSTREAM *stream)
- {
- }
-
-
- // Notification that the c-client has released critical
- // Accepts: MAIL stream
-
- void mm_nocritical (MAILSTREAM *stream)
- {
- }
-
- // Notification of a disk write error in the c-client
- // Accepts: MAIL stream
- // system error code
- // seriousness flag
- // Returns: abort flag
-
- long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
- {
- char tmp[MAILTMPLEN],tmpx[MAILTMPLEN];
- sprintf (tmp,"Error writing %s",stream->mailbox);
- sprintf (tmpx,"%s%s -- continuing will retry",strerror (errno),
- serious ? " -- mailbox possibly damaged" : "");
- NXRunAlertPanel (tmp,tmpx,NIL,NIL,NIL);
- return NIL;
- }
-
-
- // Log a fatal event to telemetry
- // Accepts: string to log
-
- void mm_fatal (char *string)
- {
- mm_log (string,ERROR);
- }
-
- // Periodic routines
-
-
- // Periodic wake-up call
- // Accepts: timed entry identifier
- // current time
- // data received when timed event set up
-
- DPSTimedEntryProc mm_wakeup (DPSTimedEntry teNumber,double now,void *userData)
- {
- // ping with a no-op
- [(MBoxWindow *) userData noop:NIL];
- return NIL;
- }
-
- // Streamprop simulator for MBoxwindow to stream backpointers
-
-
- // This isn't really the best way of doing this, but I seriously doubt there
- // are going to be that many simultaneous MBoxwindow's and this way doesn't
- // involve having to hack into the C client. Note that the nomenclature is a
- // bit funny; the "window" stored on the streamprop is the MBoxWindow object
- // that you can send messages to and not an actual window.
- //
- // We also store the number of messages on the streamprop, because when the
- // mailbox is selected on a new Open Mailbox there is no MBoxWindow to stash
- // it on. The MBoxWindow isn't made until the mailbox is completely open.
- //
- // For the same reason, getstreamprop () creates a streamprop with a null
- // MBoxWindow if there is no streamprop registered, whereas putstreamprop ()
- // will return an error if no streamprop exists yet.
-
-
- // List of stream properties
- STREAMPROP *streamproplist = NIL;
-
-
- // Register MBoxWindow for stream
- // Accepts: stream (streamprop for it must already exist)
- // MBoxWindow or NIL to kill the streamprop
-
- void putstreamprop (MAILSTREAM *stream,MBoxWindow *window)
- {
- STREAMPROP *s = streamproplist;
- while (s) { // run through the list
- if (s->stream == stream) { // found it yet?
- // yes, register MBoxWindow if there
- if (window) s->window = window;
- else { // killing the streamprop
- // bind any successor streamprops to prior
- if (s->previous) s->previous->next = s->next;
- // if no prior, successors is new list
- else streamproplist = s->next;
- // bind prior to successors
- if (s->next) s->next->previous = s->previous;
- fs_give ((void **) &s); // flush this streamprop
- }
- return; // all done
- }
- else s = s->next; // run down the list
- }
- // lost the streamprop?
- fatal ("Attempt to register MBoxWindow on unknown MAIL stream");
- }
-
- // Return streamprop for stream
- // Accepts: MAIL stream
- // Returns: streamprop (created if it doesn't exist)
-
- STREAMPROP *getstreamprop (MAILSTREAM *stream)
- {
- STREAMPROP *s = streamproplist;
- while (s) { // run down the stream list
- // return the streamprop when we find it
- if (s->stream == stream) return s;
- else s = s->next;
- }
- // not found, make a new streamprop
- s = (STREAMPROP *) fs_get (sizeof (STREAMPROP));
- s->stream = stream; // bind the stream
- s->window = NIL; // no MBoxWindow yet
- s->nmsgs = 0; // number of messages not set yet
- s->previous = NIL; // this is the front of the list
- // append other streamprops if any
- if (s->next = streamproplist) s->next->previous = s;
- return (streamproplist = s); // and put us on the head of the list
- }
-
- // Fix new lines (change CRLF to \n)
- // Accepts: character string
- // Returns: same string with returns squeezed out
-
- char *fixnl (char *text)
- {
- int i,j;
- if (text) { // we're a no-op if no text
- // squeeze out any CR's
- for (i = j = 0; text[i] != '\0'; i++)
- if (text[i] != '\015' || text[i+1] != '\012') text [j++] = text[i];
- text[j] = '\0'; // tie off end of string
- }
- return (text);
- }
-
-
- // Get text from view, breaking lines and changing \n to CRLF
- // Accepts: view
- // Returns: text string or NIL
-
- unsigned char *gettext (Text *view)
- {
- unsigned char *s,*d;
- unsigned char *ret = NIL;
- int i = [view textLength];
- if (i++) { // any text at all?
- // get enough space for the text in worst case
- d = ret = (unsigned char *) fs_get (i * 2);
- // get text from the view
- [view getSubstring:(char *) (s = ret + i) start:0 length:i];
- if (nolinebreaking) { // non line breaking case is easy
- while (*s) { // copy string, inserting CR's before LF's
- // if CR, copy it and skip LF check
- if (*s == '\015') *d++ = *s++;
- // else if LF, insert a CR before it
- else if (*s == '\012') *d++ = '\015';
- if (*s) *d++ = *s++; // copy (may be null if last char was CR)
- }
- if (d[-1] != '\012') { // force ending with a newline
- *d++ = '\015';
- *d++ = '\012';
- }
- *d = '\0'; // tie off destination
- }
- else linebreak (d,s); // line breaking case
- }
- return (ret);
- }
-
- // Copy text, breaking lines
- // Accepts: destination pointer
- // source pointer
-
- void linebreak (unsigned char *dst,unsigned char *src)
- {
- unsigned char *s = src;
- unsigned char *b = NIL;
- unsigned char *l = dst;
- int i = 0; // init character position
- while (*s) switch (*s) { // until source runs out
- case '\015': // return
- case '\012': // line feed
- wordcopy (&b,&dst,&src,s); // copy any word
- if (*s == '\015') *dst++ = *s++;
- else *dst++ = '\015'; // write CR before bare LF
- if (*s == '\012') *dst++ = *s++;
- i = 0; // clear position count
- src = s; // source starts on next character
- l = dst; // note new line
- break;
- case '\t': // tab
- i |= 7; // move to in front of stop, fall into space
- case ' ': // space
- wordcopy (&b,&dst,&src,s); // copy word if any
- // count line, increment pointer
- if (++i <= MAXLINELENGTH) s++;
- else { // line will overflow, write new line
- *dst++ = '\015'; *dst++ = '\012'; i = 0;
- // skip to end of whitespace
- while (*s == ' ' || *s == '\t') s++;
- src = s; // update source to end of whitespace
- l = dst; // note new line
- }
- break;
- default: // some other (presumably printing)
- if (!b) b = s; // remember start of word
- if (++i > MAXLINELENGTH) { // count character, see if overflow
- // if line empty must break up the word
- if (l == dst) wordcopy (&b,&dst,&src,s);
- else { // word goes on next line
- *dst++ = '\015'; *dst++ = '\012'; i = 0;
- src = s = b; // start new line on current word
- l = dst; // note new line
- }
- }
- // no overflow, possibly break on hyphen
- else if (*s++ == '-') wordcopy (&b,&dst,&src,s);
- break;
- }
- wordcopy (&b,&dst,&src,s); // copy word if one still remains
- if (dst[-1] != '\012') { // force ending with a newline
- *dst++ = '\015';
- *dst++ = '\012';
- }
- *dst = '\0'; // tie off destination
- }
-
- // Copy word - helper routine for linebreak ()
- // Accepts: pointer to start of word
- // pointer to destination pointer
- // pointer to start of copy region (may include leading whitespace)
- // end of copy region
-
- void wordcopy (unsigned char **wrd,unsigned char **dst,unsigned char **src,
- unsigned char *end)
- {
- int i;
- if (*wrd) { // only do this if saw a word
- memcpy (*dst,*src,(i = end - *src));
- *wrd = NIL; // no longer a pending word
- *src += i; // update source
- *dst += i; // update destination
- }
- }
-
- // Append message to local mailbox
- // Accepts: file to append to
- // address block of sender
- // elt holding message date
- // message header text
- // message text
-
- void append_msg (FILE *file,ADDRESS *s,MESSAGECACHE *elt,char *hdr,
- unsigned char *text)
- {
- char tmp[MAILTMPLEN];
- unsigned char c;
- struct tm d;
- unsigned char *m = (unsigned char *) fs_get ((hdr ? strlen (hdr) : 0) + 2 +
- (text ? strlen ((char *) text) :
- 0));
- unsigned char *t = m;
- // copy header and text
- if (hdr) while (c = *hdr++) if (c != '\r') *t++ = c;
- if (text) while (c = *text++) if (c != '\r') *t++ = c;
- *t = '\0'; // tie off string
- switch (format) {
- case MBOX: // Berkeley header
- if (s) sprintf (tmp,"%s@%s",s->mailbox,s->host ? s->host : localhost);
- else strcpy (tmp,"somebody");
- d.tm_sec = elt->seconds; d.tm_min = elt->minutes; d.tm_hour = elt->hours;
- d.tm_mday = elt->day; d.tm_mon = elt->month - 1;
- d.tm_year = elt->year + (BASEYEAR - 1900);
- d.tm_gmtoff = 0; // timezone not used
- mktime (&d); // get day of the week
- fprintf (file,"From %s %s",tmp,asctime (&d));
- t = m; // start of message
- while (c = *t++) { // for each character of message
- fputc (c,file); // copy character, insert broket if needed
- if (c == '\n' && t[0] == 'F' && t[1] == 'r' && t[2] == 'o' && t[3] == 'm'
- && t[4] ==' ') fputc ('>',file);
- }
- fputc ('\n',file); // must have a final newline
- break;
- case MTXT: // Tenex header
- fprintf (file,"%s,%ld;000000000000\n",mail_date (tmp,elt),t - m);
- fputs ((char *) m,file); // blat the text
- break;
- default: // something badly broken
- fatal ("Unknown mailbox format in append_msg!");
- break;
- }
- fs_give ((void **) &m); // flush the scratch text
- }
-
- // Output filtered header
- // Accepts: envelope
- // Returns: string of filtered header
-
- char *filtered_header (ENVELOPE *env)
- {
- char tmp[64*MAILTMPLEN];
- char *s = tmp;
- if (env->date) {
- sprintf (s,"Date: %s\n",env->date);
- s += strlen (s);
- }
- else *s = '\0'; // start with empty string
- write_address (&s,"From",env->from);
- if (env->subject) {
- sprintf (s,"Subject: %s\n",env->subject);
- s += strlen (s);
- }
- write_address (&s,"To",env->to);
- write_address (&s,"cc",env->cc);
- write_address (&s,"bcc",env->bcc);
- strcat (s,"\n"); // delimit with new line
- return cpystr (tmp);
- }
-
-
- // Write address list
- // Accepts: destination pointer
- // name of this header
- // address list
-
- void write_address (char **dest,char *tag,ADDRESS *adr)
- {
- char c;
- char *s = *dest;
- char *t = *dest;
- rfc822_address_line (dest,tag,NIL,adr);
- do if ((c = *s++) != '\r') *t++ = c;
- while (c); // filter out newlines
- *dest = --t;
- }
-