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

-- Copyright  Xerox Corporation 1979, 1980

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

FTPUserStore: PROGRAM
  IMPORTS Storage: MDSStorage, 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