;;; -*- Mode:LISP; Package:KERMIT; Base:8; Readtable:ZL -*-



;;Copyright LISP Machine, Inc. 1984, 1985, 1986
;;   See filename "Copyright" for
;;licensing and release information.
;;



;;; A KERMIT server is a KERMIT program running remotely with no "user
;;; interface". All commands to the server arrive in packets from the
;;; local KERMIT....

;;; Between transactions, a KERMIT server waits for packets containing
;;; server commands. The packet sequence number is always set back to 0
;;; after a transaction. A KERMIT server in command wait should be
;;; looking for packet 0. Certain server commands will result in the
;;; exchange of multiple packets. Those operations proceed exactly like
;;; file transfer. 

;;; Server operation must be implemented in two places: in the server
;;; itself, and in any KERMIT program that will be communicating with a
;;; server. The server must have code to read the server commands from
;;; packets and respond to them. the user KERMIT must have code to parse
;;; commands to send requests to servers, to form the server command
;;; packets, and to handle the responses to those server commands....

;;; Server commands are as follows:
;;; S  Send Initiate (exchange parameters, server waits for a file).
;;; R  Receive Initiate (ask the server to send the specified files).
;;; I  Initialize (exchange parameters)....
;;; G  Generic KERMIT Command.  Single character in data field (possibly
;;;    followed by operands, shown in {braces}, optional fields in
;;;    [brackets]) specifies the command:
;;;
;;;    ...
;;;    L  Logout, Bye
;;;    F  Finish (Shut down the server, but don't logout).
;;;    ...

;;; Between transactions, when the server has no tasks pending, it may
;;; send out periodic NAKs (always with type 1 checksums) to prevent a
;;; deadlock in case a command was sent to it but was lost.  These NAKs
;;; can pile up in the local "user" KERMIT's unput buffer (if it has
;;; one), so the user KERMIT should be prepared to clear its input
;;; buffer before sending a command to a server.



(declare (special kstate)			;in calls.lisp
	 )

(defconst *timint-for-server-wait* 45 "Amount of time to wait before timeout when in server mode")


(defun kermit-remote-server (tty &optional working-directory)
  (send kstate ':remote-server tty working-directory))


(defun receive-file-header (packet num &aux ourfilename)
  num
  (multiple-value-bind (ignore num ignore data) (rpack)
    data
    (cond ((not (= num *packet-number*))
	   #\A)
	  (t (setq ourfilename (string-for-kermit-outfile packet))
	     (cond ((setq *fp* (open-file-out-or-not ourfilename))
		    (format interaction-pane "~&Receiving ~A as ~A"
			    packet
			    ourfilename)
		    (or *remote* (update-status-label ourfilename nil))
		    (spack #\Y *packet-number* 0 nil)
		    (setq *oldtry* *numtry*)
		    (setq *numtry* 0)
		    (bump-packet-number)
		    #\D)
		   (t (format interaction-pane "~&Cannot create ~S" packet)
						;experimental error packet sending--mhd
		      (spack #\E *packet-number* 45	;
			     "Kermit-Q: Error in file header.")
		      #\A))))))






(DEFUN SERVER-COMMAND-WAIT ()

  (CONDITION-CASE ()				;; in case of a sys:abort condition
						;; just return nil; thus they just
						;; abort out of kermit server, not
						;; the login server too.


						;; PS-terminal doesn't die then!!

  (LOOP INITIALLY (AND *DEBUG* (FORMAT T "~&Entering Kermit Server Command Wait...~%"))
	WITH *TIMINT* = *TIMINT-FOR-SERVER-WAIT*
	WITH *REMOTE* = T
	WITH *STATE* = #\W			;my own name: WAIT
	FOR *BYTECOUNT* = NIL
	FOR *NUMTRY* = 0 AND *PACKET-NUMBER* = 0 AND *OLDTRY* = 0

	DOING
	(FLUSHINPUT)
	(MULTIPLE-VALUE-BIND (TYPE NUM LEN DATA) (RPACK) LEN
	  (SELECT TYPE
	    (#\S (COND ((EQ NUM 0)		;you do the job of Rinit and Rfile
			(RPAR DATA)		;here, then jump into Recsw at Rdata
			(SETQ DATA (SPAR DATA))
			(SPACK #\Y *PACKET-NUMBER* 6 DATA)
			(SETQ *OLDTRY* *NUMTRY*)
			(SETQ *NUMTRY* 0)
			(BUMP-PACKET-NUMBER)
			(RECEIVE-FILE-HEADER DATA NUM)
			(SETQ DATA-XFER-START-TIME (TIME) *BYTECOUNT* 0)
			(RECSW #\D *PACKET-NUMBER* *NUMTRY*))))
	    (#\R (COND ((NOT (= *PACKET-NUMBER* NUM)))
		       (T
			  (COND ((SETQ *FILELIST* (KERMIT-FILELIST DATA)
				       *FILNAM* (CAR *FILELIST*))
				 (IF *DEBUG* (FORMAT INTERACTION-PANE
						     "Files to send:~A" *FILELIST*))
				 (BUMP-PACKET-NUMBER)
				 (SENDSW #\S *PACKET-NUMBER*))
				(T (SPACK #\E *PACKET-NUMBER*
					  25 "Error: File Not Found"))))))
	    (#\G (COND ((EQ LEN 1)
			(COND ((EQ (AREF DATA 0) #\L)	;generic logout
			       (SPACK #\Y *PACKET-NUMBER* 0 NIL)
			       (AND *DEBUG* (FORMAT T "...logout on ~A"
						    (time:print-current-date nil)))
			       (RETURN ':LOGOUT))
			      ((EQ (AREF DATA 0) #\F)	;generic finish
			       (SPACK #\Y *PACKET-NUMBER* 0 NIL)
			       (AND *DEBUG* (FORMAT T "...finishing on ~A"
						    (time:print-current-date nil)))
			       (RETURN NIL))))))

	    (*FALSE* (SPACK #\A *PACKET-NUMBER* 0 NIL))

	    (OTHERWISE
	     (SPACK #\E *PACKET-NUMBER* 60
		    "unimplemented server command                               "))))

  )
    (SYS:ABORT NIL)))
