-- GrapevineUser (Cedar) - retrieval of messages

-- GVRetrieveImpl.mesa

-- Andrew Birrell September 1, 1982 10:15 am

DIRECTORY
BodyDefs  USING[ maxRNameLength, RName],
ConvertUnsafe USING[ AppendRope, ToRope ],
GVBasics  USING[ ItemHeader, RName, Timestamp ],
GVRetrieve  USING[],
PupStream  USING[ StreamClosing ],
RetrieveDefs,
RetrieveXDefs USING[ Handle ],
Rope    USING[ Cat, Length, ROPE, Substr ],
Stream   USING[ CompletionCode, GetBlock, SubSequenceType, TimeOut ];


GVRetrieveImpl: CEDAR MONITOR LOCKS handle USING handle: Handle
IMPORTS ConvertUnsafe, RetrieveDefs, Rope, PupStream, Stream
EXPORTS GVRetrieve =

BEGIN

ROPE: TYPE = Rope.ROPE;

Handle: TYPE = REF Object;
Object: PUBLIC TYPE = MONITORED RECORD[
h: RetrieveDefs.Handle,
p: RetrieveDefs.AccessProcs ← NULL];


Create: PUBLIC PROC[pollingInterval: CARDINAL,
  reportChanges: PROCEDURE[RetrieveDefs.MBXState] ← NIL]
  RETURNS[Handle] = TRUSTED
BEGIN
RETURN[NEW[Object ← [h: RetrieveDefs.Create[pollingInterval, reportChanges]]]]
END;


Close: PUBLIC ENTRY PROC[handle: Handle] = TRUSTED
{ RetrieveDefs.Destroy[handle.h]; handle.h ← LOOPHOLE[NIL]--sorry!-- };



NewUser: PUBLIC PROC[ handle: Handle, user: GVBasics.RName, password: ROPE] = TRUSTED
BEGIN
u: BodyDefs.RName = [BodyDefs.maxRNameLength];
p: STRING = [64];
ConvertUnsafe.AppendRope[p,password];
IF Rope.Length[user] <= BodyDefs.maxRNameLength
THEN ConvertUnsafe.AppendRope[u, user];
--ELSE leave "u" empty, which is an invalid user name --
RetrieveDefs.NewUser[handle.h, u, p];
END;

MailboxState: PUBLIC PROC[ handle: Handle] RETURNS[ state: RetrieveDefs.MBXState] = TRUSTED
{ RETURN[ RetrieveDefs.MailboxState[handle.h] ] };


WaitForMail: PUBLIC PROC[ handle: Handle ] = TRUSTED
{ RetrieveDefs.WaitForMail[handle.h] };


NextServer: PUBLIC ENTRY PROC[ handle: Handle ]
RETURNS[ noMore: BOOLEAN,
  state: RetrieveDefs.ServerState,
  type: RetrieveDefs.ServerType ] = TRUSTED
BEGIN
[noMore, state, handle.p] ← RetrieveDefs.NextServer[handle.h];
type ← handle.p.type;
END;


ServerName: PUBLIC PROC[ handle: Handle] RETURNS [serverName: GVBasics.RName] = TRUSTED
BEGIN
n: BodyDefs.RName = [BodyDefs.maxRNameLength];
RetrieveDefs.ServerName[handle.h, n];
RETURN[ConvertUnsafe.ToRope[n]]
END;


Failed: PUBLIC ERROR[why: RetrieveDefs.FailureReason] = CODE;

NotAvailableForMTPServer: ERROR = CODE;


NextMessage: PUBLIC ENTRY PROC[handle: Handle]
RETURNS[msgExists, archived, deleted: BOOLEAN] = TRUSTED
BEGIN
ENABLE { RetrieveDefs.Failed => ERROR Failed[why]; UNWIND => NULL };
[msgExists, archived, deleted] ← handle.p.nextMessage[handle.h]; -- signals if MTP --
END;


StartMessage: PUBLIC ENTRY PROC[handle: Handle ]
RETURNS[postmark: GVBasics.Timestamp,
   sender: GVBasics.RName,
   returnTo: GVBasics.RName] = TRUSTED
BEGIN
ENABLE { RetrieveDefs.Failed => ERROR Failed[why]; UNWIND => NULL };
s: BodyDefs.RName = [BodyDefs.maxRNameLength];
r: BodyDefs.RName = [BodyDefs.maxRNameLength];
WITH p: handle.p SELECT FROM
GV => p.startMessage[handle.h, @postmark, s, r];
MTP => ERROR NotAvailableForMTPServer[];
ENDCASE => ERROR;
RETURN[postmark, ConvertUnsafe.ToRope[s], ConvertUnsafe.ToRope[r]]
END;

NextItem: PUBLIC ENTRY PROC[handle: Handle] RETURNS[GVBasics.ItemHeader] = TRUSTED
BEGIN
ENABLE { RetrieveDefs.Failed => ERROR Failed[why]; UNWIND => NULL };
RETURN[ handle.p.nextItem[handle.h] ]
END;


