-- File: FTPUserRetrieve.mesa, Edit: -- MAS Apr 16, 1980 2:33 PM -- HGM July 31, 1980 5:38 PM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY FTPDefs, FTPPrivateDefs, String USING [EquivalentString], MDSStorage USING [Node, Free]; FTPUserRetrieve: PROGRAM IMPORTS String, Storage: MDSStorage, FTPDefs, FTPPrivateDefs EXPORTS FTPDefs SHARES FTPDefs = BEGIN OPEN FTPDefs, FTPPrivateDefs; FTPRetrieveFile: PUBLIC PROCEDURE [ ftpuser: FTPUser, localFile, remoteFile: STRING, fileType: FileType] RETURNS [byteCount: LONG INTEGER] = BEGIN OPEN ftpuser; -- local constants nextFile: STRING = propertyList[serverFilename]; bufferSize: CARDINAL = maximumDumpBlockSize + minimumDumpBlockSize; -- local variables retrieveState: {inactive, initiated, waiting, transferring, finishing} ← inactive; mark, code: Byte; fileHandle: FileHandle ← NIL; fileInfoObject: FileInfoObject ← [ fileType: fileType, byteSize: 0, byteCount: 0, creationDate: NIL, writeDate: NIL, readDate: NIL, author: NIL]; dumpStateObject: DumpStateObject ← [ bufferAddress: NIL, bufferLength: 0, ftper: ftper, blockType: 0]; -- verify purpose and state VerifyPurposeAndState[ ftpuser, files, SELECT state FROM connected, enumeratingFiles => state, ENDCASE => inventoryingDumpFile]; -- intercept errors BEGIN ENABLE UNWIND => BEGIN ENABLE FTPError => IF ftpError IN CommunicationError THEN CONTINUE; IF dumpStateObject.bufferAddress # NIL THEN BEGIN nextBlockType ← dumpStateObject.blockType; Storage.Free[dumpStateObject.bufferAddress]; END; IF fileHandle # NIL THEN filePrimitives.CloseFile[ftpuser.fileSystem, fileHandle, TRUE]; IF retrieveState = transferring THEN BEGIN SELECT GetCommand[ftper].mark FROM markYes => NULL; markNo => GetEOC[ftper]; ENDCASE => Abort[illegalProtocolSequence]; IF state = connected THEN FinishMultiFileOperation[ftpuser]; END; -- What if loading? IF retrieveState = initiated THEN FinishMultiFileOperation[ftpuser]; END; -- initiate remote retrieve IF state = connected THEN BEGIN -- send retrieve command PutCommand[ftper, markRetrieve, 0]; -- construct property list containing absolute and virtual filenames, credentials, and file information ResetPropertyList[propertyList]; WriteFilename[remoteFile, propertyList, NIL, NIL, primaryPropertyList]; WriteFileInfo[propertyList, @fileInfoObject]; -- send property list and EOC PutPropertyList[ftper, propertyList]; PutEOC[ftper]; -- note retrieve initiated retrieveState ← initiated; END -- verify enumeration or inventory intent and filename ELSE BEGIN IF intent # retrieval THEN Abort[illegalProcedureCallSequence]; IF nextFile = NIL OR ~String.EquivalentString[remoteFile, nextFile] THEN Abort[filenameUnexpected]; WriteProperty[propertyList, serverFilename, NIL]; END; -- sustain remote retrieve IF state = connected THEN BEGIN -- note waiting to transfer retrieveState ← waiting; -- receive property list and EOC [mark, code] ← GetCommand[ftper]; SELECT mark FROM markHereIsPropertyList => BEGIN GetPropertyList[ftper, propertyList]; GetEOC[ftper]; END; markNo => BEGIN GetEOC[ftper]; AbortWithExplanation[CodeToSignal[code], ftper.inputString]; END; markEndOfCommand => AbortWithExplanation[noSuchFile, ftper.inputString]; ENDCASE => Abort[illegalProtocolSequence]; END; -- setup info for OpenFile ReadFileInfo[propertyList, @fileInfoObject]; -- Open local file [fileHandle, ] ← filePrimitives.OpenFile[ ftpuser.fileSystem, localFile, write, FALSE, @fileInfoObject]; -- accept the offered file IF state # inventoryingDumpFile THEN BEGIN -- request the file and await acknowledgment PutCommandAndEOC[ftper, markYes, 0]; [mark, code] ← GetCommand[ftper]; SELECT mark FROM markHereIsFile => NULL; markNo => BEGIN AbortWithExplanation[CodeToSignal[code], ftper.inputString]; END; ENDCASE => Abort[illegalProtocolSequence]; -- note transfer in progress retrieveState ← transferring; END; -- retrieve the file byteCount ← ftper.totalByteCount; IF state # inventoryingDumpFile THEN filePrimitives.WriteFile[ ftpuser.fileSystem, fileHandle, ReceiveBlock, ftper] ELSE BEGIN -- allocate a buffer dumpStateObject.bufferAddress ← Storage.Node[bufferSize]; -- receive the file filePrimitives.WriteFile[ ftpuser.fileSystem, fileHandle, LoadBlock, @dumpStateObject]; nextBlockType ← dumpStateObject.blockType; -- release the buffer Storage.Free[dumpStateObject.bufferAddress]; dumpStateObject.bufferAddress ← NIL; END; byteCount ← ftper.totalByteCount - byteCount; -- terminate remote retrieve retrieveState ← finishing; IF state # inventoryingDumpFile THEN GetSpecificCommand[ftper, markYes]; IF state = connected THEN FinishMultiFileOperation[ftpuser]; -- close local file END; -- enable filePrimitives.CloseFile[ftpuser.fileSystem, fileHandle, FALSE]; END; END. -- of FTPUserRetrieve