-- 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.