NextBlock: PUBLIC ENTRY PROC[handle: Handle, maxlength: INT] RETURNS[block: ROPE] = TRUSTED
BEGIN
ENABLE { RetrieveDefs.Failed => ERROR Failed[why]; UNWIND => NULL };
wanted: INT ← maxlength;
block ← NIL;
WHILE wanted > 0
DO sLength: CARDINAL = 120; -- non-critical number; choose to ensure small frame --
s: STRING = [sLength];
limit: CARDINAL = MIN[wanted, sLength];
s.length ← handle.p.nextBlock[handle.h, DESCRIPTOR[@s.text, limit]];
IF s.length = 0 THEN EXIT; -- end of item --
block ← Rope.Cat[block, ConvertUnsafe.ToRope[s]];
wanted ← wanted - s.length;
ENDLOOP;
END;

WrongCallSequence: ERROR = CODE;
ReadingBeyondEndOfItem: ERROR = CODE;

GetBlock: PUBLIC ENTRY PROC[handle: Handle,
block: REF TEXT,
startIndex: NAT,
stopIndexPlusOne: NAT]
RETURNS[nBytesRead: NAT] = TRUSTED
BEGIN
ENABLE UNWIND => NULL;
lowHandle: RetrieveXDefs.Handle = LOOPHOLE[handle.h]; -- sigh! --
why: Stream.CompletionCode;
sst: Stream.SubSequenceType;
IF lowHandle.state # inBody THEN ERROR WrongCallSequence[];
[nBytesRead, why, sst] ← Stream.GetBlock[lowHandle.currentStr,
[ @block.text + SIZE[CARDINAL], startIndex,
MIN[startIndex+lowHandle.header.length, stopIndexPlusOne]] !
PupStream.StreamClosing, Stream.TimeOut => ERROR Failed[communicationFailure] ];
IF why # normal THEN ERROR;
lowHandle.header.length ← lowHandle.header.length - nBytesRead;
END;

GetChar: PUBLIC ENTRY PROC[handle: Handle] RETURNS[CHAR] = TRUSTED
BEGIN
ENABLE UNWIND => NULL;
lowHandle: RetrieveXDefs.Handle = LOOPHOLE[handle.h]; -- sigh! --
why: Stream.CompletionCode;
sst: Stream.SubSequenceType;
buffer: PACKED ARRAY [0..1] OF CHAR;
nBytesRead: CARDINAL;
IF lowHandle.state # inBody THEN ERROR WrongCallSequence[];
IF lowHandle.header.length = 0 THEN ERROR ReadingBeyondEndOfItem[];
[nBytesRead, why, sst] ← Stream.GetBlock[lowHandle.currentStr,
[ @buffer, 0, 1] !
PupStream.StreamClosing, Stream.TimeOut => ERROR Failed[communicationFailure] ];
IF why # normal THEN ERROR;
IF nBytesRead # 1 THEN ERROR;
lowHandle.header.length ← lowHandle.header.length - nBytesRead;
RETURN[buffer[0]]
END;

CharsAvail: PUBLIC ENTRY PROC[handle: Handle] RETURNS[BOOL] = TRUSTED
BEGIN
lowHandle: RetrieveXDefs.Handle = LOOPHOLE[handle.h]; -- sigh! --
RETURN[lowHandle.header.length # 0]
END;

Accept: PUBLIC ENTRY PROC[handle: Handle] = TRUSTED
BEGIN
ENABLE { RetrieveDefs.Failed => ERROR Failed[why]; UNWIND => NULL };
handle.p.accept[handle.h];
END;


ReadTOC: PUBLIC ENTRY PROC[handle: Handle] RETURNS[ ROPE ] = TRUSTED
BEGIN
ENABLE { RetrieveDefs.Failed => ERROR Failed[why]; UNWIND => NULL };
t: BodyDefs.RName = [BodyDefs.maxRNameLength];
WITH p: handle.p SELECT FROM
GV => p.readTOC[handle.h, t];
MTP => ERROR NotAvailableForMTPServer[];
ENDCASE => ERROR;
RETURN[ConvertUnsafe.ToRope[t]]
END;

WriteTOC: PUBLIC ENTRY PROC[handle: Handle, entry: ROPE ] = TRUSTED
BEGIN
ENABLE { RetrieveDefs.Failed => ERROR Failed[why]; UNWIND => NULL };
t: BodyDefs.RName = [BodyDefs.maxRNameLength];
ConvertUnsafe.AppendRope[t, Rope.Substr[entry, 0, BodyDefs.maxRNameLength]];
WITH p: handle.p SELECT FROM
GV => p.writeTOC[handle.h, t];
MTP => ERROR NotAvailableForMTPServer[];
ENDCASE => ERROR;
END;

DeleteMessage: PUBLIC ENTRY PROC[handle: Handle] = TRUSTED
BEGIN
ENABLE { RetrieveDefs.Failed => ERROR Failed[why]; UNWIND => NULL };
WITH p: handle.p SELECT FROM
GV => p.deleteMessage[handle.h];
MTP => ERROR NotAvailableForMTPServer[];
ENDCASE => ERROR;
END;


END.