-- FTPServerFiles.mesa, Edit: HGM July 31, 1980  5:27 PM  

-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  FTPDefs,
  FTPPrivateDefs;

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

-- **********************!  Constants  !***********************

ftpsystem: POINTER TO FTPSystem = LocateFtpSystemObject[];
 
-- **********************!  Module Presence Test Procedure  !***********************

ServerFilesLoaded: PUBLIC PROCEDURE =
  BEGIN
  -- report in
    ftpsystem.serverFilesLoaded ← TRUE;
  END;
 
-- **********************!  File Commands  !***********************

PTFDirectory: PUBLIC PROCEDURE [ftpserver: FTPServer] =
  BEGIN OPEN ftpserver, ftpserver.ftplistener;
  -- EnumerateFiles appendage
    PutFilename: PROCEDURE [unused: UNSPECIFIED, file: STRING, fileInfo: FileInfo] =
      BEGIN
      -- local variables
        pointer: POINTER TO PropertyListObject ← NIL;
      -- record at least one file
        atLeastOneFile ← TRUE;
      -- send notice of coming property list
        PutCommand[ftper, markHereIsPropertyList, 0];
      -- construct property list containing absolute and virtual filenames and file information
        ResetPropertyList[propertyList];
        WriteFilename[file, propertyList,
          filePrimitives, ftpserver.fileSystem, DESCRIPTOR[pointer↑]];
        WriteFileInfo[propertyList, fileInfo];
      -- send property list
        PutPropertyList[ftper, propertyList];
      END;
  -- local constants
    files: STRING = [maxStringLength];
  -- local variables
    atLeastOneFile: BOOLEAN ← FALSE;
  -- receive property list and EOC
    GetPropertyList[ftper, propertyList];  GetEOC[ftper];
  -- extract filename from property list
    ReadFilename[files, propertyList, filePrimitives, ftpserver.fileSystem];
  -- enumerate files
    filePrimitives.EnumerateFiles[ftpserver.fileSystem, files, enumeration, PutFilename, NIL];
    IF ~atLeastOneFile THEN Abort[noSuchFile];
  -- send EOC
    PutEOC[ftper];
  END;

PTFStore: PUBLIC PROCEDURE [ftpserver: FTPServer, mark: Byte] =
  BEGIN OPEN ftpserver, ftpserver.ftplistener;
  -- Note:  Implements both Store and NewStore.
  -- local constants
    file: STRING = [maxStringLength];
  -- local variables
    pointer: POINTER TO PropertyListObject ← NIL;
    fileHandle: FileHandle;
    successful: BOOLEAN;
    closing: BOOLEAN ← FALSE;
    fileInfoObject: FileInfoObject ← [binary,8,0,NIL,NIL,NIL,NIL];
  -- receive property list and EOC
    GetPropertyList[ftper, propertyList];  GetEOC[ftper];
  -- extract filename from property list
    ReadFilename[file, propertyList, filePrimitives, ftpserver.fileSystem];
  -- setup info for OpenFile
    ReadFileInfo[propertyList,@fileInfoObject];
  -- open file for write
    [fileHandle, ] ← filePrimitives.OpenFile[ftpserver.fileSystem, file, write, FALSE, @fileInfoObject];
    BEGIN ENABLE UNWIND =>
      IF ~closing THEN filePrimitives.CloseFile[ftpserver.fileSystem, fileHandle, TRUE];
  -- send Yes or property list and EOC
    IF mark = markStore THEN PutCommandAndEOC[ftper, markYes, 0]
    ELSE
      BEGIN
      -- send notice of coming property list
        PutCommand[ftper, markHereIsPropertyList, 0];
      -- construct property list containing absolute and virtual filenames
        ResetPropertyList[propertyList];
        WriteFilename[file, propertyList,
          filePrimitives, ftpserver.fileSystem, DESCRIPTOR[pointer↑]];
      -- send property list and EOC
        PutPropertyList[ftper, propertyList];  PutEOC[ftper];
      END;
  -- receive file
    GetSpecificCommand[ftper, markHereIsFile];
    filePrimitives.WriteFile[ftpserver.fileSystem, fileHandle, ReceiveBlock, ftper !
      FTPError =>
        -- Skip over rest of file if disk full or such
        IF ftpError ~IN CommunicationError THEN [] ← GetAnswerAndEOC[ftper]];
  -- receive end-of-file indicator
    successful ← GetAnswerAndEOC[ftper];
  -- close the file
    closing ← TRUE;
    filePrimitives.CloseFile[ftpserver.fileSystem, fileHandle, ~successful];
  -- acknowledge [un]successful transmission with Yes/No and EOC
    IF successful THEN PutCommandAndEOC[ftper, markYes, 0]
    ELSE PutCommandAndEOC[ftper, markNo, codeStoreNotCompleted];
    END; -- enable
  END;

