;;; -*- Mode:LISP; Package:MAIL; Base:10; Readtable:T; Lowercase:T -*- ;;; ;;; Chaosnet Interface to the Mailer ;;; ;;; (c) 1985 Lisp Machine Incorporated ;;; ;;; The Store and Forward server (defun mail-server-chaos (&aux final-ok qfile) (let ((user-id "Mail-Server")) (ignoring-errors (as-mail-server (stream (chaos:open-stream nil "MAIL")) (send stream :add-as-server "MAIL") (ignoring-errors ; catch network lossage (with-text-buffer (text stream) (let ((recipients (expand-addresses (get-mail-recipients stream)))) (with-output-to-string (s text) (write-line (reception-line stream) s)) (get-mail-text stream text) (finish-output stream) (let ((result (write-queue-file (setq qfile (qfile-name)) recipients `(:failures 0 :source-network-type :chaos :source-protocol :chaos-mail :source-host ,(send stream :foreign-host)) TEXT STREAM))) (cond ((errorp result) (setq final-ok ()) (if (probef qfile) (deletef qfile))) (t (setq final-ok :queued-ok)))))) (if final-ok (format stream "+Message sent successfully.~%")) (force-output stream))) (when (and (eq final-ok :queued-ok) (not *delay-delivery*)) (mail-deliver-qfile qfile))))) (defun get-mail-recipients (stream) (do ((line) (address-error-p) (result) (recipients nil)) (nil) (setq line (funcall stream :line-in)) (and (equal line "") (return (nreverse recipients))) (multiple-value (address-error-p result) (mailer-parse-address line)) (cond (address-error-p (format stream "-Bad address [~A]: ~A~%" line result)) ((and (stringp result) (not (address-existent-p result))) (FORMAT STREAM "-Unknown user ~A.~%" result)) (t (FORMAT STREAM "+Recipient address ~A ok.~%" result) (PUSH result RECIPIENTS))) (force-output stream))) (add-initialization "MAIL" '(process-run-function "MAIL Server" 'mail-server-chaos) nil 'chaos:server-alist) ;;; The delivery method (defun chaos-delivery-single-host (host addresses text report-stream &aux s failed) (format report-stream "~& Attempting delivery to ~A (~D addresses):~%" host (length addresses)) (condition-case (error) (unwind-protect (progn (setq s (chaos:open-stream host "MAIL")) (dolist (a addresses) (write-line (foreign-mailer-address a) s) (force-output s) (if (char= #\+ (read-char s)) (format report-stream "~&OK: ~A~%" (read-line s)) (format report-stream "~&Failure for ~S: ~A" a (read-line s)) (push a failed))) (terpri s) (write-string text s) ;; >> No Common Lisp way of doing this. (send s :eof) (let ((first (read-char s))) (if (char= #\+ first) (format report-stream "~&Message queued~%") (format report-stream "~&~:[Permanent~;Temporary~] failure: ~A~%" (char= first #\-) (read-line s)) (setq failed addresses)))) (when s (lisp:close s :abort t))) (error (format report-stream "Error while delivering: ~A" error) addresses) ; all addresses considered failed (:no-error failed))) (defun chaos-delivery-method (addresses text report-stream &aux failed) (format report-stream "Attempting chaosnet delivery (~D addresses):" (length addresses)) (do ((addrs addresses (cdr addrs)) address host (to-deliver nil nil)) ((null addrs) failed) (setq address (car addrs) host (foreign-class address)) (cond ((null (setq host (si:parse-host host t))) (format report-stream "~&Unknown host ~A" (foreign-class address)) (push address failed)) (t (setq to-deliver (ncons address)) (dolist (a (cdr addrs)) (when (eq (si:parse-host (foreign-class a) t) host) (push a to-deliver) (setq addrs (delq a addrs)))) (setq failed (nconc failed (chaos-delivery-single-host host to-deliver text report-stream))))))) (define-delivery-method chaos-mail (address) (and (eq (address-type address) :foreign) (stringp (foreign-class address))) (addresses text report-stream) (chaos-delivery-method addresses text report-stream)) ;;; Other non-standard features ;;; Update the mailing list remotely (defun update-mailing-list-file (&optional (host (main-lm-mail-server-host))) (if (eq host si:local-host) (read-mailing-list-file) (with-open-stream (c (chaos:open-stream host "UPDATE-MAILING-LIST" :direction :input)) (if (char= #\+ (read-char c)) (format t "~&Everything OK.~%") (format t "~&Error: ") (stream-copy-until-eof c standard-output))))) (defun mailing-list-update-server-chaos () (catch-error (as-mail-server (s (chaos:open-stream () "UPDATE-MAILING-LIST" :direction :output)) (condition-case (error) (read-mailing-list-file) (error (write-char #/- s) (send error :report s)) (:no-error (format s "+~A~%" error))) (force-output s)) ())) (add-initialization "UPDATE-MAILING-LIST" '(process-run-function "Mailing List Updater" 'mailing-list-update-server-chaos) nil 'chaos:server-alist) ;;; Standard CHAOSnet protocol EXPAND-MAILING-LIST (defun expand-mailing-list-server () (catch-error (as-mail-server (s (chaos:open-stream () "EXPAND-MAILING-LIST")) (loop (multiple-value-bind (line eofp) (read-line s) (let ((initial (address-existent-p line))) (flet ((address-out (user host) (write-string user s) (write-string "@" s) (write-string (or host (send si:local-host :name)) s))) (typecase initial (null (write-string "-No such address" s)) (string (write-line "+Local address" s) (address-out initial nil)) (list (write-line "+Mailing list" s) (dolist (address (expand-addresses initial)) (write-line (address-as-string address) s))) (error (write-string "-Error in parsing address" s)) (t (write-string "-Internal error" s)))) (terpri s) (force-output s) (if eofp (return nil)))))) nil)) (add-initialization "EXPAND-MAILING-LIST" '(process-run-function "Expand Mailing List Server" 'expand-mailing-list-server) nil 'chaos:server-alist)