-- Transport Mechanism: User: Access to mail on GV servers --

-- [Juniper]<DMS>MS>RetrieveGV.mesa

-- Andrew Birrell  21-Jan-81 17:08:41 --

DIRECTORY
BodyDefs	USING[ ItemHeader, ItemLength, RName, Timestamp ],
Inline		USING[ LowHalf ],
NameInfoDefs	USING[ AuthenticateInfo ],
ProtocolDefs	USING[ CreateStream, DestroyStream, Init, Failed,
		       FailureReason, Handle,
                       mailServerOutputSocket,
                       ReceiveAck, ReceiveBoolean, ReceiveByte,
                       ReceiveCount, ReceiveItemHeader,
                       ReceiveRemark, ReceiveRName, ReceiveTimestamp,
                       SendNow, SendMSOperation, SendPassword,
                       SendRemark, SendRName ],
PupDefs		USING[ PupAddress ],
PupStream	USING[ StreamClosing ],
RetrieveDefs	USING[ Failed, FailureReason ],
RetrieveXDefs	USING[ Handle, ServerAddress ],
Stream		USING[ Block, CompletionCode, GetBlock, Handle,
		       SubSequenceType, TimeOut ];

RetrieveGV: MONITOR LOCKS handle USING handle: RetrieveXDefs.Handle
   IMPORTS Inline, ProtocolDefs, PupStream, RetrieveDefs,
           RetrieveXDefs, Stream
   EXPORTS RetrieveXDefs =

BEGIN


Fail: PROC[why: ProtocolDefs.FailureReason] =
   { ERROR RetrieveDefs.Failed[IF why = protocolError
                               THEN unknownFailure
                               ELSE communicationFailure] };

WrongCallSequence: ERROR = CODE;


GVNextMessage: PUBLIC ENTRY PROCEDURE[ handle: RetrieveXDefs.Handle ]
             RETURNS[ msgExists: BOOLEAN,
                      archived: BOOLEAN,
                      deleted: BOOLEAN ] =
   BEGIN
   ENABLE { ProtocolDefs.Failed => Fail[why]; UNWIND => NULL };
   IF handle.state = beforeMBX
   THEN BEGIN
        addr: PupDefs.PupAddress ← RetrieveXDefs.ServerAddress[handle];
        -- we exited by a signal if the address wasn't available --
        addr.socket ← ProtocolDefs.mailServerOutputSocket;
        IF handle.currentStr # NIL THEN ERROR;
        handle.currentStr ←
                     ProtocolDefs.CreateStream[handle.currentMBX.addr ];
        ProtocolDefs.SendMSOperation[handle.currentStr, openMBX];
        ProtocolDefs.SendRName[handle.currentStr, handle.userName];
        ProtocolDefs.SendPassword[str:handle.currentStr, pw:handle.userKey,
                                  key:[0,0,0,0]];
        ProtocolDefs.SendNow[handle.currentStr];
        BEGIN
           info: NameInfoDefs.AuthenticateInfo =
                 LOOPHOLE[ProtocolDefs.ReceiveByte[handle.currentStr]];
           SELECT info FROM
              allDown => ERROR RetrieveDefs.Failed[communicationFailure];
              notFound, group, badPwd =>
                         ERROR RetrieveDefs.Failed[badCredentials];
              individual => NULL;
           ENDCASE => ERROR RetrieveDefs.Failed[unknownFailure];
        END;
        handle.messages ← ProtocolDefs.ReceiveCount[handle.currentStr];
        handle.state ← afterMessage;
        END;
   WHILE handle.state = inBody DO [] ← InnerNextItem[handle]; ENDLOOP;
   IF handle.state = beforeBody THEN handle.state ← afterMessage;
   -- handle.state is now afterMessage or afterMBX --
   IF handle.messages = 0
   THEN { handle.state ← afterMBX; RETURN[FALSE,FALSE,FALSE] }
   ELSE BEGIN
        handle.messages ← handle.messages-1;
        ProtocolDefs.SendMSOperation[handle.currentStr, nextMessage];
        ProtocolDefs.SendNow[handle.currentStr];
        msgExists ← ProtocolDefs.ReceiveBoolean[handle.currentStr];
        archived ← ProtocolDefs.ReceiveBoolean[handle.currentStr];
        deleted ← ProtocolDefs.ReceiveBoolean[handle.currentStr];
        IF msgExists
        THEN handle.state ← beforeBody
        ELSE ERROR;
        END;
   END;

