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]; 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 -- 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. XGrapevineUser (Cedar): User: "hot" module for mail polling RetrievePoll.mesa Andrew Birrell June 6, 1983 11:08 am NameInfoSpecialDefs.CleanUp[]; 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 { .... }. Ê ˜Jšœ:™:Jšœ™Jšœ%™%J˜šÏk ˜ Jšœ œœ˜1Jšœ œ ˜Jšœœ&˜3Jšœ œ˜Jšœ œ˜*JšœœA˜YJšœœ˜,Jšœœó˜€Jšœ œE˜SJšœœ ˜—J˜šœ œœœ"˜PJšœK˜RJšœ˜J˜—Jš˜J˜JšœœÏc)œ˜HJšœœž$œ˜CJ˜J˜šÏnœœœ œ%˜LJšœœž$˜2˜2J˜1—J˜:Jšœ œœ"˜;Jšœœž&˜CJšœœ˜JšœS˜ZJšœ˜šœœ˜(Jšœ œ7˜GJšœœ˜9šœ˜ J˜J˜Jšœœœ˜,Jšœž˜7šœ˜ Jšœ˜Jšœ˜Jšœžœ˜;Jšœ=˜@Jšœ ˜%šœœ˜Jšœ.˜2Jšœ˜Jšœœ˜3Jšœœ˜—Jšœ˜Jšœ˜—Jšœœ˜Jš œ˜ Jšœ˜—Jšœ˜!šœœ˜JšœZ˜ZJšœ˜˜$J˜*—Jš˜—šœœ˜ ˜ Jšœžœ˜Jšœœ˜Jšœ5˜9Jšœ˜šœœ˜šœ˜ J˜ J˜"Jšœžœ˜'Jšœ˜!šœ˜ Jšœž ˜)Jšœ˜#Jšœ˜—Jš˜—šœ˜ ˜ ˜(J˜——˜šœ˜J˜J˜J˜J˜—Jšœœ˜—Jš˜——Jšž*œ˜+Jšœ˜—šœž˜*Jšœž˜Jšœ œœž˜6J˜"Jšœ=˜@Jšœ ˜%šœœ˜šœ˜ J˜2J˜0J˜J˜˜(Jšœ ˜Jšœ˜Jšœ ˜$—J˜J˜Jšœ œ˜Jšœ˜——Jšœ˜Jšœ ˜ Jšœž˜9Jšœœ˜Jšœ˜!šœ˜ šœ"˜%J˜—Jšœ ˜%šœœœ˜2Jšœ'˜+—Jšœ˜Jšž-˜-Jšœ+˜-Jšœ˜#Jšœ™Jšœ˜—Jšœ˜—šœž˜&J˜———Jšœ˜J˜Jšœœœœ˜:Jšœ ˜J˜!Jšœœœ˜/Jšœ˜J˜J˜—šŸ œœœ#˜=J˜!Jšœ˜ J˜2Jšœ œœœ˜Jšœ˜Jšœ˜"šœ˜ J˜2Jšœ)˜)Jšœœœ˜J˜"Jšœ2˜2š˜Jšœœ)˜1Jšœ*˜*Jšœœœ˜#J˜&Jšœ1˜1—Jšœ˜J˜!J˜>J˜J˜J˜Jšœ˜—Jšœ˜J˜J˜—šŸœ œ$˜?J˜Jšœœž+˜9šœ˜J˜$Jšœœœœ˜.Jšœœœ ˜/Jšœ˜—Jšœ˜Jšœ˜J˜—šŸœœ œ#˜EJ˜Jšœœ˜Jšœ˜ Jšœœ˜šœ˜ Jšœœ œ˜7Jšœœ˜ Jšœ˜—Jšœœœ˜&šœ˜ J˜Jšœ!œ˜Hšœœ˜Jšœ˜Jšœ˜!šœ˜ Jšœœ˜%Jšœœ ˜+Jšœœ ˜(šœ ˜šœž˜/Jš˜Jšœœ˜Jšœ˜Jšœ˜!Jšœ,ž˜IJšœ˜—˜Jšœ˜Jšœž œ˜-—˜ J˜&—˜ J˜#—˜ J˜#—J˜JšœB™BJšœ?™?šœ<™<šœœœ˜+J˜J˜˜J˜%——Jšœœ˜—Jšœœ˜Jšœ˜———Jšœ˜Jšœ˜—Jšœœ˜ Jšœ˜J˜J˜—šŸœœœ œ#˜LJ˜J˜Jš˜Jšœœ˜Jš œ˜ Jšœ˜šœœ˜˜ Jš˜Jšœ˜Jšœ7˜;J˜4Jšœ˜—˜Jš˜šœ ˜J˜?J˜B—Jšœœ˜Jšœ˜—˜ Jš˜Jšœ˜Jšœ5˜9J˜6Jšœ˜—Jšœœ˜—J˜Jšœ˜šœž0˜5J˜—š˜Jšž'˜'Jšœ œœ˜Jšœ œœ˜Jšœ=˜@Jšœ ˜%šœœœœ˜0Jšœœ œ˜*—Jšœ˜Jšž-˜-šœœœ˜%J˜*J˜)Jšœœ˜J˜Jšœ˜——Jšœ˜Jšœ˜J˜—šŸ œœœ œ#˜IJ˜Jš˜Jš œ˜ Jšœœ˜*Jšœ˜J˜Jšœ˜J˜J˜J˜—Jšœ˜J˜J˜—…—#Â/·