// RemoteVMemInit.bcpl - handles pulling in remote sysout
// Last change May 20, 1984  4:26 PM by Bill van Melle
// Last change August 1, 1983  2:31 PM by Bill van Melle
// Last change January 21, 1983  11:14 AM by Bill van Melle
// Last change October 25, 1982  3:15 PM by Bill van Melle
// Last change June 8, 1982  10:41 PM by Bill van Melle
// Last change March 18, 1982  11:49 AM by Bill van Melle
// Last change December 29, 1981  11:50 AM by Bill van Melle
// Last change November 30, 1981  9:59 PM by Bill van Melle
// Last change September 6, 1981  3:34 PM by Bill van Melle

	get "Pup.decl"
	get "FtpProt.decl"
	get "AltoDefs.d"

external [	// procedure defined here
	RemoteInitVmem
		// from RemoteVmemInit1.bcpl
	Retrieve; Wss; FixPassword; SwapCursors; RestoreCursor
		// from LocalVmemInit.bcpl
	AppendString; SysinFailure

		// O.S. procedures
	CallSwat; Allocate; Free; InitializeZone; Zero; Puts; Ws
	Block; Dismiss
		// from Raid
	ReadStrng

		// misc procedures used
	GiveUp 
	Enqueue; InitializeContext; CallContextList; Noop
	
		// pup procs
	GetPartner; OpenLevel1Socket; InitPupLevel1; DestroyPupLevel1
	InitFtpUtil; InitFtpPList; OpenRTPSocket; CreateBSPStream
	UserOpen; InitPList; UserRetrieve; FreePList; UserClose
	ReleasePBI
		// statics used
	dsp 
	UserName; UserPassword; CtxRunning
	SysinName; SysinHostName
		// statics defined
	sysZone; sysoutFailed; ftpBadPup
	]

static [
	done; sysZone
	sysoutFailed = -1
	ftpBadPup = 0
	]

manifest [
	WordsPerPage = 256
	myBufferSize = WordsPerPage*7
	myZoneSize = 6500 + myBufferSize
	firstMouseX = 260
	firstMouseY = 50
	lastMouseY = 800
	ptAbort = #11
	ptError = #4
	ftpNoUserName = 2
	ftpBadUserName = 16
	ftpBadUserPassword = 17
	ftpBusy = 73
	]

structure String: [ length byte; char↑1,255 byte ]
 


let RemoteInitVmem () be

[
let zone = vec myZoneSize	// make local sysZone that goes away when we finish
let oldSysZone = sysZone
sysZone = InitializeZone(zone, myZoneSize)
let ctxQ = vec 1
ctxQ!0 = 0		// make a context queue
InitFtpUtil()
InitFtpPList()
InitPupLevel1 (sysZone, ctxQ, 10)
Enqueue(ctxQ, InitializeContext (Allocate(sysZone, 1000), 1000, FTPGuy, lenExtraCtx))

done = false
CallContextList (ctxQ!0) repeatuntil done
DestroyPupLevel1()
sysZone = oldSysZone
]