GVReadTOC: PUBLIC ENTRY PROCEDURE[handle: RetrieveXDefs.Handle,
                                  text: STRING] =
   BEGIN
   ENABLE { ProtocolDefs.Failed => Fail[why]; UNWIND => NULL };
   WHILE handle.state = inBody DO [] ← InnerNextItem[handle]; ENDLOOP;
   IF handle.state # beforeBody THEN ERROR WrongCallSequence[];
   ProtocolDefs.SendMSOperation[handle.currentStr, readTOC];
   ProtocolDefs.SendNow[handle.currentStr];
   ProtocolDefs.ReceiveRemark[handle.currentStr, text];
   END;

GVStartMessage: PUBLIC ENTRY PROCEDURE[handle: RetrieveXDefs.Handle,
                postmark: POINTER TO BodyDefs.Timestamp ← NIL,
                sender: BodyDefs.RName ← NIL,
                returnTo: BodyDefs.RName ← NIL ] =
   BEGIN
   ENABLE { ProtocolDefs.Failed => Fail[why]; UNWIND => NULL };
   InnerStartMessage[handle, postmark, sender, returnTo];
   END;

InnerStartMessage: INTERNAL PROCEDURE[handle: RetrieveXDefs.Handle,
                postmark: POINTER TO BodyDefs.Timestamp ← NIL,
                sender: BodyDefs.RName ← NIL,
                returnTo: BodyDefs.RName ← NIL ] =
   BEGIN
   WHILE handle.state = inBody DO [] ← InnerNextItem[handle]; ENDLOOP;
   IF handle.state # beforeBody THEN ERROR WrongCallSequence[];
   ProtocolDefs.SendMSOperation[handle.currentStr, readMessage];
   ProtocolDefs.SendNow[handle.currentStr];
   handle.state ← inBody;
   handle.header ← ProtocolDefs.ReceiveItemHeader[handle.currentStr ];
   IF handle.header.type # PostMark THEN ERROR;
   IF postmark # NIL
   THEN postmark↑ ← ProtocolDefs.ReceiveTimestamp[handle.currentStr]
   ELSE InnerSkipItem[handle];
   handle.header ← ProtocolDefs.ReceiveItemHeader[handle.currentStr];
   IF handle.header.type # Sender THEN ERROR;
   IF sender # NIL
   THEN ProtocolDefs.ReceiveRName[handle.currentStr, sender]
   ELSE InnerSkipItem[handle];
   handle.header ← ProtocolDefs.ReceiveItemHeader[handle.currentStr];
   IF handle.header.type # ReturnTo THEN ERROR;
   IF returnTo # NIL
   THEN ProtocolDefs.ReceiveRName[handle.currentStr, returnTo]
   ELSE InnerSkipItem[handle];
   handle.header.length ← 0; -- no more data in this item --
   END;

GVNextItem: PUBLIC ENTRY PROCEDURE[handle: RetrieveXDefs.Handle]
                         RETURNS[itemHeader: BodyDefs.ItemHeader] =
   BEGIN
   ENABLE { ProtocolDefs.Failed => Fail[why]; UNWIND => NULL };
   IF handle.state # inBody THEN InnerStartMessage[handle];
   RETURN[ InnerNextItem[handle] ]
   END;

InnerNextItem: INTERNAL PROCEDURE[handle: RetrieveXDefs.Handle]
                          RETURNS[itemHeader: BodyDefs.ItemHeader ] =
   BEGIN
   IF handle.state # inBody THEN ERROR WrongCallSequence[];
   IF handle.header.length > 0 OR handle.spareByte
   THEN InnerSkipItem[handle];
   handle.header ← ProtocolDefs.ReceiveItemHeader[handle.currentStr];
   -- arrange for InnerSkip to include padding byte --
   handle.spareByte ← handle.header.length MOD 2 # 0;
   IF handle.header.type = LastItem
   THEN BEGIN
        why: Stream.CompletionCode ← normal;
        buffer: CHARACTER;
        IF handle.header.length > 0 THEN InnerSkipItem[handle];
        WHILE why # sstChange
        DO [,why,] ← Stream.GetBlock[handle.currentStr, [@buffer, 0, 1] !
      PupStream.StreamClosing, Stream.TimeOut => Fail[communicationError]];
        ENDLOOP;
        handle.state ← beforeBody;
        END;
   itemHeader ← handle.header;
   END;

