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