<> <> <> DIRECTORY Atom USING [ GetPName, MakeAtom ], BasicTime USING [ Now, Update ], Commander USING [ CommandProc, Register ], CommandTool USING [ NextArgument ], IO, LarkPlay USING [ PlayString, Tone, ToneSpec, ToneSpecRec ], LupineRuntime USING [ BindingError ], NamesGV USING [ GVGetAttribute, GVSetAttribute, GVUpdate ], NamesRPC USING [ StartConversation ], Nice, Pup USING [ nullSocket ], Rope USING [ Concat, Cat, Fetch ], RPC USING [ AuthenticateFailed, EncryptionKey, ExportFailed, ImportFailed ], SafeStorage USING [ GetCanonicalType, Type ], Thrush USING [ AlertKind, CallUrgency, ConversationHandle, Enhandle, HandleFault, KillHandle, NB, PartyHandle, PartyType, H, NetAddress, noMachine, nullHandle, pERROR, Priorities, RingEnable, Rname, ROPE, SHHH, SmartsHandle, TBD, ThHandle, unencrypted ], ThNet USING [ CloseWhitePagesDatabase, FeepName, HowToDial, InitWhitePagesDatabase, List, NewListing, pd, WhitePagesEnter, WhitePagesEntry, WPListing, WPState ], ThParty, ThPartyPrivate USING [ CFRef, DehandleParty, DehandleSmarts, DoAdvance, PartyBody, PartyData, SmartsBody, SmartsData, Supervise ], ThPartyRpcControl, ThPartyRpcServerImpl, ThPartyMonitorImpl, ThSmarts USING [ SmartsInterface, SmartsProperties ], ThSmartsRpcControl USING [ ImportNewInterface, InterfaceRecord ], ThVersions USING [ ThrushVR ], Triples USING [ Any, Erase, Foreach, ForeachProc, Make, Select ], TU USING [ MakeUnique, RefAddr ], VoiceUtils USING [ CmdOrToken, CurrentPasskey, InstanceFromNetAddress, MakeAtom, MakeRName, Registrize, FindWhere, Problem, RegisterWhereToReport, Report, ReportFR, WhereProc ] ; ThPartyInitImpl: CEDAR MONITOR LOCKS root IMPORTS Atom, BasicTime, IO, Commander, CommandTool, LarkPlay, LupineRuntime, NamesGV, NamesRPC, Nice, root: ThPartyMonitorImpl, Rope, RPC, SafeStorage, SmartsRpc: ThSmartsRpcControl, Thrush, ThNet, ThPartyPrivate, ThPartyRpcControl, ThPartyRpcServerImpl, ThVersions, Triples, TU, VoiceUtils EXPORTS ThParty, ThPartyPrivate SHARES ThPartyMonitorImpl, ThPartyRpcServerImpl = { OPEN IO; <> AlertKind: TYPE = Thrush.AlertKind; CallUrgency: TYPE = Thrush.CallUrgency; CFRef: TYPE = ThPartyPrivate.CFRef; ConversationHandle: TYPE = Thrush.ConversationHandle; PartyBody: TYPE = ThPartyPrivate.PartyBody; -- Concrete RTPartyType: SafeStorage.Type = SafeStorage.GetCanonicalType[CODE[PartyBody]]; PartyData: TYPE = ThPartyPrivate.PartyData; -- REF Concrete PartyHandle: TYPE = Thrush.PartyHandle; -- Handle ROPE: TYPE = Thrush.ROPE; SmartsBody: TYPE = ThPartyPrivate.SmartsBody; -- Concrete SmartsData: TYPE = ThPartyPrivate.SmartsData; -- REF Concrete RTSmartsType: SafeStorage.Type =SafeStorage.GetCanonicalType[CODE[SmartsBody]]; SmartsHandle: TYPE = Thrush.SmartsHandle; -- Handle SmartsInterface: TYPE = ThSmarts.SmartsInterface; LocalSmartsInterface: TYPE = SmartsRpc.InterfaceRecord; nullHandle: Thrush.PartyHandle = Thrush.nullHandle; SHHH: TYPE = Thrush.SHHH; none: SHHH = Thrush.unencrypted; TBD: TYPE = Thrush.TBD; pERROR: ERROR = Thrush.pERROR; H: PROC[r: REF] RETURNS[Thrush.ThHandle] = INLINE {RETURN[Thrush.H[r]]; }; larkRegistry: ROPE=".Lark"; ringTone: LarkPlay.ToneSpec _ NIL; subduedRingTone: LarkPlay.ToneSpec _ NIL; outsideRingTune: PUBLIC LarkPlay.ToneSpec _ NIL; outsideRingTuneRope: ROPE _ "@300;G%>G<%G%>G<%G%>G<%G%>G<%G%>*C"; thPartyExported: BOOL_FALSE; wpState: ThNet.WPState; standardPriorities: Thrush.Priorities = LIST [ LIST [$Supervisor], LIST [$Manager], LIST[$LocatedTerminal], LIST [$AdjacentTerminal], LIST [$VoiceTerminal, $LabPhone], LIST [$Secretary], LIST [$Receptionist] ]; <> GetParty: PUBLIC ENTRY PROC[shh: SHHH_NIL, partyID: PartyHandle, rName: Thrush.Rname, type: Thrush.PartyType] RETURNS [newPartyID: PartyHandle] = { ENABLE UNWIND=>NULL; <<|Need an NB return on failure|>> RETURN[H[GetActiveParty[partyID, rName, type]]]; }; GetActiveParty: INTERNAL PROC[partyID: PartyHandle, rName: Thrush.Rname, type: Thrush.PartyType] RETURNS [newParty: PartyData] = { rAtom: ATOM; IF rName=NIL THEN RETURN[NIL]; IF type=service THEN RETURN[GetIdleParty[partyID, rName]]; rName _ VoiceUtils.Registrize[rName]; rAtom _ VoiceUtils.MakeAtom[rName]; RETURN[NARROW[Triples.Select[$RnameForParty, --party--, rAtom]]]; }; GetIdleParty: INTERNAL PROC[partyID: PartyHandle, serviceName: ROPE] RETURNS [newPartyData: PartyData _ NIL] = { ENABLE UNWIND=>NULL; party: PartyData_ ThPartyPrivate.DehandleParty[partyID]; rAtom: ATOM; serviceNameAtom: ATOM = MakeServiceRname[serviceName].serviceRnameAtom; IF party = NIL THEN RETURN; rAtom _ NARROW[Triples.Select[$RnameForParty, party, -- rAtom --]]; rAtom _ VoiceUtils.MakeAtom[Rope.Cat[Atom.GetPName[rAtom], ".", serviceName]]; party _ NARROW[Triples.Select[$RnameForParty, --party--, serviceNameAtom]]; IF party = NIL OR party.numEnabled = 0 THEN RETURN; TU.MakeUnique[$RnameForParty, party, rAtom]; RETURN[party]; }; GetPartyFromFeepNum: PUBLIC ENTRY PROC[ shh: SHHH_none, partyID: PartyHandle, feepNum: Thrush.ROPE_NIL] RETURNS [newPartyID: PartyHandle_nullHandle] = { ENABLE UNWIND=>NULL; party: PartyData _ NIL; rName, officeNumber: ROPE; listing: ThNet.WPListing; [rName, officeNumber, listing] _ ThNet.WhitePagesEntry [ wpState: wpState, name: feepNum, feep: TRUE, key: $officeNumber]; IF rName=NIL THEN RETURN; party _ GetActiveParty[partyID, rName, individual]; IF party#NIL THEN RETURN[H[party]]; IF officeNumber=NIL THEN RETURN; RETURN[GetPartyFromNumberInt[partyID, officeNumber, rName, TRUE]]; }; GetPartyFromNumber: PUBLIC ENTRY PROC[ shh: SHHH, partyID: PartyHandle, phoneNumber: Thrush.ROPE, description: ROPE, trunkOK: BOOL] RETURNS [newPartyID: PartyHandle] = { ENABLE UNWIND => NULL; RETURN[GetPartyFromNumberInt[partyID, phoneNumber, description, trunkOK]]; }; GetPartyFromNumberInt: INTERNAL PROC[ partyID: PartyHandle, phoneNumber: Thrush.ROPE, description: ROPE, trunkOK: BOOL] RETURNS [newPartyID: PartyHandle] = { isExt: BOOLEAN _ FALSE; num: ROPE _ NIL; party: PartyData = ThPartyPrivate.DehandleParty[partyID]; <> myExtAtom: ATOM = IF party=NIL THEN NIL ELSE NARROW[Triples.Select[$Extension, party,]]; myExt: ROPE = IF myExtAtom=NIL THEN NIL ELSE Atom.GetPName[myExtAtom]; IF phoneNumber#NIL THEN [num, isExt] _ ThNet.HowToDial[phoneNumber, myExt]; IF isExt THEN { extAtom: ATOM=Atom.MakeAtom[num]; newPartyID _ H[NARROW[Triples.Select[$Extension, --party--, extAtom]]]; IF newPartyID#nullHandle THEN RETURN[newPartyID]; }; IF ~trunkOK THEN RETURN[nullHandle]; RETURN[H[GetTrunkParty[partyID: partyID, description: description, address: num]]]; }; GetTrunkParty: INTERNAL PROC[ partyID: PartyHandle, description: Thrush.ROPE, address: Thrush.ROPE] RETURNS[trunkParty: PartyData_NIL] = { party: PartyData = ThPartyPrivate.DehandleParty[partyID]; IF party#NIL THEN trunkParty _ NARROW[Triples.Select[$TrunkParty, party, Triples.Any]]; IF trunkParty = NIL OR trunkParty.numConvs#0 THEN RETURN; <<<< This is still not right. Trunk parties ought to get invented as callers want them, deleted somehow (how?), connected to smarts when call attempt starts (one only at a time). Problem reserved for future reference (maybe a structure that the GC can hack.>>>> TRUSTED { SELECT trunkParty.type FROM trunk => { trunkParty.outgoing _ address; trunkParty.reservedBy _ partyID; Triples.Erase[$RnameForTrunk, trunkParty, Triples.Any]; IF description#NIL THEN Triples.Make[$RnameForTrunk, trunkParty, Atom.MakeAtom[description]]; }; ENDCASE => ERROR; }; }; GetNumbersForRName: PUBLIC PROC[shh: SHHH_none, rName: Thrush.Rname] RETURNS [fullRName: ROPE, number: ROPE, homeNumber: ROPE] = { listing: ThNet.WPListing; [fullRName, number, listing] _ ThNet.WhitePagesEntry[ wpState: wpState, name: rName, key: $officeNumber]; IF listing#NIL THEN homeNumber _ ThNet.WhitePagesEntry[ wpState: wpState, key: $outsideNumber, listing: listing].entry; }; <<>> <> GetRname: PUBLIC PROC[shh: SHHH_none, partyID: PartyHandle] RETURNS [rName: Thrush.Rname] = { RETURN[GetRnameFromParty[ThPartyPrivate.DehandleParty[partyID]]]; }; CreateParty: PUBLIC PROC[shh: SHHH_NIL, rName: Thrush.Rname, type: Thrush.PartyType] RETURNS [partyID: PartyHandle_nullHandle] = { <<|Need NB indicating why null handle returned.|>> <> party: PartyData_NIL; partyRname: Thrush.Rname = IF type#service THEN rName ELSE MakeServiceRname[rName].serviceRname; larkName: ROPE= partyRname.Concat[larkRegistry]; rAtom: ATOM=VoiceUtils.MakeAtom[partyRname]; extension: ATOM_NIL; wpListing: ThNet.WPListing _ NIL; ExistingParty: ENTRY PROC = -- INLINE -- { ENABLE UNWIND=>NULL; party_IF type=service THEN NIL ELSE NARROW[Triples.Select[$RnameForParty, --party--, rAtom]]; IF party#NIL AND ~party.partyFailed AND party.type#type THEN { VoiceUtils.Problem[remark: "Same party name, new type", where: $System]; party_NIL; }; }; NewParty: ENTRY PROC = -- INLINE -- { <> party _ NEW[PartyBody _ [type: type]]; SELECT type FROM individual, service => { IF extension#NIL THEN TU.MakeUnique[$Extension, party, extension]; party.serviceName _ rName; -- useful in service case only }; ENDCASE; party.supervisor _ NIL; IF partyRname#NIL THEN TU.MakeUnique[$RnameForParty, party, rAtom]; TU.MakeUnique[$Priorities, party, standardPriorities]; IF wpListing#NIL THEN { ThNet.WhitePagesEnter[wpState: wpState, listing: wpListing]; []_ThNet.CloseWhitePagesDatabase[ wpState: wpState]; }; partyID _ Thrush.Enhandle[party]; IF type=individual THEN SetRingEnable[partyID: Thrush.Enhandle[party], update: TRUE]; -- Get defaults }; IF partyRname=NIL AND type#trunk THEN RETURN; ExistingParty[]; IF party#NIL THEN RETURN[H[party]]; <> SELECT type FROM individual, service => { <> attr: ROPE_NamesGV.GVGetAttribute[larkName, $extension, NIL]; feepNum: ROPE; IF attr=NIL THEN { attr _ GetNumbersForRName[rName: partyRname].number; IF attr#NIL AND ~(([attr,]_ThNet.HowToDial[attr]).isLocalExtension) THEN attr_NIL; }; IF attr#NIL THEN { extension _ VoiceUtils.MakeAtom[attr]; feepNum _ ThNet.FeepName[partyRname]; IF ThNet.WhitePagesEntry[ wpState: wpState, name: feepNum, feep: TRUE, key: $officeNumber].fullRName = NIL THEN { wpListing _ ThNet.NewListing[]; ThNet.List[wpListing, $rName, partyRname]; ThNet.List[wpListing, $name, partyRname]; -- Just in case needed ThNet.List[wpListing, $officeNumber, Rope.Cat["8923", attr]]; <> }; }; }; ENDCASE; <> NewParty[]; VoiceUtils.Report[IO.PutFR["CreateParty[%s, %s] -> [ %g, %g ]", rope[rName], rope[SELECT type FROM individual => "individual", trunk=>"trunk", service=>"service", ENDCASE=>NIL], card[partyID], IF party=NIL THEN atom[$Unknown] ELSE TU.RefAddr[party]], $Party, party]; }; Register: PUBLIC PROC[shh: SHHH, partyID: PartyHandle, interface: SmartsInterface, properties: ThSmarts.SmartsProperties, oldSmartsID: SmartsHandle] RETURNS [smartsID: SmartsHandle] = { <> RETURN[DoRegister[ThPartyPrivate.DehandleParty[partyID], NIL, interface, properties]]; }; <> RegisterLocal: PUBLIC PROC[partyID: PartyHandle, interface: LocalSmartsInterface, properties: ThSmarts.SmartsProperties, oldSmartsID: SmartsHandle] RETURNS [smartsID: SmartsHandle] = { <<|Need NB why failed|>> <> RETURN[DoRegister[ThPartyPrivate.DehandleParty[partyID], interface, NIL, properties]]; }; DoRegister: PROC[party: PartyData, localInterface: LocalSmartsInterface, remoteInterface: SmartsInterface, properties: ThSmarts.SmartsProperties] RETURNS [smartsID: Thrush.SmartsHandle_nullHandle] ={ <<|Need NB why failed|>> rName: ROPE; smarts: SmartsData; shh: SHHH; interface: SmartsRpc.InterfaceRecord; IF party=NIL THEN RETURN[nullHandle]; IF properties.role = manager THEN { <> smarts _ NARROW[Triples.Select[$Manager, party, Triples.Any]]; IF smarts#NIL THEN Deregister[shh: none, smartsID: H[smarts]]; }; rName_GetRnameFromParty[IF party.type#trunk THEN party ELSE GetHostParty[party]]; IF rName=NIL THEN { VoiceUtils.Problem["No RName for own party", $System]; RETURN; }; <> shh _ NamesRPC.StartConversation[caller: serverInterfaceName.instance, callee: rName, key: serverPassword, level: --<>--CBCCheck ! RPC.AuthenticateFailed => { VoiceUtils.Problem["Authenticate failed", $System]; GOTO Failed}]; interface _ IF remoteInterface=NIL THEN localInterface ELSE SmartsRpc.ImportNewInterface[interfaceName: remoteInterface^ ! RPC.ImportFailed => { VoiceUtils.Problem["Import failed", $System]; GOTO Failed}]; <> smarts_NEW[ThPartyPrivate.SmartsBody_[interface: interface, properties: properties, type: party.type, shh: shh, authenticated: FALSE--, remote: remoteInterface#NIL--]]; smartsID _ EnterSmarts[party, smarts]; VoiceUtils.Report[IO.PutFR["RegisterSmarts[%g, %s] -> [%g, %g]", TU.RefAddr[party], rope[SELECT properties.role FROM voiceTerminal=>"voice", manager=>"mgr", ENDCASE=>"NIY"], card[smartsID], TU.RefAddr[smarts]], $Party, party]; EXITS Failed => smartsID_nullHandle; }; GetHostParty: PROC[party: PartyData] RETURNS [hostParty: PartyData ] = { RETURN[NARROW[Triples.Select[$TrunkParty, Triples.Any, party]]]; }; RegisterClone: PUBLIC PROC[ shh: SHHH_none, partyID: PartyHandle, clonePartyID: PartyHandle, oldSmartsID: SmartsHandle_nullHandle ] RETURNS [smartsID: SmartsHandle] = { cloneParty: PartyData_ThPartyPrivate.DehandleParty[clonePartyID]; party: PartyData _ ThPartyPrivate.DehandleParty[partyID]; cloneSmarts: SmartsData; smData: SmartsData; IF party=NIL OR cloneParty=NIL THEN RETURN[nullHandle]; cloneSmarts _ NARROW[Triples.Select[$VoiceTerminal, cloneParty, -- Smarts --]]; IF cloneSmarts=NIL THEN RETURN[nullHandle]; smData_NEW[ ThPartyPrivate.SmartsBody _ [ interface: cloneSmarts.interface, properties: cloneSmarts.properties, shh: cloneSmarts.shh, authenticated: cloneSmarts.authenticated--, remote: cloneSmarts.remote--]]; smartsID _ EnterSmarts[party, smData]; VoiceUtils.Report[IO.PutFR["RegisterClone[%g, %g] -> [%g, %g]", TU.RefAddr[party], TU.RefAddr[cloneParty], card[smartsID], TU.RefAddr[smData]], $Party, party]; }; Deregister: PUBLIC ENTRY PROC[shh: SHHH, smartsID: SmartsHandle] = { OPEN Triples; ENABLE { UNWIND => NULL; }; party: PartyData; cfRef: CFRef; smarts: SmartsData; CleanupParty: INTERNAL ForeachProc -- [trip: Triples.TripleRec] -- RETURNS [continue: BOOLEAN _ TRUE] -- = { WITH trip.att SELECT FROM r: CFRef => IF (party.numEnabled=0 OR r.voiceSmartsID = smartsID) AND r.event.state#idle THEN { cfRef _ r; [] _ ThPartyPrivate.DoAdvance[ smartsID: smartsID, party: party, conv: NARROW[trip.obj], cfRef: cfRef, state: idle, reason: terminating]; -- Idle party in conversation!! RETURN[FALSE]; }; ENDCASE; }; DeregisterOneParty: INTERNAL ForeachProc -- [trip: TripleRec] -- RETURNS [continue: BOOLEAN _ TRUE] -- = { WITH trip.obj SELECT FROM p: PartyData => party _ p; ENDCASE => RETURN; -- not a party to smarts connection Erase[trip.att, party, smarts -- (trip.val) --]; party.numSmarts _ party.numSmarts - 1; IF smarts.enablesParty THEN party.numEnabled _ party.numEnabled - 1; SELECT NARROW[trip.att, ATOM] FROM $Manager => { adjSmarts: SmartsData=NARROW[Select[$AdjacentTerminal, party, Any]]; Erase[$ManagerOwner, trip.val, Any]; IF adjSmarts#NIL THEN { Erase[$AdjacentTerminal, party, Any]; party.numSmarts _ party.numSmarts - 1; IF adjSmarts.enablesParty THEN party.numEnabled _ party.numEnabled - 1; }; }; ENDCASE; DO <> cfRef_NIL; Foreach[Any, Any, party, CleanupParty]; IF cfRef = NIL THEN EXIT; ENDLOOP; IF party.numSmarts = 0 THEN { <> party.partyFailed_TRUE; ThPartyPrivate.Supervise[party]; VoiceUtils.ReportFR["DeleteParty[%g, %g]", $System, NIL, card[H[party]], TU.RefAddr[party]]; }; RETURN[FALSE]; }; smarts _ ThPartyPrivate.DehandleSmarts[smartsID]; IF smarts=NIL THEN RETURN; DO -- Outside loop avoids conflict when Deregister erases things. party_NIL; Foreach[Any, Any, smarts, DeregisterOneParty]; IF party = NIL THEN EXIT; ENDLOOP; VoiceUtils.ReportFR["UnregisterSmarts[%g, %g]", $System, NIL, card[smartsID], TU.RefAddr[smarts]]; Thrush.KillHandle[smartsID, RTSmartsType! Thrush.HandleFault=>{VoiceUtils.Problem["Problem killing smarts handle", $System]; CONTINUE}]; }; <<>> EnterSmarts: PROC[party: PartyData, smarts: SmartsData] RETURNS[smartsID: SmartsHandle] = { <> <> <> managerOwnerRAtom: ATOM_NIL; EnterSmartsE: ENTRY PROC RETURNS[smartsID: SmartsHandle] = -- INLINE -- { <> attr: ATOM=WITH props: smarts.properties SELECT FROM backstop=> $BackStop, manager=> $Manager, supervisor=> $Supervisor, voiceTerminal=> $VoiceTerminal, ENDCASE=>ERROR pERROR; smartsID _ Thrush.Enhandle[smarts]; TU.MakeUnique[attr, party, smarts]; party.numSmarts _ party.numSmarts + 1; IF smarts.enablesParty THEN party.numEnabled _ party.numEnabled + 1; WITH props: smarts.properties SELECT FROM <> manager => IF managerOwnerRAtom#NIL THEN { <> OPEN Triples; TU.MakeUnique[$ManagerOwner, smarts, managerOwnerRAtom]; WITH Select[$RnameForParty, --party--, managerOwnerRAtom] SELECT FROM managerOwnerParty: PartyData => { mgrOwnSmarts: SmartsData _ NARROW[Select[$VoiceTerminal, managerOwnerParty, --smarts--]]; IF mgrOwnSmarts#NIL THEN { TU.MakeUnique[$AdjacentTerminal, party, mgrOwnSmarts]; party.numSmarts _ party.numSmarts + 1; IF mgrOwnSmarts.enablesParty THEN party.numEnabled _ party.numEnabled + 1; }; }; ENDCASE; }; <> voiceTerminal => IF party.type=individual THEN { OPEN Triples; managerOwnerSmarts: REF; managerOwnerParty: PartyData; managerOwnerRAtom _ NARROW[Select[$RnameForParty, party, -- rAtom --]]; IF managerOwnerRAtom=NIL THEN RETURN; -- This is really an Error, though. managerOwnerSmarts _ Select[$ManagerOwner, --smarts--, managerOwnerRAtom]; IF managerOwnerSmarts=NIL THEN RETURN; managerOwnerParty _ NARROW[Select[$Manager, -- party--, managerOwnerSmarts]]; IF managerOwnerParty=NIL THEN RETURN; TU.MakeUnique[$AdjacentTerminal, managerOwnerParty, smarts]; managerOwnerParty.numSmarts _ managerOwnerParty.numSmarts + 1; IF smarts.enablesParty THEN managerOwnerParty.numEnabled _ managerOwnerParty.numEnabled + 1; }; ENDCASE; }; WITH props: smarts.properties SELECT FROM <> manager => { netAddress: Thrush.NetAddress _ [props.netAddress.net, props.netAddress.host, Pup.nullSocket]; managerOwner: ROPE _ NamesGV.GVGetAttribute[ rName: VoiceUtils.InstanceFromNetAddress[netAddress, larkRegistry], attribute: $owner, default: NIL]; IF managerOwner#NIL THEN managerOwnerRAtom _ VoiceUtils.MakeAtom[managerOwner]; }; ENDCASE; RETURN[EnterSmartsE[]]; }; Enable: PUBLIC ENTRY PROC[shh: SHHH_none, smartsID: SmartsHandle] RETURNS [nb: Thrush.NB] = { smarts: SmartsData=ThPartyPrivate.DehandleSmarts[smartsID]; IF smarts=NIL THEN RETURN[noSuchSmarts]; IF smarts.enablesParty THEN RETURN[success]; smarts.enablesParty_TRUE; RETURN[Able[smarts, 1]]; }; <<>> Disable: PUBLIC ENTRY PROC[shh: SHHH_none, smartsID: SmartsHandle] RETURNS [nb: Thrush.NB] = { smarts: SmartsData=ThPartyPrivate.DehandleSmarts[smartsID]; IF smarts=NIL THEN RETURN[noSuchSmarts]; IF ~smarts.enablesParty THEN RETURN[success]; smarts.enablesParty_FALSE; RETURN[Able[smarts, -1]]; }; Able: INTERNAL PROC[smarts: SmartsData, direction: INT] RETURNS[nb: Thrush.NB_success] = { AbleOne: Triples.ForeachProc -- [trip: TripleRec] -- RETURNS [continue: BOOLEAN _ TRUE] -- ={ WITH trip.obj SELECT FROM party: PartyData => { res: INT=party.numEnabled+direction; IF res<0 THEN nb_invalidTransition ELSE party.numEnabled _ res; }; ENDCASE; }; Triples.Foreach[Triples.Any, Triples.Any, smarts, AbleOne]; }; DescribeParty: PUBLIC ENTRY PROC[shh: SHHH_none, partyID: PartyHandle] RETURNS[ description: Thrush.ROPE] = { ENABLE UNWIND=>NULL; RETURN[DoDescribeParty[partyID]]; }; DoDescribeParty: PUBLIC INTERNAL PROC[ partyID: PartyHandle ] RETURNS[ description: Thrush.ROPE] = TRUSTED { party: PartyData = ThPartyPrivate.DehandleParty[partyID]; IF party=NIL THEN RETURN[NIL]; description _ GetRnameFromParty[party]; SELECT party.type FROM individual => RETURN[description]; trunk => { more: ROPE=IF party.outgoing#NIL THEN party.outgoing ELSE "outside line"; IF description=NIL THEN description _ more ELSE description _ IO.PutFR["%s (%s)", IO.rope[description], IO.rope[more]]; RETURN[description]; }; service => RETURN[IO.PutFR["%g service", rope[party.serviceName]]]; ENDCASE; }; <<>> GetRnameFromParty: PROC[party: PartyData] RETURNS [rName: Thrush.Rname] = { rRef: REF ANY; IF party=NIL THEN RETURN[NIL]; rRef _ Triples.Select[$RnameForParty, party, --rRef--]; IF rRef=NIL THEN rRef_Triples.Select[$RnameForTrunk, party, --rRef--]; IF rRef=NIL THEN RETURN[NIL]; RETURN[Atom.GetPName[NARROW[rRef]]]; }; MakeServiceRname: PUBLIC PROC[serviceName: Thrush.ROPE] RETURNS [serviceRname: Thrush.ROPE, serviceRnameAtom: ATOM] = { serviceRname _ Rope.Cat[serviceName, larkRegistry]; serviceRnameAtom _ VoiceUtils.MakeAtom[serviceRname]; }; <> GetCurrentParty: PUBLIC ENTRY PROC[shh: Thrush.SHHH_none, smartsID: Thrush.SmartsHandle] RETURNS [partyID: Thrush.PartyHandle_Thrush.nullHandle] = { <<| Should have an NB |>> ENABLE UNWIND=>NULL; smarts: REF _ ThPartyPrivate.DehandleSmarts[smartsID]; IF smarts=NIL THEN RETURN; FOR p: Thrush.Priorities _ standardPriorities, p.rest WHILE p#NIL DO FOR s: Thrush.Priorities _ NARROW[p.first], s.rest WHILE s#NIL DO att: ATOM_NARROW[s.first]; party: REF_Triples.Select[att, Triples.Any, smarts]; IF party#NIL THEN RETURN[H[party]]; ENDLOOP; ENDLOOP; }; GetPartySmarts: PUBLIC ENTRY PROC[partyID: Thrush.PartyHandle, kind: ATOM ] RETURNS [smartsID: Thrush.SmartsHandle]={ ENABLE UNWIND=>NULL; party: PartyData_ThPartyPrivate.DehandleParty[partyID]; IF party=NIL THEN RETURN [Thrush.nullHandle]; RETURN[H[Triples.Select[kind, party, --smarts--]]]; }; GetStdRingInfo: PUBLIC PROC [partyID: PartyHandle] = TRUSTED { party: PartyData_ThPartyPrivate.DehandleParty[partyID]; larkRname: ROPE; rdAtom: ATOM; IF party=NIL THEN RETURN; SELECT party.type FROM individual => { larkRname _ GetRnameFromParty[party].Concat[larkRegistry]; IF larkRname=larkRegistry THEN RETURN; party.ringEnable _ SELECT NamesGV.GVGetAttribute[rName: larkRname, attribute: $ringmode, default: "R"].Fetch[0] FROM 'R => on, 'S => subdued, 'O => off, ENDCASE => on; rdAtom _ VoiceUtils.MakeAtom[NamesGV.GVGetAttribute[rName: larkRname, attribute: $dotune, default: "false"]]; party.ringDo _ SELECT rdAtom FROM $true => ownTune, $false => standard, $tune => ownTune, $both => bothTunes, ENDCASE => standard; party.ringTuneRope _ Rope.Cat[NamesGV.GVGetAttribute[larkRname, $tunea, NIL], NamesGV.GVGetAttribute[larkRname, $tuneb, NIL], NamesGV.GVGetAttribute[larkRname, $tunec, NIL]]; }; ENDCASE; }; SetStdRingInfo: PUBLIC PROC[partyID: PartyHandle, update: BOOL_FALSE] = TRUSTED { party: PartyData_ThPartyPrivate.DehandleParty[partyID]; larkRname: ROPE; IF party=NIL THEN RETURN; SELECT party.type FROM individual => { r: ROPE; larkRname _ GetRnameFromParty[party].Concat[larkRegistry]; IF larkRname=larkRegistry THEN RETURN; r _ SELECT party.ringEnable FROM on => "R", subdued => "S", off => "O", ENDCASE => NIL; IF r#NIL THEN NamesGV.GVSetAttribute[larkRname, $ringmode, r]; IF update THEN NamesGV.GVUpdate[larkRname]; }; ENDCASE; }; SetRingEnable: PUBLIC PROC[shh: SHHH_none, partyID: PartyHandle, ringEnable: Thrush.RingEnable_noChange, ringInterval: INT_0, update: BOOL_FALSE] = TRUSTED { party: PartyData_ThPartyPrivate.DehandleParty[partyID]; GetStdRingInfo[partyID]; SELECT party.type FROM individual => { IF ringEnable=noChange THEN ringEnable _ party.ringEnable; party.ringEnable _ ringEnable; party.ringTime _ BasicTime.Update[BasicTime.Now[], ringInterval]; [] _ PrepareRingTune[party]; SELECT ringEnable FROM on, subdued, off => { IF update AND ringEnable#party.defaultRingEnable THEN SetStdRingInfo[partyID, ThNet.pd.autoGVUpdate]; party.defaultRingEnable _ ringEnable; }; ENDCASE; }; ENDCASE; }; PrepareRingTune: PROC[party: PartyData] RETURNS[ringTune: LarkPlay.ToneSpec] = TRUSTED { SELECT party.type FROM individual => { <> IF ThNet.pd.ringsInvalid THEN { ThNet.pd.ringsInvalid _ FALSE; ringTone _ NEW[LarkPlay.ToneSpecRec _ [ repeatIndefinitely: TRUE, volume: ThNet.pd.defaultRingVolume, tones: LIST[LIST[ [f1: 440, f2: 480, on: 2000, off: 4000], [f1: 440, f2: 480, on: 2000, off: 4000]]]]]; subduedRingTone _ NEW[LarkPlay.ToneSpecRec _ [ repeatIndefinitely: FALSE, volume: ThNet.pd.defaultRingVolume+ThNet.pd.subduedVolumeInterval, tones: LIST[LIST[[f1: 440, f2: 480, on: 500, off: 0]]] ]]; outsideRingTune _ LarkPlay.PlayString[outsideRingTuneRope, FALSE, ThNet.pd.defaultRingVolume]; }; IF party.ringDo#standard AND party.ringTuneRope#NIL THEN ringTune _ LarkPlay.PlayString[music: party.ringTuneRope, file: FALSE, volume: ThNet.pd.defaultRingVolume] ELSE { pattern: LarkPlay.ToneSpec = SELECT party.ringEnable FROM subdued, subduedTimed => subduedRingTone, ENDCASE => ringTone; ringTune _ NEW[LarkPlay.ToneSpecRec_pattern^]; }; IF ringTune#NIL THEN SELECT party.ringEnable FROM subdued, subduedTimed => { ringTune.volume _ ringTune.volume + ThNet.pd.subduedVolumeInterval; ringTune.repeatIndefinitely _ FALSE; }; ENDCASE; party.ringTune _ ringTune; }; ENDCASE; }; ReportSystem: VoiceUtils.WhereProc = CHECKED { s_NIL; }; ReportParty: VoiceUtils.WhereProc = TRUSTED { party: PartyData = NARROW[whereData]; smarts: SmartsData; IF s=NIL THEN s _ VoiceUtils.FindWhere[$System, NIL]; IF party=NIL THEN RETURN; smarts _ NARROW[Triples.Select[$VoiceTerminal, party, Triples.Any]]; IF smarts=NIL THEN RETURN; WITH props: smarts.properties SELECT FROM voiceTerminal => { netAddress: Thrush.NetAddress = [ props.netAddress.net, props.netAddress.host, Pup.nullSocket]; s_Nice.LarkConLogStream[netAddress]; }; ENDCASE; IF s#NIL AND s=Nice.LarkConLogStream[Thrush.noMachine] THEN s_VoiceUtils.FindWhere[$System, NIL]; }; WhitePagesInit: Commander.CommandProc = { treeName, intExtName: ROPE; IF wpState#NIL AND ThNet.CloseWhitePagesDatabase[wpState] THEN wpState_NIL; treeName _ CommandTool.NextArgument[cmd]; intExtName _ CommandTool.NextArgument[cmd]; IF wpState=NIL THEN wpState _ ThNet.InitWhitePagesDatabase[ treeName, -- Optional, defaults supplied by WP impl., intExtName, -- so NILs are OK here $write]; }; serverInterfaceName: ThPartyRpcControl.InterfaceName; serverPassword: RPC.EncryptionKey; ThPartyInit: Commander.CommandProc = { ENABLE RPC.ExportFailed => { VoiceUtils.Problem["ThParty export failed", $System]; GOTO Failed; }; instance: ROPE = VoiceUtils.MakeRName[style: rName, name: VoiceUtils.CmdOrToken[cmd: cmd, key: "ThrushServerInstance", default: "Strowger.Lark"]]; serverInterfaceName _ [ type: "ThParty.Lark", instance: instance, version: ThVersions.ThrushVR]; serverPassword _ VoiceUtils.CurrentPasskey[VoiceUtils.CmdOrToken[ cmd: cmd, key: "ThrushServerPassword", default: "MFLFLX"]]; VoiceUtils.RegisterWhereToReport[ReportSystem, $System]; VoiceUtils.RegisterWhereToReport[ReportParty, $Party]; ThPartyRpcControl.UnexportInterface[!LupineRuntime.BindingError=>CONTINUE]; ThPartyRpcControl.ExportInterface[ interfaceName: serverInterfaceName, user: instance, password: serverPassword]; VoiceUtils.ReportFR["Export[ThParty.Lark, %s]", $System, NIL, rope[serverInterfaceName.instance]]; EXITS Failed => ThPartyRpcControl.UnexportInterface[!LupineRuntime.BindingError=>CONTINUE]; }; Commander.Register["WhitePages", WhitePagesInit, "WhitePages > -- Initialize and Open White Pages Database"]; Commander.Register["ThParty", ThPartyInit, "ThParty \nInitialize and Export ThParty"]; }. <<>> <> <> <> <> <> <> <> < service>> < serviceName, to generalize>> <> <> <> <> <> <> <> <> <> <> <> <<>>