;;; -*- Mode:Lisp; Readtable:CL; Package:USER; Base:10; Patch-File:T -*-
;;; Patch file for System version 123.140
;;; Reason:
;;;  FTP and SMTP control connections are supposed to be Telnet streams.  They used to
;;;  simply ignore Telnet Escape sequences, but now respond appropriately to option
;;;  negotiations.
;;; Written 4-Dec-87 12:20:52 by pld at site Gigamos Cambridge
;;; while running on Jack Flanders from band 2
;;; with Experimental System 123.138, Experimental Local-File 73.3, Experimental FILE-Server 22.1, Experimental Unix-Interface 11.0, Experimental Tape 18.0, Experimental KERMIT 34.0, Experimental ZMail 71.0, Experimental Lambda-Diag 15.0, microcode 1754, SDU Boot Tape 3.12, SDU ROM 8.



; From modified file DJ: L.NETWORK.IP-TCP.SERVER; FTP.LISP#80 at 4-Dec-87 12:21:06
#10R FTP#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "FTP")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; SERVER; FTP  "

(defun ftp-readline (stream &optional (getc #'(lambda (stream)
						(send stream :tyi))))
  (catch 'eof-tag
    (with-output-to-string (s)
      (do ((c (funcall getc stream) (funcall getc stream)))
	  ((eq c #o12))
	(cond ((null c)
	       (throw 'eof-tag nil))
	      ((= c telnet-iac)			;Telnet escape code
	       (unless (setq c (funcall getc stream))
		 (throw 'eof-tag nil))
	       (case (cadr (assoc c telnet:*telsyms* :test #'eq))
		 (telnet:do			;DO -- send a WONT
		  (unless (setq c (funcall getc stream))
		    (throw 'eof-tag nil))
		  (telnet:send-iac stream 'telnet:wont c))
		 (telnet:will			;WILL -- send a DONT
		  (unless (setq c (funcall getc stream))
		    (throw 'eof-tag nil))
		  (telnet:send-iac stream 'telnet:dont c))
		 ((telnet:dont telnet:wont)	;DONT or WONT -- ignore option
		  (unless (setq c (funcall getc stream))
		    (throw 'eof-tag nil)))))
	      ((= c #o15))			;Ignore CR -- wait for LF
	      (t
	       (send s :tyo c)))))))


))

; From modified file DJ: L.NETWORK.IP-TCP.SERVER; FTP.LISP#80 at 4-Dec-87 12:21:08
#10R FTP#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "FTP")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; SERVER; FTP  "


(defun ftp-cmdparse (state)
  (let ((cmdline (ftp-readline (ftpstate-ctrl-stream state)
			       #'(lambda (stream)
				   (loop
				     (when (process-wait-with-timeout "FTP command"
								      (and *ftp-server-timeout* (* 60. *ftp-server-timeout*))
								      #'(lambda (s)
									  (send s :listen))
								      stream)
				       ;;:listen returned non-NIL -- return what :tyi returns
				       (return (send stream :tyi)))
				     (unless (ftpstate-data-transfer-in-progress state)
				       ;;:listen returned NIL -- timeout.  If control connection idle, return NIL
				       (return nil))))))
	cmd arg)
    (cond ((not cmdline) (ftp-reply state 221 "You could at least say goodbye.") nil)
	  (t
	   (push cmdline (ftpstate-cmd-reply-history state))
	   (setq cmd (ftp-cmd-from-string cmdline))
	   (if (and (ftpstate-data-transfer-in-progress state) (not (eq cmd 'abor)))
	       (process-wait "wait for data transfer" 
			     #'(lambda nil (not (ftpstate-data-transfer-in-progress state)))))
	   (setf (ftpstate-current-cmd state) cmd)
	   (setq arg (ftp-first-cmd-arg cmdline (member cmd ftp-allow-spaces-in-arg-cmdlist :test #'eq)))

	   (cond ((and (ftpstate-next-cmd state)
		       (not (eq cmd 'rein))
		       (not (eq cmd 'quit))
		       (not (eq cmd (ftpstate-next-cmd state))))
		  (ftp-reply state 503 "Bad sequence of commands.")
		  (setf (ftpstate-next-cmd state) nil))

		 ((stringp cmd) (ftp-reply state 500 "~A command unrecognized" cmd))

		 ((member cmd ftp-unimplemented-cmdlist :test #'eq) (ftp-not-implemented state cmd))

		 ((and (not (ftpstate-logged-in-p state)) (member cmd ftp-logged-in-cmdlist :test #'eq))
		  (ftp-reply state 530 "Not logged in."))

		 ((and (or (not arg) (zerop (length arg)))
		       (member cmd ftp-require-arg-cmdlist :test #'eq))
		  (ftp-reply state 501 "Syntax error: missing argument."))

		 (t (funcall (get cmd 'ftp-server-handle) state arg cmdline)))
	   (not (eq cmd 'quit))))))

))

; From modified file DJ: L.NETWORK.IP-TCP.SERVER; SMTP.LISP#22 at 4-Dec-87 12:21:18
#10R TCP-APPLICATION#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "TCP-APPLICATION")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; SERVER; SMTP  "


(defun smtp-server-function (stream)
  (smtp-reply stream 220 "~A Simple Mail Transfer Service Ready"
	      (send si:local-host :name))
  (catch 'smtp-server-function
    (do ((*smtp-server-state* nil)
	 (si:user-id "SMTP_SERVER")
	 (string (ftp:ftp-readline stream)
		 (ftp:ftp-readline stream)))
	((null string))
      (smtp-server-command string stream))))

))

; From modified file DJ: L.NETWORK.IP-TCP.SERVER; TERMCAP.LISP#38 at 3-Dec-87 11:36:48
#10R TELNET#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "TELNET")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; SERVER; TERMCAP  "

(defun send-iac (stream &rest commands)
  (send stream :tyo (get 'iac 'telnet-sym))
  (dolist (char commands)
    (send stream :tyo (cond ((not (symbolp char)) char)
			    ((string-equal char "TELOPT_" :end1 7)
			     (get char 'telnet-opt))
			    ('else
			     (get char 'telnet-sym)))))
  (send stream :force-output))

))
