-- file: STPsB.mesa - Simple/Stream Transfer Protocol -- Traditional FTP-like interface stuff in here -- Edited by: -- Smokey on: 12-Mar-81 12:15:19 -- Evans on: Feb 8, 1980 10:16 AM -- Karlton on: September 17, 1980 6:12 PM DIRECTORY Inline USING [LowHalf], PupStream USING [CloseReason, StreamClosing], Storage USING [Free, FreePages, FreeString, FreeStringNil, Pages], STP USING [ Access, Completion, CompletionProcType, Confirmation, ConfirmProcType, CreateFileStream, CredentialsErrors, defaultOptions, Error, FileErrors, FileType, GetFileTimes, NoteFileProcType], STPOps USING [ CheckConnection, CollectCode, CollectString, ErrorIfNextNotYes, FindFileType, GenerateErrorString, GenerateStreamClosingError, GetCommand, GetHereIsAndPList, Handle, LookAtMark, MakeRemoteName, markDelete, markDirectory, markEOC, markHereIsFile, markNo, markNewStore, markRetrieve, markYes, MyGetMark, NameToPList, Object, Operation, PList, PutCommand, PutPList, ResetPList, SelectError, SetByteSize, SetCreateTime, SetFileDates, SetFileType, SmashClosed, UserStateToPList, ValidProperties], Stream USING [ Block, CompletionCode, EndOfStream, Handle, Object, PutBlock, SetSST, SSTChange, SubSequenceType, TimeOut], Streams USING [Destroy], Time USING [Packed]; STPsB: MONITOR IMPORTS Inline, PupStream, Storage, STP, STPOps, Stream, Streams EXPORTS STP, STPOps = BEGIN OPEN PupStream, STPOps; -- Data and stuff BytesPerPage: CARDINAL = 512; -- AltoDefs.BytesPerPage -- Object: PUBLIC TYPE = STPOps.Object; -- Procedures for normal FTP-like interface Delete: PUBLIC PROCEDURE [ stp: STPOps.Handle, name: STRING, confirm: STP.ConfirmProcType, complete: STP.CompletionProcType] = BEGIN DoFiles[stp, name, confirm, complete, delete]; END; Enumerate: PUBLIC PROCEDURE [ stp: STPOps.Handle, name: STRING, proc: STP.NoteFileProcType] = BEGIN Foo1: STP.ConfirmProcType = BEGIN RETURN[answer: IF proc[file] = yes THEN do ELSE abort, localStream: NIL]; END; Foo2: STP.CompletionProcType = BEGIN END; DoFiles[stp, name, Foo1, Foo2, directory]; END; Retrieve: PUBLIC PROCEDURE [ stp: STPOps.Handle, file: STRING, confirm: STP.ConfirmProcType ← NIL, complete: STP.CompletionProcType ← NIL] = BEGIN DoFiles[stp, file, confirm, complete, retrieve]; END; Store: PUBLIC PROCEDURE [ stp: STPOps.Handle, file: STRING, stream: Stream.Handle ← NIL, noteFile: STP.NoteFileProcType ← NIL, fileType: STP.FileType, creation: Time.Packed] = BEGIN myStream: BOOLEAN ← stream = NIL; Confirm: STP.ConfirmProcType = {IF noteFile = NIL OR noteFile[file] = yes THEN RETURN[do, stream] ELSE RETURN[skip, NIL]}; CheckConnection[stp]; ResetPList[stp.plist]; UserStateToPList[stp]; NameToPList[stp.plist, file, alto]; IF myStream THEN BEGIN stream ← STP.CreateFileStream[stp.plist[nameBody], read]; creation ← STP.GetFileTimes[stream].create; END; IF fileType = unknown THEN fileType ← FindFileType[stream]; SetFileType[stp, fileType]; STPOps.SetByteSize[stp, fileType]; STPOps.SetCreateTime[stp, creation]; DoFiles[stp, file, Confirm, NIL, store]; IF myStream THEN Streams.Destroy[stream]; END; -- common routine for Delete, Enumerate and Retrieve DoFiles: PUBLIC PROCEDURE [ stp: STPOps.Handle, file: STRING, confirm: STP.ConfirmProcType, complete: STP.CompletionProcType, op: STPOps.Operation] = BEGIN reason: PupStream.CloseReason; reply: STP.Confirmation; string: STRING ← NIL; local: Stream.Handle ← NIL; killConnection: BOOLEAN ← TRUE; CleanUp: PROCEDURE = BEGIN IF killConnection THEN SmashClosed[stp]; Storage.FreeString[string] END; {ENABLE { PupStream.StreamClosing => {reason ← why; GOTO streamClosing}; Stream.TimeOut => {reason ← transmissionTimeout; GOTO streamClosing}; UNWIND => CleanUp[]}; IF op # store THEN BEGIN CheckConnection[stp]; ResetPList[stp.plist]; UserStateToPList[stp]; NameToPList[stp.plist, file, alto]; END; PutPList[ stp, SELECT op FROM delete => markDelete, directory => markDirectory, retrieve => markRetrieve, store => markNewStore, ENDCASE => ERROR]; DO IF LookAtMark[stp] = markEOC THEN {[] ← MyGetMark[stp]; EXIT}; GetHereIsAndPList[stp, op # directory ! STP.Error => IF code IN STP.CredentialsErrors OR code IN STP.FileErrors THEN killConnection ← FALSE]; SELECT TRUE FROM confirm = NIL => {reply ← do; local ← NIL}; ENDCASE => [reply, local] ← confirm[string ← MakeRemoteName[stp.plist, alto]]; string ← Storage.FreeStringNil[string]; SELECT reply FROM do => { completion: STP.Completion; SELECT op FROM delete => { code: CHARACTER ← 0C; mark: Stream.SubSequenceType; PutCommand[stp, markYes, 0C, "Yes, please"L]; [mark, code] ← GetCommand[stp, @stp.remoteString]; SELECT mark FROM markYes => completion ← ok; markNo => completion ← error; ENDCASE => GenerateErrorString[protocolError, stp.remoteString, code]}; retrieve => { PutCommand[stp, markYes, 0C, "Yes, please"L]; completion ← IF GetFile[stp, local, stp.plist[nameBody]] THEN ok ELSE error}; store => { PutFile[stp, local, stp.plist[nameBody]]; ErrorIfNextNotYes[stp]}; ENDCASE; IF complete # NIL THEN complete[completion, stp.remoteString]; IF LookAtMark[stp] = markEOC THEN {[] ← MyGetMark[stp]; EXIT}}; skip => { IF op # directory THEN PutCommand[stp, markNo, 106C, "No Thanks"L]; IF op = store THEN { mark: Stream.SubSequenceType; code: CHARACTER; [mark, code] ← GetCommand[stp, @stp.remoteString]; IF mark # markNo THEN GenerateErrorString[protocolError, stp.remoteString, code]}; }; abort => {CleanUp[]; RETURN}; ENDCASE => ERROR; ResetPList[stp.plist]; ENDLOOP; EXITS streamClosing => GenerateStreamClosingError[reason]}; END; -- Procedures for doing FTP protocol operations GetFile: PUBLIC PROCEDURE [ stp: STPOps.Handle, stream: Stream.Handle, file: STRING] RETURNS[gotIt: BOOLEAN] = BEGIN mark: Stream.SubSequenceType; mine: BOOLEAN ← FALSE; {ENABLE UNWIND => IF mine AND stream # NIL THEN Streams.Destroy[stream]; CheckConnection[stp]; SELECT (mark ← MyGetMark[stp]) FROM markHereIsFile => NULL; markNo => { [] ← CollectCode[stp]; CollectString[stp, @stp.remoteString]; RETURN[FALSE]}; ENDCASE => SelectError[stp, "HereIsFile Expected"L, mark]; IF stream = NIL THEN {mine ← TRUE; stream ← STP.CreateFileStream[file, write]}; TransferTheFile[stp: stp, from: stp.byteStream, to: stream]; SetFileDates[stp, stream]}; IF mine THEN Streams.Destroy[stream]; -- HACK! ErrorIfNextNotYes[stp]; -- should do something about file on local disk RETURN[TRUE] END; PutFile: PUBLIC PROCEDURE [ stp: STPOps.Handle, stream: Stream.Handle, file: STRING, sendEOC: BOOLEAN ← TRUE] = BEGIN CheckConnection[stp]; Stream.SetSST[stp.byteStream, markHereIsFile]; TransferTheFile[stp: stp, from: stream, to: stp.byteStream]; PutCommand[stp, markYes, 0C, "Transfer Completed"L, sendEOC]; END; TransferTheFile: PUBLIC PROCEDURE [stp: STPOps.Handle, from, to: Stream.Handle] = BEGIN OPEN Inline; block: Stream.Block ← [NIL, 0, BytesPerPage]; BEGIN ENABLE UNWIND => Storage.FreePages[LowHalf[block.blockPointer]]; bytesTransferred: CARDINAL; why: Stream.CompletionCode; savedSST: Stream.SubSequenceType; block.blockPointer ← Storage.Pages[1]; DO block.startIndex ← 0; block.stopIndexPlusOne ← BytesPerPage; [bytesTransferred, why, savedSST] ← from.get[ from, block, STP.defaultOptions ! Stream.EndOfStream => BEGIN why ← endOfStream; bytesTransferred ← nextIndex; CONTINUE; END; Stream.SSTChange => BEGIN why ← sstChange; savedSST ← sst; bytesTransferred ← nextIndex; CONTINUE; END]; block.stopIndexPlusOne ← bytesTransferred; Stream.PutBlock[to, block, FALSE]; SELECT why FROM normal => NULL; sstChange => {stp.mark ← savedSST; stp.gotMark ← TRUE; EXIT}; endOfStream => EXIT; ENDCASE => ERROR; ENDLOOP; END; Storage.FreePages[LowHalf[block.blockPointer]]; END; END. -- of STPsB