-- 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