-- Transport Mechanism: User: Access to mail on GV servers --
-- [Juniper]<DMS>MS>RetrieveGV.mesa
-- 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: 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;
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: 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: 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.