-- FTPUserMailIn.mesa, Edit: HGM September 13, 1980  1:46 PM  

-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  FTPDefs,
  FTPPrivateDefs,
  String USING [AppendString, StringToLongNumber],
  Storage USING [Node, Free];

FTPUserMailIn: PROGRAM
  IMPORTS String, Storage, FTPPrivateDefs EXPORTS FTPDefs SHARES FTPDefs =
  BEGIN OPEN FTPDefs, FTPPrivateDefs;



  -- **********************!  Retrieval Primitives  !***********************

  FTPBeginRetrievalOfMessages: PUBLIC PROCEDURE [
    ftpuser: FTPUser, mailboxName: STRING] =
    BEGIN OPEN ftpuser;
    -- verify purpose and state
    VerifyPurposeAndState[ftpuser, mail, connected];
    -- send retrieve mail command
    PutCommand[mtper, markRetrieveMail, 0];
    -- construct property list containing credentials and mailbox name
    ResetPropertyList[propertyList];
    WriteProperty[propertyList, userName, primaryPropertyList[userName]];
    WriteProperty[propertyList, userPassword, primaryPropertyList[userPassword]];
    WriteProperty[propertyList, connectName, primaryPropertyList[connectName]];
    WriteProperty[
      propertyList, connectPassword, primaryPropertyList[connectPassword]];
    WriteProperty[propertyList, mailbox, mailboxName];
    -- send property list and EOC
    PutPropertyList[mtper, propertyList];
    PutEOC[mtper];
    -- update retrieval state based upon remote server's action
    UpdateRetrievalState[ftpuser];
    END;

  FTPIdentifyNextMessage: PUBLIC PROCEDURE [
    ftpuser: FTPUser, messageInfo: MessageInfo] =
    BEGIN OPEN ftpuser;
    -- Note:  Flushes remainder of text of previous message if necessary;
    --   returns messageInfo.byteCount=0 if no more messages.
    -- flush whatever text remains of previous message
    IF state = messageTextPending THEN
      BEGIN
      bufferSize: CARDINAL = 256;
      buffer: POINTER;
      -- allocate a buffer
      buffer ← Storage.Node[bufferSize];
      -- receive blocks of message until no more remain
      UNTIL FTPRetrieveBlockOfMessage[
	ftpuser, buffer, bufferSize ! UNWIND => Storage.Free[buffer]] = 0 DO
	ENDLOOP;
      -- release buffer
      Storage.Free[buffer];
      END;
    -- receive description of next message if any
    IF state = messageHeaderPending THEN
      BEGIN
      -- receive message information
      GetMessageInfo[ftpuser, messageInfo];
      -- record number of bytes expected and initialize number received
      numberOfBytesExpected ← messageInfo.byteCount;
      numberOfBytesReceived ← 0;
      -- prepare to receive text of message
      GetSpecificCommand[mtper, markHereIsFile];
      -- note readiness to receive text of message
      state ← messageTextPending;
      END
      -- indicate no more messages

    ELSE
      BEGIN
      -- verify purpose and state
      VerifyPurposeAndState[ftpuser, mail, mailboxExhausted];
      -- signal mailbox exhausted
      messageInfo.byteCount ← 0;
      END;
    END;

  FTPRetrieveBlockOfMessage: PUBLIC PROCEDURE [
    ftpuser: FTPUser, destination: POINTER, maxWordCount: CARDINAL]
    RETURNS [actualByteCount: CARDINAL] =
    BEGIN OPEN ftpuser;
    -- Note:  Returns zero if end of message.
    -- local variables
    bytePointerObject: BytePointerObject ←
      [destination, FALSE, bytesPerWord*maxWordCount];
    -- verify purpose and state
    VerifyPurposeAndState[ftpuser, mail, messageTextPending];
    -- receive next block of message
    -- Note:  The FTPUtilities procedure, ReceiveBlock, is here replicated in-line
    --   to reduce Alto working sets.
    BEGIN OPEN mtper;
    communicationPrimitives.ReceiveBytes[
      communicationSystem, connection, @bytePointerObject, TRUE];
    actualByteCount ← bytesPerWord*maxWordCount - bytePointerObject.count;
    END; -- open
    -- update running byte count
    numberOfBytesReceived ← numberOfBytesReceived + actualByteCount;
    IF numberOfBytesReceived > numberOfBytesExpected THEN
      Abort[messageLongerThanAdvertised];
    -- determine if end of message
    IF actualByteCount = 0 THEN
      BEGIN
      -- verify advertised byte count
      IF numberOfBytesReceived < numberOfBytesExpected THEN
	Abort[messageShorterThanAdvertised];
      -- update retrieval state based upon remote server's action
      UpdateRetrievalState[ftpuser];
      END;
    END;

  FTPEndRetrievalOfMessages: PUBLIC PROCEDURE [ftpuser: FTPUser] =
    BEGIN OPEN ftpuser;
    -- Note:  Flushes remainder of mailbox's contents if necessary.
    -- local constants
    deliveryDate: STRING = [maxStringLength];
    -- local variables
    messageInfoObject: MessageInfoObject ←
      [byteCount: 1, deliveryDate: deliveryDate, opened:, deleted:];
    -- flush whatever messages remain of mailbox's contents
    UNTIL messageInfoObject.byteCount = 0 DO
      FTPIdentifyNextMessage[ftpuser, @messageInfoObject]; ENDLOOP;
    -- order flushing of mailbox
    PutCommandAndEOC[mtper, markFlushMailbox, 0];
    -- wait for confirmation
    GetYesAndEOC[mtper];
    -- note mail retrieval complete
    state ← connected;
    END;

  -- **********************!  Retrieval Subroutines  !***********************

  GetMessageInfo: PROCEDURE [ftpuser: FTPUser, messageInfo: MessageInfo] =
    BEGIN OPEN ftpuser, messageInfo;
    -- Note:  Caller supplies string for delivery date.
    -- receive property list
    GetPropertyList[mtper, propertyList];
    -- decode byte count
    IF propertyList[length] = NIL THEN Abort[missingMessageLength];
    byteCount ← String.StringToLongNumber[propertyList[length], 10];
    -- decode delivery date
    deliveryDate.length ← 0;
    IF propertyList[dateReceived] # NIL THEN
      String.AppendString[deliveryDate, propertyList[dateReceived]];
    -- decode opened and deleted
    opened ← DecodeBooleanProperty[propertyList[opened]];
    deleted ← DecodeBooleanProperty[propertyList[deleted]];
    END;

  UpdateRetrievalState: PROCEDURE [ftpuser: FTPUser] =
    BEGIN OPEN ftpuser;
    -- local variables
    mark, code: Byte;
    -- receive command
    [mark, code] ← GetCommand[mtper];
    -- interpret command
    SELECT mark FROM
      markHereIsPropertyList => state ← messageHeaderPending;
      markYes =>
	BEGIN
	-- receive EOC
	GetEOC[mtper];
	-- note all messages retrieved
	state ← mailboxExhausted;
	END;
      markNo =>
	BEGIN
	-- receive EOC
	GetEOC[mtper];
	-- note mail retrieval complete
	state ← connected;
	-- abort
	AbortWithExplanation[CodeToSignal[code], mtper.inputString];
	END;
      ENDCASE => Abort[illegalProtocolSequence];
    END;


  END. -- of FTPUserMailIn