DIRECTORY BasicTime USING [GMT], IO USING [EndOf, EndOfStream, Error, GetBlock, GetChar, GetIndex, GetLength, int, PutBlock, PutFR, SetIndex, STREAM], PupStream USING [CloseReason, ConsumeMark, PupByteStreamAbort, SendMark, StreamClosing, TimeOut], Rope USING [ROPE], STP USING [Close, Completion, CompletionProcType, Confirmation, ConfirmProcType, CredentialsErrors, Error, ErrorCode, FileErrors, NoteFileProcType, Type], STPOps USING [CheckConnection, CollectCode, CollectString, ErrorIfNextNotYes, ErrorIfNextNotEOC, ErrorCodeToSTPErrorCode, GenerateErrorString, GenerateProtocolError, GenerateStreamClosingError, GetCommand, GetHereIsAndPList, GetPList, Handle, LookAtMark, MarkEncountered, MakeRemoteName, markComment, markDelete, markDirectory, markEOC, markHereIsFile, markHereIsPList, markNo, markNewDirectory, markNewStore, markRename, markRetrieve, markYes, MyGetMark, NameToPList, Object, Operation, PList, PutCommand, PutPList, ResetPList, SelectError, SetByteSize, SetCreateTime, SetFileType, SetPListItem, UserStateToPList], STPReplyCode USING[ReplyCode]; STPsB: CEDAR PROGRAM IMPORTS IO, PupStream, STP, STPOps EXPORTS STP, STPOps = { OPEN PupStream, STPOps; Object: PUBLIC TYPE = STPOps.Object; Delete: PUBLIC PROC [stp: STPOps.Handle, name: Rope.ROPE, confirm: STP.ConfirmProcType, complete: STP.CompletionProcType] = { DoFiles[stp, name, confirm, complete, delete]; }; Enumerate: PUBLIC PROC [stp: STPOps.Handle, name: Rope.ROPE, proc: STP.NoteFileProcType] = { Foo1: STP.ConfirmProcType = { RETURN[answer: IF proc[file] = yes THEN do ELSE abort, localStream: NIL]; }; Foo2: STP.CompletionProcType = { }; DoFiles[stp, name, Foo1, Foo2, directory]; }; Rename: PUBLIC PROC [stp: STPOps.Handle, old, new: Rope.ROPE] = { mark, saveMark: [0..256); code: CHAR _ 0C; CheckConnection[stp]; ResetPList[stp.plist]; UserStateToPList[stp]; NameToPList[stp.plist, old, alto]; PutPList[stp, markRename, FALSE]; ResetPList[stp.plist]; UserStateToPList[stp]; NameToPList[stp.plist, new, alto]; PutPList[stp, 0B]; [saveMark, code, stp.remoteString] _ GetCommand[stp]; IF (mark _ MyGetMark[stp]) # markEOC THEN GenerateProtocolError[stp, eocExpected, mark]; IF saveMark # markYes THEN GenerateErrorString[stp, requestRefused, stp.remoteString, code]; }; Retrieve: PUBLIC PROC [stp: STPOps.Handle, file: Rope.ROPE, confirm: STP.ConfirmProcType _ NIL, complete: STP.CompletionProcType _ NIL] = { DoFiles[stp, file, confirm, complete, retrieve]; }; Store: PUBLIC PROC [stp: STPOps.Handle, file: Rope.ROPE, stream: IO.STREAM, noteFile: STP.NoteFileProcType _ NIL, fileType: STP.Type, creation: BasicTime.GMT] = { size: INT _ 0; 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 fileType = unknown THEN fileType _ FindFileType[stream]; SetFileType[stp, fileType]; STPOps.SetByteSize[stp, fileType]; STPOps.SetCreateTime[stp, creation]; size _ stream.GetLength[ ! IO.Error => CONTINUE;]; IF size # 0 THEN STPOps.SetPListItem[stp.plist, "Size", IO.PutFR["%g", IO.int[size]]]; DoFiles[stp, file, Confirm, NIL, store]; }; DoFiles: PUBLIC PROC [stp: STPOps.Handle, file: Rope.ROPE, confirm: STP.ConfirmProcType, complete: STP.CompletionProcType, op: STPOps.Operation] = { reason: PupStream.CloseReason; reply: STP.Confirmation; string: Rope.ROPE _ NIL; local: IO.STREAM _ NIL; killConnection: BOOLEAN _ TRUE; CleanUp: PROC = { IF killConnection THEN SmashClosed[stp]; }; {ENABLE { PupStream.StreamClosing => {reason _ why; GOTO streamClosing}; PupStream.TimeOut => {reason _ transmissionTimeout; GOTO streamClosing}; UNWIND => CleanUp[]}; IF op # store THEN { CheckConnection[stp]; ResetPList[stp.plist]; UserStateToPList[stp]; NameToPList[stp.plist, file, alto]; }; IF op = directory THEN SELECT TryNewDirectory[stp, confirm ! STP.Error => IF code IN STP.CredentialsErrors OR code IN STP.FileErrors THEN killConnection _ FALSE] FROM finished => RETURN; aborted => {CleanUp[]; RETURN}; tryOldDirectory => NULL; -- just falls thru ENDCASE => ERROR; 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]]; SELECT reply FROM do => { completion: STP.Completion; SELECT op FROM delete => { code: CHAR _ 0C; mark: [0..256); PutCommand[stp, markYes, 0C, "Yes, please"]; [mark, code, stp.remoteString] _ GetCommand[stp]; SELECT mark FROM markYes => completion _ ok; markNo => IF complete = NIL THEN GenerateErrorString[stp, requestRefused, stp.remoteString, code] ELSE completion _ error; ENDCASE => GenerateErrorString[stp, protocolError, stp.remoteString, code]}; retrieve => { code: CHAR _ 0C; gotIt: BOOLEAN; PutCommand[stp, markYes, 0C, "Yes, please"]; [gotIt, code] _ GetFile[stp, local, stp.plist[nameBody]]; completion _ IF gotIt THEN ok ELSE error; IF completion = error AND complete = NIL THEN GenerateErrorString[stp, requestRefused, stp.remoteString, code]}; 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"]; IF op = store THEN { mark: [0..256); code: CHAR; [mark, code, stp.remoteString] _ GetCommand[stp]; IF mark # markNo THEN GenerateErrorString[stp, protocolError, stp.remoteString, code]}; }; abort => {CleanUp[]; RETURN}; ENDCASE => ERROR; ResetPList[stp.plist]; ENDLOOP; EXITS streamClosing => GenerateStreamClosingError[stp, reason]}; }; TryNewDirectory: PROC[stp: STPOps.Handle, confirm: STP.ConfirmProcType] RETURNS[{finished, aborted, tryOldDirectory}] = { mark: [0..256); PutPList[stp, markNewDirectory]; DO SELECT (mark _ MyGetMark[stp]) FROM markComment => stp.remoteString _ CollectString[stp]; markHereIsPList => { string: Rope.ROPE; reply: STP.Confirmation; DO GetPList[stp: stp, gobbleEOC: FALSE, propertiesOk: TRUE ! MarkEncountered => { mark: [0..256) _ LookAtMark[stp]; IF mark = markEOC THEN EXIT ELSE GenerateProtocolError[stp, eocExpected, mark] }]; string _ MakeRemoteName[stp.plist, alto]; [answer: reply] _ confirm[string]; IF reply = abort THEN RETURN[aborted]; ENDLOOP; ErrorIfNextNotEOC[stp]; RETURN[finished]; }; markNo => { code: CHAR = CollectCode[stp]; errorCode: STP.ErrorCode = ErrorCodeToSTPErrorCode[requestRefused, code]; stp.remoteString _ CollectString[stp]; ErrorIfNextNotEOC[stp]; SELECT errorCode FROM protocolError, requestRefused => RETURN[tryOldDirectory]; ENDCASE; IF LOOPHOLE[code, STPReplyCode.ReplyCode] = badCommand THEN RETURN[tryOldDirectory]; GenerateErrorString[stp, errorCode, stp.remoteString, code]; }; ENDCASE => GenerateProtocolError[stp, badMark, mark, 0C]; ENDLOOP; }; GetFile: PUBLIC PROC [ stp: STPOps.Handle, stream: IO.STREAM, file: Rope.ROPE] RETURNS[gotIt: BOOLEAN, code: CHAR] = { mark: [0..256); CheckConnection[stp]; SELECT (mark _ MyGetMark[stp]) FROM markHereIsFile => NULL; markNo => { code _ CollectCode[stp]; stp.remoteString _ CollectString[stp]; gotIt _ FALSE; RETURN}; ENDCASE => SelectError[stp, "HereIsFile Expected", mark]; TransferTheFile[stp: stp, from: stp.byteStream, to: stream]; stp.mark _ PupStream.ConsumeMark[stp.byteStream]; stp.gotMark _ TRUE; ErrorIfNextNotYes[stp]; -- should do something about file on local disk gotIt _ TRUE; RETURN }; PutFile: PUBLIC PROC [ stp: STPOps.Handle, stream: IO.STREAM, file: Rope.ROPE, sendEOC: BOOLEAN _ TRUE] = { CheckConnection[stp]; PupStream.SendMark[stp.byteStream, markHereIsFile]; TransferTheFile[stp: stp, from: stream, to: stp.byteStream]; PutCommand[stp, markYes, 0C, "Transfer Completed", sendEOC]; }; TransferTheFile: PROC [stp: STPOps.Handle, from, to: IO.STREAM] = { buffer: REF TEXT = NEW[TEXT[1024]]; buffer.length _ buffer.maxLength; DO nBytes: NAT = from.GetBlock[buffer, 0, buffer.maxLength]; to.PutBlock[buffer, 0, nBytes]; IF from.EndOf[] THEN EXIT; ENDLOOP; }; SmashClosed: PUBLIC PROC [stp: STPOps.Handle] = { IF stp = NIL OR stp.byteStream = NIL THEN RETURN; PupStream.PupByteStreamAbort[stp.byteStream, "Unwinding..."]; STP.Close[stp ! STP.Error => IF code = noConnection THEN CONTINUE]; }; FindFileType: PUBLIC PROC [stream: IO.STREAM] RETURNS [fileType: STP.Type] = { ENABLE IO.Error => IF ec = NotImplementedForThisStream THEN GOTO unknown; currentIndex: INT = stream.GetIndex[]; stream.SetIndex[0]; fileType _ text; DO IF stream.GetChar[ ! IO.EndOfStream => EXIT] > 177C THEN {fileType _ binary; EXIT}; ENDLOOP; stream.SetIndex[currentIndex]; EXITS unknown => fileType _ unknown; }; }. ŒSTPsB.mesa - Simple/Stream Transfer Protocol (FTP-like interface) Smokey, 17-Jul-81 7:48:31 JGS, 17-Aug-81 11:26:24 Karlton, 15-Mar-82 17:26:37 Loretta, 10-Nov-81 16:07:59 Davirro, 24-Sep-82 10:35:49 Daniels, 21-Sep-82 13:05:55 Andrew Birrell, June 1, 1983 3:51 pm Schroeder, December 7, 1983 10:40 am Bob Hagmann January 28, 1986 3:53:34 pm PST Hadari, February 11, 1986 7:02:54 pm PST Data and types Procedures for normal FTP-like interface common routine for Delete, Enumerate and Retrieve RRA: sometimes the new directory sends so much stuff that poor old servers can't handle it, so we try to use the old protocol. Procedures for doing FTP protocol operations First smash connection so it will not give us any grief, THEN close it Bob Hagmann January 28, 1986 3:50:50 pm PST add size to the store property list changes to: Store, DIRECTORY Κ q˜codešœA™AKšœ!™!Kšœ&™&Kšœ"™"Kšœ"™"Kšœ"™"Kšœ"™"Kšœ$™$K™$K™+K™(—K˜šΟk ˜ Kšœ œœ˜Kšœœeœ˜uKšœ œR˜aKšœœœ˜Kšœœ‘˜šKšœœή˜κKšœ œ ˜K˜—šœœ˜Kšœœ œ˜"Kšœœ ˜Kšœœ˜K˜—Kšœ™˜Kšœœœ˜$K˜—Kšœ)™)˜šΟnœœœ!œ œœœ˜}Kšœ.˜.Kšœ˜K˜—š ž œœœ!œœœ˜\šœœ˜Kšœ˜Kš œ œœœœ˜IKšœ˜—Kšœœ˜#K˜*Kšœ˜K˜—š žœœœ%œœ˜AK˜Kšœœ˜K˜K˜K˜K˜"Kšœœ˜!K˜K˜K˜"K˜K˜5šœ#˜)K˜.—šœ˜K˜A—Kšœ˜K˜—šžœœœ!œ œœ œœœ˜‹Kšœ0˜0Kšœ˜K˜—šžœœœ!œ œœ œœ œœœ˜’Kšœœ˜šœ œ˜ Kšœ œœ˜.Kšœ œœœ˜+—K˜K˜K˜K˜#Kšœœ!˜;K˜K˜"K˜$Kšœœ œ˜2KšœV˜VKšœœ ˜(Kšœ˜K˜——Kšœ1™1˜šžœœœ!œ œœ,œ˜”K˜Kšœœ˜Kšœ œœ˜Kšœœœœ˜Kšœœœ˜šžœœ˜Kšœ˜Kšœœ˜(Kšœ˜—šœœ˜ Kšœ*œ˜>Kšœ4œ˜HKšœ˜—šœ ˜Kšœ˜K˜K˜K˜K˜#Kšœ˜—šœœ˜šœ!œ ˜3šœœœœœœ œ˜@Kšœœ˜——Kšœ œ˜Kšœœ ˜'Kšœœ Οc˜2Kšœœ˜—˜ K˜šœ˜K˜K˜K˜K˜Kšœœ˜—š˜Kšœœœ˜>šœ(œ ˜4šœœœœœœ œ˜@Kšœœ˜——šœœ˜Kšœ œœ˜+KšœG˜N—šœ˜˜Kšœ œ ˜šœ˜˜ Kšœœ˜K˜K˜,K˜1šœ˜K˜šœ œ ˜KšœA˜EKšœ˜—KšœE˜L——˜ Kšœœ˜Kšœœ˜K˜,Kšœ9˜9Kšœ œœœ˜)Kšœœ ˜(KšœC˜G—˜ K˜)K˜—Kšœ˜—Kšœ œœ(˜>Kšœœœ˜?—˜ šœ˜K˜+—šœ œ˜K˜Kšœœ˜ K˜1šœ˜K˜A——K˜—Kšœœ˜Kšœœ˜—K˜Kšœ˜—š˜K˜:—Kšœ˜K˜———šžœœœœ*˜zK˜K˜ š˜šœ˜#K˜5˜Kšœ œ˜Kšœœ˜š˜šœœ˜7˜K˜!Kšœœ˜Kšœ.˜2K˜——K˜)K˜"Kšœœœ ˜&Kšœ˜—K˜Kšœ ˜K˜—šœ ˜ Kšœœ˜šœ œ ˜K˜/—K˜&K˜šœ ˜šœ!œ˜9Kšœ~™~—Kšœ˜—šœœ,˜;Kšœ˜—K˜