GVNextBlock: PUBLIC ENTRY PROCEDURE[ handle: RetrieveXDefs.Handle,
              buffer: DESCRIPTOR FOR PACKED ARRAY OF CHARACTER ]
           RETURNS[ bytes: CARDINAL ] =
   BEGIN
   -- returns 0 forever if we're at the end of the item --
   ENABLE { ProtocolDefs.Failed => Fail[why]; UNWIND => NULL };
   why: Stream.CompletionCode;
   sst: Stream.SubSequenceType;
   IF handle.state # inBody THEN ERROR WrongCallSequence[];
   [bytes, why, sst] ← Stream.GetBlock[handle.currentStr,
      [ BASE[buffer], 0, IF LENGTH[buffer] > handle.header.length
                         THEN Inline.LowHalf[handle.header.length]
                         ELSE LENGTH[buffer] ] !
      PupStream.StreamClosing, Stream.TimeOut => Fail[communicationError]];
   IF why # normal THEN ERROR;
   handle.header.length ← handle.header.length - bytes;
   END;

InnerSkipItem: INTERNAL PROCEDURE[ handle: RetrieveXDefs.Handle ] =
   BEGIN
   length: CARDINAL = 128;
   buffer: PACKED ARRAY[0..length)OF CHARACTER;
   used: CARDINAL;
   why: Stream.CompletionCode;
   sst: Stream.SubSequenceType;
   IF handle.state # inBody THEN ERROR WrongCallSequence[];
   IF handle.spareByte THEN handle.header.length ← handle.header.length+1;
   handle.spareByte ← FALSE;
   WHILE handle.header.length > 0
   DO BEGIN
      wanted: CARDINAL = IF handle.header.length > length
         THEN length
         ELSE Inline.LowHalf[handle.header.length];
      [used, why, sst] ← Stream.GetBlock[handle.currentStr,
         [@buffer, 0, wanted] ! PupStream.StreamClosing, Stream.TimeOut =>
                Fail[communicationError] ];
      IF why # normal THEN ERROR;
      handle.header.length ← handle.header.length - used;
      END;
   ENDLOOP;
   END;

GVWriteTOC: PUBLIC ENTRY PROCEDURE[handle: RetrieveXDefs.Handle,
                                   text: STRING] =
   BEGIN
   ENABLE { ProtocolDefs.Failed => Fail[why]; UNWIND => NULL };
   WHILE handle.state = inBody DO [] ← InnerNextItem[handle]; ENDLOOP;
   IF handle.state # beforeBody THEN ERROR WrongCallSequence[];
   ProtocolDefs.SendMSOperation[handle.currentStr, writeTOC];
   ProtocolDefs.SendRemark[handle.currentStr, text];
   ProtocolDefs.SendNow[handle.currentStr];
   ProtocolDefs.ReceiveAck[handle.currentStr];
   handle.state ← beforeBody;
   END;

GVDeleteMessage: PUBLIC ENTRY PROCEDURE[handle: RetrieveXDefs.Handle] =
   BEGIN
   ENABLE { ProtocolDefs.Failed => Fail[why]; UNWIND => NULL };
   WHILE handle.state = inBody DO [] ← InnerNextItem[handle]; ENDLOOP;
   IF handle.state # beforeBody THEN ERROR WrongCallSequence[];
   ProtocolDefs.SendMSOperation[handle.currentStr, deleteMessage];
   ProtocolDefs.SendNow[handle.currentStr];
   ProtocolDefs.ReceiveAck[handle.currentStr];
   handle.state ← beforeBody;
   END;

GVAccept: PUBLIC INTERNAL PROCEDURE[ handle: RetrieveXDefs.Handle ] =
   BEGIN
   ENABLE { ProtocolDefs.Failed => Fail[why]; UNWIND => NULL };
   IF handle.state # afterMBX THEN ERROR WrongCallSequence[];
   ProtocolDefs.SendMSOperation[handle.currentStr, flushMBX];
   ProtocolDefs.SendNow[handle.currentStr];
   ProtocolDefs.ReceiveAck[handle.currentStr];
   END;

GVClose: PUBLIC INTERNAL PROCEDURE[handle: RetrieveXDefs.Handle] =
   BEGIN
   IF handle.currentStr # NIL
   THEN ProtocolDefs.DestroyStream[handle.currentStr];
   handle.currentStr ← NIL; handle.state ← beforeMBX;
   END;

ProtocolDefs.Init[];


END.