DIRECTORY Atom USING [ GetPName, MakeAtom ], BasicTime USING [ Now, Update ], Commander USING [ CommandProc, Register ], CommandToolExtras USING [ NextArgument ], ExplicitExport USING [ ExportExplicitly ], IO, LarkPlay USING [ PlayString, Tone, ToneSpec, ToneSpecRec ], Log USING [ FindWhere, Problem, ProblemHandle, RegisterWhereToReport, Report, ReportFR, WhereProc ], LupineRuntime USING [ BindingError ], Names USING [ CmdOrToken, CurrentPasskey, InstanceFromNetAddress, MakeAtom, Registrize ], NamesGV USING [ GVGetAttribute, GVSetAttribute, GVUpdate ], NamesRPC USING [ StartConversation ], Nice, 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, nullHandle, Machine, MakeUnique, pERROR, PhoneNumber, 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 [ RefAddr ] ; ThPartyInitImpl: CEDAR MONITOR LOCKS root IMPORTS Atom, BasicTime, IO, Commander, CommandToolExtras, ExplicitExport, LarkPlay, Log, LupineRuntime, Names, NamesGV, NamesRPC, Nice, root: ThPartyMonitorImpl, Rope, RPC, SafeStorage, SmartsRpc: ThSmartsRpcControl, Thrush, ThNet, ThPartyPrivate, ThPartyRpcControl, ThPartyRpcServerImpl, ThVersions, Triples, TU 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"; recordingRname: Thrush.Rname = "idle.jay.lark"; recordingRAtom: PUBLIC ATOM _ Atom.MakeAtom[recordingRname]; 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] RETURNS [newPartyID: PartyHandle] = { ENABLE UNWIND=>NULL; RETURN[H[GetActiveParty[rName]]]; }; GetActiveParty: INTERNAL PROC[rName: Thrush.Rname] RETURNS [newParty: PartyData] = { rAtom: ATOM; IF rName=NIL THEN RETURN[NIL]; rName _ Names.Registrize[rName]; rAtom _ Names.MakeAtom[rName]; RETURN[NARROW[Triples.Select[$RnameForParty, --party--, rAtom]]]; }; GetJayParty: PUBLIC ENTRY PROC[shh: SHHH_none, partyID: PartyHandle] RETURNS [newPartyID: PartyHandle_nullHandle] = { ENABLE UNWIND=>NULL; party: PartyData_ ThPartyPrivate.DehandleParty[partyID]; rAtom: ATOM; IF party = NIL THEN RETURN; rAtom _ NARROW[Triples.Select[$RnameForParty, party, -- rAtom --]]; rAtom _ Names.MakeAtom[Atom.GetPName[rAtom].Concat[".Jay"]]; party _ NARROW[Triples.Select[$RnameForParty, --party--, recordingRAtom]]; IF party = NIL OR party.numEnabled = 0 THEN RETURN; Thrush.MakeUnique[$RnameForParty, party, rAtom]; RETURN[H[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[rName]; 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; TRUSTED { WITH tp: trunkParty SELECT FROM trunk => { tp.outgoing _ address; tp.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] = { party: PartyData_NIL; rn: Thrush.Rname = IF type#recording THEN rName ELSE recordingRname; larkName: ROPE=rn.Concat[larkRegistry]; nameAttr: ATOM=$RnameForParty; rAtom: ATOM=Names.MakeAtom[rn]; extension: ATOM_NIL; wpListing: ThNet.WPListing _ NIL; ExistingParty: ENTRY PROC = -- INLINE -- { ENABLE UNWIND=>NULL; party_IF type=recording THEN NIL ELSE NARROW[Triples.Select[nameAttr, --party--, rAtom]]; IF party#NIL AND ~party.partyFailed AND party.type#type THEN { Log.Problem[remark: "Same party name, new type", where: $System]; party_NIL; }; }; NewParty: ENTRY PROC = -- INLINE -- { SELECT type FROM individual => { party_NEW[PartyBody _ [v: individual []]]; IF extension#NIL THEN Thrush.MakeUnique[$Extension, party, extension]; }; recording => party_NEW[PartyBody _ [v: recording []]]; trunk => party _ NEW [ PartyBody _ [ v: trunk [ NIL, NIL ] ] ]; ENDCASE => { Log.Problem["Unknown party type", $System]; RETURN; }; party.supervisor _ NIL; IF rName#NIL THEN Thrush.MakeUnique[nameAttr, party, rAtom]; Thrush.MakeUnique[$Priorities, party, standardPriorities]; IF wpListing#NIL THEN { ThNet.WhitePagesEnter[wpState: wpState, listing: wpListing]; []_ThNet.CloseWhitePagesDatabase[ wpState: wpState]; }; partyID _ Thrush.Enhandle[party]; }; rName_rn; IF rName=NIL AND type#trunk THEN RETURN; ExistingParty[]; IF party#NIL THEN RETURN[H[party]]; SELECT type FROM individual => { extR: ROPE_NamesGV.GVGetAttribute[larkName, $extension, NIL]; feepNum: ROPE; IF extR=NIL THEN { extR _ GetNumbersForRName[rName: rName].number; IF extR#NIL AND ~(([extR,]_ThNet.HowToDial[extR]).isLocalExtension) THEN extR_NIL; }; IF extR#NIL THEN { extension _ Names.MakeAtom[extR]; feepNum _ ThNet.FeepName[rName]; IF ThNet.WhitePagesEntry[ wpState: wpState, name: feepNum, feep: TRUE, key: $officeNumber].fullRName = NIL THEN { wpListing _ ThNet.NewListing[]; ThNet.List[wpListing, $rName, rName]; ThNet.List[wpListing, $name, rName]; -- Just in case needed ThNet.List[wpListing, $officeNumber, Rope.Cat["8923", extR]]; }; }; }; ENDCASE; NewParty[]; SetRingEnable[partyID: partyID, update: TRUE]; -- Get defaults Log.Report[IO.PutFR["CreateParty[%s, %s] -> [ %g, %g ]", rope[rName], rope[SELECT type FROM individual => "individual", trunk=>"trunk", recording=>"recording", ENDCASE=>NIL], card[partyID], IF party=NIL THEN atom[$Unknown] ELSE TU.RefAddr[party]], $Party, party]; }; ReleaseTrunkParty: PUBLIC ENTRY PROC[shh: SHHH, partyID: PartyHandle] = { NULL }; 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] = { RETURN[DoRegister[ThPartyPrivate.DehandleParty[partyID], interface, NIL, properties]]; }; DoRegister: PROC[party: PartyData, localInterface: LocalSmartsInterface, remoteInterface: SmartsInterface, properties: ThSmarts.SmartsProperties] RETURNS [smartsID: Thrush.SmartsHandle_nullHandle] ={ 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 RETURN[Log.ProblemHandle["No RName for own party", $System]]; shh _ NamesRPC.StartConversation[caller: myName.instance, callee: rName, key: serverPassword, level: --<>--CBCCheck ! RPC.AuthenticateFailed => { smartsID _ Log.ProblemHandle["Authenticate failed", $System]; GOTO Failed}]; interface _ IF remoteInterface=NIL THEN localInterface ELSE SmartsRpc.ImportNewInterface[interfaceName: remoteInterface^ ! RPC.ImportFailed => { smartsID _ Log.ProblemHandle["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]; Log.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]; Log.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]; Log.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; Log.ReportFR["UnregisterSmarts[%g, %g]", $System, NIL, card[smartsID], TU.RefAddr[smarts]]; Thrush.KillHandle[smartsID, RTSmartsType! Thrush.HandleFault=>{Log.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]; Thrush.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 => { OPEN Triples; Thrush.MakeUnique[$ManagerOwner, smarts, managerOwnerRAtom]; WITH Select[$RnameForParty, --party--, managerOwnerRAtom] SELECT FROM managerOwnerParty: PartyData => { mgrOwnSmarts: SmartsData _ NARROW[Select[$VoiceTerminal, managerOwnerParty, --smarts--]]; IF mgrOwnSmarts#NIL THEN { Thrush.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; Thrush.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 => { managerOwner: ROPE _ NamesGV.GVGetAttribute[ rName: Names.InstanceFromNetAddress[props.machine, larkRegistry], attribute: $owner, default: NIL]; IF managerOwner#NIL THEN managerOwnerRAtom _ Names.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]; WITH p: party SELECT FROM individual => RETURN[description]; trunk => { more: ROPE=IF p.outgoing#NIL THEN p.outgoing ELSE "outside line"; IF description=NIL THEN description _ more ELSE description _ IO.PutFR["%s (%s)", IO.rope[description], IO.rope[more]]; RETURN[description]; }; recording => RETURN["Recording service"]; 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]]]; }; GetCurrentParty: PUBLIC ENTRY PROC[shh: Thrush.SHHH_none, smartsID: Thrush.SmartsHandle] RETURNS [partyID: Thrush.PartyHandle_Thrush.nullHandle] = { 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; WITH iParty: party^ SELECT FROM individual => { larkRname _ GetRnameFromParty[party].Concat[larkRegistry]; IF larkRname=larkRegistry THEN RETURN; iParty.ringEnable _ SELECT NamesGV.GVGetAttribute[rName: larkRname, attribute: $ringmode, default: "R"].Fetch[0] FROM 'R => on, 'S => subdued, 'O => off, ENDCASE => on; rdAtom _ Names.MakeAtom[NamesGV.GVGetAttribute[rName: larkRname, attribute: $dotune, default: "false"]]; iParty.ringDo _ SELECT rdAtom FROM $true => ownTune, $false => standard, $tune => ownTune, $both => bothTunes, ENDCASE => standard; iParty.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; WITH iParty: party^ SELECT FROM individual => { r: ROPE; larkRname _ GetRnameFromParty[party].Concat[larkRegistry]; IF larkRname=larkRegistry THEN RETURN; r _ SELECT iParty.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]; WITH iParty: party^ SELECT FROM individual => { IF ringEnable=noChange THEN ringEnable _ iParty.ringEnable; iParty.ringEnable _ ringEnable; iParty.ringTime _ BasicTime.Update[BasicTime.Now[], ringInterval]; [] _ PrepareRingTune[party]; SELECT ringEnable FROM on, subdued, off => { IF update AND ringEnable#iParty.defaultRingEnable THEN SetStdRingInfo[partyID, ThNet.pd.autoGVUpdate]; iParty.defaultRingEnable _ ringEnable; }; ENDCASE; }; ENDCASE; }; PrepareRingTune: PROC[party: PartyData] RETURNS[ringTune: LarkPlay.ToneSpec] = TRUSTED { WITH iParty: party^ SELECT 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 iParty.ringDo#standard AND iParty.ringTuneRope#NIL THEN ringTune _ LarkPlay.PlayString[music: iParty.ringTuneRope, file: FALSE, volume: ThNet.pd.defaultRingVolume] ELSE { pattern: LarkPlay.ToneSpec = SELECT iParty.ringEnable FROM subdued, subduedTimed => subduedRingTone, ENDCASE => ringTone; ringTune _ NEW[LarkPlay.ToneSpecRec_pattern^]; }; IF ringTune#NIL THEN SELECT iParty.ringEnable FROM subdued, subduedTimed => { ringTune.volume _ ringTune.volume + ThNet.pd.subduedVolumeInterval; ringTune.repeatIndefinitely _ FALSE; }; ENDCASE; iParty.ringTune _ ringTune; }; ENDCASE; }; ReportSystem: Log.WhereProc = CHECKED { s_NIL; }; ReportParty: Log.WhereProc = TRUSTED { party: PartyData = NARROW[whereData]; smarts: SmartsData; IF s=NIL THEN s _ Log.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 => s_Nice.LarkConLogStream[props.machine]; ENDCASE; IF s#NIL AND s=Nice.LarkConLogStream[[[0],[0]]] THEN s_Log.FindWhere[$System, NIL]; }; WhitePagesInit: Commander.CommandProc = { treeName, intExtName: ROPE; IF wpState#NIL AND ThNet.CloseWhitePagesDatabase[wpState] THEN wpState_NIL; treeName _ CommandToolExtras.NextArgument[cmd]; intExtName _ CommandToolExtras.NextArgument[cmd]; IF wpState=NIL THEN wpState _ ThNet.InitWhitePagesDatabase[ treeName, -- Optional, defaults supplied by WP impl., intExtName, -- so NILs are OK here $write]; }; myName: ThPartyRpcControl.InterfaceName; serverPassword: RPC.EncryptionKey; ThPartyInit: Commander.CommandProc = { ENABLE RPC.ExportFailed => { Log.Problem["ThParty export failed", $System]; GOTO Failed; }; myName _ [ type: "ThParty.Lark", instance: Names.CmdOrToken[cmd: cmd, key: "ThrushServerInstance", default: "Morley.Lark"], version: ThVersions.ThrushVR]; serverPassword _ Names.CurrentPasskey[Names.CmdOrToken[ cmd: cmd, key: "ThrushServerPassword", default: "MFLFLX"]]; IF thPartyExported THEN RETURN; Log.RegisterWhereToReport[ReportSystem, $System]; Log.RegisterWhereToReport[ReportParty, $Party]; ThPartyRpcControl.ExportInterface[ interfaceName: myName, user: myName.instance, password: serverPassword]; ExplicitExport.ExportExplicitly["ThParty.Lark", myName.instance, ThVersions.ThrushVR, ThPartyRpcControl.LupineProtocolVersion, ThPartyRpcServerImpl.ServerDispatcher]; Log.ReportFR["Export[ThParty.Lark, %s]", $System, NIL, rope[myName.instance]]; thPartyExported _ TRUE; EXITS Failed => ThPartyRpcControl.UnexportInterface[!LupineRuntime.BindingError=>CONTINUE]; }; Commander.Register["WhitePages", WhitePagesInit, "WhitePages > -- Initialize and Open White Pages Database"]; Commander.Register["ThParty", ThPartyInit, "ThParty > -- Initialize and Export ThParty"]; }. èThPartyInitImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Last modified by D. Swinehart, March 5, 1985 6:42:41 pm PST Copies Implementation of ThParty |Need an NB return on failure| myExt is used as Intelnet authorization code. << 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.>> Return the Rname for a given party |Need NB indicating why null handle returned.| Party does not already exist. Make one if possible. Get data base information Telephone system extension Will be entered in NewParty, under monitor Party does not already exist. Make one if possible. <> Doesn't handle oldHandle (ReRegister) yet. Implementation of ThPartyPrivate |Need NB why failed| Doesn't handle oldHandle (ReRegister) yet. |Need NB why failed| Allow only one Manager. Allow it to be the new one. Take care of encryption, and talking to calling smarts Create Smarts body, link with Party; we're rolling. Remove party from each conversation in which smarts is the voiceSmarts, or to which no enabled smarts remain connected. Outside loop avoids Foreach/Erase conflicts. Party is serving no smarts; destroy the party itself. Discussion: The idea is that as Smarts register with a Party they are connected by links whose attributes are derived from their properties and degree of authentication. When Parties initiate actions that involve Smarts they will select the Smarts to respond in an order determined by the attributes, including SmartsRole, authentication (maybe, see notes), etc. Each kind of Smarts has its own link name to its Party A Manager also needs to know the party to whom the physical workstation belongs, as an aid to locating the corresponding voice terminal. Set up the relationship, then locate the adjacent terminal, if one already exists. When the Manager registers first, the $Adjacent link must be sent when the Lark smarts arrives or dies and revives. A Manager also needs to know the party to whom the physical workstation belongs, as an aid to locating the corresponding voice terminal. Set up the relationship, then locate the adjacent terminal, if one already exists. See if it works just to get the first instance of the highest priority connection that exists, using standardPriorities. | Should have an NB | Set Defaults ʘJšœ™šœ Ïmœ1™˜DJ˜———š   œžœžœžœžœ.˜TJšžœ&˜-J™.Jšœžœ˜Jšœžœžœžœ˜DJšœ žœ˜'Jšœ žœ˜Jšœžœ˜Jšœ žœžœ˜Jšœžœ˜!š  œžœžœŸ œ˜*Jšžœžœžœ˜Jš œžœžœžœžœžœŸ œ ˜Yš žœžœžœžœžœ˜>JšœHžœ˜O—J˜—š œžœžœŸ œ˜%Jšœ4™4šžœž˜˜Jšœžœ!˜*Jšžœ žœžœ1˜FJ˜—Jšœžœ ˜6Jšœžœžœžœ˜?Jšžœ2žœ˜C—Jšœžœ˜Jšžœžœžœ+˜šœ žœ+˜8šœžœž˜"JšœDžœžœ˜R—Jš œžœžœžœžœžœ!˜X—šœ˜J˜——š  œžœžœžœžœžœ˜QJ™8J˜—š œžœžœžœ3˜RJ˜AJšžœ˜$Jšœ*™*Jšžœ3žœ˜YJ˜—Jšœ ™ J˜š  œžœžœ7˜QJ˜AJšžœ˜$J™Jšœ*™*Jšžœ>žœ˜YJ˜—š  œžœ8˜HJ˜HJšžœ.˜5J™Jšœžœ˜ J˜Jšœžœ˜ J˜%Jšžœžœžœžœ ˜%šžœžœ˜#J™3Jšœ žœ/˜>Jšžœžœžœ!žœ ˜>J˜—Jšœžœžœžœ˜QJšžœžœžœžœ7˜OJ˜Jšœ6™6šœH˜HJšœŸ œ žœ˜KJšœ@žœ ˜N—šœ žœžœžœ˜6šžœ@žœ˜WJšœ:žœ ˜H—J˜—Jšœ3™3šœžœJ˜TJšœ+žœžœ˜T—Jšœ&˜&˜9J˜Jšœžœžœ)žœ ˜YJšœ4˜4—šž˜J˜—Jšœ˜J˜—š  œžœžœ˜HJšžœžœ6˜CJ˜—š  œžœžœ˜Jšœžœ˜%Jšœ?˜?Jšœžœ˜&JšœA˜AJšœ9˜9J˜J˜Jš žœžœžœ žœžœžœ ˜7Jšœžœ,Ÿ œ˜OJšžœ žœžœžœ ˜+šœžœ˜)˜FJ˜a——Jšœ&˜&˜8JšœO˜OJšœ˜—Jšœ˜J˜—š   œžœžœžœžœ˜DJšžœ ˜ šžœž˜Jšžœžœ˜J˜—J˜J˜ J˜J•StartOfExpansionD -- [trip: Triples.TripleRec] RETURNS [continue: BOOLEAN _ TRUE] -- ˜š  œžœ Ðck˜?JšŸ(œ˜-šžœ žœž˜šœ ˜ šžœžœžœžœ˜SJ˜ šœ˜Jšœ˜Jšœ ˜ Jšœžœ ˜Jšœ ˜ J˜ JšœŸ˜5—Jšžœžœ˜J˜——Jšžœ˜—J˜J˜—–D -- [trip: Triples.TripleRec] RETURNS [continue: BOOLEAN _ TRUE] -- š œžœ ¡˜=Jš¡(œ˜-šžœ žœž˜J˜JšžœžœŸ#˜6—J˜1J˜&Jšžœžœ)˜Dšžœžœ žœž˜"˜ Jšœžœ(˜DJ˜$šžœ žœžœ˜J˜%J˜&Jšœžœ)˜GJ˜—J˜—Jšžœ˜—šž˜J™¥Jšœžœ˜ J˜'Jšžœ žœžœžœ˜Jšžœ˜—šžœžœ˜J™5Jšœžœ˜Jšœ ˜ Jšœ-žœžœ žœ˜UJ˜—Jšžœžœ˜J˜—J˜J˜1Jšžœžœžœžœ˜šžœŸ>˜AJšœžœ˜ Jšœ.˜.Jšžœ žœžœžœ˜Jšžœ˜—Jšœ2žœžœ˜[šœ)˜)JšœLžœ˜W—J˜J™—š  œžœ'žœ˜[Jšœ ™ Jšœ™JšœÀ™ÀJšœžœžœ˜š   œžœžœžœŸ œ˜IJ™6šœžœžœžœž˜4J˜Jšœ˜J˜J˜Jšžœžœ˜—J˜#J˜'Jšœ&˜&Jšžœžœ)˜DJ˜šžœžœž˜)J™Ü˜ Jšžœ ˜ Jšœ<˜<šžœŸ œžœž˜Ešœ!˜!šœ˜Jšžœ+Ÿ œ˜>—šžœžœžœ˜Jšœ:˜:J˜&Jšžœžœ)˜JJ˜—J˜—Jšžœ˜—J˜—J™sšœžœžœ˜0Jšžœ ˜ Jšœžœ˜Jšœ˜JšœžœŸ œ˜GJš žœžœžœžœŸ#˜IJšœ+Ÿ œ˜JJšžœžœžœžœ˜&JšœžœŸ œ˜MJšžœžœžœžœ˜%Jšœ@˜@Jšœ>˜>šžœž˜Jšœ@˜@—J˜—Jšžœ˜—J˜J˜—šžœžœž˜)J™Ü˜ šœžœ˜,JšœA˜AJšœžœ˜!—Jšžœžœžœ2˜JJ˜—Jšžœ˜—Jšžœ˜J˜—J˜š  œžœžœžœžœ˜AJšžœ žœ˜J˜;Jšžœžœžœžœ˜(Jšžœžœžœ ˜,Jšœžœ˜Jšžœ˜J˜J™—š  œžœžœžœžœ˜BJšžœ žœ˜J˜;Jšžœžœžœžœ˜(Jšžœžœžœ ˜-Jšœžœ˜Jšžœ˜J˜—J˜š œžœžœ žœ˜7Jšžœ žœ ˜"š œ¡˜1Jš¡(œ˜,šžœ žœž˜˜Jšœžœ˜$Jšžœžœ˜"Jšžœ˜J˜—Jšžœ˜—J˜—J˜;J˜—J˜š   œžœžœžœžœ˜0J˜Jšžœžœ˜&Jšžœžœžœ˜Jšžœ˜!J˜—J˜š œžœžœžœ˜=Jšžœžœžœ˜.J˜9Jš žœžœžœžœžœ˜Jšœ'˜'šžœ žœž˜Jšœžœ˜"šœ ˜ Jš œžœžœ žœžœ žœ˜AJšžœ žœžœ˜*Jšžœžœžœžœ ˜LJšžœ˜J˜—Jšœ)˜)Jšžœ˜—J˜J™—š œžœžœ˜KJšœžœžœ˜Jš žœžœžœžœžœ˜Jšœ-Ÿœ˜7Jšžœžœžœ,Ÿœ˜FJš žœžœžœžœžœ˜Jšžœžœ ˜'—J˜J™xš œžœž œ žœ%˜XJšžœ4˜;J™Jšžœžœžœ˜Jšœžœ+˜6Jšžœžœžœžœ˜šžœ3žœžœž˜Dš žœžœžœžœž˜AJšœžœžœ ˜Jšœžœ*˜4Jšžœžœžœžœ ˜#Jšžœ˜—Jšžœ˜ J˜——š  œžœžœžœ$ž˜IJšœžœ"˜+Jšžœžœžœ˜Jšœ7˜7Jšžœžœžœžœ˜-JšžœžœŸ œ˜6J˜—š œžœžœžœ˜>Jšœ7˜7Jšœ žœ˜Jšœžœ˜ Jšžœžœžœžœ˜šžœžœž˜J˜Jšœ:˜:Jšžœžœžœ˜&šœž˜šœVž˜ZJ˜ J˜J˜ Jšžœ˜——Jšœh˜hšœžœž˜"Jšœ˜J˜J˜J˜Jšžœ ˜—šœIžœ˜NJšœ*žœ˜/Jšœ*žœ˜0—J˜Jšžœ˜—J˜J˜—š  œžœžœžœžœžœ˜QJšœ7˜7Jšœ žœ˜Jšžœžœžœžœ˜šžœžœž˜J˜Jšœžœ˜Jšœ:˜:Jšžœžœžœ˜&Jš œžœžœ(žœžœ˜XJšžœžœžœ1˜>Jšžœžœ˜+J˜Jšžœ˜—J˜J˜—š  œžœžœžœSžœ žœžœžœ˜Jšœ7˜7J˜Jšžœžœž˜J˜J˜;J˜JšœB˜BJšœ˜Jšžœ ž˜šœ˜šžœžœ%ž˜6Jšœ/˜/—Jšœ&˜&J˜—Jšžœ˜ J˜Jšžœ˜J˜—J˜š œžœžœ žœ˜XJšžœžœž˜J˜J™ šžœžœ˜Jšœžœ˜šœ žœ˜'Jšœžœ%˜=šœžœžœ˜Jšœ(˜(Jšœ-˜-——šœžœ˜.Jšœžœ˜JšœB˜BJšœž œ&˜6Jšœ˜—Jšœ;žœ˜^J˜—šžœžœžœž˜:Jšœk˜k—šžœ˜šœžœž˜:Jšœ*žœ ˜>—Jšœ žœ ˜.J˜—š žœ žœžœžœž˜2šœ˜JšœC˜CJšœžœ˜$J˜—Jšžœ˜—Jšœ˜J˜Jšžœ˜J˜J˜—šœžœ˜'Jšœžœ˜J˜J˜—šœžœ˜&Jšœžœ ˜%Jšœ˜Jšžœžœžœ!˜.Jšžœžœžœžœ˜Jšœ žœ5˜DJšžœžœžœžœ˜šžœžœž˜)Jšœ8žœ˜A—Jš žœžœžœ$žœžœ˜SJ˜J˜—š œ˜)Jšœžœ˜Jš žœ žœžœ(žœ žœ˜KJšœ/˜/Jšœ1˜1šžœ žœžœ(˜;Jšœ Ÿ+˜5Jšœ Ÿ˜#J˜—J˜—J˜J˜(Jšœžœ˜"J˜š  œ˜&šž˜JšžœBžœ ˜T—˜ J˜JšœZ˜ZJ˜—šœ7˜7J˜;—Jšžœžœžœ˜Jšœ1˜1Jšœ/˜/˜"J˜J˜J˜—J˜¦Jšœ2žœ˜NJšœžœ˜šž˜JšœKžœ˜U—J˜—J˜Jšœ—˜—šœ*˜*JšœZ˜Z—J˜J˜—…—f*Ž