and FTPGuy (ctx) be
[
Block()
Zero(lv ctx>>FtpCtx.bspSoc, lenFTPI)	// clear out extra ctx stuff
@mouseX, @mouseY = firstMouseX, firstMouseY
let oldCursorBitMap = vec 15
SwapCursors (oldCursorBitMap,
	table [ #177400; #177400; #177400; #177400;
		#177400; #177400; #177400; #177400;
		#377; #377; #377; #377; #377; #377; #377; #377 ]) 
			// show ftp cursor

let sink = vec lST
Zero(sink, lST)
sink>>ST.puts = Noop
let fport = vec lenPort
unless GetPartner (SysinHostName, dsp, fport, 0, socketFTP)
   do GiveUp ("No such host: ", SysinHostName)

let soc = Allocate(sysZone, lenBSPSoc)
OpenLevel1Socket (soc, 0 , fport)	// open socket to partner
				// establish connection with ftp server
if (not OpenRTPSocket (soc, 0, 0, 0, FTPGuyPupHandler))
    & ((not ftpBadPup) %
	 (not OpenRTPSocket (soc, 0, 0, 0, FTPGuyPupHandler, 1000)))
   then [
	let errstring = vec 50
	errstring!0 = 0
	AppendString(errstring, "Can't open FTP connection with ")
	AppendString(errstring, SysinHostName)
	AppendString(errstring, ":*N// ")
	test ftpBadPup
	   ifso	[
		let type, firstByte = nil, nil
		switchon ftpBadPup>>PBI.pup.type
		 into [
		  case ptError:
			type = "[Error] "
			firstByte = 25
			endcase
		  case ptAbort:
			type = "[Abort] "
			firstByte = 3
			endcase
		      ]
		Wss(dsp, type)
		let nBytes = ftpBadPup>>PBI.pup.length-pupOvBytes-firstByte+1
		for i = firstByte to firstByte+nBytes-1
		  do Puts(dsp, ftpBadPup>>PBI.pup.bytes↑i)
		Dismiss(100)	// wait 1 second so user can see it
		ReleasePBI(ftpBadPup)
		AppendString(errstring, type)
		let curlen = errstring>>String.length
		for i = 0 to nBytes-1
		  do errstring>>String.char↑(i+curlen+1) =
			ftpBadPup>>PBI.pup.bytes↑(i+firstByte)
		errstring>>String.length = curlen+nBytes
		ReleasePBI(ftpBadPup)
		]
	  ifnot AppendString(errstring, "No response")
	GiveUp (errstring)
	]

ctx>>FtpCtx.bspStream = CreateBSPStream (soc)
ctx>>FtpCtx.bspSoc = soc
ctx>>FtpCtx.dspStream = dsp
ctx>>FtpCtx.lst = sink		// log stream
ctx>>FtpCtx.dls = sink		// debugging log stream
ctx>>FtpCtx.dbls = ctx>>FtpCtx.bspStream
ctx>>FtpCtx.buffer = Allocate(sysZone, myBufferSize)
ctx>>FtpCtx.bufferLength = myBufferSize
ctx>>FtpCtx.debugFlag = false

unless UserOpen (Version)
   do CallSwat ("UserOpen failed")

let localPL = InitPList()
localPL>>PL.SFIL = SysinName
localPL>>PL.UNAM = UserName
localPL>>PL.UPSW = UserPassword

[
let mark = UserRetrieve (localPL, Retrieve, FTPGuyCleanup)
let busyCnt = 0
unless sysoutFailed
   do break
if sysoutFailed ne -1
   then mark = sysoutFailed
let code = mark<<Mark.subCode
switchon code into
 [
  case ftpNoUserName: case ftpBadUserName: case ftpBadUserPassword:
	FixPassword(SysinHostName)
	endcase
  case ftpBusy:
	if busyCnt < 10
	   then [
		busyCnt = busyCnt+1
		Wss(dsp, "*N")
		Wss(dsp, ctx>>FtpCtx.getCmdString)
		Wss(dsp, "; will retry")
		Dismiss(1500)		// wait 15 seconds
		endcase
		]
  default:
	SysinFailure (ctx>>FtpCtx.getCmdString)
 ]
] repeat

UserClose()
RestoreCursor (oldCursorBitMap)
done = true
]

and Version (stream, nil) be Wss(stream, "Lisp FTP User")

and FTPGuyPupHandler(pbi) be

[
test sysoutFailed &
   (pbi>>PBI.pup.type eq ptError %
    pbi>>PBI.pup.type eq ptAbort)
   ifso [
	if ftpBadPup then ReleasePBI (ftpBadPup)
	ftpBadPup = pbi
	]
  ifnot ReleasePBI (pbi)
]

and FTPGuyCleanup (remotePL, ok, mark) be

[
if not ok
   then sysoutFailed = mark
]