<> <> <> <> DIRECTORY BasicTime USING [ Update, Now ], CardTab USING [ Create, Delete, EachPairAction, Fetch, Pairs, Ref, Store ], DESFace USING [ GetRandomKey, Key ], IV USING [ KeyTableBody ], MBQueue USING [ QueueClientAction ], Process USING [ Detach, Pause, SecondsToTicks, SetTimeout ], Pup USING [ nullSocket, Socket ], PupName USING [ AddressToRope ], RefID USING [ ID, Reseal, Unseal ], Rope USING [ Equal, ROPE ], RPC USING [ CallFailed, GetConversationID, ShortROPE ], ThNet USING [ pd ], ThParty, ThPartyPrivate USING [ ConversationBody, ConversationData, ConvState, ConvStateBody, DoDescribeParty, GetRnameFromParty, PartyBody, PartyData, PollSpec, PollSpecBody, SetPoaching, SmartsBody, SmartsData ], ThPartyMonitorImpl, Thrush USING [ ActionID, ActionReport, AlertKind, CallUrgency, ConversationID, ConvEvent, ConvEventBody, Credentials, EncryptionKey, epoch, InterfaceSpec, KeyTable, NB, NetAddress, noAddress, nullConvID, nullID, nullKey, PartyID, Reason, ROPE, SHHH, SmartsID, StateID, StateInConv, unencrypted ], ThSmarts USING [ noneScheduled ], ThSmartsRpcControl USING [ InterfaceRecord ], Triples USING [ Any, Erase, Foreach, ForeachProc, Is, Make, Select ], TU USING [ MakeUnique ], VoiceUtils USING [ OwnNetAddress, Problem ] ; ThPartyOpsImpl: CEDAR MONITOR LOCKS root IMPORTS BasicTime, CardTab, DESFace, MBQueue, Process, PupName, RefID, root: ThPartyMonitorImpl, Rope, RPC, ThNet, ThParty, ThPartyPrivate, Thrush, Triples, TU, VoiceUtils EXPORTS ThParty, ThPartyPrivate SHARES ThPartyMonitorImpl = { <> ConvEvent: TYPE = Thrush.ConvEvent; ConvState: TYPE = ThPartyPrivate.ConvState; ConversationData: TYPE = ThPartyPrivate.ConversationData; ConversationID: TYPE = Thrush.ConversationID; Credentials: TYPE = Thrush.Credentials; Reseal: PROC[r: REF] RETURNS[RefID.ID] = INLINE {RETURN[RefID.Reseal[r]]; }; NB: TYPE = Thrush.NB; nullConvID: Thrush.ConversationID = Thrush.nullConvID; PartyBody: TYPE = ThPartyPrivate.PartyBody; -- Concrete PartyData: TYPE = ThPartyPrivate.PartyData; -- REF Concrete PartyID: TYPE = Thrush.PartyID; -- ID nullID: Thrush.PartyID = Thrush.nullID; Reason: TYPE = Thrush.Reason; ROPE: TYPE = Thrush.ROPE; SHHH: TYPE = Thrush.SHHH; none: SHHH = Thrush.unencrypted; SmartsBody: TYPE = ThPartyPrivate.SmartsBody; -- Concrete SmartsData: TYPE = ThPartyPrivate.SmartsData; -- REF Concrete SmartsID: TYPE = Thrush.SmartsID; -- ID <> nextConv: ConversationID _ Thrush.epoch; nullishKey: Thrush.EncryptionKey _ ALL[[b:0, p:1]]; -- Thrush.nullKey, parity-corrected. <> CreateConversation: PUBLIC ENTRY PROC[ shhh: SHHH, credentials: Credentials, state: Thrush.StateInConv, urgency: Thrush.CallUrgency, alertKind: Thrush.AlertKind, reason: Thrush.Reason, comment: ROPE, subject: ROPE, checkConflict: BOOL ] RETURNS [ nb: NB _ $success, convEvent: ConvEvent_NIL ] = { ENABLE UNWIND => NULL; conv: ConversationData = NEW[ThPartyPrivate.ConversationBody _ [ timeOfID: BasicTime.Now[], startTime: BasicTime.Now[], subject: subject, urgency: urgency, alertKind: alertKind, conferenceHost: ConferenceHost[], keyTable: NEW[IV.KeyTableBody[20B]] ] ]; IF ThNet.pd.nullKeyCorrectParity THEN FOR i: [0..16) IN [0..16) DO conv.keyTable[i] _ ALL[[b: 0, p: 1]]; ENDLOOP; TRUSTED { []_EnterKey[conv, DESFace.GetRandomKey[]]; }; <> conv.convID _ EnhandleConv[conv]; credentials.stateID _ 0; credentials.state _ $neverWas; credentials.convID _ conv.convID; [nb, convEvent] _ DoAdvance[credentials: credentials, state: state, newInConv: TRUE, reason: reason, comment: comment, checkConflict: checkConflict]; IF nb # $success THEN DeleteConversation[ conv ]; }; <> <> <> <> <>> Alert: PUBLIC ENTRY PROC[ shhh: SHHH, credentials: Credentials, calledPartyID: PartyID, comment: ROPE ] RETURNS [ nb: NB ] = { ENABLE UNWIND => NULL; calledParty: PartyData; visitedParty: PartyData; nb2: NB _ $wuddYaTalkinAbout; calledParty _ UnsealParty[calledPartyID]; visitedParty _ IF calledParty=NIL THEN NIL ELSE NARROW[Triples.Select[$Visiting, calledParty, --visitee--]]; IF visitedParty#NIL THEN nb2 _ AlertOne[credentials, Reseal[visitedParty], calledPartyID]; nb _ AlertOne[credentials, calledPartyID]; IF nb#$success AND nb2=$success THEN nb_$success; }; <> <> <> <> <> <> <> <> <> <> <> <>> AlertOne: PUBLIC INTERNAL PROC[ credentials: Credentials, calledPartyID: PartyID, intendedPartyID: PartyID _ nullID ] RETURNS [ nb: NB ] = { <> ENABLE UNWIND => NULL; convState: ConvState; conv: ConversationData; callingParty: PartyData; calledParty: PartyData; partyID: PartyID = credentials.partyID; [, callingParty,, nb] _ Verify[credentials]; IF nb # $success THEN RETURN; credentials.partyID _ calledPartyID; [conv, calledParty, convState, nb] _ Verify[credentials]; SELECT nb FROM $notInConv, $success => NULL; $success => IF conv#NIL THEN RETURN[$alreadyInConv]; -- already in the conversation! $noSuchParty => RETURN[$noSuchParty2]; ENDCASE=> RETURN; ThPartyPrivate.SetPoaching[calledParty, callingParty]; <> IF VoiceParty[calledParty] = VoiceParty[UnsealParty[partyID]] THEN RETURN[$narcissism]; <> IF nb = $success AND conv#NIL THEN RETURN[$alreadyInConv]; <> <<credentials.smartsID remains the originator's  this produces a strange value for convEvent.situation.other.smartsID; at present, no one uses that  if they do, they'll have to understand.>> credentials.stateID _ 0; IF calledParty=NIL OR (calledParty.type = $trunk AND calledParty.reservedBy # Reseal[callingParty]) THEN RETURN[nb: $noSuchParty2]; nb _ DoAdvance[credentials: credentials, intendedPartyID: intendedPartyID, state: $notified, newInConv: TRUE, checkConflict: FALSE].nb; }; <> <> <> <> <> <> <> <> <> <> <> <>> Advance: PUBLIC ENTRY PROC[ shhh: SHHH, credentials: Credentials, state: Thrush.StateInConv, reportToAll: BOOL, reason: Thrush.Reason, comment: ROPE, bilateral: BOOL, checkConflict: BOOL _ FALSE ] RETURNS [nb: NB, convEvent: ConvEvent] = { ENABLE UNWIND => NULL; [nb, convEvent] _ DoAdvance[ credentials: credentials, state: state, reportToAll: reportToAll, reason: reason, comment: comment, newInConv: FALSE, bilateral: bilateral, checkConflict: checkConflict ]; }; <> <> <> <> <> <> <> <> <> <> <> <> DoAdvance: PUBLIC INTERNAL PROC [ credentials: Credentials, intendedPartyID: PartyID _ nullID, state: Thrush.StateInConv, reportToAll: BOOL_FALSE, reason: Thrush.Reason_NIL, comment: ROPE_NIL, newInConv: BOOL, -- if TRUE, party must not be in the conversation yet . . . and vice versa bilateral: BOOL_FALSE, checkConflict: BOOL _ FALSE ] RETURNS [nb: NB_$success, convEvent: ConvEvent_NIL] = { convState: ConvState; conv: ConversationData; party: PartyData; IF state = $idle THEN checkConflict _ FALSE; -- force to unengaged state on idle! [conv, party, convState, nb] _ Verify[credentials, newInConv]; IF intendedPartyID#nullID THEN IF convState.intendedPartyID # nullID THEN ERROR -- Why does this case arise? ELSE convState.intendedPartyID _ intendedPartyID; -- See ThPartyPrivate.ConvState SELECT nb FROM $success => { IF convState=NIL THEN RETURN; nb _ EngageParty[party:party, convID:conv.convID, doEngage:checkConflict, test:TRUE]; IF nb # $success THEN { IF convState.state=$neverWas THEN party.numConvs _ party.numConvs-1; <> RETURN[nb: nb]; }; IF state = $active AND party.partyActive THEN RETURN[nb: $partyAlreadyActive]; IF conv.numIdle = conv.numParties AND state # $idle THEN RETURN[nb: $convIdle]; credentials.state _ state; }; $stateMismatch, $interfaceError => credentials.state _ convState.state; -- Don't change state, but return current one as report. ENDCASE => RETURN; IF nb=$success AND state = $active AND ~party.partyActive THEN { voiceP: PartyData; voiceS: SmartsData; <> IF (voiceP_VoiceParty[party])=NIL OR (voiceS _ NARROW[Triples.Select[$Smarts, voiceP, NIL]])=NIL OR voiceS.properties.role#$voiceTerminal THEN RETURN[nb: $voiceTerminalUnavailable]; <> IF IsBilateralConv[conv] AND conv.cStateBilateral#convState THEN RETURN[nb: $bilateralConv]; IF bilateral THEN { IF conv.numActive>1 THEN RETURN[nb: $conferenceConv]; conv.cStateBilateral _ convState; }; }; IF nb=$success THEN [] _ EngageParty[party:party, convID:conv.convID, doEngage:checkConflict, test:FALSE]; convEvent _ PostConvEvent[ credentials: credentials, convState: convState, reason: reason, comment: comment, reportToAll: reportToAll]; }; <> <> <> <> <> <> <> <> <> <> <> <> IsBilateralConv: INTERNAL PROC[conv: ConversationData] RETURNS [is: BOOL] = { cStateB: ConvState _ conv.cStateBilateral; RETURN[cStateB#NIL AND cStateB.state=$active AND Triples.Select[cStateB, conv, NIL]#NIL AND conv.numActive>1]; }; GetConversationInfo: PUBLIC ENTRY PROC [ shh: SHHH_none, convID: ConversationID ] RETURNS [ nb: NB_$success, cInfo: ThParty.ConversationInfo_[] ] = { [nb, cInfo] _ GetConversationInfoInt[UnsealConv[convID]]; }; <> <> GetConversationInfoInt: INTERNAL PROC[conv: ConversationData ] RETURNS [ nb: NB_$success, cInfo: ThParty.ConversationInfo_[] ] = { IF conv=NIL THEN RETURN[nb: $noSuchConv]; cInfo.subject _ conv.subject; cInfo.urgency _ conv.urgency; cInfo.alertKind _ conv.alertKind; cInfo.startTime _ conv.startTime; cInfo.conferenceHost _ conv.conferenceHost; cInfo.numParties _ conv.numParties; cInfo.numActive _ conv.numActive; cInfo.numIdle _ conv.numIdle; cInfo.bilateralConv _ IsBilateralConv[conv]; cInfo.originator _ Reseal[Triples.Select[$Originator, conv, -- originator --]]; }; <> <> GetPartyInfo: PUBLIC ENTRY PROC [ shh: SHHH_none, credentials: Credentials, nameReq: ThParty.NameReq, allParties: BOOL ] RETURNS [ nb: NB, pInfo: ThParty.PartyInfo_NIL ] = { conv: ConversationData = UnsealConv[credentials.convID]; convState: ConvState; ownParty: PartyData = UnsealParty[credentials.partyID]; name: Rope.ROPE; intendedName: Rope.ROPE; intendedPartyID: PartyID; activeIndex: NAT_0; -- when incremented, the index of the next active party description inactiveIndex: NAT; -- when incremented, the index of the next non-active party description GetParties: INTERNAL FinishProcType = { <> index: NAT; HowToReport _ NIL; IF Triples.Select[$Poaching, party, -- poachee party -- ]#NIL AND smarts.properties.role#$voiceTerminal THEN RETURN; -- Wait for voice terminal index _ SELECT TRUE FROM party=ownParty => 0, convState.state=$active => (activeIndex_activeIndex+1), ENDCASE => (inactiveIndex_inactiveIndex+1); pInfo.numParties _ MAX[index+1, pInfo.numParties]; name _ IF nameReq=$none THEN NIL ELSE ThPartyPrivate.DoDescribeParty[party, nameReq]; intendedPartyID _ convState.intendedPartyID; pInfo[index] _ [ partyID: Reseal[party], name: name, intendedPartyID: intendedPartyID, intendedName: IF nameReq=$none THEN NIL ELSE IF intendedPartyID = nullID THEN name ELSE IF (intendedName _ThPartyPrivate.DoDescribeParty[UnsealParty[intendedPartyID], nameReq])#NIL THEN intendedName ELSE name, type: party.type, state: convState.state, numConvs: party.numConvs, enabled: party.enabled, partyActive: party.partyActive, voicePath: smarts.properties.role=$voiceTerminal, partyEngaged: party.partyEngaged#nullConvID, -- if anyone needs it. socket: smarts.properties.netAddress ]; }; nb _IF conv=NIL THEN $noSuchConv ELSE IF ownParty=NIL THEN $noSuchParty ELSE $success; IF nb=$success THEN { convState _ GetConvState[conv, ownParty]; IF convState=NIL THEN nb _ $notInConv }; IF nb#$success THEN RETURN; inactiveIndex _ conv.numActive-(IF convState.state=$active THEN 1 ELSE 0); pInfo _ NEW[ThParty.PartyInfoSeq[IF allParties THEN conv.numParties ELSE 1]]; pInfo.conversationInfo _ GetConversationInfoInt[conv].cInfo; pInfo[0] _ []; Report[ FinishProc: GetParties, reportToAll: allParties, conv: conv, party: ownParty, whatToReport: NIL]; }; <> <> <> <> GetKeyTable: PUBLIC ENTRY PROC [ shh: SHHH_none, credentials: Credentials ] RETURNS [ nb: NB, keyTable: Thrush.KeyTable_NIL ] = { conv: ConversationData; [conv,,,nb] _ Verify[credentials]; IF conv#NIL THEN keyTable _ conv.keyTable; -- always answer the question if possible IF nb=$stateMismatch THEN nb_$success; }; <> <> <> <> <> <> RegisterKey: PUBLIC ENTRY PROC [ shh: SHHH _ none, credentials: Credentials, key: Thrush.EncryptionKey, reportNewKeys: BOOL] -- note: reportNewKeys is no longer used RETURNS [ nb: NB, keyIndex: [0..17B] _ 0 ] = TRUSTED { ENABLE UNWIND => NULL; conv: ConversationData; ekResults: EKResults_$duplicate; [conv, , , nb] _ Verify[credentials]; <> SELECT nb FROM $success, $stateMismatch => NULL; ENDCASE => RETURN; [keyIndex, ekResults] _ EnterKey[conv, key]; nb _ IF ekResults#$new THEN $success ELSE $newKeys; }; <> <> <> <> <> <> <> UnregisterKey: PUBLIC ENTRY PROC[ shh: SHHH _ none, credentials: Credentials, key: Thrush.EncryptionKey ] RETURNS [ nb: NB _ $success ] = { conv: ConversationData; [conv, , , nb] _ Verify[credentials]; SELECT nb FROM $success, $stateMismatch => nb _ $success; ENDCASE => RETURN; RemoveKey[conv, key]; }; <> <> <> <> <> <> RegisterServiceInterface: PUBLIC ENTRY PROC[ shhh: SHHH _ none, credentials: Thrush.Credentials, interfaceSpecPattern: Thrush.InterfaceSpec ] RETURNS[nb: NB, interfaceSpec: Thrush.InterfaceSpec] = { interface: REF Thrush.InterfaceSpec; instance: RPC.ShortROPE_NIL; hostHint: Thrush.NetAddress; party: PartyData; type: RPC.ShortROPE = interfaceSpecPattern.interfaceName.type; [party: party, nb: nb] _ Verify[credentials]; IF nb = $noSuchParty AND party = NIL THEN RETURN; -- else just not yet enabled nb _ $success; interface _ FindServiceInterface[party, type]; IF interface = NIL THEN { interface _ NEW[Thrush.InterfaceSpec _ [interfaceName: [type: type], serviceID: interfaceSpecPattern.serviceID, interfaceID: interfaceSpecPattern.interfaceID]]; party.actionInterfaces _ CONS[interface, party.actionInterfaces]; }; <> IF interfaceSpecPattern.hostHint # Thrush.noAddress THEN hostHint _ interfaceSpecPattern.hostHint ELSE IF shhh=NIL THEN hostHint _ VoiceUtils.OwnNetAddress[] ELSE { na: Thrush.NetAddress = LOOPHOLE[RPC.GetConversationID[shhh]]; hostHint _ [na.net, na.host, Pup.nullSocket]; }; interface.hostHint _ hostHint; instance _ interfaceSpecPattern.interfaceName.instance; IF instance = NIL THEN { hostHint.socket _ NewId[]; instance _ PupName.AddressToRope[hostHint]; }; interface.interfaceName.instance _ instance; interface.interfaceName.version _ interfaceSpecPattern.interfaceName.version; interface.serviceID _ interfaceSpecPattern.serviceID; interfaceSpec _ interface^; }; <> <> LookupServiceInterface: PUBLIC ENTRY PROC[ shhh: SHHH _ none, credentials: Thrush.Credentials, serviceParty: PartyID, type: RPC.ShortROPE ] RETURNS[nb: NB _ $success, interfaceSpec: Thrush.InterfaceSpec] = { interface: REF Thrush.InterfaceSpec; party: PartyData; <<[nb: nb] _ Verify[credentials]; -- There's no strong reason for checking credentials.>> <