-- file: FTPUserStore.mesa,  Edit: HGM July 31, 1980  7:25 PM  

-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  FTPDefs,
  FTPPrivateDefs,
  Storage USING [Node, Free];

FTPUserStore: PROGRAM
  IMPORTS Storage, FTPDefs, FTPPrivateDefs EXPORTS FTPDefs SHARES FTPDefs =
  BEGIN OPEN FTPDefs, FTPPrivateDefs;



  FTPStoreFile: PUBLIC PROCEDURE [
    ftpuser: FTPUser, localFile, remoteFile: STRING, fileType: FileType]
    RETURNS [byteCount: LONG INTEGER] =
    BEGIN OPEN ftpuser;
    -- local constants
    bufferSize: CARDINAL = maximumDumpBlockSize + minimumDumpBlockSize;
    -- local variables
    creationDate: STRING = [maxDateLength];
    writeDate: STRING = [maxDateLength];
    readDate: STRING = [maxDateLength];
    storeState: {inactive, initiated, waiting, transferring} ← inactive;
    fileHandle: FileHandle ← NIL;
    fileInfoObject: FileInfoObject ←
      [fileType: fileType, byteSize: 0, byteCount: 0, creationDate: creationDate,
	writeDate: writeDate, readDate: readDate, author: NIL];
    dumpStateObject: DumpStateObject ←
      [bufferAddress: NIL, bufferLength: 0, ftper: ftper, blockType:];
    -- verify purpose and state
    VerifyPurposeAndState[
      ftpuser, files, IF state = connected THEN connected ELSE dumpFileBeingSent];
    -- intercept errors
    BEGIN
    ENABLE
      UNWIND =>
	BEGIN
	ENABLE FTPError => IF ftpError IN CommunicationError THEN CONTINUE;
	IF dumpStateObject.bufferAddress # NIL THEN
	  Storage.Free[dumpStateObject.bufferAddress];
	IF fileHandle # NIL THEN
	  filePrimitives.CloseFile[ftpuser.fileSystem, fileHandle, TRUE];
	IF storeState = initiated THEN
	  SELECT GetCommand[ftper].mark FROM
	    markHereIsPropertyList =>
	      BEGIN
	      GetPropertyList[ftper, propertyList];
	      GetEOC[ftper];
	      storeState ← transferring;
	      END;
	    markNo => GetEOC[ftper];
	    ENDCASE; --illegalProtocolSequence
	IF storeState = transferring THEN
	  BEGIN
	  PutCommandAndEOC[ftper, markNo, codeFileDataError];
	  GetSpecificCommand[ftper, markNo];
	  GetEOC[ftper];
	  END;
	END;
    -- determine file type if necessary
    [fileHandle, fileInfoObject.fileType] ← filePrimitives.OpenFile[
      ftpuser.fileSystem, localFile, read, fileType = unknown, @fileInfoObject];
    -- initiate remote store
    IF state = connected THEN
      BEGIN
      -- send store command
      PutCommand[ftper, markNewStore, 0];
      -- construct property list containing absolute and virtual filenames, credentials, and file information
      ResetPropertyList[propertyList];
      WriteFilename[remoteFile, propertyList, NIL, NIL, primaryPropertyList];
      IF fileType # unknown THEN fileInfoObject.fileType ← fileType;
      fileInfoObject.byteSize ← IF fileInfoObject.fileType = binary THEN 8 ELSE 0;
      WriteFileInfo[propertyList, @fileInfoObject];
      -- send property list and EOC
      PutPropertyList[ftper, propertyList];
      PutEOC[ftper];
      -- note store initiated
      storeState ← initiated;
      END;
    -- sustain remote store
    IF state = connected THEN
      BEGIN
      -- note waiting to transfer
      storeState ← waiting;
      -- sustain remote store
      GetSpecificCommand[ftper, markHereIsPropertyList];
      GetPropertyList[ftper, propertyList];
      GetEOC[ftper];
      -- signal transmission of file
      PutCommand[ftper, markHereIsFile, 0];
      -- note transfer in progress
      storeState ← transferring;
      END;
    -- send the file
    byteCount ← ftper.totalByteCount;
    IF state = connected THEN
      filePrimitives.ReadFile[ftpuser.fileSystem, fileHandle, SendBlock, ftper]
    ELSE
      BEGIN
      SendHeaderBlock[@dumpStateObject, remoteFile, creationDate];
      -- allocate a buffer
      dumpStateObject.bufferAddress ← Storage.Node[bufferSize];
      -- send the file
      filePrimitives.ReadFile[
	ftpuser.fileSystem, fileHandle, DumpBlock, @dumpStateObject];
      -- release the buffer
      Storage.Free[dumpStateObject.bufferAddress];
      dumpStateObject.bufferAddress ← NIL;
      END;
    byteCount ← ftper.totalByteCount - byteCount;
    -- terminate remote store
    IF state = connected THEN
      BEGIN
      -- send Yes and EOC
      PutCommandAndEOC[ftper, markYes, 0];
      storeState ← waiting;
      -- close local file
      -- Note:  Closing of the local file is deferred
      --   until end-of-file has been sent to the remote server
      --   so that closing of the local and remote files can be overlapped.
      filePrimitives.CloseFile[ftpuser.fileSystem, fileHandle, FALSE];
      fileHandle ← NIL;
      -- receive acknowledgment
      GetYesAndEOC[ftper];
      END
      -- close local file

    ELSE filePrimitives.CloseFile[ftpuser.fileSystem, fileHandle, FALSE];
    END; -- enable

    END;


  END. -- of FTPUserStore