GrapevineUser (Cedar): User: "hot" module for mail polling
RetrievePoll.mesa
Andrew Birrell June 6, 1983 11:08 am
DIRECTORY
BasicTime USING[ earliestGMT, GMT, Now, Period ],
GVBasics USING[ RName ],
GVNames USING[ AuthenticateInfo, AuthenticateKey ],
GVProtocol USING[ GetSocket ],
GVRetrieve USING[ MBXState, ServerState ],
GVRetrieveInternal USING[ FindAddress, FindRegistryAndMailboxes, Handle, MBXPtr, noMBX ],
Process USING[ SecondsToTicks, SetTimeout ],
PupDefs USING[ AppendRopeToPupBuffer, GetFreePupBuffer, GetPupContentsBytes, PupAddress, PupBuffer, PupRouterSendThis, PupSocket, PupSocketDestroy, PupSocketKick, PupSocketMake, MoveRopeToPupBuffer, ReturnFreePupBuffer, SetPupContentsWords, veryLongWait ],
PupTypes USING[ fillInSocketID, miscSrvSoc, userAuthBad, userAuthOk, userAuthReq ],
Rope USING[ Length ];
RetrievePoll:
CEDAR MONITOR
LOCKS handle
USING handle: GVRetrieveInternal.Handle
IMPORTS BasicTime, GVNames, GVProtocol, GVRetrieveInternal, Process, PupDefs, Rope
EXPORTS GVRetrieveInternal =
BEGIN
transmitLimit: CARDINAL = 5 -- max number of transmissions of poll --;
retransmitDelay: CARDINAL = 2 -- seconds bfore re-transmittting --;
SendPollProcess:
PUBLIC
ENTRY
PROCEDURE[handle: GVRetrieveInternal.Handle] =
TRUSTED BEGIN -- main program for sending polls --
socket: PupDefs.PupSocket = PupDefs.PupSocketMake[
PupTypes.fillInSocketID, , PupDefs.veryLongWait];
socketAddr: PupDefs.PupAddress = socket.getLocalAddress[];
replyPoll: PROCESS = FORK PollReplyProcess[handle, socket];
transmissions: CARDINAL ← 0; -- number of retransmissions so far --
handle.pollReplying ← TRUE;
TRUSTED{ Process.SetTimeout[@handle.pollCond, Process.SecondsToTicks[retransmitDelay] ] };
WHILE handle.pollWanted
DO now: BasicTime.
GMT = BasicTime.Now[];
elapsedSecs: INT = BasicTime.Period[from: handle.pollStarted, to: now];
IF handle.newPollWanted OR elapsedSecs >= handle.interval
THEN
BEGIN
handle.pollStarted ← now;
transmissions ← 0;
IF handle.mbxState NOT IN [userOK..notEmpty]
THEN handle.mbxState ← unknown --retry authentication--
ELSE
BEGIN
IF handle.notEmptyMBXCount = 0
AND handle.unknownMBXCount # 0
THEN handle.mbxState ← userOK --was someEmpty or allDown--;
FOR this: GVRetrieveInternal.MBXPtr ← handle.MBXChain, this.next
WHILE this # GVRetrieveInternal.noMBX
DO
IF this.addrState = unknown
THEN GVRetrieveInternal.FindAddress[handle, this];
IF this.addrState = known
AND (handle.newPollWanted OR this.state # notEmpty)
THEN this.replyWanted ← TRUE;
ENDLOOP;
END;
handle.newPollWanted ← FALSE;
BROADCAST handle.mbxStateChange;
END;
IF transmissions >= transmitLimit
THEN
TRUSTED
BEGIN
Process.SetTimeout[@handle.pollCond, Process.SecondsToTicks[handle.interval-elapsedSecs]];
WAIT handle.pollCond;
Process.SetTimeout[@handle.pollCond,
Process.SecondsToTicks[retransmitDelay] ];
END
ELSE
SELECT handle.mbxState
FROM
unknown =>
BEGIN -- authenticate --
IF NOT handle.mbxKnown
THEN GVRetrieveInternal.FindRegistryAndMailboxes[handle];
IF handle.mbxState = unknown
THEN
IF handle.registry =
MTP
THEN
BEGIN
SendAuthReq[handle, socketAddr];
transmissions ← transmissions + 1;
WAIT handle.pollCond--wait for reply--;
IF transmissions >= transmitLimit
THEN
BEGIN
IF handle.mbxState = unknown --no reply--
THEN SetMBXState[handle, cantAuth];
END;
END
ELSE
BEGIN
info: GVNames.AuthenticateInfo =
GVNames.AuthenticateKey[handle.userName,
handle.userKey];
SetMBXState[handle,
SELECT info
FROM
individual => userOK,
badPwd => badPwd,
group, notFound => badName,
allDown => cantAuth,
ENDCASE => ERROR];
END
--ELSE "FindRegistryAndMailboxes" failed--;
END;
IN [userOK..notEmpty] =>
--authenticated--
BEGIN -- poll the mailboxes --
finished: BOOLEAN ← TRUE; --whether all have replied--
transmissions ← transmissions + 1;
FOR this: GVRetrieveInternal.MBXPtr ← handle.MBXChain, this.next
WHILE this # GVRetrieveInternal.noMBX
DO
IF this.replyWanted
THEN
BEGIN
b: PupDefs.PupBuffer = PupDefs.GetFreePupBuffer[];
PupDefs.MoveRopeToPupBuffer[b, handle.userName];
b.pupType ← mailCheckLaurel;
b.pupID ← handle.pollID;
b.dest ← [this.addr.net, this.addr.host,
IF this.type = MTP
THEN PupTypes.miscSrvSoc
ELSE GVProtocol.GetSocket[MSPoll] ];
b.source ← socketAddr;
PupDefs.PupRouterSendThis[b];
finished ← FALSE;
END;
ENDLOOP;
IF finished
THEN transmissions ← transmitLimit -- all have replied --
ELSE WAIT handle.pollCond;
IF transmissions >= transmitLimit
THEN
BEGIN
FOR this: GVRetrieveInternal.MBXPtr ←
handle.MBXChain, this.next
WHILE this # GVRetrieveInternal.noMBX
DO
IF this.addrState = unknown
OR this.replyWanted
THEN NoteChangedMBX[handle, this, unknown];
ENDLOOP;
-- special case for user with no mailboxes --
IF handle.MBXChain = GVRetrieveInternal.noMBX
THEN SetMBXState[handle, allEmpty];
NameInfoSpecialDefs.CleanUp[];
END;
END;
ENDCASE =>
-- couldn't authenticate --
transmissions ← transmitLimit;
ENDLOOP;
PupDefs.PupSocketKick[socket];
WHILE handle.pollReplying DO WAIT handle.pollCond ENDLOOP;
JOIN replyPoll;
PupDefs.PupSocketDestroy[socket];
handle.polling ← FALSE; NOTIFY handle.pollCond;
END;
SendAuthReq:
INTERNAL
PROC[handle: GVRetrieveInternal.Handle,
socketAddr: PupDefs.PupAddress] =
TRUSTED BEGIN
this: GVRetrieveInternal.MBXPtr = handle.MBXChain;
IF this.type # MTP THEN ERROR;
IF this.addrState # known
THEN SetMBXState[handle, cantAuth]
ELSE
BEGIN
b: PupDefs.PupBuffer = PupDefs.GetFreePupBuffer[];
b.pupWords[0] ← handle.userName.Length[];
b.pupWords[1] ← LAST[CARDINAL];
PupDefs.SetPupContentsWords[b, 2];
PupDefs.AppendRopeToPupBuffer[b, handle.userName];
BEGIN
pos: INT = (PupDefs.GetPupContentsBytes[b] +1)/2;
b.pupWords[pos] ← handle.userPwd.Length[];
b.pupWords[pos+1] ← LAST[CARDINAL];
PupDefs.SetPupContentsWords[b, pos+2];
PupDefs.AppendRopeToPupBuffer[b, handle.userPwd];
END;
b.pupType ← PupTypes.userAuthReq;
b.dest ← [this.addr.net, this.addr.host, PupTypes.miscSrvSoc];
b.pupID ← handle.pollID;
b.source ← socketAddr;
PupDefs.PupRouterSendThis[b];
END;
END;
PollReplyProcess:
PROCEDURE[ handle: GVRetrieveInternal.Handle,
socket: PupDefs.PupSocket ] =
TRUSTED BEGIN -- main program for replies to the polls --
DO
BEGIN
b: PupDefs.PupBuffer = socket.get[];
IF NOT ConsiderPollReply[handle, b] THEN EXIT;
IF b # NIL THEN PupDefs.ReturnFreePupBuffer[b];
END;
ENDLOOP;
END;
ConsiderPollReply:
ENTRY
PROCEDURE[handle: GVRetrieveInternal.Handle,
b: PupDefs.PupBuffer]
RETURNS[BOOLEAN] =
TRUSTED BEGIN
IF NOT handle.pollWanted
THEN
BEGIN
handle.pollReplying ← FALSE; BROADCAST handle.pollCond;
RETURN[FALSE]
END;
IF b # NIL AND b.pupID = handle.pollID
THEN
BEGIN
mbx: GVRetrieveInternal.MBXPtr;
FOR mbx ← handle.MBXChain, mbx.next UNTIL mbx = GVRetrieveInternal.noMBX
DO
IF mbx.addrState = known
AND mbx.addr.net = b.source.net
AND mbx.addr.host = b.source.host
THEN
BEGIN
noProcessPupErrorCode: CARDINAL = 2B;
cantGetTherePupErrorCode: CARDINAL = 1002B;
eightHopsPupErrorCode: CARDINAL = 1004B;
SELECT b.pupType
FROM
PupTypes.userAuthOk =>
-- MTP authentication --
BEGIN
mbx.replyWanted ← TRUE;
IF handle.mbxState = unknown
THEN SetMBXState[handle, userOK];
handle.pollStarted ← BasicTime.earliestGMT; --force new poll for mbx's --
END;
PupTypes.userAuthBad =>
IF handle.mbxState = unknown
THEN SetMBXState[handle, badPwd--badName?--];
mailIsNew =>
NoteChangedMBX[handle, mbx, notEmpty];
mailNotNew =>
NoteChangedMBX[handle, mbx, empty];
mailError =>
NoteChangedMBX[handle, mbx, empty];
error =>
The following LOOPHOLE brought to you courtesy of the Alto version
of PupTypes has PupErrorCode: TYPE = RECORD[WORD] and the Pilot
version has PupErrorCode: TYPE = MACHINE DEPENDENT { .... }.
SELECT
LOOPHOLE[b.errorCode,
CARDINAL]
FROM
noProcessPupErrorCode,
cantGetTherePupErrorCode,
eightHopsPupErrorCode =>
NoteChangedMBX[handle, mbx, unknown];
ENDCASE => NULL;
ENDCASE => NULL;
END;
ENDLOOP;
END;
RETURN[TRUE]
END;
NoteChangedMBX:
PUBLIC
INTERNAL
PROCEDURE[handle: GVRetrieveInternal.Handle,
mbx: GVRetrieveInternal.MBXPtr,
new: GVRetrieve.ServerState] =
BEGIN
mbx.replyWanted ← FALSE;
BROADCAST handle.mbxStateChange;
IF new # mbx.state
THEN
SELECT new
FROM
unknown =>
BEGIN
IF mbx.state = notEmpty
THEN handle.notEmptyMBXCount ← handle.notEmptyMBXCount - 1;
handle.unknownMBXCount ← handle.unknownMBXCount + 1;
END;
empty =>
BEGIN
SELECT mbx.state
FROM
unknown => handle.unknownMBXCount ← handle.unknownMBXCount - 1;
notEmpty => handle.notEmptyMBXCount ← handle.notEmptyMBXCount - 1;
ENDCASE => NULL;
END;
notEmpty =>
BEGIN
IF mbx.state = unknown
THEN handle.unknownMBXCount ← handle.unknownMBXCount - 1;
handle.notEmptyMBXCount ← handle.notEmptyMBXCount + 1;
END;
ENDCASE => ERROR;
mbx.state ← new;
IF new = unknown
THEN
-- if server is down, its address may change! --
mbx.addrState ← unknown;
BEGIN
-- consider whether poll is complete --
complete: BOOLEAN ← TRUE;
emptyFound: BOOLEAN ← FALSE;
FOR this: GVRetrieveInternal.MBXPtr ← handle.MBXChain, this.next
WHILE this # GVRetrieveInternal.noMBX
DO
IF this.state = empty
THEN emptyFound ←
TRUE;
IF this.replyWanted THEN complete ← FALSE;
ENDLOOP;
-- definitive calculation of global state! --
SetMBXState[ handle,
SELECT
TRUE
FROM
(handle.notEmptyMBXCount # 0) => notEmpty,
(handle.unknownMBXCount = 0) => allEmpty,
(NOT complete) => userOK,
emptyFound => someEmpty
ENDCASE => allDown ];
END;
END;
SetMBXState:
PUBLIC
INTERNAL
PROCEDURE[handle: GVRetrieveInternal.Handle,
state: GVRetrieve.MBXState] =
BEGIN
BROADCAST handle.mbxStateChange;
IF state # userOK AND handle.changes # NIL
THEN handle.changes[state];
handle.mbxState ← state;
END;
END.