RetrieveGV.mesa: Access to mail on GV servers
Copyright © 1985 by Xerox Corporation. All rights reserved.
Birrell, August 29, 1983 12:04 pm
MBrown, September 17, 1983 8:12 pm
DIRECTORY
GVBasics USING[ ItemHeader, ItemLength, RName, Timestamp ],
GVNames USING[ AuthenticateInfo ],
GVProtocol USING[ Close, CreateStream, Failed, FailureReason, Handle, ReceiveAck, ReceiveBoolean, ReceiveByte, ReceiveBytes, ReceiveCount, ReceiveItemHeader, ReceiveRemark, ReceiveRName, ReceiveTimestamp, SendNow, SendMSOperation, SendPassword, SendRemark, SendRName ],
GVRetrieve USING[ Failed, FailureReason ],
GVRetrieveInternal USING[ Handle, HandleObject, NoteChangedMBX, ServerAddress ],
IO USING[ CharsAvail, CreateStreamProcs, CreateStream, EndOfStream, STREAM, StreamProcs, UnsafeBlock ],
PupDefs USING[ PupAddress ],
PupStream USING[ ConsumeMark, StreamClosing, TimeOut ],
Rope USING[ ROPE ];
RetrieveGV: CEDAR MONITOR LOCKS handle USING handle: GVRetrieveInternal.Handle
IMPORTS GVProtocol, GVRetrieve, GVRetrieveInternal, IO, PupStream
EXPORTS GVRetrieve, GVRetrieveInternal = BEGIN
Handle: PUBLIC TYPE = GVRetrieveInternal.Handle;
HandleObject: PUBLIC TYPE = GVRetrieveInternal.HandleObject;
Fail: PROC[why: GVProtocol.FailureReason, text: Rope.ROPE] = {
ERROR GVRetrieve.Failed[IF why = protocolError
THEN unknownFailure
ELSE communicationFailure, text] };
WrongCallSequence: ERROR = CODE;
NextMessage: PUBLIC ENTRY PROCEDURE[ handle: GVRetrieveInternal.Handle ]
RETURNS[ msgExists: BOOLEAN,
archived: BOOLEAN,
deleted: BOOLEAN ] = BEGIN
ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL };
IF handle.state = beforeMBX
THEN BEGIN
addr: PupDefs.PupAddress ← GVRetrieveInternal.ServerAddress[handle];
-- we exited by a signal if the address wasn't available --
IF handle.currentStr # NIL THEN ERROR;
IF handle.currentMBX.type # GV
THEN ERROR GVRetrieve.Failed[communicationFailure, "Can't access MTP mailboxes from Cedar"];
handle.currentStr ← GVProtocol.CreateStream[handle.currentMBX.addr, MSRetrieve];
GVProtocol.SendMSOperation[handle.currentStr, openMBX];
GVProtocol.SendRName[handle.currentStr, handle.userName];
GVProtocol.SendPassword[str:handle.currentStr, pw:handle.userKey];
GVProtocol.SendNow[handle.currentStr];
BEGIN
info: GVNames.AuthenticateInfo =
LOOPHOLE[GVProtocol.ReceiveByte[handle.currentStr]];
SELECT info FROM
allDown => ERROR GVRetrieve.Failed[communicationFailure, "You mailbox server can't contact an authentication server"];
notFound, group, badPwd =>
ERROR GVRetrieve.Failed[badCredentials, "Your mailbox server doesn't like your name or password"];
individual => NULL;
ENDCASE => ERROR GVRetrieve.Failed[unknownFailure, "Unknown return code from mailbox"];
END;
handle.messages ← GVProtocol.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;
GVProtocol.SendMSOperation[handle.currentStr, nextMessage];
GVProtocol.SendNow[handle.currentStr];
msgExists ← GVProtocol.ReceiveBoolean[handle.currentStr];
archived ← GVProtocol.ReceiveBoolean[handle.currentStr];
deleted ← GVProtocol.ReceiveBoolean[handle.currentStr];
IF msgExists
THEN handle.state ← beforeBody
ELSE ERROR;
END;
END;
ReadTOC: PUBLIC ENTRY PROC[handle: GVRetrieveInternal.Handle] RETURNS[ Rope.ROPE ] =
BEGIN
ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL };
WHILE handle.state = inBody DO [] ← InnerNextItem[handle]; ENDLOOP;
IF handle.state # beforeBody THEN ERROR WrongCallSequence[];
GVProtocol.SendMSOperation[handle.currentStr, readTOC];
GVProtocol.SendNow[handle.currentStr];
RETURN[GVProtocol.ReceiveRemark[handle.currentStr]];
END;
StartMessage: PUBLIC ENTRY PROCEDURE[handle: GVRetrieveInternal.Handle] RETURNS[
postmark: GVBasics.Timestamp,
sender: GVBasics.RName,
returnTo: GVBasics.RName ] =
BEGIN
ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL };
[postmark, sender, returnTo] ← InnerStartMessage[handle];
END;
InnerStartMessage: INTERNAL PROCEDURE[handle: GVRetrieveInternal.Handle] RETURNS[
postmark: GVBasics.Timestamp,
sender: GVBasics.RName,
returnTo: GVBasics.RName ] =
BEGIN
WHILE handle.state = inBody DO [] ← InnerNextItem[handle]; ENDLOOP;
IF handle.state # beforeBody THEN ERROR WrongCallSequence[];
GVProtocol.SendMSOperation[handle.currentStr, readMessage];
GVProtocol.SendNow[handle.currentStr];
handle.state ← inBody;
handle.header ← GVProtocol.ReceiveItemHeader[handle.currentStr ];
IF handle.header.type # PostMark THEN ERROR;
postmark ← GVProtocol.ReceiveTimestamp[handle.currentStr];
handle.header ← GVProtocol.ReceiveItemHeader[handle.currentStr];
IF handle.header.type # Sender THEN ERROR;
sender ← GVProtocol.ReceiveRName[handle.currentStr];
handle.header ← GVProtocol.ReceiveItemHeader[handle.currentStr];
IF handle.header.type # ReturnTo THEN ERROR;
returnTo GVProtocol.ReceiveRName[handle.currentStr];
handle.header.length ← 0; -- no more data in this item --
END;
NextItem: PUBLIC ENTRY PROCEDURE[handle: GVRetrieveInternal.Handle]
RETURNS[itemHeader: GVBasics.ItemHeader] =
BEGIN
ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL };
IF handle.state # inBody THEN [] ← InnerStartMessage[handle];
RETURN[ InnerNextItem[handle] ]
END;
InnerNextItem: INTERNAL PROCEDURE[handle: GVRetrieveInternal.Handle]
RETURNS[itemHeader: GVBasics.ItemHeader ] =
BEGIN
IF handle.state # inBody THEN ERROR WrongCallSequence[];
IF handle.header.length > 0 OR handle.spareByte THEN InnerSkipItem[handle];
handle.header ← GVProtocol.ReceiveItemHeader[handle.currentStr];
handle.itemLength ← handle.header.length;
-- arrange for InnerSkip to include padding byte --
handle.spareByte ← handle.header.length MOD 2 # 0;
IF handle.header.type = LastItem
THEN BEGIN
IF handle.header.length > 0 THEN InnerSkipItem[handle];
[] ← PupStream.ConsumeMark[handle.currentStr !
PupStream.StreamClosing => Fail[communicationError, text];
PupStream.TimeOut => Fail[communicationError, "Mailbox server not sending data"]];
handle.state ← beforeBody;
END;
itemHeader ← handle.header;
END;
GVGetChar: PROC[self: IO.STREAM] RETURNS[CHAR] =
{ RETURN[EntryGetChar[NARROW[self.streamData]]] };
EntryGetChar: ENTRY PROC[handle: GVRetrieveInternal.Handle] RETURNS[CHAR] =
BEGIN
ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL };
IF handle.header.length = 0 THEN ERROR IO.EndOfStream[handle.currentStr];
RETURN[ LOOPHOLE[GVProtocol.ReceiveByte[handle.currentStr]] ]
END;
GVUnsafeGetBlock: UNSAFE PROCEDURE[ self: IO.STREAM, block: IO.UnsafeBlock ] RETURNS[ nBytesRead: INT ] =
UNCHECKED{ RETURN[EntryUnsafeGetBlock[NARROW[self.streamData], block]] };
EntryUnsafeGetBlock: ENTRY UNSAFE PROC[handle: GVRetrieveInternal.Handle, block: IO.UnsafeBlock] RETURNS[nBytesRead: INT] =
UNCHECKED BEGIN
-- returns 0 forever if we're at the end of the item --
ENABLE { GVProtocol.Failed => CHECKED{ Fail[why, text] }; UNWIND => NULL };
amount: INT = MIN[block.count, handle.header.length];
IF handle.state # inBody THEN ERROR WrongCallSequence[];
GVProtocol.ReceiveBytes[handle.currentStr,
[block.base, block.startIndex, block.startIndex+amount]];
handle.header.length ← handle.header.length - amount;
RETURN[amount]
END;
GVEndOf: PROC[self: IO.STREAM] RETURNS[BOOL] =
{ RETURN[EntryEndOf[NARROW[self.streamData]]] };
EntryEndOf: ENTRY PROC[handle: GVRetrieveInternal.Handle] RETURNS[BOOL] =
BEGIN
RETURN[handle.header.length = 0]
END;
GVGetLength: PROC[self: IO.STREAM] RETURNS[length: INT] =
{ RETURN[EntryGetLength[NARROW[self.streamData]]] };
EntryGetLength: ENTRY PROC[handle: GVRetrieveInternal.Handle] RETURNS[length: INT] =
BEGIN
RETURN[handle.itemLength]
END;
GVCharsAvail: PROC[self: IO.STREAM, wait: BOOL] RETURNS[INT] =
{ RETURN[IF EntryCharsAvail[NARROW[self.streamData]] THEN 1 ELSE 0] };
EntryCharsAvail: ENTRY PROC[handle: GVRetrieveInternal.Handle] RETURNS[BOOL] =
BEGIN
RETURN[handle.header.length > 0 AND handle.currentStr.CharsAvail[]>0]
END;
GVCloseStream: PROC[self: IO.STREAM, abort: BOOLFALSE] =
{ EntryCloseStream[NARROW[self.streamData], abort] };
EntryCloseStream: ENTRY PROC[handle: GVRetrieveInternal.Handle, abort: BOOLFALSE] =
BEGIN
InnerSkipItem[handle]
END;
myStreamProcs: REF IO.StreamProcs = IO.CreateStreamProcs[
variety: $input, class: $Grapevine,
getChar: GVGetChar,
endOf: GVEndOf,
getLength: GVGetLength,
charsAvail: GVCharsAvail,
unsafeGetBlock: GVUnsafeGetBlock,
close: GVCloseStream ];
GetItem: PUBLIC ENTRY PROC[handle: GVRetrieveInternal.Handle] RETURNS[IO.STREAM] =
BEGIN
IF handle.state # inBody THEN ERROR WrongCallSequence[];
RETURN[IO.CreateStream[myStreamProcs, handle] ];
END;
InnerSkipItem: INTERNAL PROCEDURE[ handle: GVRetrieveInternal.Handle ] =
BEGIN
length: CARDINAL = 128;
buffer: PACKED ARRAY[0..length)OF CHARACTER;
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 TRUSTED BEGIN
wanted: INT = MIN[handle.header.length, length];
GVProtocol.ReceiveBytes[handle.currentStr, [LOOPHOLE[LONG[@buffer]], 0, wanted]
! GVProtocol.Failed => Fail[communicationError, text] ];
handle.header.length ← handle.header.length - wanted;
END;
ENDLOOP;
END;
WriteTOC: PUBLIC ENTRY PROCEDURE[handle: GVRetrieveInternal.Handle,
entry: Rope.ROPE] =
BEGIN
ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL };
WHILE handle.state = inBody DO [] ← InnerNextItem[handle]; ENDLOOP;
IF handle.state # beforeBody THEN ERROR WrongCallSequence[];
GVProtocol.SendMSOperation[handle.currentStr, writeTOC];
GVProtocol.SendRemark[handle.currentStr, entry];
GVProtocol.SendNow[handle.currentStr];
GVProtocol.ReceiveAck[handle.currentStr];
handle.state ← beforeBody;
END;
DeleteMessage: PUBLIC ENTRY PROCEDURE[handle: GVRetrieveInternal.Handle] =
BEGIN
ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL };
WHILE handle.state = inBody DO [] ← InnerNextItem[handle]; ENDLOOP;
IF handle.state # beforeBody THEN ERROR WrongCallSequence[];
GVProtocol.SendMSOperation[handle.currentStr, deleteMessage];
GVProtocol.SendNow[handle.currentStr];
GVProtocol.ReceiveAck[handle.currentStr];
handle.state ← beforeBody;
END;
Accept: PUBLIC ENTRY PROCEDURE[ handle: GVRetrieveInternal.Handle ] =
BEGIN
ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL };
IF handle.state # afterMBX THEN ERROR WrongCallSequence[];
GVProtocol.SendMSOperation[handle.currentStr, flushMBX];
GVProtocol.SendNow[handle.currentStr];
GVProtocol.ReceiveAck[handle.currentStr];
GVRetrieveInternal.NoteChangedMBX[handle, handle.currentMBX, empty];
END;
GVClose: PUBLIC INTERNAL PROCEDURE[handle: GVRetrieveInternal.Handle] =
BEGIN
ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL };
IF handle.currentStr # NIL
THEN GVProtocol.Close[handle.currentStr];
handle.currentStr ← NIL; handle.state ← beforeMBX;
END;
END.