From 3ef7a79b5274241e4fa129b04f21220554179b93 Mon Sep 17 00:00:00 2001 From: Larry Masinter Date: Thu, 26 Aug 2021 21:40:09 -0700 Subject: [PATCH] Restore unixmail (#385) * Restore unixmail from archive * Restore unixmail from archive --- library/UNIXMAIL | 235 +++++++++++++++++++++++++++++++++++++++++ library/UNIXMAIL.DFASL | Bin 0 -> 23363 bytes library/UNIXMAIL.TEDIT | Bin 0 -> 9624 bytes 3 files changed, 235 insertions(+) create mode 100644 library/UNIXMAIL create mode 100644 library/UNIXMAIL.DFASL create mode 100644 library/UNIXMAIL.TEDIT diff --git a/library/UNIXMAIL b/library/UNIXMAIL new file mode 100644 index 00000000..7e98a652 --- /dev/null +++ b/library/UNIXMAIL @@ -0,0 +1,235 @@ +(DEFINE-FILE-INFO READTABLE "XCL" PACKAGE "INTERLISP") (FILECREATED "10-Feb-2000 12:03:28" |{DSK}medley3.5>library>unixmail.;42| 82019 |changes| |to:| (FNS UNIXSPOOL.OPENMAILBOX) |previous| |date:| " 6-Jul-99 15:22:12" |{DSK}medley3.5>library>unixmail.;41|) ; Copyright (c) 1989, 1990, 1991, 1992, 1997, 1999, 1920 by ENVOS Corporation. All rights reserved. (PRETTYCOMPRINT UNIXMAILCOMS) (RPAQQ UNIXMAILCOMS ((DECLARE\: DOEVAL@COMPILE DONTCOPY (FILES (SOURCE) LAFITEDECLS NSMAIL) (RECORDS UNIXMAILBOX UNIXMAILFILEINFO UNIXMAILPARSE)) (ALISTS (LAFITEMODELST UNIX)) (* |;;| "JDS 4/6/97: CHANGE TRANSMIT SMTP INTERACTION TO put <> around mail-from name, which SMTP seems to require.") (* |;;| "These variables control how mail is sent and received. UNIXMAIL.SEND.MODE controls whether the SMTP stream is opened via process-stream (PROCESS) or TCP socket (TCP). UNIXMAIL.RECEIVE.MODE controls whether mail is received through the Berkeley mailer (MAILER) or by reading the spool file directly (SPOOL). PROCESS and MAILER can only be done under an emulator; TCP and SPOOL will also work on D-machines (but may need other library packages like TCP or NFS).") (INITVARS (UNIXMAIL.SEND.MODE 'PROCESS) (UNIXMAIL.RECEIVE.MODE 'SPOOL)) (* |;;| "List used by \\UNIXMAIL.AUTHENTICATE to construct the MAILSERVEROPS list") (VARS UNIXMAIL.MSOPS.LIST) (* |;;| "These variables control filenames, hostnames, etc. They default to NIL, meaning they are not used or the mailer will try and figure them out itself") (INITVARS UNIXMAIL.SEND.HOST UNIXMAIL.DOMAIN.NAME (UNIXMAIL.SEND.PROCESS '( "/usr/bin/mconnect" "/usr/etc/mconnect" )) (UNIXMAIL.RECEIVE.PROCESS "/usr/ucb/mail -N") UNIXMAIL.SPOOL.FILE (UNIXMAIL.DONT.RECEIVE.STATUS "") (UNIXMAIL.WRAP.LINES T) (UNIXMAIL.WRAP-LIMIT 72) (UNIXMAIL.TABWIDTH 8)) (* |;;| "Functions used to receive mail") (FNS UNIX.POLLNEWMAIL UNIX.NEXTMESSAGE UNIXMAILER.OPENMAILBOX UNIXMAILER.RETRIEVEMESSAGE UNIXMAILER.CLOSEMAILBOX UNIXSPOOL.OPENMAILBOX UNIXSPOOL.RETRIEVEMESSAGE UNIXSPOOL.CLOSEMAILBOX) (* |;;| "Functions used to send mail") (FNS UNIX.FLUSH.STREAM UNIX.RETRIEVE.LINE \\UNIXMAIL.SEND \\UNIXMAIL.SEND.WRAPLINES \\SMTP-DUMP \\UNIXMAIL.SEND.PARSE \\UNIXMAIL.CHECK.ABORT \\UNIXMAIL.MUNG.RECIPIENTS \\UNIXMAIL.SMTP \\UNIXMAIL.SMTP.FLUSH \\UNIXMAIL.CHANGE.MODE) (* |;;| "This returns multiple-values, so it's a CL:LAMBDA (what the heck).") (FUNCTIONS \\UNIXMAIL.SMTP.TCP.STREAMS) (* |;;| "Other functions Lafite uses and needs Unix equivalents for") (FNS \\UNIXMAIL.AUTHENTICATE \\UNIXMAIL.LOGIN \\UNIXMAIL.PARSENAMES \\UNIXMAIL.MAKEANSWERFORM \\UNIXMAIL.MESSAGE.FROM.SELF.P \\UNIXMAIL.MESSAGE.P \\UNIXMAIL.REALADDRESS \\UNIXMAIL.FQNAME \\UNIXMAIL.FIXMICROSOFT) (* |;;| "This is a stub needed by the TEdit-uuencode strategy; if we ever decide on a reasonable way to do this and make it part of Lafite, this may go away") (P (MOVD? 'NILL 'UNIX.UUDECODE.IF.NEEDED)) (* |;;| "Hack to install easy interface for line wrapping. I should really put line-wrapping into Lafite as a whole.") (P (CL:WHEN (CAR (NLSETQ (EDITE LAFITESENDINGMENUITEMS '(F \\SENDMSG.CHANGE.MODE)))) (* |;;| "The CONS below insures that Lafite notices you've changed LAFITESENDINGMENUITEMS, so the menu will get updated.") (SETQ LAFITESENDINGMENUITEMS (EDITE (CONS (CAR LAFITESENDINGMENUITEMS) (CDR LAFITESENDINGMENUITEMS)) '(CHANGE \\SENDMSG.CHANGE.MODE TO \\UNIXMAIL.CHANGE.MODE))))) (PROP FILETYPE UNIXMAIL))) (DECLARE\: DOEVAL@COMPILE DONTCOPY (FILESLOAD (SOURCE) LAFITEDECLS NSMAIL) (DECLARE\: EVAL@COMPILE (RECORD UNIXMAILBOX (UMSTREAM UMNUMBERS UMNEXT UMLOCKSTREAM)) (RECORD UNIXMAILFILEINFO (UMFNAME UMFTIME)) (RECORD UNIXMAILPARSE (UNIXMAILSUBJECT UNIXFROM UNIXTO UNIXOTHER FORMATTED? UNIXBODY)) ) ) (ADDTOVAR LAFITEMODELST (UNIX 3 \\UNIXMAIL.SEND.PARSE \\UNIXMAIL.SEND \\UNIXMAIL.MAKEANSWERFORM \\UNIXMAIL.AUTHENTICATE \\UNIXMAIL.MESSAGE.P \\UNIXMAIL.MESSAGE.FROM.SELF.P \\UNIXMAIL.LOGIN)) (* |;;| "JDS 4/6/97: CHANGE TRANSMIT SMTP INTERACTION TO put <> around mail-from name, which SMTP seems to require." ) (* |;;| "These variables control how mail is sent and received. UNIXMAIL.SEND.MODE controls whether the SMTP stream is opened via process-stream (PROCESS) or TCP socket (TCP). UNIXMAIL.RECEIVE.MODE controls whether mail is received through the Berkeley mailer (MAILER) or by reading the spool file directly (SPOOL). PROCESS and MAILER can only be done under an emulator; TCP and SPOOL will also work on D-machines (but may need other library packages like TCP or NFS)." ) (RPAQ? UNIXMAIL.SEND.MODE 'PROCESS) (RPAQ? UNIXMAIL.RECEIVE.MODE 'SPOOL) (* |;;| "List used by \\UNIXMAIL.AUTHENTICATE to construct the MAILSERVEROPS list") (RPAQQ UNIXMAIL.MSOPS.LIST ((MAILER UNIX.POLLNEWMAIL UNIXMAILER.OPENMAILBOX UNIX.NEXTMESSAGE UNIXMAILER.RETRIEVEMESSAGE UNIXMAILER.CLOSEMAILBOX) (SPOOL UNIX.POLLNEWMAIL UNIXSPOOL.OPENMAILBOX UNIX.NEXTMESSAGE UNIXSPOOL.RETRIEVEMESSAGE UNIXSPOOL.CLOSEMAILBOX))) (* |;;| "These variables control filenames, hostnames, etc. They default to NIL, meaning they are not used or the mailer will try and figure them out itself" ) (RPAQ? UNIXMAIL.SEND.HOST NIL) (RPAQ? UNIXMAIL.DOMAIN.NAME NIL) (RPAQ? UNIXMAIL.SEND.PROCESS '("/usr/bin/mconnect" "/usr/etc/mconnect")) (RPAQ? UNIXMAIL.RECEIVE.PROCESS "/usr/ucb/mail -N") (RPAQ? UNIXMAIL.SPOOL.FILE NIL) (RPAQ? UNIXMAIL.DONT.RECEIVE.STATUS "") (RPAQ? UNIXMAIL.WRAP.LINES T) (RPAQ? UNIXMAIL.WRAP-LIMIT 72) (RPAQ? UNIXMAIL.TABWIDTH 8) (* |;;| "Functions used to receive mail") (DEFINEQ (UNIX.POLLNEWMAIL (LAMBDA (ADDRESS REGISTEREDNAME CREDENTIALS MAILSERVER) (* \; "Edited 19-May-99 10:34 by rmk:") (* \; "Edited 24-Oct-90 00:04 by jrb:") (* |;;| "We have mail iff our mail spool file (either the value of UNIXMAIL.SPOOL.FILE or /usr/spool/mail/) exists and its date is later than the last time we got our Unix mail. In relentlessly hackish use of the existing MAILSERVER structure, MAILPORT holds a UNIXMAILFILEINFO which remembers the name of our mail file and when we last looked at it.") (LET (X (FILEINFO (OR (|fetch| (MAILSERVER MAILPORT) |of| MAILSERVER) (|replace| (MAILSERVER MAILPORT) |of| MAILSERVER |with| (|create| UNIXMAILFILEINFO UMFNAME _ (OR UNIXMAIL.SPOOL.FILE (CL:CONCATENATE 'STRING "{UNIX}/usr/spool/mail/" (|fetch| (LAFITEMODEDATA SHORTUSERNAME) |of| *LAFITE-MODE-DATA*))) UMFTIME _ 0))))) (AND (CL:PROBE-FILE (|fetch| (UNIXMAILFILEINFO UMFNAME) |of| FILEINFO)) (SETQ X (GETFILEINFO (|fetch| (UNIXMAILFILEINFO UMFNAME) |of| FILEINFO) 'LENGTH)) (IGREATERP X 0) (SETQ X (GETFILEINFO (|fetch| (UNIXMAILFILEINFO UMFNAME) |of| FILEINFO) 'IWRITEDATE)) (IGREATERP X (|fetch| (UNIXMAILFILEINFO UMFTIME) |of| FILEINFO)))))) (UNIX.NEXTMESSAGE (LAMBDA (MAILBOX) (* \; "Edited 5-Jan-89 18:18 by bane") (CAR (|fetch| UMNEXT |of| MAILBOX)))) (UNIXMAILER.OPENMAILBOX (LAMBDA (ADDRESS REGISTEREDNAME CREDENTIALS MAILSERVER)(* \; "Edited 15-Oct-90 20:31 by jrb:") (* |;;| "A Unix \"mailbox\" is a process-stream talking to /usr/ucb/mail ") (|if| (OR (|fetch| (MAILSERVER NEWMAILP) |of| MAILSERVER) (UNIX.POLLNEWMAIL ADDRESS REGISTEREDNAME CREDENTIALS MAILSERVER)) |then| (LET* ((MSTREAM (CREATE-PROCESS-STREAM UNIXMAIL.RECEIVE.PROCESS)) (UMBOX (|create| UNIXMAILBOX UMSTREAM _ MSTREAM UMNUMBERS _ NIL UMNEXT _ NIL)) NUMBERS) (* |;;| "Get it in condition to be talked to") (CL:FORMAT MSTREAM "set screen=10000~%set prompt= ~%") (BLOCK 1000) (UNIX.FLUSH.STREAM MSTREAM) (* |;;| "OK, get it to print the headers followed by a line with a strange character on it (char code 254)") (CL:FORMAT MSTREAM "h~%echo ") (PRINTCCODE 254 MSTREAM) (PRINTCCODE (CHARCODE NEWLINE) MSTREAM) (* |;;|  "Before we really get rolling, scream if UNIXMAIL.DONT.RECEIVE.STATUS isn't a string") (OR (STRINGP UNIXMAIL.DONT.RECEIVE.STATUS) (ERROR "UNIXMAIL.DONT.RECEIVE.STATUS isn't a string" UNIXMAIL.DONT.RECEIVE.STATUS)) (* |;;| "Headers look like this:") (* |;;| " other junk like size, subject...") (|until| (EQ (READCCODE MSTREAM) 254) |do| (|if| (NULL (STRPOS (CL:READ-CHAR MSTREAM ) UNIXMAIL.DONT.RECEIVE.STATUS )) |then| (|push| NUMBERS (READ MSTREAM))) (UNIX.FLUSH.STREAM MSTREAM (CHARCODE NEWLINE )) |finally| (UNIX.FLUSH.STREAM MSTREAM (CHARCODE NEWLINE))) (|if| NUMBERS |then| (SETQ NUMBERS (DREVERSE NUMBERS)) (|replace| UMNUMBERS |of| UMBOX |with| NUMBERS) (|replace| UMNEXT |of| UMBOX |with| NUMBERS) (|create| OPENEDMAILBOX MAILBOX _ UMBOX PROPERTIES _ (LIST '\#OFMESSAGES (LENGTH NUMBERS))) |else| (CL:FORMAT MSTREAM "x~%") (* \; "Empty; close the mail process") (* \;  "and remember that we've checked on the mailbox.") (* \;  "HACK! Depends on \\LAFITE.RETRIEVEMESSAGES binding of MAILSERVER") (|replace| (UNIXMAILFILEINFO UMFTIME) |of| (|fetch| (MAILSERVER MAILPORT) |of| MAILSERVER) |with| (IDATE)) 'EMPTY)) |else| 'EMPTY))) (UNIXMAILER.RETRIEVEMESSAGE (LAMBDA (MAILBOX MSGOUTFILE) (* \; "Edited 5-Sep-90 22:58 by jrb:") (LET ((MSTREAM (|fetch| UMSTREAM |of| MAILBOX)) (OUTSTART (GETFILEPTR MSGOUTFILE))) (* |;;| "The UMCOUNT in the MAILBOX is the number of the message we're about to read in. The echo command below makes the message text be followed by a line starting with the character 254; you'll never see that in a message that has gone over an SMTP channel (guaranteed 7-bit chars).") (CL:FORMAT MSTREAM "p ~d~%echo " (|pop| (|fetch| UMNEXT |of| MAILBOX))) (PRINTCCODE 254 MSTREAM) (PRINTCCODE (CHARCODE NEWLINE) MSTREAM) (UNIX.FLUSH.STREAM MSTREAM (CHARCODE NEWLINE)) (* \; "Throw away \"Message 1:\" line") (|while| (UNIX.RETRIEVE.LINE MSTREAM MSGOUTFILE) |do| NIL) (* |;;|  "This could use a little error-handling, of course... perhaps a LOT of error handling.") (* |;;| "Check out the tail of the message and uudecode it if it's an encoded message") (UNIX.UUDECODE.IF.NEEDED MSGOUTFILE OUTSTART)))) (UNIXMAILER.CLOSEMAILBOX (LAMBDA (MAILBOX FLUSH?) (* \; "Edited 5-Sep-90 23:01 by jrb:") (LET ((MSTREAM (|fetch| UMSTREAM |of| MAILBOX))) (* |;;| "If FLUSH?, first clean out the mailbox") (|if| FLUSH? |then| (CL:FORMAT MSTREAM "d~{ ~D~}~%" (|fetch| UMNUMBERS |of| MAILBOX))) (* |;;| "Then close it up") (CL:FORMAT MSTREAM "q~%") (CLOSEF MSTREAM) (* |;;|  "Twiddle a second to let the mailer run, then remember what time we closed the mailbox") (BLOCK 1000) (* |;;| "HACK! Depends on \\LAFITE.RETRIEVEMESSAGES binding of MAILSERVER") (|replace| (UNIXMAILFILEINFO UMFTIME) |of| (|fetch| (MAILSERVER MAILPORT) |of| MAILSERVER) |with| (IDATE ))))) (UNIXSPOOL.OPENMAILBOX (LAMBDA (ADDRESS REGISTEREDNAME CREDENTIALS MAILSERVER) (* \;  "Edited 10-Feb-2000 12:03 by rmk:") (* \;  "Edited 10-Feb-2000 12:02 by rmk:") (* \; "Edited 11-Mar-99 16:09 by rmk:") (* \; "Edited 11-Mar-99 16:08 by rmk:") (IF (OR (FETCH (MAILSERVER NEWMAILP) OF MAILSERVER) (UNIX.POLLNEWMAIL ADDRESS REGISTEREDNAME CREDENTIALS MAILSERVER)) THEN (LET (MSTREAM UMBOX NUMBERS LOCKSTREAM) (BIND WRITEDATE (LOCKFILE _ (PACK* "{UNIX}" UNIXMAIL.SPOOL.FILE ".lock")) (TRYNUM _ 0) UNTIL (NLSETQ (SETQ LOCKSTREAM (OPEN LOCKFILE :DIRECTION :OUTPUT :IF-EXISTS :ERROR))) DO (IF (NULL (SETQ WRITEDATE (CAR (NLSETQ (GETFILEINFO LOCKFILE 'IWRITEDATE))))) THEN (* |;;|  "Error on writedate means file doesn't exist, go around immediately to acquire the lock.") (SETQ TRYNUM 0) ELSEIF (IGREATERP (- (IDATE) WRITEDATE) 300) THEN (* \; "Delete and try again") (SETQ TRYNUM 0) (DELFILE LOCKFILE) ELSE (ADD TRYNUM 1) (* \;  "File still exists and was recently modified; wait and try again") (CL:WHEN (EQ TRYNUM 4) (LAB.PROMPTPRINT MAILFOLDER "Unix mailbox file is locked, can't open") (ERROR!)) (DISMISS (TIMES TRYNUM 5000)))) (PRINTOUT LOCKSTREAM "0") (CLOSEF LOCKSTREAM) (* \;  "Close the lock file but use the closed stream later for the DELFILE.") (* |;;|  "Note: The THROUGH must come before the EOL, otherwise the file reverts back to CR.") (SETQ MSTREAM (OPENSTREAM (FETCH (UNIXMAILFILEINFO UMFNAME) OF (FETCH (MAILSERVER MAILPORT) OF MAILSERVER)) 'INPUT NIL '((TYPE TEXT) (EXTERNALFORMAT :THROUGH) (EOL LF)))) (SETQ UMBOX (CREATE UNIXMAILBOX UMSTREAM _ MSTREAM UMNUMBERS _ NIL UMNEXT _ NIL UMLOCKSTREAM _ LOCKSTREAM)) (* |;;| "Merrily scan the spool file remembering where all the messages start; there had better be at least one. All messages in Unix spool files start with the character sequence \"(OR beginning-of-file Newline)From \"; this sequence is guaranteed to occur nowhere else but at the start of messages.") (IF (ZEROP (FILEPOS "From " MSTREAM)) THEN (PUSH NUMBERS 0) (* |;;| "The (CONCAT...) stuff below is to avoid having a string with a LF character in it; the file package/reader/printer have been known to EOL-translate such characters in strings inappropriately.") (BIND POS WHILE (SETQ POS (FILEPOS (CONSTANT (CONCAT (CHARACTER (CHARCODE LF)) "From ")) MSTREAM)) DO (PUSH NUMBERS (ADD1 POS)) (READCCODE MSTREAM)) (SETQ NUMBERS (DREVERSE NUMBERS)) (REPLACE UMNUMBERS OF UMBOX WITH NUMBERS) (REPLACE UMNEXT OF UMBOX WITH NUMBERS) (SETFILEPTR MSTREAM 0) (CREATE OPENEDMAILBOX MAILBOX _ UMBOX PROPERTIES _ (LIST '\#OFMESSAGES (LENGTH NUMBERS))) ELSE (CL:UNLESS (ZEROP (GETEOFPTR MSTREAM)) (LAB.PROMPTPRINT MAILFOLDER "Mail spool file is not in Unix format: " (FETCH (UNIXMAILFILEINFO UMFNAME) OF (FETCH (MAILSERVER MAILPORT) OF MAILSERVER )) ": ")) (CLOSEF MSTREAM) 'EMPTY)) ELSE 'EMPTY))) (UNIXSPOOL.RETRIEVEMESSAGE (LAMBDA (MAILBOX MSGOUTFILE) (* \; "Edited 10-Mar-99 08:59 by rmk:") (* \; "Edited 10-Mar-99 08:57 by rmk:") (* \; "Edited 10-Mar-99 08:55 by rmk:") (* \; "Edited 10-Mar-99 08:54 by rmk:") (* \; "Edited 10-Mar-99 08:44 by rmk:") (* \; "Edited 26-Feb-99 11:25 by rmk:") (LET ((MSTREAM (|fetch| UMSTREAM |of| MAILBOX)) (OUTSTART (GETFILEPTR MSGOUTFILE))) (* |;;| "The numbers in the UMNEXT of the mailbox are file positions in the spool file of the start of each message, so to get a message, just COPYCHARS from the start of the current message to the start of the next one.") (* |;;| "") (* |;;| "NOTE, however, that a message in a Unix mailbox begins with a \"From \" line which should not be copied.") (LET ((MSTART (|pop| (|fetch| UMNEXT |of| MAILBOX))) (MEND (OR (CAR (|fetch| UMNEXT |of| MAILBOX)) (GETEOFPTR MSTREAM)))) (* |;;| "Confirm and skip the From line.") (CL:UNLESS (EQ (CHARCODE F) (READCCODE MSTREAM)) (ERROR "Not a valid Unix mail-spool file" (FULLNAME MSTREAM))) (UNTIL (MEMB (BIN MSTREAM) (CHARCODE (LF CR)))) (COPYCHARS MSTREAM MSGOUTFILE (GETFILEPTR MSTREAM) MEND)) (* |;;|  "This could use a little error-handling, of course... perhaps a LOT of error handling.") (* |;;| "Check out the tail of the message and uudecode it if it's an encoded message") (UNIX.UUDECODE.IF.NEEDED MSGOUTFILE OUTSTART)))) (UNIXSPOOL.CLOSEMAILBOX (LAMBDA (MAILBOX FLUSH?) (* \; "Edited 10-Mar-99 08:45 by rmk:") (* \; "Edited 15-Oct-90 20:46 by jrb:") (LET ((MSTREAM (|fetch| UMSTREAM |of| MAILBOX))) (* |;;| "If FLUSH?, nuke the spool file") (|if| FLUSH? |then| (SETFILEINFO (|fetch| (UNIXMAILFILEINFO UMFNAME) |of| (|fetch| (MAILSERVER MAILPORT) |of| MAILSERVER) ) 'LENGTH 0) (* |;;| "HACK! Depends on \\LAFITE.RETRIEVEMESSAGES binding of MAILSERVER") (|replace| (UNIXMAILFILEINFO UMFTIME) |of| (|fetch| (MAILSERVER MAILPORT) |of| MAILSERVER) |with| (IDATE))) (* |;;| " In any event, close the mailbox stream") (CLOSEF MSTREAM) (DELFILE (FULLNAME (FETCH UMLOCKSTREAM OF MAILBOX)))))) ) (* |;;| "Functions used to send mail") (DEFINEQ (UNIX.FLUSH.STREAM (LAMBDA (STREAM CHAR) (* \; "Edited 13-Sep-90 15:58 by jrb:") (* |;;| "Just vacuum out the stream until you see CHAR (if it's NIL, read until EOF)") (* |;;| "If CHAR is supplied, stream must not be at EOF") (|if| CHAR |then| (|until| (OR (NOT (READP STREAM)) (EQ (READCCODE STREAM) CHAR)) |do| NIL) |else| (|until| (NOT (READP STREAM)) |do| (READCCODE STREAM))) STREAM)) (UNIX.RETRIEVE.LINE (LAMBDA (MSTREAM MSGOUTFILE) (* \; "Edited 18-Sep-89 14:39 by jrb:") (* |;;| "Copies a line of text from MSTREAM to MSGOUTFILE except if that line starts with a strange character (charcode 254; see UNIX.RETRIEVEMESSAGE). Returns NIL on seeing such a line") (BLOCK) (* \;  "This looks like a good place...") (LET ((CHAR (READCCODE MSTREAM))) (|if| (EQ CHAR 254) |then| (SETQ CHAR NIL)) (* |;;| "When we get here, if CHAR is non-NIL, it needs to be printed to MSGOUTFILE") (|if| CHAR |then| (PRINTCCODE CHAR MSGOUTFILE) (|until| (EQ CHAR (CHARCODE NEWLINE)) |do| (PRINTCCODE (SETQ CHAR (READCCODE MSTREAM )) MSGOUTFILE)) T)))) (\\UNIXMAIL.SEND + (LAMBDA (MSG PARSE EDITORWINDOW ABORTWINDOW) (* \; "Edited 4-Jul-99 21:26 by rmk:") + (* \; "Edited 2-Apr-99 15:44 by rmk:") + (* \; + "Edited 6-Apr-97 17:27 by sybalsky:mv:envos") + + (* |;;| "The strategy here is to talk to an SMTP server and throw the message at it.") + + (|if| (AND (TEDIT.FORMATTEDFILEP MSG) + (EQ (|fetch| (UNIXMAILPARSE FORMATTED?) |of| PARSE) + 'TEDIT)) + |then| (\\LAFITE.SEND.FAIL EDITORWINDOW + "UNIX mode can't send raw TEdit; try TEdit-UUencoded") + |else| + (\\UNIXMAIL.CHECK.ABORT ABORTWINDOW) + (CL:MULTIPLE-VALUE-BIND + (SMIN SMOUT) + (\\UNIXMAIL.SMTP.TCP.STREAMS) + (LET + ((PWINDOW (AND EDITORWINDOW (GETPROMPTWINDOW EDITORWINDOW))) + (RECIPIENTS (CL:REMOVE-DUPLICATES (FOR R IN (|fetch| (UNIXMAILPARSE UNIXTO) + |of| PARSE) + COLLECT (\\UNIXMAIL.REALADDRESS R)) + :TEST + (FUNCTION STRING-EQUAL))) + RESULT) + (|if| (NULL SMIN) + |then| (\\LAFITE.SEND.FAIL EDITORWINDOW (CL:FORMAT NIL + "Couldn't open SMTP stream because ~s" + SMOUT))) + (CL:FLET + ((GIVE-UP-AND-DIE (&REST WHY) + + (* |;;| "Close up the SMTP streams, send up a flare for the user and return NIL") + + (IGNORE-ERRORS (CLOSEF SMIN) + (CLOSEF SMOUT)) + (\\LAFITE.SEND.FAIL EDITORWINDOW (CL:APPLY #'CL:FORMAT NIL WHY)) + NIL)) + + (* |;;| "Get the connection ready to talk to") + + (|for| I |from| 1 |to| 5 |while| (SETQ RESULT (\\UNIXMAIL.SMTP.FLUSH + SMIN)) |do| NIL) + (|if| RESULT + |then| (GIVE-UP-AND-DIE "Couldn't open SMTP stream because ~a" RESULT) + |else| + (|if| PWINDOW + |then| (CLEARW PWINDOW) + (CL:FORMAT PWINDOW "Delivering ~:[~;formatted ~]to ~D recipient~:P" + (|fetch| (UNIXMAILPARSE FORMATTED?) |of| PARSE) + (LENGTH RECIPIENTS))) + (|if| (SETQ RESULT (CL:CATCH 'SMTP-LOST + + (* |;;| "First, announce who we are") + + (\\UNIXMAIL.SMTP SMIN SMOUT (CL:FORMAT NIL "HELO ~a~%" + (UNIX-GETPARM "HOSTNAME" + ))) + + (* |;;| "Then send who it's from") + + (\\UNIXMAIL.SMTP SMIN SMOUT + (CL:FORMAT NIL "MAIL FROM: <~a>~%" + (\\UNIXMAIL.FQNAME (|fetch| ( + LAFITEMODEDATA + + SHORTUSERNAME + ) + |of| + *LAFITE-MODE-DATA* + )))) + + (* |;;| "Then the recipients") + + (|for| R |in| RECIPIENTS + |do| (\\UNIXMAIL.SMTP SMIN SMOUT + (CL:FORMAT NIL "RCPT TO: <~a>~%" R))) + + (* |;;| "Print a '.' to show we're this far") + + (AND PWINDOW (|printout| PWINDOW '\.)) + + (* |;;| + "Then the message itself; the SMTP response here is a 300-range number meaning \"Lay it on me\"") + + (\\UNIXMAIL.SMTP SMIN SMOUT "DATA~%" 300 399) + + (* |;;| "First hose out the other fields") + + (CL:FORMAT SMOUT "From: ~a~%" (\\UNIXMAIL.FQNAME + (|fetch| (LAFITEMODEDATA + SHORTUSERNAME) + |of| *LAFITE-MODE-DATA*)) + ) + (|for| F |in| (|fetch| (UNIXMAILPARSE UNIXOTHER) + |of| PARSE) + |do| (CL:FORMAT SMOUT "~a: ~a~%" (CAR F) + (CADR F)) + |finally| (|if| (|fetch| (UNIXMAILPARSE + FORMATTED?) + |of| PARSE) + |then| (CL:FORMAT SMOUT "Format: ~a~%" + (|fetch| + (UNIXMAILPARSE + FORMATTED?) + |of| PARSE))) + (TERPRI SMOUT)) + + (* |;;| "Print a '.' to show we're this far") + + (AND PWINDOW (|printout| PWINDOW '\.)) + (|if| (TEDIT.FORMATTEDFILEP MSG) + |then| (SELECTQ (|fetch| (UNIXMAILPARSE FORMATTED?) + |of| PARSE) + (TEDIT (GIVE-UP-AND-DIE + "UNIX mode can't send raw TEdit; try TEdit-UUencoded" + )) + (TEDIT-UUENCODE + (* |;;| + "Nuke the header in a copy of the msg") + + (SETQ MSG (COPYTEXTSTREAM MSG)) + (TEDIT.DELETE MSG 1 (|fetch| + (UNIXMAILPARSE + UNIXBODY) + |of| PARSE)) + (UNIX.TEDIT-UUENCODE.MESSAGE MSG SMOUT)) + (NIL + (* |;;| "Strip TEdit formatting") + + (\\SMTP-DUMP + (LAFITE.MAKE.PLAIN.TEXTSTREAM + MSG + (|fetch| (UNIXMAILPARSE UNIXBODY) + |of| PARSE)) + SMOUT)) + (GIVE-UP-AND-DIE + "Send failed due to strange Format: ~A" + (|fetch| (UNIXMAILPARSE + FORMATTED?) + |of| PARSE))) + |else| (\\SMTP-DUMP MSG SMOUT)) + (|if| (OPENP SMOUT) + |then| (\\UNIXMAIL.CHECK.ABORT ABORTWINDOW SMIN + SMOUT) + (\\UNIXMAIL.SMTP SMIN SMOUT "~%.~%") + + (* |;;| "Print a '.' to show we're this far") + + (AND PWINDOW (|printout| PWINDOW '\.)) + (\\UNIXMAIL.SMTP SMIN SMOUT "QUIT~%") + NIL + |else| (BLOCK 1000) + 554))) + |then| (GIVE-UP-AND-DIE "SMTP error is ~a" RESULT) + |else| (\\UNIXMAIL.CHECK.ABORT ABORTWINDOW SMIN SMOUT) + (IGNORE-ERRORS (CLOSEF SMIN) + (CLOSEF SMOUT)) + (LENGTH RECIPIENTS))))))))) (\\UNIXMAIL.SEND.WRAPLINES + (LAMBDA (INSTREAM OUTSTREAM) (* \; "Edited 4-Jul-99 21:14 by rmk:") + (* \; "Edited 4-Jul-99 21:11 by rmk:") + (LET ((BUF (CL:MAKE-ARRAY (IPLUS UNIXMAIL.WRAP-LIMIT 8) + :ELEMENT-TYPE + 'CL:CHARACTER)) + (BPTR 0) + (BLENGTH 0) + (LASTWS NIL) + C) + (CL:FLET ((ADDCHAR (C) + (CL:SETF (CL:CHAR BUF BPTR) + C) + (CL:INCF BPTR) + (CL:INCF BLENGTH)) + (DUMP-BUFFER NIL (CL:DOTIMES (I BPTR) + (CL:WRITE-CHAR (CL:CHAR BUF I) + OUTSTREAM)) + (TERPRI OUTSTREAM) + (SETQ BPTR 0 BLENGTH 0))) + (|while| (SETQ C (CL:READ-CHAR INSTREAM NIL NIL)) + |do| (CASE C + (#\Newline + (DUMP-BUFFER) + (SETQ LASTWS NIL)) + (#\. + (|if| (EQ 0 BPTR) + |then| (ADDCHAR C)) + (ADDCHAR C)) + (#\Space + (SETQ LASTWS BPTR) + (ADDCHAR C)) + (#\Tab + (CL:INCF BLENGTH (CL:1- UNIXMAIL.TABWIDTH)) + (SETQ BLENGTH (ITIMES UNIXMAIL.TABWIDTH (CL:FLOOR BLENGTH + UNIXMAIL.TABWIDTH)) + ) + (ADDCHAR C)) + (CL:OTHERWISE (ADDCHAR C))) + (|if| (IGREATERP BLENGTH UNIXMAIL.WRAP-LIMIT) + |then| (|if| LASTWS + |then| (SETQ C (IDIFFERENCE (IDIFFERENCE BPTR LASTWS) + 1)) + (SETQ BPTR LASTWS) + (DUMP-BUFFER) + (|if| (EQ (CL:CHAR BUF (CL:INCF LASTWS)) + #\.) + |then| (CL:SETF (CL:CHAR BUF (CL:DECF + LASTWS)) + #\.) + (CL:INCF C)) + (|for| I |from| 0 + |to| (IDIFFERENCE C 1) |as| BPTR + |from| LASTWS + |do| (CL:SETF (CL:CHAR BUF I) + (CL:CHAR BUF BPTR))) + (SETQ BPTR C BLENGTH C LASTWS NIL) + |else| (DUMP-BUFFER) + (SETQ LASTWS NIL))) |finally| (DUMP-BUFFER)))))) (\\SMTP-DUMP + (LAMBDA (INSTREAM OUTSTREAM) (* \; "Edited 4-Jul-99 21:20 by rmk:") + (* \; "Edited 4-Jul-99 21:17 by rmk:") + + (* |;;| "In both wrapped and unwrapped cases, we have to treat specially lines beginning with '.': the '.' must be doubled when talking to the SMTP server.") + + (|if| UNIXMAIL.WRAP.LINES + |then| (\\UNIXMAIL.SEND.WRAPLINES INSTREAM OUTSTREAM) + |else| (LET ((STARTC (GETFILEPTR INSTREAM)) + ENDC) + (WHILE (SETQ ENDC (FILEPOS (CONSTANT (CONCAT (CHARACTER (CHARCODE NEWLINE)) + ".")) + INSTREAM STARTC)) + DO (COPYCHARS INSTREAM OUTSTREAM STARTC (ADD1 ENDC)) + (PRIN1 ".." OUTSTREAM) + (SETQ STARTC (IPLUS ENDC 2)) FINALLY (COPYCHARS INSTREAM OUTSTREAM + STARTC (GETEOFPTR + INSTREAM))))))) (\\UNIXMAIL.SEND.PARSE (LAMBDA (MSG EDITORWINDOW) (* \; "Edited 30-Jun-99 23:54 by rmk:") (* \; "Edited 28-Feb-99 19:27 by rmk:") (* \; "Edited 26-Feb-99 23:59 by rmk:") (* \; "Edited 20-Sep-91 16:59 by jrb:") (* |;;| "Do some obvious checks here and build a \\UNIXMAILPARSE for UNIXMAIL.SEND to munch on") (PROG ((MSGFIELDS (\\LAFITE.PREPARE.SEND MSG EDITORWINDOW)) SENDINGFORMAT HEADEREOF FROMFIELD RECIPIENTS OTHERSTUFF SUBJECT) (|if| MSGFIELDS |then| (|if| (EQ (CAAR MSGFIELDS) 'EOF) |then| (SETQ HEADEREOF (CADR (|pop| MSGFIELDS)))) (|for| PAIR |in| MSGFIELDS |do| (SELECTQ (CAR PAIR) (|Date| (\\SENDMESSAGEFAIL EDITORWINDOW "User-supplied Date not allowed")) (|Sender| (\\SENDMESSAGEFAIL EDITORWINDOW "User-supplied Sender not allowed")) ((TO T\o |cc|) (SETQ RECIPIENTS (NCONC RECIPIENTS (\\UNIXMAIL.PARSENAMES (CDR PAIR)))) (|push| OTHERSTUFF PAIR)) (|From| (SETQ FROMFIELD (\\UNIXMAIL.PARSENAMES (CDR PAIR))) (|push| OTHERSTUFF PAIR)) (|Format| (SETQ SENDINGFORMAT (CADR PAIR))) (|Subject| (SETQ SUBJECT (CADR PAIR)) (|push| OTHERSTUFF PAIR)) (|push| OTHERSTUFF PAIR))) (|if| (NULL RECIPIENTS) |then| (\\SENDMESSAGEFAIL EDITORWINDOW "No recipients!") (RETURN NIL)) (|if| (NULL SENDINGFORMAT) |then| (SETQ SENDINGFORMAT (OR (\\LAFITE.CHOOSE.MSG.FORMAT MSG HEADEREOF EDITORWINDOW) (RETURN)))) (RETURN (|create| UNIXMAILPARSE UNIXMAILSUBJECT _ SUBJECT UNIXFROM _ FROMFIELD UNIXTO _ RECIPIENTS UNIXOTHER _ OTHERSTUFF UNIXBODY _ HEADEREOF FORMATTED? _ (SELECTQ SENDINGFORMAT ((NIL TEXT) NIL) SENDINGFORMAT))))))) (\\UNIXMAIL.CHECK.ABORT (LAMBDA (ABORTWINDOW SMIN SMOUT) (* \; "Edited 2-Apr-99 14:37 by rmk:") (* \; "Edited 2-Apr-99 14:36 by rmk:") (* \; "Edited 2-Apr-99 13:52 by rmk:") (* \; "Edited 2-Apr-99 12:31 by rmk:") (* \;  "Perhaps the Abort button was pushed?") (BLOCK) (CL:WHEN (AND ABORTWINDOW (WINDOWPROP ABORTWINDOW 'ABORT)) (CL:WHEN SMIN (IGNORE-ERRORS (CLOSEF SMIN) (CLOSEF SMOUT))) (ERROR!)))) (\\UNIXMAIL.MUNG.RECIPIENTS (LAMBDA (RECIPIENTS) (* \; "Edited 11-Mar-99 16:38 by rmk:") (* |;;| "Coerces addresses from XNS formats to Internet formats, needed for answering messages that arrived via XNS.") (FOR R COLPOS IN RECIPIENTS COLLECT (IF (NOT (SETQ COLPOS (STRPOS ":" R))) THEN (* |;;|  "Either a vanilla name or an Internet address, mailer will handle it") (IF (STRPOS ".XEROX" R -6 NIL T NIL UPPERCASEARRAY) THEN (* \;  "domain was stripped out of unix address") (SETQ R (CONCAT R ".com"))) ELSEIF (STRPOS "@" R) THEN (* |;;| "A converted internet address, fix up last domain. foo@fum.fie:Xerox, foo@fum.fie:SMTP:Xerox, foo@fum:fie all go to foo@fum.fie") (IF (STRPOS ":XEROX" R -6 NIL T NIL UPPERCASEARRAY) THEN (* |;;| "Strip Xerox") (SETQ R (SUBSTRING R 1 -7)) (IF (IGREATERP COLPOS (NCHARS R)) ELSEIF (STRPOS ":SMTP" R -5 T NIL UPPERCASEARRAY) THEN (* |;;| "Another colon, followed by SMTP") (SETQ R (SUBSTRING R 1 -6)) ELSE (* |;;| "Another colon not followed by SMTP") (RPLCHARCODE R COLPOS (CHARCODE \.))) ELSE (* |;;| "No Xerox, last segment is domain") (RPLCHARCODE R COLPOS (CHARCODE \.))) ELSE (* |;;|  "A pure NS address: foo:bar:bas -> foo.bar@baz.xerox.com, foo:bar:xerox -> foo.bar@xerox.com") (LET ((FOO (SUBSTRING R 1 (SUB1 COLPOS))) (BAR (SUBSTRING R (ADD1 COLPOS) (SUB1 (OR (SETQ COLPOS (STRPOS ":" R (ADD1 COLPOS))) 0)))) (BAZ (AND COLPOS (SUBSTRING R (ADD1 COLPOS))))) (* |;;| "Spaces in FOO and BAR go to underscore") (FOR I C FROM 1 WHILE (SETQ C (NTHCHARCODE FOO I)) WHEN (EQ C (CHARCODE SPACE)) DO (RPLCHARCODE FOO I (CHARCODE _))) (CL:WHEN BAR (FOR I C FROM 1 WHILE (SETQ C (NTHCHARCODE BAR I)) WHEN (EQ C (CHARCODE SPACE)) DO (RPLCHARCODE BAR I (CHARCODE _)))) (* |;;| "Spaces in BAZ disappear") (CL:WHEN (AND BAZ (STRPOS " " BAZ)) (FOR I C (NEWI _ 1) (NEWBAZ _ (ALLOCSTRING (NCHARS BAZ))) FROM 1 WHILE (SETQ C (NTHCHARCODE BAZ I)) UNLESS (EQ C (CHARCODE SPACE)) DO (RPLCHARCODE NEWBAZ NEWI C) (ADD NEWI 1) FINALLY (SETQ BAZ (SUBSTRING NEWBAZ 1 (SUB1 NEWI) (CONSTANT (CONCAT)) )))) (IF (STREQUAL (L-CASE BAZ) "xerox") THEN (SETQ BAZ NIL)) (SETQ R (IF BAZ THEN (CONCAT FOO "." BAR "@" BAZ ".xerox.com") ELSE (CONCAT FOO "." BAR "@xerox.com"))))) R))) (\\UNIXMAIL.SMTP (LAMBDA (SIN SOUT STRING LO HI) (* \;  "Edited 6-Apr-97 17:30 by sybalsky:mv:envos") (* |;;| "Very dumb protocol handler; shows STRING to STREAM and reads the response number, returning T or NIL depending on the range of the result. Currently we only accept 200-range answers (completions)") (LET (RESULT) (CL:FORMAT SOUT STRING) (FORCEOUTPUT SOUT) (|if| (SETQ RESULT (\\UNIXMAIL.SMTP.FLUSH SIN LO HI)) |then| (CL:THROW 'SMTP-LOST RESULT))))) (\\UNIXMAIL.SMTP.FLUSH (LAMBDA (STREAM LO HI) (* \; "Edited 6-Jul-99 15:16 by rmk:") (* \; "Edited 4-Jul-99 12:55 by rmk:") (* \;  "Edited 8-Mar-99 13:08 by N.H.Briggs") (* |;;|  "STREAM is about to throw an SMTP exchange at us; get the number and shake hands appropriately") (LET (RESULT ERRORTYPE CHAR) (* |;;| "Flush any continuation result codes") (|while| (EQUAL "-" (SUBSTRING (FOR RPT FROM 1 TO 10 DO (* |;;| "Try again 10 times on end-of-file error, hoping that the error comes from a timing glitch. Both RESULT and the value of the loop will be NIL for any other kind of error and for the 10th end-of-file, which will be reported as the error type.") (CL:MULTIPLE-VALUE-SETQ (RESULT ERRORTYPE) (IGNORE-ERRORS (RATOM STREAM))) (CL:WHEN RESULT (RETURN RESULT)) REPEATWHILE (EQ 'END-OF-FILE (TYPENAME ERRORTYPE)) ) 4 4)) DO (UNIX.FLUSH.STREAM STREAM (CHARCODE NEWLINE) )) (|if| (NOT (SMALLP RESULT)) |then| (UNIX.FLUSH.STREAM STREAM (CHARCODE NEWLINE)) (CL:FORMAT NIL "Not an SMTP number:~S" (OR ERRORTYPE RESULT)) |else| (|if| (OR (IGREATERP RESULT (OR HI 299)) (ILESSP RESULT (OR LO 200))) |then| (SETQ RESULT (CL:FORMAT NIL "~a: " RESULT)) (|until| (OR (NOT (READP STREAM)) (EQ (SETQ CHAR (READCCODE STREAM)) (CHARCODE NEWLINE))) |do| (SETQ RESULT (CONCAT RESULT (CHARACTER CHAR)))) RESULT |else| (UNIX.FLUSH.STREAM STREAM (CHARCODE NEWLINE)) NIL))))) (\\UNIXMAIL.CHANGE.MODE (LAMBDA (WINDOW TEXTSTREAM MENU ITEM) (SELECTQ (MENU (|create| MENU TITLE _ "Mail mode" ITEMS _ `(("Change Mode" '\\SENDMSG.CHANGE.MODE "Change the mail protocol used to send this message") (,(|if| UNIXMAIL.WRAP.LINES |then| "Line wrap Off" |else| "Line wrap On") 'UNIXMAIL.WRAP.LINES ,(|if| UNIXMAIL.WRAP.LINES |then| "Send message as is" |else| "Insert newlines to make message lines shorter than UNIXMAIL.WRAP-LIMIT" ))))) (\\SENDMSG.CHANGE.MODE (\\SENDMSG.CHANGE.MODE WINDOW TEXTSTREAM MENU ITEM)) (UNIXMAIL.WRAP.LINES (SETQ UNIXMAIL.WRAP.LINES (NOT UNIXMAIL.WRAP.LINES)) (\\SENDMESSAGE.PROMPT WINDOW (|if| UNIXMAIL.WRAP.LINES |then| "Line wrapping is on" |else| "Line wrapping is off"))) NIL) (* |;;| "Exit with error so that the window is restored to previous state") (ERROR!))) ) (* |;;| "This returns multiple-values, so it's a CL:LAMBDA (what the heck).") (CL:DEFUN \\UNIXMAIL.SMTP.TCP.STREAMS () (* \; "Edited 27-Feb-99 13:55 by rmk:") (* |;;| "Opens two streams representing the input and output streams of an SMTP TCP connection. On failure return NIL and a string describing the failure.") (SELECTQ UNIXMAIL.SEND.MODE (PROCESS (|if| (EQ (MACHINETYPE) 'MAIKO) |then| (* |;;| "UNIXMAIL.SEND.PROCESS can be a list of possibilities because the process may be in different places in different operating systems (e.g. solaris vs. sunos). If the first one doesn't exist at this time, we search the remaining ones and move the first one we find to the beginning of the list for next time. This could be done as an AFTERSYSOUTFORMS, but easy enough just to do it here.") (LET ((S (CREATE-PROCESS-STREAM (CONCAT (IF (NLISTP UNIXMAIL.SEND.PROCESS) THEN UNIXMAIL.SEND.PROCESS ELSEIF (INFILEP (PACKFILENAME 'HOST 'DSK 'BODY (CAR UNIXMAIL.SEND.PROCESS))) THEN (CAR UNIXMAIL.SEND.PROCESS) ELSE (FOR P IN (CDR UNIXMAIL.SEND.PROCESS) WHEN (INFILEP (PACKFILENAME 'HOST 'DSK 'BODY P)) DO (SETQ UNIXMAIL.SEND.PROCESS (CONS P (DREMOVE P UNIXMAIL.SEND.PROCESS) )) (RETURN P))) (IF UNIXMAIL.SEND.HOST THEN (CONCAT " " UNIXMAIL.SEND.HOST) ELSE ""))))) (CL:VALUES S S)) |else| (CL:VALUES NIL "this MACHINETYPE can't do Unix process-streams; change UNIXMAIL.SEND.MODE" ))) (SOCKET (|if| (EQ (MACHINETYPE) 'MAIKO) |then| (LET ((S (OPENTCPSTREAM (OR UNIXMAIL.SEND.HOST (UNIX-GETPARM "HOSTNAME" )) 25))) (CL:VALUES S S)) |else| (LET ((S (TCP.OPEN UNIXMAIL.SEND.HOST 25 NIL 'ACTIVE 'INPUT T))) (|if| S |then| (CL:VALUES S (TCP.OTHER.STREAM S)) |else| (CL:VALUES NIL "TCP.OPEN failed; check your Lisp TCP configuration" ))))) (ERROR "Unrecognized UNIXMAIL.SEND.MODE:" UNIXMAIL.SEND.MODE))) (* |;;| "Other functions Lafite uses and needs Unix equivalents for") (DEFINEQ (\\UNIXMAIL.AUTHENTICATE (LAMBDA NIL (* \; "Edited 2-Apr-99 15:33 by rmk:") (* \; "Edited 2-Apr-99 15:25 by rmk:") (* \;  "Edited 10-Mar-99 18:00 by N.H.Briggs") (* |;;| "No authentication really necessary (we're depending on underlying Unix to keep us from doing illegal things), so just return an appropriate LAFITEMODEDATA") (* |;;| "Unfortunately, all the various versions of the username have to be the short one, because Lafite insists they all be acceptable mailing addresses, in particular FULLUSERNAME... *sigh*...") (LET ((FQNAME (\\UNIXMAIL.FQNAME (UNIX-USERNAME)))) (|create| LAFITEMODEDATA LAFITEOPS _ (FASSOC 'UNIX LAFITEMODELST) FULLUSERNAME _ (CONCAT (UNIX-FULLNAME) " <" FQNAME ">") UNPACKEDUSERNAME _ FQNAME SHORTUSERNAME _ (UNIX-USERNAME) MAILSERVERS _ (LIST (|create| MAILSERVER MAILSERVERNAME _ "Unix mail" MAILSERVEROPS _ (OR (CDR (FASSOC UNIXMAIL.RECEIVE.MODE UNIXMAIL.MSOPS.LIST)) (ERROR "Not a known Unix mail receive mode" UNIXMAIL.RECEIVE.MODE)))))))) (\\UNIXMAIL.LOGIN (LAMBDA NIL (* \; "Edited 4-Jan-89 13:29 by bane") (|if| (EQ (MACHINETYPE) 'MAIKO) |then| (\\LAFITE.GET.USER.DATA 'UNIX NIL T) (\\LAFITE.WAKE.WATCHER) |else| (PROMPTPRINT "No Unix mode; this isn't Maiko")))) (\\UNIXMAIL.PARSENAMES (LAMBDA (FIELD) (* \; "Edited 7-Mar-99 23:31 by rmk:") (* \; "Edited 7-Mar-99 23:28 by rmk:") (* \; "Edited 4-Jan-89 15:41 by bane") (* |;;|  "Just returns a list of the unquoted, unparenthesized comma-seperated fields in this string") (FOR F RESULT INSIDE FIELD DO (FOR I C SEGMENT (START _ 1) FROM 1 DO (SELCHARQ (SETQ C (NTHCHARCODE F I)) ((\, NIL) (SETQ SEGMENT (CL:STRING-TRIM " " (SUBSTRING F START (SUB1 I)))) (PUSH RESULT (\\UNIXMAIL.FIXMICROSOFT SEGMENT)) (CL:UNLESS C (RETURN)) (SETQ START (ADD1 I))) (\" (FOR OLD I FROM (ADD1 I) BY 1 WHILE (SETQ C (NTHCHARCODE F I)) WHEN (EQ C (CHARCODE \")) DO (RETURN I) FINALLY (* \; "ran off the end") (SETQ I (SUB1 I)))) (\( (FOR OLD I (PDEPTH _ 1) FROM (ADD1 I) BY 1 WHILE (SETQ C (NTHCHARCODE F I)) DO (SELCHARQ C (\( (ADD PDEPTH 1)) (\) (IF (EQ PDEPTH 1) THEN (RETURN I) ELSE (ADD PDEPTH -1))) NIL) FINALLY (* \; "ran off the end") (SETQ I (SUB1 I)))) NIL)) FINALLY (RETURN (\\UNIXMAIL.MUNG.RECIPIENTS (DREVERSE RESULT)))))) (\\UNIXMAIL.MAKEANSWERFORM (LAMBDA (MSGDESCRIPTORS MAILFOLDER) (* \; "Edited 11-Oct-91 09:13 by jrb:") (* |;;| " Code borrowed liberally from GV.MAKEANSWERFORM") (LET ((MSGFIELDS (\\LAFITE.PARSE.MESSAGE MAILFOLDER (OR (CAR (LISTP MSGDESCRIPTORS)) MSGDESCRIPTORS))) SUBJECT FROM DATE SENDER REPLYTO TO CC OLDFROM NEWTO NEWCC) (* \; "get the fields from the file") (|for| PAIR |in| MSGFIELDS |do| (SELECTQ (CAR PAIR) (|Subject| (SETQ SUBJECT (CADR PAIR))) (|Sender| (SETQ SENDER (CADR PAIR))) (|From| (SETQ FROM (CADR PAIR))) (|Date| (SETQ DATE (CADR PAIR))) (|Reply-to| (SETQ REPLYTO (CDR PAIR))) ((T\o TO) (SETQ TO (CDR PAIR))) (|cc| (SETQ CC (CDR PAIR))) NIL)) (* |;;| "first parse the strings into recipients. Need to find the sender's registry in order to get the registry defaults correct for its recipients.") (COND (SENDER (* \;  "Sender is a mail address, and has the official registry") (SETQ OLDFROM (\\UNIXMAIL.PARSENAMES SENDER)) (* \;  "Elements are of the form (prettyname gvname . registry)") (|if| FROM |then| (* \; "Now that we have a source of official registry (we hope), parse the From field with reference to it.") (SETQ OLDFROM (\\UNIXMAIL.PARSENAMES FROM)))) (FROM (* \;  "Have to parse the From field before we can get its registry") (SETQ OLDFROM (\\UNIXMAIL.PARSENAMES FROM)))) (|if| (NULL OLDFROM) |then| (LAB.PROMPTPRINT MAILFOLDER T "Warning: message has no FROM field")) (AND TO (SETQ TO (\\UNIXMAIL.PARSENAMES TO))) (AND CC (SETQ CC (\\UNIXMAIL.PARSENAMES CC))) (SETQ NEWTO (OR (AND REPLYTO (SETQ REPLYTO (\\UNIXMAIL.PARSENAMES REPLYTO))) OLDFROM)) (SETQ NEWCC (CL:SET-DIFFERENCE (COND (REPLYTO (* \;  "Reply goes only to this address, so the only cc is to self") (MKLIST (|fetch| (LAFITEMODEDATA SHORTUSERNAME) |of| *LAFITE-MODE-DATA*))) (T (* \; "By default CC everyone who received the original message and to whom we are not directly replying already") (APPEND TO (CL:SET-DIFFERENCE CC TO :TEST #'STRING-EQUAL)))) NEWTO :TEST #'STRING-EQUAL)) (LAFITE.FILL.IN.ANSWER.FORM SUBJECT (|if| (AND (OR (NULL REPLYTO) (EQUAL REPLYTO OLDFROM)) (NULL (CDR NEWCC)) (OR (NULL NEWCC) (STRING-EQUAL (CAR NEWCC) (|fetch| (LAFITEMODEDATA SHORTUSERNAME) |of| *LAFITE-MODE-DATA* )))) |then| (* \; "Replying only to sender (and maybe self), so just say \"your\" instead of \"Joe Bob Smith 's\"") NIL |else| FROM) DATE NEWTO NEWCC #'LA.PRINT.COMMA.LIST)))) (\\UNIXMAIL.MESSAGE.FROM.SELF.P (LAMBDA (MSG) (* \; "Edited 6-Jul-99 15:22 by rmk:") (* \; "Edited 2-Apr-99 15:27 by rmk:") (* \; "Edited 6-Dec-88 15:41 by bane") (* |;;| "For the moment, we send stuff with our SHORTUSERNAME only in the FROM field") (OR (STRING-EQUAL (\\UNIXMAIL.FQNAME (|fetch| (LAFITEMSG FROM) |of| MSG)) (\\UNIXMAIL.FQNAME (|fetch| (LAFITEMODEDATA SHORTUSERNAME) |of| *LAFITE-MODE-DATA* ))) (STRING-EQUAL (\\UNIXMAIL.REALADDRESS (|fetch| (LAFITEMSG FROM) |of| MSG)) (\\UNIXMAIL.FQNAME (|fetch| (LAFITEMODEDATA SHORTUSERNAME) |of| *LAFITE-MODE-DATA* )))))) (\\UNIXMAIL.MESSAGE.P (LAMBDA (MSG) (* \; "Edited 6-Dec-88 15:39 by bane") (* |;;| "We're guessing here; basically if it doesn't look like an NS message, say maybe.") (AND (NOT (STRPOS ":" (|fetch| (LAFITEMSG FROM) |of| MSG))) '?))) (\\UNIXMAIL.REALADDRESS (LAMBDA (R) (* \; "Edited 1-Jul-99 00:00 by rmk:") (* \; "Edited 30-Jun-99 23:57 by rmk:") (* \; "Edited 2-Apr-99 15:30 by rmk:") (* |;;| "Finds the true address inside R. We look for angle brackets outside of double-quotes. If that doesn't work, send the whole string with parenthetic material replaced by white space.") (FOR I C (STARTTEXT _ 1) SEGMENTS FROM 1 WHILE (SETQ C (NTHCHARCODE R I)) DO (SELCHARQ C (< (* |;;| "If we find a top-level angle bracket, we're done") (RETURN (\\UNIXMAIL.FQNAME (CL:STRING-TRIM " " (SUBSTRING R (ADD1 I) (SUB1 (OR (STRPOS ">" R (ADD1 I)) 0))))))) (\" (CL:UNLESS (EQ I STARTTEXT) (PUSH SEGMENTS (LIST 'TEXT STARTTEXT (SUB1 I)))) (PUSH SEGMENTS (LIST 'QUOTES I (FOR OLD I FROM (ADD1 I) BY 1 WHILE (SETQ C (NTHCHARCODE R I)) WHEN (EQ C (CHARCODE \")) DO (RETURN I) FINALLY (* \; "ran off the end") (SETQ I (SUB1 I))))) (SETQ STARTTEXT (ADD1 I))) (\( (CL:UNLESS (EQ I STARTTEXT) (PUSH SEGMENTS (LIST 'TEXT STARTTEXT (SUB1 I)))) (PUSH SEGMENTS (LIST 'PARENS I (FOR OLD I (PDEPTH _ 1) FROM (ADD1 I) BY 1 WHILE (SETQ C (NTHCHARCODE R I)) DO (SELCHARQ C (\( (ADD PDEPTH 1)) (\) (IF (EQ PDEPTH 1) THEN (RETURN I) ELSE (ADD PDEPTH -1))) NIL) FINALLY (* \; "ran off the end") (SETQ I (SUB1 I))))) (SETQ STARTTEXT (ADD1 I))) NIL) FINALLY (RETURN (\\UNIXMAIL.FQNAME (IF (NULL SEGMENTS) THEN R ELSE (CL:STRING-TRIM " " (CONCATLIST (FOR S IN SEGMENTS COLLECT (SELECTQ (CAR S) ((TEXT QUOTES) (SUBSTRING R (CADR S) (CADDR S))) (PARENS " ") R)))))))))) (\\UNIXMAIL.FQNAME (LAMBDA (NAME) (* \; "Edited 2-Apr-99 15:24 by rmk:") (* \; "Edited 2-Apr-99 15:23 by rmk:") (* \; "Edited 2-Apr-99 15:21 by rmk:") (* |;;| "Returns the fully qualified version of name") (IF (OR (STRPOS "@" NAME) (NULL UNIXMAIL.DOMAIN.NAME) (EQ 0 (NCHARS NAME))) THEN NAME ELSE (CONCAT NAME "@" UNIXMAIL.DOMAIN.NAME)))) (\\UNIXMAIL.FIXMICROSOFT (LAMBDA (STRING) (* \; "Edited 7-Mar-99 16:13 by rmk:") (* \; "Edited 7-Mar-99 16:10 by rmk:") (* \; "Edited 7-Mar-99 16:10 by rmk:") (* \; "Edited 7-Mar-99 16:09 by rmk:") (* \; "Edited 7-Mar-99 16:08 by rmk:") (* \; "Edited 7-Mar-99 16:05 by rmk:") (* \; "Edited 7-Mar-99 16:03 by rmk:") (* \; "Edited 7-Mar-99 16:02 by rmk:") (* \; "Edited 7-Mar-99 15:54 by rmk:") (* \; "Edited 7-Mar-99 15:53 by rmk:") (* \; "Edited 7-Mar-99 15:52 by rmk:") (* \; "Edited 7-Mar-99 15:18 by rmk:") (* \; "Edited 7-Mar-99 15:15 by rmk:") (SETQ STRING (CL:STRING-TRIM " " STRING)) (* |;;| "Try to simplify goofy addresses from Microsoft mailer. \"'foo '\" \"\" goes to foo . Foo is quoted if it contains commas") (DO (* \; "Strip leading blanks") (SELCHARQ (CHCON1 STRING) ((SPACE TAB) (GNC STRING)) (RETURN))) (DO (* \; "Strip trailing blanks") (SELCHARQ (NTHCHARCODE STRING -1) ((SPACE TAB) (GLC STRING)) (RETURN))) (LET ((HASQUOTE (EQ (CHARCODE \") (CHCON1 STRING))) SECONDQUOTE FQBRK SQBRK FBRK SBRK QUOTEDSTRING BRKSTRING) (IF (AND HASQUOTE (SETQ SECONDQUOTE (STRPOS "\"" STRING 2)) (SETQ FBRK (STRPOS "<" STRING (ADD1 SECONDQUOTE))) (SETQ SBRK (STRPOS ">" STRING (ADD1 FBRK)))) THEN (SETQ QUOTEDSTRING (CL:STRING-TRIM " " (SUBSTRING STRING 2 (SUB1 SECONDQUOTE) (CONSTANT (CONCAT))))) (SETQ BRKSTRING (CL:STRING-TRIM " " (SUBSTRING STRING (ADD1 FBRK) (SUB1 SBRK) (CONSTANT (CONCAT))))) (CL:UNLESS (STRPOS "@" BRKSTRING) (SETQ BRKSTRING (CONCAT BRKSTRING "@parc.xerox.com"))) (IF (AND (SETQ FQBRK (STRPOS "<" QUOTEDSTRING)) (SETQ SQBRK (STRPOS ">" QUOTEDSTRING (ADD1 FQBRK))) (STRING-EQUAL BRKSTRING (SUBSTRING QUOTEDSTRING (ADD1 FQBRK) (SUB1 SQBRK) (CONSTANT (CONCAT))))) THEN (* |;;| "The quoted string has a bracketed substring that matches the outer string, so that part of the quoted string is redundant.") (CL:WHEN (AND (EQ (CHARCODE \') (CHCON1 QUOTEDSTRING)) (EQ (CHARCODE \') (NTHCHARCODE QUOTEDSTRING -1))) (GNC QUOTEDSTRING) (GLC QUOTEDSTRING) (SETQ QUOTEDSTRING (CL:STRING-TRIM " " QUOTEDSTRING))) (* |;;| "Chop bracket stuff out of quoted string") (SETQ QUOTEDSTRING (CL:STRING-TRIM " " (CONCAT (OR (SUBSTRING QUOTEDSTRING 1 (SUB1 FQBRK) (CONSTANT (CONCAT))) "") (OR (SUBSTRING QUOTEDSTRING (ADD1 SQBRK) -1 (CONSTANT (CONCAT))) "")))) (IF (STRPOS "," QUOTEDSTRING) THEN (CONCAT "\"" QUOTEDSTRING "\" " (SUBSTRING STRING FBRK SBRK )) ELSE (CONCAT QUOTEDSTRING " " (SUBSTRING STRING FBRK SBRK))) ELSEIF (AND (EQ (CHARCODE \') (CHCON1 QUOTEDSTRING)) (EQ (CHARCODE \') (NTHCHARCODE QUOTEDSTRING -1)) (STRING-EQUAL BRKSTRING (SUBSTRING QUOTEDSTRING 2 -2 (CONSTANT (CONCAT))))) THEN (* |;;| "The quoted string is 'foo@parc.xerox.com' and the (possibly extended) bracketed string is the same. We can just return the original bracketed string") (SUBSTRING STRING (ADD1 FBRK) (SUB1 SBRK)) ELSE STRING) ELSE STRING)))) ) (* |;;| "This is a stub needed by the TEdit-uuencode strategy; if we ever decide on a reasonable way to do this and make it part of Lafite, this may go away" ) (MOVD? 'NILL 'UNIX.UUDECODE.IF.NEEDED) (* |;;| "Hack to install easy interface for line wrapping. I should really put line-wrapping into Lafite as a whole." ) (CL:WHEN (CAR (NLSETQ (EDITE LAFITESENDINGMENUITEMS '(F \\SENDMSG.CHANGE.MODE)))) (* |;;| "The CONS below insures that Lafite notices you've changed LAFITESENDINGMENUITEMS, so the menu will get updated.") (SETQ LAFITESENDINGMENUITEMS (EDITE (CONS (CAR LAFITESENDINGMENUITEMS) (CDR LAFITESENDINGMENUITEMS)) '(CHANGE \\SENDMSG.CHANGE.MODE TO \\UNIXMAIL.CHANGE.MODE) ))) (PUTPROPS UNIXMAIL FILETYPE :COMPILE-FILE) (PUTPROPS UNIXMAIL COPYRIGHT ("ENVOS Corporation" 1989 1990 1991 1992 1997 1999 1920)) (DECLARE\: DONTCOPY (FILEMAP (NIL (6942 25397 (UNIX.POLLNEWMAIL 6952 . 8898) (UNIX.NEXTMESSAGE 8900 . 9076) ( UNIXMAILER.OPENMAILBOX 9078 . 13474) (UNIXMAILER.RETRIEVEMESSAGE 13476 . 14683) ( UNIXMAILER.CLOSEMAILBOX 14685 . 15710) (UNIXSPOOL.OPENMAILBOX 15712 . 21987) ( UNIXSPOOL.RETRIEVEMESSAGE 21989 . 24050) (UNIXSPOOL.CLOSEMAILBOX 24052 . 25395)) (25445 55974 ( UNIX.FLUSH.STREAM 25455 . 26036) (UNIX.RETRIEVE.LINE 26038 . 27227) (\\UNIXMAIL.SEND 27229 . 37532) ( \\UNIXMAIL.SEND.WRAPLINES 37534 . 41160) (\\SMTP-DUMP 41162 . 42428) (\\UNIXMAIL.SEND.PARSE 42430 . 45662) (\\UNIXMAIL.CHECK.ABORT 45664 . 46480) (\\UNIXMAIL.MUNG.RECIPIENTS 46482 . 51350) ( \\UNIXMAIL.SMTP 51352 . 51957) (\\UNIXMAIL.SMTP.FLUSH 51959 . 54498) (\\UNIXMAIL.CHANGE.MODE 54500 . 55972)) (59451 81005 (\\UNIXMAIL.AUTHENTICATE 59461 . 61214) (\\UNIXMAIL.LOGIN 61216 . 61561) ( \\UNIXMAIL.PARSENAMES 61563 . 63873) (\\UNIXMAIL.MAKEANSWERFORM 63875 . 68757) ( \\UNIXMAIL.MESSAGE.FROM.SELF.P 68759 . 69880) (\\UNIXMAIL.MESSAGE.P 69882 . 70201) ( \\UNIXMAIL.REALADDRESS 70203 . 74239) (\\UNIXMAIL.FQNAME 74241 . 74838) (\\UNIXMAIL.FIXMICROSOFT 74840 . 81003))))) STOP \ No newline at end of file diff --git a/library/UNIXMAIL.DFASL b/library/UNIXMAIL.DFASL new file mode 100644 index 0000000000000000000000000000000000000000..f38dedbdbb43cc6dca1f9d69efdddc6a7662c4d7 GIT binary patch literal 23363 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 literal 0 HcmV?d00001 diff --git a/library/UNIXMAIL.TEDIT b/library/UNIXMAIL.TEDIT new file mode 100644 index 0000000000000000000000000000000000000000..709c98f168f8f482be837f55ff721c7051e38f89 GIT binary patch literal 9624 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 Z5vLqVc6g`c(ckccN_ynB_14wj{SR|_#0LNX literal 0 HcmV?d00001