-- Transport Mechanism: User: "hot" module for mail polling -- -- [Juniper]<DMS>MS>RetrievePoll.mesa -- Andrew Birrell 26-Feb-81 11:55:54 -- DIRECTORY BodyDefs USING[ RName ], Inline USING[ COPY, HighHalf, LongCOPY, LowHalf ], NameInfoDefs USING[ AuthenticateInfo, AuthenticateKey ], NameInfoSpecialDefs USING[ CleanUp ], Process USING[ SecondsToTicks, SetTimeout ], ProtocolDefs USING[ mailServerPollingSocket ], PupDefs USING[ GetFreePupBuffer, PupAddress, PupBuffer, PupRouterSendThis, PupSocket, PupSocketDestroy, PupSocketKick, PupSocketMake, ReturnFreePupBuffer, SetPupContentsBytes, SetPupContentsWords, veryLongWait ], PupTypes USING[ fillInSocketID, miscSrvSoc, userAuthBad, userAuthOk, userAuthReq ], RetrieveDefs USING[ MBXState, ServerState ], RetrieveXDefs USING[ FindAddress, FindRegistryAndMailboxes, Handle, MBXPtr, noMBX ], System USING[ GreenwichMeanTime ], Time USING[ Current ]; RetrievePoll: MONITOR LOCKS handle USING handle: RetrieveXDefs.Handle IMPORTS Inline, NameInfoDefs, NameInfoSpecialDefs, Process, ProtocolDefs, PupDefs, RetrieveXDefs, Time EXPORTS RetrieveXDefs = BEGIN Copy: PROC[from: POINTER, to: LONG POINTER, nwords: CARDINAL] = BEGIN -- This procedure is required because PupBuffer's are LONG in Pilot -- and Inline.LongCopy doesn't work on Alto I's. -- I think this procedure will work with a non-zero MDS base -- longFrom: LONG POINTER = LONG[from]; IF Inline.HighHalf[longFrom] = Inline.HighHalf[to] THEN -- "to" is in the MDS -- Inline.COPY[from: from, to: Inline.LowHalf[to-(longFrom-from)], nwords: nwords] ELSE -- "to" isn't in MDS, so LongCopy must be OK -- Inline.LongCOPY[from: longFrom, to: to, nwords: nwords]; END; transmitLimit: CARDINAL = 5 -- max number of transmissions of poll --; retransmitDelay: CARDINAL = 2 -- seconds bfore re-transmittting --; SendPollProcess: PUBLIC ENTRY PROCEDURE[handle: RetrieveXDefs.Handle] = 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; Process.SetTimeout[@handle.pollCond, Process.SecondsToTicks[retransmitDelay] ]; WHILE handle.pollWanted DO now: System.GreenwichMeanTime = Time.Current[]; IF handle.newPollWanted OR now >= handle.pollStarted + 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: RetrieveXDefs.MBXPtr ← handle.MBXChain, this.next WHILE this # RetrieveXDefs.noMBX DO IF this.addrState = unknown THEN RetrieveXDefs.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 BEGIN Process.SetTimeout[@handle.pollCond, Process.SecondsToTicks[Inline.LowHalf[ (handle.pollStarted + handle.interval) - now] ] ]; WAIT handle.pollCond; Process.SetTimeout[@handle.pollCond, Process.SecondsToTicks[retransmitDelay] ]; END ELSE SELECT handle.mbxState FROM unknown => BEGIN -- authenticate -- IF NOT handle.mbxKnown THEN RetrieveXDefs.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: NameInfoDefs.AuthenticateInfo = NameInfoDefs.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: RetrieveXDefs.MBXPtr ← handle.MBXChain, this.next WHILE this # RetrieveXDefs.noMBX DO IF this.replyWanted THEN BEGIN b: PupDefs.PupBuffer = PupDefs.GetFreePupBuffer[]; Copy[from: @(handle.userName.text), to: @(b.pupString), nwords: (1+handle.userName.length)/2 ]; PupDefs.SetPupContentsBytes[b, handle.userName.length]; b.pupType ← mailCheckLaurel; b.pupID ← handle.pollID; b.dest ← [this.addr.net, this.addr.host, IF this.type = MTP THEN PupTypes.miscSrvSoc ELSE ProtocolDefs.mailServerPollingSocket]; 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: RetrieveXDefs.MBXPtr ← handle.MBXChain, this.next WHILE this # RetrieveXDefs.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 = RetrieveXDefs.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: RetrieveXDefs.Handle, socketAddr: PupDefs.PupAddress] = BEGIN this: RetrieveXDefs.MBXPtr = handle.MBXChain; IF this.type # MTP THEN ERROR; IF this.addrState # known THEN SetMBXState[handle, cantAuth] ELSE BEGIN b: PupDefs.PupBuffer = PupDefs.GetFreePupBuffer[]; userWords: CARDINAL = SIZE[StringBody[handle.userName.maxlength]]; pwdWords: CARDINAL = SIZE[StringBody[handle.userPwd.maxlength]]; Copy[from: handle.userName, to: @(b.pupBody), nwords: userWords ]; Copy[from: handle.userPwd, to: @(b.pupBody)+userWords, nwords: pwdWords ]; PupDefs.SetPupContentsWords[b, userWords + pwdWords]; 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: RetrieveXDefs.Handle, socket: PupDefs.PupSocket ] = 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: RetrieveXDefs.Handle, b: PupDefs.PupBuffer] RETURNS[BOOLEAN] = 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: RetrieveXDefs.MBXPtr; FOR mbx ← handle.MBXChain, mbx.next UNTIL mbx = RetrieveXDefs.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 ← 0; --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: RetrieveXDefs.Handle, mbx: RetrieveXDefs.MBXPtr, new: RetrieveDefs.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: RetrieveXDefs.MBXPtr ← handle.MBXChain, this.next WHILE this # RetrieveXDefs.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: RetrieveXDefs.Handle, state: RetrieveDefs.MBXState] = BEGIN BROADCAST handle.mbxStateChange; IF state # userOK AND handle.changes # NIL THEN handle.changes[state]; handle.mbxState ← state; END; END.