-- Transport Mechanism: User: MTP retrieval

-- [Juniper]<Grapevine>User>RetrieveMTP.mesa

-- Mike Schroeder   13-Nov-80 17:04:12
-- Andrew Birrell   8-Sep-82 15:12:09

DIRECTORY
  BodyDefs USING [ItemHeader],
  FTPDefs USING [
    CommunicationError, CredentialError, FTPBeginRetrievalOfMessages,
    FTPCreateUser, FTPDestroyUser, FTPEndRetrievalOfMessages, FTPError,
    FTPIdentifyNextMessage, FTPInitialize, FTPOpenConnection,
    FTPRetrieveBlockOfMessage, FTPSetCredentials, MailError, MessageInfoObject,
    PupCommunicationPrimitives],
  PupDefs USING [AppendPupAddress],
  RetrieveDefs USING [Failed],
  RetrieveXDefs USING [Handle, ServerAddress];

RetrieveMTP: MONITOR LOCKS handle USING handle: RetrieveXDefs.Handle
  IMPORTS FTPDefs, PupDefs, RetrieveDefs, RetrieveXDefs EXPORTS RetrieveXDefs =
  BEGIN

  OPEN FTPDefs;


  MTPNextMessage: PUBLIC ENTRY PROCEDURE [handle: RetrieveXDefs.Handle]
    RETURNS [msgExists: BOOLEAN, archived: BOOLEAN, deleted: BOOLEAN] =
    BEGIN
    ENABLE UNWIND => NULL;
    dateString: STRING = [40];  -- arbitrary number, but big enough! --
    m: MessageInfoObject ← [
      byteCount:, deliveryDate: dateString, opened:, deleted:];
    SELECT handle.state FROM
      end => ERROR;
      beginning =>
        BEGIN
        addressString: STRING = [22] --377#377#177777|177777-- ;
        handle.ftpUser ← FTPCreateUser[NIL, PupCommunicationPrimitives[]];
        FTPSetCredentials[
          handle.ftpUser, primary, handle.userName, handle.userPwd];
        PupDefs.AppendPupAddress[
          addressString, RetrieveXDefs.ServerAddress[handle]];
        FTPOpenConnection[
          handle.ftpUser, addressString, mail, NIL !
          FTPError =>
            BEGIN
            handle.state ← end;
            SELECT ftpError FROM
              noSuchHost => ERROR RetrieveDefs.Failed[noSuchServer];
              connectionRejected => ERROR RetrieveDefs.Failed[connectionRejected];
              IN CommunicationError =>
                ERROR RetrieveDefs.Failed[communicationFailure];
              ENDCASE => ERROR RetrieveDefs.Failed[unknownFailure];
            END];
        FTPBeginRetrievalOfMessages[
          handle.ftpUser, handle.userName !
          FTPError =>
            BEGIN
            handle.state ← end;
            SELECT ftpError FROM
              IN CredentialError, IN MailError =>
                ERROR RetrieveDefs.Failed[badCredentials];
              unidentifiedTransientError, fileBusy =>  --mailbox busy
                ERROR RetrieveDefs.Failed[connectionRejected];
              IN CommunicationError =>
                ERROR RetrieveDefs.Failed[communicationFailure];
              ENDCASE => ERROR RetrieveDefs.Failed[unknownFailure];
            END];
        END;
      ENDCASE;
    FTPIdentifyNextMessage[
      handle.ftpUser, @m !
      FTPError =>
        BEGIN
        handle.state ← end;
        ERROR RetrieveDefs.Failed[
          IF ftpError IN CommunicationError THEN communicationFailure
          ELSE unknownFailure];
        END];
    msgExists ← m.byteCount # 0;
    deleted ← m.deleted;
    archived ← FALSE;
    handle.header.length ← m.byteCount;
    handle.header.type ← Text;
    handle.state ← IF msgExists THEN message ELSE lastMessage;
    END;  --NextMessage--

  MTPNextItem: PUBLIC ENTRY PROCEDURE [handle: RetrieveXDefs.Handle]
    RETURNS [itemHeader: BodyDefs.ItemHeader] =
    BEGIN
    ENABLE UNWIND => NULL;
    SELECT handle.state FROM
      message => {
        itemHeader ← handle.header;
        handle.state ← block;
        handle.spareByte ← FALSE};
      block, lastBlock => {itemHeader ← [LastItem, 0]; handle.state ← lastBlock};
      ENDCASE => ERROR;
    END;  --NextMessage--

  MTPNextBlock: PUBLIC ENTRY PROCEDURE [
    handle: RetrieveXDefs.Handle,
    buffer: DESCRIPTOR FOR PACKED ARRAY OF CHARACTER] RETURNS [bytes: CARDINAL] =
    BEGIN
    ENABLE UNWIND => NULL;
    SELECT handle.state FROM
      block =>
        BEGIN
        -- much of this code is dedicated to the design of FTPDefs. --
        -- "handle" may contain an extra byte from previous call --
        IF handle.spareByte THEN
          BEGIN
          buffer[0] ← handle.spareByteValue;
          handle.spareByte ← FALSE;
          bytes ← 1;
          END
        ELSE bytes ← 0;
        WHILE bytes < LENGTH[buffer] DO
          extraWord: PACKED ARRAY [0..1] OF CHARACTER;
          bWordsUsed: CARDINAL = (bytes + 1) / 2;
          firstFree: POINTER = BASE[buffer] + bWordsUsed;
          bWordsFree: CARDINAL = LENGTH[buffer] / 2 - bWordsUsed;
          base: POINTER = IF bWordsFree = 0 THEN @extraWord ELSE firstFree;
          length: CARDINAL =
            IF bWordsFree = 0 THEN LENGTH[extraWord] / 2 ELSE bWordsFree;
          given: CARDINAL = FTPRetrieveBlockOfMessage[
            handle.ftpUser, base, length !
            FTPError =>
              BEGIN
              handle.state ← end;
              ERROR RetrieveDefs.Failed[
                IF ftpError IN CommunicationError THEN communicationFailure
                ELSE unknownFailure];
              END];
          IF given = 0 THEN {handle.state ← lastBlock; EXIT};
          IF bWordsFree = 0 THEN
            BEGIN
            -- data was placed in extraWord --
            buffer[bytes] ← extraWord[0];
            bytes ← bytes + 1;
            IF given > 1 THEN
              BEGIN
              handle.spareByteValue ← extraWord[1];
              handle.spareByte ← TRUE;
              END;
            END
          ELSE
            BEGIN
            -- data was placed in buffer --
            IF bytes MOD 2 # 0 THEN
              FOR i: CARDINAL IN [bytes..bytes + given) DO
                buffer[i] ← buffer[i + 1]; ENDLOOP;
            bytes ← bytes + given;
            END;
          ENDLOOP;
        END;
      lastBlock => bytes ← 0;
      ENDCASE => ERROR;
    END;  --NextBlock--

  MTPAccept: PUBLIC INTERNAL PROCEDURE [handle: RetrieveXDefs.Handle] =
    BEGIN
    IF handle.state # lastMessage THEN ERROR;
    handle.state ← end;
    FTPEndRetrievalOfMessages[
      handle.ftpUser !
      FTPError =>
        ERROR RetrieveDefs.Failed[
          IF ftpError IN CommunicationError THEN communicationFailure
          ELSE unknownFailure]];
    END;  --Accept--

  MTPClose: PUBLIC INTERNAL PROCEDURE [handle: RetrieveXDefs.Handle] =
    BEGIN
    IF handle.ftpUser # NIL THEN
      BEGIN
      FTPDestroyUser[handle.ftpUser];
      handle.ftpUser ← NIL;
      handle.state ← beginning;
      END;
    END;  --Close--


  FTPInitialize[];

  END. -- RetrieveMTP--