-- Copyright (C) 1981, 1984, 1985  by Xerox Corporation. All rights reserved. 
-- RetrieveGV.mesa, Transport Mechanism: User: Access to mail on GV servers --

-- HGM, 15-Sep-85  7:58:31
-- Mark Johnson    12-Nov-81 10:22:52 --
-- 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: LONG 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: LONG 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: LONG 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;
      bufferAddr: LONG POINTER ← @buffer;
      IF handle.header.length > 0 THEN InnerSkipItem[handle];
      WHILE why # sstChange DO
        [, why, ] ← Stream.GetBlock[
          handle.currentStr, [LOOPHOLE[bufferAddr], 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: LONG 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;
    bufferAddr: LONG POINTER ← BASE[buffer];
    IF handle.state # inBody THEN ERROR WrongCallSequence[];
    [bytes, why, sst] ← Stream.GetBlock[
      handle.currentStr, [
      LOOPHOLE[bufferAddr], 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;
    bufferAddr: LONG POINTER ← @buffer;
    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, [LOOPHOLE[bufferAddr], 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: LONG 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.