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