RetrievePoll.mesa: User: "hot" module for mail polling
Copyright © 1985 by Xerox Corporation. All rights reserved.
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: BOOLEANTRUE; --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: BOOLEANTRUE;
emptyFound: BOOLEANFALSE;
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.