<> <> DIRECTORY BluejayUtils USING [ DescribeInterval ], DESFace USING [ GetRandomKey, Key ], IO, Lark USING [ ConnectionSpecRec, KeyTableBody, VoiceSocket ], Log USING [ Problem, ProblemFR, SLOG ], OrderedSymbolTable, PupTypes USING [ PupSocketID ], SafeStorage USING [ GetCanonicalType, Type ], BasicTime USING [ Update, Now ], Thrush USING [ AlertKind, CallUrgency, ConversationHandle, ConvEvent, ConvEventBody, Credentials, Dehandle, EncryptionKey, epoch, EventSequence, EventSequenceBody, H, IntervalSpec, IntervalSpecs, NB, nullConvHandle, nullHandle, nullKey, PartyHandle, PartyType, PhoneNumber, Reason, ROPE, SHHH, SmartsHandle, StateID, StateInConv, ThHandle, unencrypted, VoiceTime ], ThNet USING [ pd ], ThParty, ThPartyPrivate USING [ CFRec, CFRef, ConversationBody, ConversationData, DoDescribeParty, logSizeIncrement, PartyBody, PartyData, SmartsBody, SmartsData, Supervise ], ThPartyMonitorImpl, ThSmartsRpcControl USING [ InterfaceRecord ], Triples USING [ Any, Foreach, ForeachProc, Make, Select ], TU USING [ RefAddr ] ; ThPartyOpsImpl: CEDAR MONITOR LOCKS root IMPORTS BluejayUtils, DESFace, root: ThPartyMonitorImpl, SafeStorage, BasicTime, IO, Log, OrderedSymbolTable, ThNet, ThPartyPrivate, Thrush, Triples, TU EXPORTS ThParty, ThPartyPrivate SHARES ThPartyMonitorImpl = { <> AlertKind: TYPE = Thrush.AlertKind; CallUrgency: TYPE = Thrush.CallUrgency; CFRef: TYPE = ThPartyPrivate.CFRef; RTConvType: SafeStorage.Type = SafeStorage.GetCanonicalType[CODE[ThPartyPrivate.ConversationBody]]; ConvEvent: TYPE = Thrush.ConvEvent; ConversationData: TYPE = ThPartyPrivate.ConversationData; ConversationHandle: TYPE = Thrush.ConversationHandle; Credentials: TYPE = Thrush.Credentials; H: PROC[r: REF] RETURNS[Thrush.ThHandle] = INLINE {RETURN[Thrush.H[r]]; }; NB: TYPE = Thrush.NB; none: SHHH = Thrush.unencrypted; nullConvHandle: Thrush.ConversationHandle = Thrush.nullConvHandle; nullHandle: Thrush.PartyHandle = Thrush.nullHandle; PartyBody: TYPE = ThPartyPrivate.PartyBody; -- Concrete RTPartyType: SafeStorage.Type = SafeStorage.GetCanonicalType[CODE[PartyBody]]; PartyData: TYPE = ThPartyPrivate.PartyData; -- REF Concrete PartyHandle: TYPE = Thrush.PartyHandle; -- Handle Reason: TYPE = Thrush.Reason; ROPE: TYPE = Thrush.ROPE; SHHH: TYPE = Thrush.SHHH; SmartsBody: TYPE = ThPartyPrivate.SmartsBody; -- Concrete SmartsData: TYPE = ThPartyPrivate.SmartsData; -- REF Concrete RTSmartsType: SafeStorage.Type =SafeStorage.GetCanonicalType[CODE[SmartsBody]]; SmartsHandle: TYPE = Thrush.SmartsHandle; -- Handle StateID: TYPE = Thrush.StateID; StateInConv: TYPE = Thrush.StateInConv; <> nextConv: ConversationHandle _ Thrush.epoch; convTable: OrderedSymbolTable.Table; convClassIncrement: INT _ 100000000B; -- members of same class are identical in low 24 bits. <> Alert: PUBLIC ENTRY PROC[ shhh: SHHH, credentials: Credentials, state: StateInConv_initiating, -- IF reserved, reason specifies reason for cancelling prev. reason: Reason_wontSay, -- IF reserving, specifies reason for cancelling last one. calledPartyID: PartyHandle, urgency: CallUrgency, alertKind: AlertKind, newConv: BOOL_FALSE, comment: ROPE ] RETURNS [ nb: NB, convID: ConversationHandle ] = { ENABLE UNWIND => NULL; cfRef: CFRef; conv: ConversationData; callingParty: PartyData; calledParty: PartyData; Log.SLOG[]; IF newConv OR credentials.convID=nullConvHandle THEN { credentials.convID _ CreateConv[ partyID: credentials.partyID, convID: credentials.convID, urgency: urgency, alertKind: alertKind].convID; newConv_TRUE; }; [conv, callingParty, cfRef, nb] _ Verify[credentials]; convID _ credentials.convID; IF nb#success THEN RETURN; IF cfRef#NIL AND cfRef.event.state=idle THEN { nb _ AllIdle[conv]; IF nb#success THEN RETURN; }; conv.urgency _ urgency; conv.alertKind _ alertKind; IF calledPartyID # nullHandle OR state = initiating THEN { calledParty _ DehandleParty[calledPartyID]; IF calledParty=NIL THEN {nb_noSuchParty2; RETURN}; <<<< Not satisfied with this, yet. If trunkParty info has been changed, don't make the call.>>>> TRUSTED { WITH calledParty SELECT FROM trunk => IF reservedBy # credentials.partyID THEN {nb_noSuchParty2; RETURN}; ENDCASE; }; IF calledPartyID=credentials.partyID THEN {nb_narcissism; RETURN}; [] _ PostConvEvent[conv: conv, party: calledParty, state: pending, reason: wontSay, comment: comment ]; }; <<<< State must be initiating if calledPartyID#nullHandle. Nobody's checking. >>>> [] _ PostConvEvent[conv: conv, party: callingParty, state: state, reason: reason, smartsID: credentials.smartsID, comment: comment ]; }; Advance: PUBLIC ENTRY PROC[ shhh: SHHH, credentials: Credentials, state: StateInConv, reason: Thrush.Reason, comment: ROPE ] RETURNS [nb: NB] = { ENABLE UNWIND => NULL; conv: ConversationData; party: PartyData; cfRef: CFRef; Log.SLOG[]; [conv, party, cfRef, nb] _ Verify[credentials]; IF nb#success THEN RETURN; nb _ DoAdvance[credentials.smartsID, party, conv, cfRef, state, reason, comment]; }; DoAdvance: PUBLIC INTERNAL PROC [ smartsID: SmartsHandle, party: PartyData, conv: ConversationData, cfRef: CFRef, state: StateInConv, reason: Thrush.Reason, comment: ROPE ] RETURNS [nb: NB] = { otherCfRef: CFRef; otherParty: PartyData _ NIL; otherState: Thrush.StateInConv; OtherProc: Triples.ForeachProc _ SELECT state FROM idle => IdleLast, ringing, active => FindCaller, ENDCASE => NIL; IdleLast: Triples.ForeachProc = { <> WITH trip.att SELECT FROM r: CFRef => { otherState _ idle; SELECT r.event.state FROM idle => RETURN[TRUE]; ENDCASE=>NULL; IF otherParty#NIL THEN { otherParty_NIL; RETURN[FALSE]; }; otherParty_NARROW[trip.val]; otherCfRef _ r; }; ENDCASE; }; FindCaller: Triples.ForeachProc = { <> WITH trip.att SELECT FROM r: CFRef => { otherState _ IF state = ringing THEN maybe ELSE active; SELECT r.event.state FROM initiating, maybe => NULL; ENDCASE => RETURN[TRUE]; otherParty _ NARROW[trip.val]; otherCfRef _ r; RETURN[FALSE]; }; ENDCASE; }; SELECT validTransitions[state][cfRef.event.state] FROM no, cantReq => RETURN[invalidTransition]; noop => RETURN; -- success valid => NULL; ENDCASE => Log.ProblemFR["%g, %g: Impossible", $System, NIL, TU.RefAddr[party], TU.RefAddr[cfRef]]; cfRef_PostConvEvent[ conv: conv, party: party, smartsID: smartsID, state: state, reason: reason, comment: comment]; nb _ success; IF OtherProc#NIL THEN Triples.Foreach[Triples.Any, conv, Triples.Any, OtherProc]; IF otherParty # NIL THEN { SELECT validTransitions[otherState][otherCfRef.event.state] FROM no => { Log.Problem[IO.PutFR["%g, %g, %g: State mismatch among parties", TU.RefAddr[party], TU.RefAddr[otherParty], TU.RefAddr[cfRef]], $System]; RETURN; }; <> cantReq, valid => NULL; noop => RETURN; ENDCASE => { Log.ProblemFR["%g, %g: Impossible", $System, NIL, TU.RefAddr[otherParty], TU.RefAddr[cfRef]]; RETURN; }; []_PostConvEvent[ <> conv: conv, party: otherParty, state: otherState, reason: reason, comment: comment]; }; }; OtherParty: PUBLIC ENTRY PROC[ shhh: SHHH_none, credentials: Credentials ] RETURNS[ nb: NB, partyID: PartyHandle_nullHandle, description: Thrush.ROPE, conference: BOOL_FALSE ] = { conv: ConversationData; [conv, , , nb] _ Verify[credentials]; IF nb=stateMismatch THEN nb_success; IF nb#success THEN RETURN; [partyID, conference] _ FindOtherParty[credentials.partyID, conv]; description _ SELECT TRUE FROM conference => "Group of parties", partyID#nullHandle => ThPartyPrivate.DoDescribeParty[partyID], ENDCASE => NIL; }; <<>> FindOtherParty: PUBLIC PROC[myPartyID: PartyHandle, conv: ConversationData] RETURNS[partyID: PartyHandle_nullHandle, conference: BOOL_FALSE ] = { FOR i: NAT IN [1..conv.currentStateID] DO ce: ConvEvent = conv.log[i]; IF myPartyID=ce.credentials.partyID THEN LOOP; partyID _ ce.credentials.partyID; IF conv.numParties>2 THEN { conference_TRUE; RETURN; }; EXIT; ENDLOOP; }; <<>> CreateConversation: PUBLIC ENTRY PROC[ shhh: SHHH, credentials: Credentials, urgency: CallUrgency, alertKind: AlertKind ] RETURNS [ nb: NB, convID: ConversationHandle ] = { ENABLE UNWIND => NULL; conv: ConversationData = CreateConv[credentials.partyID, Thrush.nullConvHandle, urgency, alertKind]; RETURN[success, conv.convID]; }; CreateConv: INTERNAL PROC[ partyID: PartyHandle, convID: ConversationHandle, -- non-null if want a related converation. urgency: CallUrgency_normal, alertKind: AlertKind_standard] RETURNS [ conv: ConversationData_NIL ] = TRUSTED { conv _ NEW[ThPartyPrivate.ConversationBody _ [ creatorPartyID: partyID, timeOfID: BasicTime.Now[], urgency: urgency, alertKind: alertKind, log: NEW[Thrush.EventSequenceBody[ThPartyPrivate.logSizeIncrement]]]]; conv.keyTable _ NEW[Lark.KeyTableBody[20B]]; []_EnterKey[conv, DESFace.GetRandomKey[]]; -- = this conv's key, will be key index 1. conv.convID _ EnhandleConv[conv, convID]; }; DestroyConversation: PUBLIC INTERNAL PROC[ conv: ConversationData ] = { IF conv.numParties#0 THEN Log.ProblemFR["%g: Conversation still has parties", $System, NIL, TU.RefAddr[conv]]; KillHandleConv[conv.convID]; }; MergeConversations: PUBLIC PROC[ shhh: SHHH, credentials: Credentials, -- of surviving conversation otherStateID: StateID, -- of dissolving conversation otherConvID: ConversationHandle ] RETURNS [ nb: NB ] = {NULL}; SetIntervals: PUBLIC ENTRY PROC[ shhh: SHHH _ none, credentials: Credentials, intervalSpecs: Thrush.IntervalSpecs ] RETURNS [ nb: NB ] = { ENABLE UNWIND => NULL; conv: ConversationData; party: PartyData; cfRef: CFRef; WachetAuf: INTERNAL Triples.ForeachProc = { WITH trip.att SELECT FROM r: CFRef => { party_NARROW[trip.val]; ThPartyPrivate.Supervise[party]; }; ENDCASE; }; [conv, party, cfRef, nb] _ Verify[credentials]; IF nb#success THEN RETURN; IF cfRef=NIL OR cfRef.event=NIL OR cfRef.event.state#active THEN RETURN[convNotActive]; [] _ PostConvEvent[conv, party, credentials.smartsID, cfRef.event.state, , ,intervalSpecs]; FOR iSL: Thrush.IntervalSpecs _ intervalSpecs, iSL.rest WHILE iSL#NIL DO iS: Thrush.IntervalSpec = iSL.first; IF ~ThNet.pd.encryptVoice THEN iS.keyIndex_0; IF iS.type=request THEN iS.intID.stateID _ conv.currentStateID; ENDLOOP; Triples.Foreach[Triples.Any, conv, Triples.Any, WachetAuf]; <> }; DescribeInterval: PUBLIC PROC[ shhh: SHHH _ none, credentials: Credentials, targetInterval: Thrush.IntervalSpec, minSilence: Thrush.VoiceTime _ 1 -- Smallest silent interval that will be considered silence. ] RETURNS [ nb: NB, exists: BOOL_FALSE, intervals: Thrush.IntervalSpecs_NIL ] = { conv: ConversationData; party: PartyData; cfRef: CFRef; [conv, party, cfRef, nb] _ VerifyEnt[credentials]; IF nb#success AND nb#stateMismatch THEN RETURN; [exists, intervals] _ BluejayUtils.DescribeInterval[targetInterval.tune, targetInterval.interval, minSilence]; }; RegisterKey: PUBLIC ENTRY PROC [ shh: SHHH _ none, credentials: Credentials, <> <> <> <> key: Thrush.EncryptionKey ] RETURNS [ nb: NB, keyIndex: [0..17B] _ 0 ] = { ENABLE UNWIND => NULL; conv: ConversationData; cfRef: CFRef; [conv, , cfRef, nb] _ Verify[credentials]; <> IF nb=success OR nb=stateMismatch THEN keyIndex _ EnterKey[conv, key].keyIndex; }; SetSubject: PUBLIC ENTRY PROC[ shh: SHHH, convID: ConversationHandle, subject: ROPE ] = { ENABLE UNWIND => NULL; conv: ConversationData = DehandleConv[convID]; IF conv=NIL THEN RETURN ELSE conv.subject _ subject; }; GetSubject: PUBLIC ENTRY PROC[ shh: SHHH, convID: ConversationHandle ] RETURNS [subject: ROPE] = { ENABLE UNWIND => NULL; conv: ConversationData = DehandleConv[convID]; RETURN[IF conv=NIL THEN NIL ELSE conv.subject]; }; ConversationsForParty: PUBLIC ENTRY PROC [ shh: SHHH_none, partyID: PartyHandle ] = { party: PartyData _ DehandleParty[partyID]; ReallyWachetAuf: INTERNAL Triples.ForeachProc = { WITH trip.att SELECT FROM r: CFRef => { conv: ConversationData _ NARROW[trip.obj]; r.lastNotedID _ 0; ThPartyPrivate.Supervise[party]; }; ENDCASE; }; IF party#NIL THEN Triples.Foreach[Triples.Any, Triples.Any, party, ReallyWachetAuf]; }; <> VerifyEnt: PUBLIC ENTRY PROC[ credentials: Credentials ] RETURNS [ conv: ConversationData, party: PartyData, cfRef: CFRef, nb: Thrush.NB ] = { [conv, party, cfRef, nb] _ Verify[credentials]; }; Verify: PUBLIC INTERNAL PROC[ credentials: Credentials ] RETURNS [ conv: ConversationData, party: PartyData, cfRef: CFRef, nb: Thrush.NB ] = { <> <> smarts: SmartsData _ DehandleSmarts[credentials.smartsID]; conv_DehandleConv[credentials.convID]; party_DehandleParty[credentials.partyID]; nb _ SELECT NIL[REF ANY] FROM smarts=> noSuchSmarts, party=> noSuchParty, conv=> noSuchConv, ENDCASE=> success; IF nb=success AND party#NIL THEN IF party.partyFailed THEN nb_noSuchParty ELSE IF party.numEnabled=0 THEN nb _ partyNotEnabled; IF conv#NIL AND party#NIL THEN cfRef _ NARROW[Triples.Select[--cfRef--, conv, party]]; IF nb=success THEN IF cfRef#NIL THEN { IF credentials.stateID IF credentials.stateID#0 THEN nb _ notInConv; stateMismatch => nb _ notInConv; ENDCASE; }; PostConvEvent: INTERNAL PROC[ conv: ConversationData, party: PartyData, smartsID: SmartsHandle _ nullHandle, state: Thrush.StateInConv, reason: Thrush.Reason_wontSay, comment: Thrush.ROPE_NIL, intervalSpecs: Thrush.IntervalSpecs_NIL ] RETURNS [cfRef: CFRef_NIL] = { event, lastEvent: ConvEvent; lastState: Thrush.StateInConv; conv.currentStateID _ conv.currentStateID+1; conv.timeOfID _ BasicTime.Now[]; TRUSTED { event _ NEW[Thrush.ConvEventBody _ [ credentials: [ H[party], smartsID, conv.convID, conv.currentStateID ], time: conv.timeOfID, state: state, reason: reason, urgency: conv.urgency, alertKind: conv.alertKind, address: (WITH p: party SELECT FROM trunk => p.outgoing, ENDCASE => NIL), comment: comment, intervalSpecs: intervalSpecs ]]; }; InsertEvent[conv, event]; IF conv.newKeyTable THEN event.keyTable _ conv.keyTable; conv.newKeyTable_FALSE; cfRef _ NARROW[Triples.Select[--cfRef--, conv, party]]; IF cfRef = NIL THEN { cfRef _ NEW[ThPartyPrivate.CFRec _ [ event: NIL, sockID: NewId[] ]]; Triples.Make[cfRef, conv, party]; conv.numParties _ conv.numParties+1; party.numConvs _ party.numConvs+1; }; <> SELECT state FROM initiating, active, canActivate => IF cfRef.voiceSmartsID=nullHandle THEN { smarts: SmartsData _ DehandleSmarts[smartsID]; IF smarts#NIL THEN WITH smarts.properties SELECT FROM backstop, supervisor => NULL; -- << ERROR? >> voiceTerminal => cfRef.voiceSmartsID _ smartsID; manager => { smarts _ NARROW[Triples.Select[$AdjacentTerminal, party, -- smarts --]]; IF smarts # NIL THEN cfRef.voiceSmartsID _ H[smarts]; }; ENDCASE; }; ENDCASE; <> lastEvent _ cfRef.event; lastState _ IF lastEvent=NIL THEN idle ELSE lastEvent.state; cfRef.event _ event; cfRef.lastPostedID _ conv.currentStateID; IF state=active THEN { IF lastState#active THEN IF party.partyActive THEN event.state _ canActivate ELSE [] _ Activate[conv, cfRef, party] } --<> ELSE IF lastState = active THEN { party.partyActive _ FALSE; conv.numActive _ conv.numActive - 1; }; ThPartyPrivate.Supervise[party]; }; Activate: INTERNAL PROC[conv: ConversationData, myCfRef: CFRef, myParty: PartyData] <> RETURNS[isSpec: BOOL_FALSE] = { sockets: ARRAY[0..2) OF Lark.VoiceSocket; parties: ARRAY[0..2) OF PartyData; specIndex: NAT_0; GetOneActive: INTERNAL Triples.ForeachProc = { cfRef: CFRef; smarts: SmartsData; sockID: PupTypes.PupSocketID; IF specIndex>=2 THEN RETURN[FALSE]; WITH trip.att SELECT FROM r: CFRef => cfRef _ r; ENDCASE=> RETURN[TRUE]; IF cfRef.voiceSmartsID = nullHandle THEN RETURN[TRUE]; smarts _ DehandleSmarts[cfRef.voiceSmartsID]; IF smarts=NIL THEN RETURN--[FALSE]--; -- fail back sockID _ LOOPHOLE[cfRef.sockID]; WITH smarts.properties SELECT FROM voiceTerminal => sockets[specIndex] _ [ [ machine.net], [machine.host], sockID ]; ENDCASE => Log.Problem["Non-voice terminal trying to be voice smarts", $System]; parties[specIndex] _ NARROW[trip.val]; specIndex _ specIndex+1; }; myParty.partyActive_TRUE; conv.numActive _ conv.numActive + 1; Triples.Foreach[Triples.Any, conv, Triples.Any, GetOneActive]; IF specIndex#2 THEN RETURN[FALSE]; SELECT myParty FROM =parties[0] => specIndex _ 0; =parties[1] => specIndex _ 1; ENDCASE => Log.Problem["Impossible", $System]; myCfRef.event.spec _ NEW[Lark.ConnectionSpecRec _ [ protocol: interactive, encoding: muLaw, sampleRate: 8000, packetSize: 160, -- sample bytes, that is. buffer: out1, keyIndex: IF ThNet.pd.encryptVoice THEN 1 ELSE 0, localSocket: sockets[specIndex], remoteSocket: sockets[1-specIndex], blankA: 0 ]]; RETURN[TRUE]; }; GetEvent: PUBLIC INTERNAL PROC[conv: ConversationData, stateID: Thrush.StateID] RETURNS [ Thrush.ConvEvent_NIL ] = { IF conv=NIL THEN Log.ProblemFR["%g: No such conversation", $System, NIL, TU.RefAddr[conv]] ELSE IF stateID=0 THEN RETURN[NIL] ELSE IF stateID>=conv.log.size THEN Log.Problem["State ID out of range", $System] ELSE RETURN[conv.log[stateID]]; }; InsertEvent: INTERNAL PROC[conv: ConversationData, entry: ConvEvent] = { IF conv.currentStateID >= conv.log.size THEN { oldLog: Thrush.EventSequence = conv.log; newLog: Thrush.EventSequence = NEW[Thrush.EventSequenceBody[oldLog.size+ThPartyPrivate.logSizeIncrement]]; FOR i: NAT IN [0..oldLog.size) DO newLog[i]_oldLog[i]; ENDLOOP; conv.log _ newLog; }; conv.log[conv.currentStateID] _ entry; }; NewId: PROC RETURNS [ LONG CARDINAL ] = TRUSTED { idKey: DESFace.Key _ DESFace.GetRandomKey[]; -- connection specs. keys: LONG POINTER TO ARRAY[0..2) OF LONG CARDINAL _ LOOPHOLE[LONG[@idKey]]; RETURN[keys[0]]; }; AllIdle: INTERNAL PROC[conv: ConversationData] RETURNS [nb: Thrush.NB] = { allAreIdle: BOOL_TRUE; OneIdle: Triples.ForeachProc = { <> WITH trip.att SELECT FROM r: CFRef => IF r.event.state#idle THEN { allAreIdle_FALSE; RETURN[FALSE]; }; ENDCASE;}; Triples.Foreach[Triples.Any, conv, Triples.Any, OneIdle]; RETURN[IF allAreIdle THEN success ELSE invalidTransition]; }; DehandleConv: PUBLIC INTERNAL PROC[ convID: ConversationHandle] <> RETURNS [conv: ConversationData_NIL ] = { node: OrderedSymbolTable.Node = convTable.Lookup[convID]; RETURN[IF node#NIL THEN node.conv ELSE NIL]; }; EnhandleConv: INTERNAL PROC[ conv: ConversationData, convClassID: ConversationHandle ] <> RETURNS [ convID: ConversationHandle_nextConv ] = { IF convClassID#nullConvHandle THEN convID _ BasicTime.Update[convClassID, convClassIncrement] ELSE nextConv _ BasicTime.Update[nextConv, 1]; IF convTable.Lookup[convID]#NIL THEN { IF convClassID#nullConvHandle THEN RETURN EnhandleConv[conv,convID]; Log.Problem["Struct. broken", $System]; RETURN[Thrush.nullConvHandle]; }; convTable.Insert[ NEW[OrderedSymbolTable.NodeRecord_[conv:conv]], convID ]; }; KillHandleConv: INTERNAL PROC[ convID: ConversationHandle ] = { <> []_convTable.Delete[convID]; }; DehandleParty: PUBLIC PROC[partyID: Thrush.PartyHandle, insist: BOOLEAN_FALSE] RETURNS[party: PartyData] = TRUSTED { RETURN[LOOPHOLE[Thrush.Dehandle[partyID, RTPartyType, insist]]]; }; DehandleSmarts: PUBLIC PROC[smartsID: Thrush.SmartsHandle, insist: BOOLEAN_FALSE] RETURNS[smarts: SmartsData] = TRUSTED { RETURN[LOOPHOLE[Thrush.Dehandle[smartsID, RTSmartsType, insist]]]; }; <> <<>> EKResults: TYPE = { new, duplicate, disagreement, full }; EnterKey: INTERNAL PROC[conv: ConversationData, key: Thrush.EncryptionKey] RETURNS [ keyIndex: [0..17B], results: EKResults] = { <> <> IF key=Thrush.nullKey THEN RETURN[0, duplicate]; FOR keyIndex IN [1..17B] DO IF conv.keyTable[keyIndex]=key THEN RETURN[keyIndex, duplicate] ELSE IF conv.keyTable[keyIndex][0].b=0 AND conv.keyTable[keyIndex] = Thrush.nullKey THEN EXIT; REPEAT FINISHED => RETURN[0, full]; ENDLOOP; conv.keyTable[keyIndex] _ key; conv.newKeyTable _ TRUE; RETURN[keyIndex, new]; }; <> <<>> Validity: TYPE = { noop, -- Already in this state; no transition action required valid, -- This transition is all right if other tests check out. no, -- This transition is not allowed, nohow. cantReq -- This transition may not be requested by the Party-level client. }; validTransitions: ARRAY Thrush.StateInConv OF ARRAY Thrush.StateInConv OF Validity = [ <> [ noop, valid, valid, valid, valid, valid, valid, valid, valid, valid, no ], -- idle [ valid, noop, valid, no, no, no, no, no, no, no, no ], -- reserved [ valid, valid, noop, no, no, no, no, no, no, no, no ], -- parsing [ valid, valid, valid, noop, no, no, no, no, no, no, no ], -- initiating [ cantReq, cantReq, cantReq, no, noop, no, no, no, no, no, no ], -- pending [ no, no, no, cantReq, no, noop, no, no, no, no, no ], -- maybe [ no, no, no, no, valid, no, noop, no, no, no, no ], -- ringing [ cantReq, cantReq, cantReq, cantReq, cantReq, cantReq, cantReq, noop, no, cantReq, no ], -- canAct [ valid, valid, valid, cantReq, valid, cantReq, valid, valid, noop, valid, no ], -- active [ no, no, no, no, no, no, no, valid, valid, noop, no ], -- inactive [ no, no, no, no, no, no, no, no, no, no, no ] -- any ]; OrderedSymbolTable.Initialize[ NEW[OrderedSymbolTable.NodeRecord_[]],NEW[OrderedSymbolTable.NodeRecord_[]]]; convTable _ OrderedSymbolTable.CreateTable[NEW[OrderedSymbolTable.NodeRecord_[]]]; }.