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