-- 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],
  Storage USING [Node, String, Free, FreeNodeNil, FreeString];

FTPCold: MONITOR 
    IMPORTS String, Storage, 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