-- FTPUserMailOut.mesa, Edited by: HGM July 28, 1980  9:29 PM  

-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  FTPDefs,
  FTPPrivateDefs,
  String USING [AppendChar, StringToDecimal];

FTPUserMailOut: PROGRAM
  -- import list

  IMPORTS String, FTPPrivateDefs
  -- export list

  EXPORTS FTPDefs
  -- share list

  SHARES FTPDefs, FTPPrivateDefs =
  BEGIN OPEN FTPDefs, FTPPrivateDefs;

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

  ftpsystem: POINTER TO FTPSystem = LocateFtpSystemObject[];


  -- **********************!  Delivery Primitives  !***********************

  FTPBeginDeliveryOfMessage: PUBLIC PROCEDURE [ftpuser: FTPUser] =
    BEGIN OPEN ftpuser;
    -- verify purpose and state
    VerifyPurposeAndState[ftpuser, mail, connected];
    -- If the message gets forwarded, IFSs want to know where to send complains.
    IF primaryPropertyList[userName] = NIL THEN Abort[credentialsMissing];
    -- send store mail command
    PutCommand[mtper, markStoreMail, 0];
    -- initialize number of recipients and valid recipients to zero
    numberOfRecipients ← numberOfValidRecipients ← 0;
    -- initialize property list for non-standard use
    -- Note:  FTPSendRecipientOfMessage circumvents WriteProperty
    --   to avoid allocation and release of main storage in this inner loop.
    ResetPropertyList[propertyList];
    -- note readiness to send recipients
    state ← messageRecipientsBeingSent;
    END;

  FTPSendRecipientOfMessage: PUBLIC PROCEDURE [
    ftpuser: FTPUser, mailboxName, mailboxHostName, dmsName: STRING] =
    BEGIN OPEN ftpuser;
    propertyList: PropertyList ← ftpuser.propertyList;
    -- verify purpose and state
    VerifyPurposeAndState[ftpuser, mail, messageRecipientsBeingSent];
    -- store mailbox name in property list
    -- Note:  WriteProperty is circumvented
    --   to avoid allocation and release of main storage in this inner loop.
    propertyList[mailbox] ← mailboxName;
    IF numberOfRecipients = 0 THEN
      propertyList[sender] ← primaryPropertyList[userName];
    -- send the property list
    PutPropertyList[
      mtper, propertyList !
      UNWIND => propertyList[mailbox] ← propertyList[sender] ← NIL];
    propertyList[mailbox] ← propertyList[sender] ← NIL;
    -- increment number of recipients and valid recipients
    numberOfRecipients ← numberOfRecipients + 1;
    numberOfValidRecipients ← numberOfValidRecipients + 1;
    END;

  FTPIdentifyNextRejectedRecipient: PUBLIC PROCEDURE [
    ftpuser: FTPUser, errorMessage: STRING]
    RETURNS [recipientNumber: CARDINAL, recipientError: RecipientError] =
    BEGIN OPEN ftpuser;
    -- Note: Returns recipientNumber=0 if no more mailbox exceptions.
    -- local constants
    inputString: STRING = mtper.inputString;
    textualRecipientNumber: STRING = [maxStringLength];
    -- local variables
    mark, code: Byte;
    string: STRING;
    i: CARDINAL;
    shouldBe: UserState;
    -- verify purpose and state
    shouldBe ←
      SELECT state FROM
        messageRecipientsBeingSent, initialMailboxExceptionsBeingReceived,
          messageTextBeingSent => state,
        ENDCASE => finalMailboxExceptionsBeingReceived;
    VerifyPurposeAndState[ftpuser, mail, shouldBe];
    -- signal end of recipient list or text as necessary
    SELECT state FROM
      messageRecipientsBeingSent =>
        BEGIN PutEOC[mtper]; state ← initialMailboxExceptionsBeingReceived; END;
      messageTextBeingSent =>
        BEGIN
        PutCommandAndEOC[mtper, markYes, 0];
        state ← finalMailboxExceptionsBeingReceived;
        END;
      ENDCASE;
    -- fetch next mailbox exception if any
    [mark, code] ← GetCommand[mtper];
    SELECT mark FROM
      markMailboxException =>
        BEGIN
        -- separate recipient number and error message
        string ← textualRecipientNumber;
        errorMessage.length ← 0;
        FOR i IN [0..inputString.length) DO
          IF string = textualRecipientNumber
            AND inputString[i] = mailboxExceptionIndexTerminator THEN
            string ← errorMessage
          ELSE String.AppendChar[string, inputString[i]];
          ENDLOOP;
        -- decode and verify recipient number
        recipientNumber ← String.StringToDecimal[textualRecipientNumber];
        IF recipientNumber ~IN [1..numberOfRecipients] THEN
          Abort[noSuchRecipientNumber];
        -- decode and verify error code
        recipientError ← ExceptionCodeToRecipientError[code];
        -- supply error message if none provided
        IF errorMessage.length = 0 AND ftpsystem.accessoriesLoaded THEN
          VerbalizeRecipientError[recipientError, errorMessage];
        -- decrement number of valid recipients
        IF numberOfValidRecipients = 0 THEN Abort[duplicateMailboxException];
        numberOfValidRecipients ← numberOfValidRecipients - 1;
        END;
      markYes =>
        BEGIN
        -- receive EOC
        GetEOC[mtper];
        -- lay groundwork for transmission of message
        IF state = initialMailboxExceptionsBeingReceived THEN
          BEGIN
          PutCommand[mtper, markHereIsFile, 0];
          state ← messageTextBeingSent;
          END
          -- note mail delivery complete

        ELSE state ← messageDelivered;
        -- inform caller of end of mailbox exception run
        recipientNumber ← 0;
        END;
      markNo =>
        BEGIN
        -- receive EOC
        GetEOC[mtper];
        -- note mail delivery complete
        state ← messageDelivered;
        -- abort
        AbortWithExplanation[CodeToSignal[code], inputString];
        END;
      ENDCASE => Abort[illegalProtocolSequence];
    END;

  FTPSendBlockOfMessage: PUBLIC PROCEDURE [
    ftpuser: FTPUser, source: POINTER, byteCount: CARDINAL] =
    BEGIN OPEN ftpuser;
    -- local variables
    bytePointerObject: BytePointerObject ← [source, FALSE, byteCount];
    -- verify purpose and state
    VerifyPurposeAndState[ftpuser, mail, messageTextBeingSent];
    -- send block of message
    -- Note:  The FTPUtilities procedure, SendBytes, is here replicated in-line
    --   to reduce Alto working sets.
    IF bytePointerObject.count > 0 THEN
      BEGIN OPEN mtper;
      communicationPrimitives.SendBytes[
        communicationSystem, connection, @bytePointerObject];
      END;
    END;

  FTPEndDeliveryOfMessage: PUBLIC PROCEDURE [ftpuser: FTPUser] =
    BEGIN OPEN ftpuser;
    -- verify purpose and state
    VerifyPurposeAndState[ftpuser, mail, messageDelivered];
    -- restore state
    state ← connected;
    END;


  END. -- of FTPUserMailOut