PTFRetrieve: PUBLIC PROCEDURE [ftpserver: FTPServer] =
  BEGIN OPEN ftpserver, ftpserver.ftplistener;
  -- EnumerateFiles appendage
    RetrieveSingleFile: PROCEDURE [unused: UNSPECIFIED, file: STRING, fileInfo: FileInfo] =
      BEGIN
      -- local variables
        pointer: POINTER TO PropertyListObject ← NIL;
        fileHandle: FileHandle;
        accepted: BOOLEAN;
      -- record at least one file
        atLeastOneFile ← TRUE;
      -- open file for read
        [fileHandle, fileInfo.fileType] ←
          filePrimitives.OpenFile[ftpserver.fileSystem, file, read, TRUE, fileInfo];
        BEGIN ENABLE UNWIND => filePrimitives.CloseFile[ftpserver.fileSystem, fileHandle, TRUE];
      -- send notice of coming property list
        PutCommand[ftper, markHereIsPropertyList, 0];
      -- construct property list containing absolute and virtual filenames and file information
        ResetPropertyList[propertyList];
        WriteFilename[file, propertyList,
          filePrimitives, ftpserver.fileSystem, DESCRIPTOR[pointer↑]];
        WriteFileInfo[propertyList, fileInfo];
      -- send property list and EOC
        PutPropertyList[ftper, propertyList];  PutEOC[ftper];
      -- send file if accepted by remote user process
        IF accepted ← GetAnswerAndEOC[ftper] THEN
          BEGIN
          PutCommand[ftper, markHereIsFile, 0];
          filePrimitives.ReadFile[ftpserver.fileSystem, fileHandle, SendBlock, ftper];
          PutCommand[ftper, markYes, 0];
          END;
      -- close the file
        END; -- enable
        filePrimitives.CloseFile[ftpserver.fileSystem, fileHandle, ~accepted];
      END;
  -- local variables
    atLeastOneFile: BOOLEAN ← FALSE;
    files: STRING = [maxStringLength];
  -- receive property list and EOC
    GetPropertyList[ftper, propertyList];  GetEOC[ftper];
  -- extract filename from property list
    ReadFilename[files, propertyList, filePrimitives, ftpserver.fileSystem];
  -- retrieve files
    filePrimitives.EnumerateFiles[ftpserver.fileSystem, files, retrieval, RetrieveSingleFile, NIL];
    IF ~atLeastOneFile THEN Abort[noSuchFile];
  -- send EOC
    PutEOC[ftper];
  END;

PTFDelete: PUBLIC PROCEDURE [ftpserver: FTPServer] =
  BEGIN OPEN ftpserver, ftpserver.ftplistener;
  -- EnumerateFiles appendage
    DeleteSingleFile: PROCEDURE [unused: UNSPECIFIED, file: STRING, fileInfo: FileInfo] =
      BEGIN
      -- local variables
        pointer: POINTER TO PropertyListObject ← NIL;
      -- record at least one file
        atLeastOneFile ← TRUE;
      -- send notice of coming property list
        PutCommand[ftper, markHereIsPropertyList, 0];
      -- construct property list containing absolute and virtual filenames and file information
        ResetPropertyList[propertyList];
        WriteFilename[file, propertyList,
          filePrimitives, ftpserver.fileSystem, DESCRIPTOR[pointer↑]];
        WriteFileInfo[propertyList, fileInfo];
      -- send property list and EOC
        PutPropertyList[ftper, propertyList];  PutEOC[ftper];
      -- delete file if accepted by remote user process
        IF GetAnswerAndEOC[ftper] THEN
          BEGIN
          filePrimitives.DeleteFile[ftpserver.fileSystem, file];
          PutCommand[ftper, markYes, 0];
          END;
      END;
  -- local constants
    files: STRING = [maxStringLength];
  -- local variables
    atLeastOneFile: BOOLEAN ← FALSE;
  -- receive property list and EOC
    GetPropertyList[ftper, propertyList];  GetEOC[ftper];
  -- extract filename from property list
    ReadFilename[files, propertyList, filePrimitives, ftpserver.fileSystem];
  -- delete files
    filePrimitives.EnumerateFiles[ftpserver.fileSystem, files, deletion, DeleteSingleFile, NIL];
    IF ~atLeastOneFile THEN Abort[noSuchFile];
  -- send EOC
    PutEOC[ftper];
  END;

PTFRename: PUBLIC PROCEDURE [ftpserver: FTPServer] =
  BEGIN OPEN ftpserver, ftpserver.ftplistener;
  -- local constants
    currentFile: STRING = [maxStringLength];
    newFile: STRING = [maxStringLength];
  -- receive property list and extract file's current name
    GetPropertyList[ftper, propertyList];
    ReadFilename[currentFile, propertyList, filePrimitives, ftpserver.fileSystem];
  -- receive property list and extract file's new name
    GetPropertyList[ftper, propertyList];
    ReadFilename[newFile, propertyList, filePrimitives, ftpserver.fileSystem];
  -- receive EOC
    GetEOC[ftper];
  -- rename file
    filePrimitives.RenameFile[ftpserver.fileSystem, currentFile, newFile];
  -- send Yes and EOC
    PutCommandAndEOC[ftper, markYes, 0];
  END;

-- **********************!  Main Program  !***********************

-- no operation

END. -- of FTPServerFiles