-- FTPCold.mesa - last edit:
-- MAS May 19, 1980  6:03 PM  
-- HGM July 28, 1980  10:03 PM  

-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  FTPDefs,
  FTPPrivateDefs,
  String USING [AppendString],
  MDSStorage USING [Node, String, Free, FreeNodeNil, FreeString];

FTPCold: MONITOR
  IMPORTS String, Storage: MDSStorage, FTPPrivateDefs
  EXPORTS FTPDefs, FTPPrivateDefs
  SHARES FTPDefs, FTPPrivateDefs =
  BEGIN OPEN FTPDefs, FTPPrivateDefs;

  ftpUseCount: CARDINAL ← 0;
  ftpsystem: FTPSystem ← NIL;

  -- **********************!  Signals  !***********************

  FTPError: PUBLIC SIGNAL [ftpError: FtpError, message: STRING] = CODE;
  RejectThisConnection: PUBLIC ERROR [error: STRING] = CODE;



  LocateFtpSystemObject: PUBLIC PROCEDURE RETURNS [POINTER TO FTPSystem] =
    BEGIN
    -- return
    RETURN[@ftpsystem];
    END;


  FTPInitialize: PUBLIC ENTRY PROCEDURE =
    BEGIN
    property: Property;
    strings: ARRAY Property OF STRING ← [  -- avoid clutter in Global frame
      "User-Name"L, "User-Password"L, "Connect-Name"L, "Connect-Password"L,
      "Directory"L, "Server-Filename"L, "Device"L, "Name-Body"L, "Version"L,
      "Type"L, "End-of-Line-Convention"L, "Byte-Size"L, "Size"L, "Creation-Date"L,
      "Write-Date"L, "Read-Date"L, "Author"L, "User-Account"L, "Mailbox"L,
      "Sender"L, "Length"L, "Date-received"L, "Opened"L, "Deleted"L];
    -- Note:  bufferSize expressed in pages; zero implies default.
    -- increment use count and continue iff no previous usage
    ftpUseCount ← ftpUseCount + 1;
    IF ftpUseCount > 1 THEN RETURN;
    -- allocate and initialize system object
    ftpsystem ← Storage.Node[SIZE[FTPSystemObject]];
    ftpsystem↑ ← FTPSystemObject[
      propertyNames:, userFilesLoaded: FALSE, userMailLoaded: FALSE,
      serverFilesLoaded: FALSE, serverMailLoaded: FALSE, accessoriesLoaded: FALSE,
      bufferSize: defaultBufferSize, catchUnidentifiedErrors: TRUE];
    FOR property IN Property DO
      ftpsystem.propertyNames[property] ← Storage.String[
        strings[property].length];
      String.AppendString[ftpsystem.propertyNames[property], strings[property]];
      ENDLOOP;
    -- note presence/absence of optional modules
    UserFilesLoaded[];
    UserMailLoaded[];
    ServerFilesLoaded[];
    ServerMailLoaded[];
    AccessoriesLoaded[];
    END;

  FTPFinalize: PUBLIC ENTRY PROCEDURE =
    BEGIN
    property: Property;
    -- Note: Assumes that all users and listeners have been destroyed
    --   and that all servers have destroyed themselves.
    -- decrement use count
    ftpUseCount ← ftpUseCount - 1;
    IF ftpUseCount > 0 THEN RETURN;
    FOR property IN Property DO
      Storage.FreeString[ftpsystem.propertyNames[property]]; ENDLOOP;
    -- release system object
    ftpsystem ← Storage.FreeNodeNil[ftpsystem];
    END;

  FTPSetBufferSize: PUBLIC PROCEDURE [pages: CARDINAL] =
    BEGIN
    -- set buffer size
    ftpsystem.bufferSize ← IF pages # 0 THEN pages ELSE defaultBufferSize;
    END;

  FTPCatchUnidentifiedErrors: PUBLIC PROCEDURE [setting: BOOLEAN] =
    BEGIN
    -- catch unidentified errors
    ftpsystem.catchUnidentifiedErrors ← setting;
    END;


  CreateFTPer: PUBLIC PROCEDURE [
    communicationPrimitives: CommunicationPrimitives,
    communicationSystem: CommunicationSystem] RETURNS [ftper: FTPer] =
    BEGIN
    -- allocate and initialize ftper object
    ftper ← Storage.Node[SIZE[FTPerObject]];
    ftper↑ ← FTPerObject[
      communicationPrimitives: communicationPrimitives,
      communicationSystem: communicationSystem, connection: NIL,
      totalByteCount: 0, inputString: NIL, outputString: NIL, tracing: FALSE,
      writeString:, directionOfLastTrace: in];
    -- allocate input/output strings
    BEGIN
    ENABLE UNWIND => DestroyFTPer[ftper];
    ftper.inputString ← Storage.String[maxStringLength];
    ftper.outputString ← Storage.String[maxStringLength];
    END;  -- enable

    END;

  DestroyFTPer: PUBLIC PROCEDURE [ftper: FTPer] =
    BEGIN OPEN ftper;
    -- close connection if any
    IF connection # NIL THEN
      communicationPrimitives.CloseConnection[communicationSystem, connection];
    -- release input/output strings if any
    IF inputString # NIL THEN Storage.FreeString[inputString];
    IF outputString # NIL THEN Storage.FreeString[outputString];
    -- release ftper object
    Storage.Free[ftper];
    END;


  -- **********************!  Error Primitives  !***********************

  Abort: PUBLIC PROCEDURE [ftpError: FtpError] =
    BEGIN
    -- abort with default explanation
    AbortWithExplanation[ftpError, NIL];
    END;

  AbortWithExplanation: PUBLIC PROCEDURE [ftpError: FtpError, message: STRING] =
    BEGIN
    keepTheFinkyDebuggerHappy: BOOLEAN ← TRUE;
    defaultMessage: STRING = [maxStringLength];
    IF (message = NIL OR message.length = 0) AND ftpsystem.accessoriesLoaded THEN
      VerbalizeFtpError[ftpError, message ← defaultMessage];
    IF message.length = 0 THEN message ← NIL;
    IF keepTheFinkyDebuggerHappy THEN ERROR FTPError[ftpError, message];
    END;

  END. -- FTPCold