<> <> <> 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 <
> 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; <> IF handle.MBXChain = GVRetrieveInternal.noMBX THEN SetMBXState[handle, allEmpty]; <> 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 => <> <> <> 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 <> 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; <> 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.