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