-- 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],
Storage USING [Node, Free];
FTPUserRetrieve: PROGRAM
IMPORTS String, Storage, 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