<> <> <> <> DIRECTORY Atom USING [ GetPName, MakeAtom ], BasicTime USING [ GMT, Now, OutOfRange, Period, Update ], Commander USING [ CommandProc, Register ], CommandTool USING [ NextArgument ], GVBasics USING [ Password ], IO, LupineRuntime USING [ BindingError ], MBQueue USING [ Create ], NamesGV USING [ GVAuthenticate, GVGetAttribute ], NamesRPC USING [ StartConversation ], Nice, Pup USING [ nullAddress ], PupSocket USING [ CreateEphemeral, GetLocalAddress, Socket ], RefID USING [ ID, Release, Reseal, Seal ], Rope USING [ Concat, Cat, Length, ROPE, Substr ], RPC USING [ AuthenticateFailed, EncryptionKey, ExportFailed, ImportFailed ], ThNet USING [ CloseWhitePagesDatabase, HowToDial, InitWhitePagesDatabase, pd, WhitePagesEntry, WPListing, WPState ], ThParty USING [ Credentials, NameReq, SmartsInterfaceName, SmartsInterfaceRecord, SmartsProperties ], ThPartyPrivate USING [ AlertOne, DoAdvance, ConversationData, ConvState, GetConvState, MakeSubstitution, PartyBody, PartyData, PartyConflict, SmartsBody, SmartsData, UnsealParty, UnsealSmarts, VoiceParty ], ThPartyRpcControl, ThPartyMonitorImpl, Thrush USING [ ConversationID, Credentials, NB, NetAddress, noAddress, notReallyInConv, nullID, PartyID, PartyType, ROPE, SHHH, SmartsID, unencrypted ], ThSmartsRpcControl USING [ ImportNewInterface ], ThVersions USING [ ThrushVR ], Triples USING [ Any, Erase, Foreach, ForeachProc, Item, Make, Select ], TU USING [ MakeUnique, RefAddr ], VoiceUtils USING [ CmdOrToken, CurrentPasskey, InstanceFromNetAddress, MakeAtom, MakeRName, Registrize, RnameToRspec, FindWhere, Problem, ProblemFR, RegisterWhereToReport, Report, ReportFR, WhereProc ] ; ThPartyInitImpl: CEDAR MONITOR LOCKS root IMPORTS Atom, BasicTime, Commander, CommandTool, IO, LupineRuntime, MBQueue, NamesGV, NamesRPC, Nice, PupSocket, root: ThPartyMonitorImpl, RefID, Rope, RPC, SmartsRpc: ThSmartsRpcControl, ThNet, ThPartyPrivate, ThPartyRpcControl, ThVersions, Triples, TU, VoiceUtils EXPORTS ThParty, ThPartyPrivate SHARES ThPartyMonitorImpl = { OPEN IO, pd: ThNet.pd; <> NB: TYPE = Thrush.NB; PartyData: TYPE = ThPartyPrivate.PartyData; -- REF Concrete PartyID: TYPE = Thrush.PartyID; -- ID nullID: PartyID = Thrush.nullID; ROPE: TYPE = Thrush.ROPE; SmartsData: TYPE = ThPartyPrivate.SmartsData; -- REF Concrete SmartsID: TYPE = Thrush.SmartsID; -- ID SHHH: TYPE = Thrush.SHHH; none: SHHH = Thrush.unencrypted; Reseal: PROC[r: REF] RETURNS[RefID.ID] = INLINE {RETURN[RefID.Reseal[r]]; }; Any: Triples.Item = Triples.Any; larkRegistry: ROPE=".Lark"; wpState: ThNet.WPState; <> GetParty: PUBLIC ENTRY PROC[shh: SHHH_NIL, partyID: PartyID, rName: Thrush.ROPE, type: Thrush.PartyType_NIL] RETURNS [nb: NB, newPartyID: PartyID] = { ENABLE UNWIND=>NULL; newParty: PartyData; [nb, newParty] _ GetActiveParty[partyID, rName, type]; newPartyID _ Reseal[newParty]; }; <> <> <> <> <> <> <> GetPartyFromFeepNum: PUBLIC ENTRY PROC[ shh: SHHH_none, partyID: PartyID, feepNum: Thrush.ROPE_NIL] RETURNS [nb: NB_$success, newPartyID: PartyID_nullID] = { ENABLE UNWIND=>NULL; party: PartyData _ NIL; rName: ROPE _ ThNet.WhitePagesEntry [ wpState: wpState, name: feepNum, feep: TRUE, key: $OfficeNumber].fullRName; IF rName=NIL THEN RETURN[nb: $noSuchParty2]; [nb, party] _ GetActiveParty[partyID, rName, NIL]; newPartyID _ Reseal[party]; }; <> <> <> <> <> <> <> <> GetPartyFromNumber: PUBLIC ENTRY PROC[ shh: SHHH, partyID: PartyID, phoneNumber: Thrush.ROPE, description: ROPE] RETURNS [nb: NB, newPartyID: PartyID] = { ENABLE UNWIND => NULL; [nb, newPartyID] _ GetPartyFromNumberInt[partyID, phoneNumber, description, FALSE]; }; <> <> <> <> <> <> GetNumbersForRName: PUBLIC PROC[shh: SHHH_none, rName: Thrush.ROPE] 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; }; <<>> GetActiveParty: INTERNAL PROC[partyID: PartyID, rName: Thrush.ROPE, type: Thrush.PartyType] RETURNS [nb: NB_$success, newParty: PartyData_NIL] = { <> party: PartyData _ ThPartyPrivate.UnsealParty[partyID]; [nb, newParty] _ GetActivePartyDo[party, rName, type]; <> }; <> <> <> <> <> <> <> GetActivePartyDo: INTERNAL PROC[party: PartyData, rName: Thrush.ROPE, type: Thrush.PartyType] RETURNS [nb: NB_$success, newParty: PartyData_NIL] = { number: ROPE; listing: ThNet.WPListing; wpRname: ROPE; partyID: PartyID; partyID _ Reseal[party]; IF rName=NIL THEN RETURN[$noIdentSupplied, NIL]; IF type = NIL THEN { -- request for $individual or $telephone [nb, newParty] _ GetActiveParty[partyID, rName, $individual]; IF nb # $success THEN [nb, newParty] _ GetActiveParty[partyID, rName, $telephone] ELSE IF ThPartyPrivate.VoiceParty[newParty] = ThPartyPrivate.VoiceParty[party] THEN { <> altNewParty: PartyData; altNB: NB; [altNB, altNewParty] _ GetActiveParty[partyID, rName, $telephone]; IF altNB = $success THEN RETURN[$success, altNewParty]; }; RETURN; }; SELECT type FROM $service => { [nb, newParty] _ GetIdleParty[partyID, rName]; RETURN; }; $individual, $telephone => NULL; -- go on $trunk => ERROR; -- until further notice ENDCASE => ERROR; newParty _ NARROW[ Triples.Select[type, VoiceUtils.MakeAtom[VoiceUtils.Registrize[rName]], --party-- ] ]; IF newParty#NIL AND newParty.enabled THEN { SetPoaching[newParty, party]; RETURN; }; IF type # $telephone THEN RETURN[nb: $noSuchParty2]; <> [wpRname, number, listing] _ ThNet.WhitePagesEntry [ wpState: wpState, name: rName, key: $OfficeNumber]; IF wpRname=NIL OR listing=NIL THEN RETURN[nb: $noSuchParty2]; IF number=NIL THEN number _ ThNet.WhitePagesEntry [ wpState: wpState, name: rName, key: $OutsideNumber, listing: listing].entry; IF number=NIL THEN RETURN[nb: $noSuchParty2]; [nb, partyID] _ GetPartyFromNumberInt[Reseal[party], number, wpRname, TRUE]; IF nb=$success THEN newParty _ ThPartyPrivate.UnsealParty[partyID]; }; <> GetIdleParty: INTERNAL PROC[partyID: PartyID, serviceName: ROPE] RETURNS [nb: NB, newParty: PartyData _ NIL] = { <> ENABLE UNWIND=>NULL; found: BOOL_FALSE; now: BasicTime.GMT = BasicTime.Now[]; myName: ROPE; party: PartyData_ ThPartyPrivate.UnsealParty[partyID]; serviceNameAtom: ATOM = MakeServiceRname[serviceName].serviceRnameAtom; <> FindIdleParty: Triples.ForeachProc = { <<[trip: Triples.TripleRec] RETURNS [continue: BOOLEAN _ TRUE]>> <> newParty _ NARROW[trip.val]; IF newParty.numConvs#0 THEN RETURN; IF newParty.numConvs=0 AND newParty.enabled AND (newParty.reservedBy=Thrush.nullID OR BasicTime.Period[from: newParty.reservationTime, to: now! BasicTime.OutOfRange => CONTINUE] > pd.serviceGuaranteedReservationTime) THEN { found _ TRUE; RETURN[FALSE]; }; }; IF party = NIL THEN RETURN[nb: $noSuchParty]; [nb, myName,] _ GetRnameFromParty[party]; <> IF nb # $success THEN RETURN[nb: nb, newParty: NIL]; Triples.Foreach[$service, serviceNameAtom, Triples.Any--party--, FindIdleParty]; IF ~found THEN RETURN[nb: $noSuchParty2]; <> newParty.reservedBy _ partyID; newParty.name _ myName; newParty.reservationTime _ now; }; <> <> <> <> ReleaseParty: PUBLIC ENTRY PROC[shh: SHHH_none, partyID: PartyID, targetPartyID: PartyID] RETURNS [nb: NB_$success] = { <> targetParty: PartyData _ ThPartyPrivate.UnsealParty[targetPartyID]; IF targetParty=NIL OR targetParty.reservedBy#partyID OR targetParty.type#$service THEN RETURN; targetParty.reservationTime _ BasicTime.Update[targetParty.reservationTime, -100! BasicTime.OutOfRange => CONTINUE]; }; <> GetPartyFromNumberInt: INTERNAL PROC[ partyID: PartyID, phoneNumber: Thrush.ROPE, description: ROPE, trunkRequired: BOOL] RETURNS [nb: NB_$success, newPartyID: PartyID_nullID] = { <> isExt: BOOLEAN _ FALSE; num: ROPE _ NIL; party: PartyData _ ThPartyPrivate.UnsealParty[partyID]; trunkParty: PartyData; myExtAtom: ATOM; myExt: ROPE; <> IF party=NIL THEN RETURN[$noSuchParty, nullID]; myExtAtom _ GetRnameFromParty[party, $current].rAtom; -- rName for party. myExtAtom _ IF myExtAtom=NIL THEN NIL ELSE NARROW[Triples.Select[$Extension, myExtAtom, -- extension --]]; myExt _ IF myExtAtom=NIL THEN NIL ELSE Atom.GetPName[myExtAtom]; <> <> [num, isExt] _ ThNet.HowToDial[phoneNumber, myExt]; IF isExt AND ~trunkRequired THEN { atom: ATOM_Atom.MakeAtom[num]; -- phone extension IF atom #NIL THEN atom _ NARROW[Triples.Select[$Extension, --party--, atom]]; IF atom#NIL THEN { newParty: PartyData; [nb, newParty]_ GetActiveParty[partyID, Atom.GetPName[atom], NIL]; newPartyID _ Reseal[newParty]; SELECT nb FROM $noSuchParty2 => NULL; -- extension info is there, but party isn't! ENDCASE => RETURN; }; }; <> [nb, trunkParty] _ GetRelatedParty[party]; -- trunk if telephone/individual, or vice/versa IF nb # $success THEN { party _ NARROW[Triples.Select[$Poaching, party, -- poachee--]]; IF party#NIL THEN [nb, trunkParty] _ GetRelatedParty[party]; }; IF nb # $success THEN RETURN; trunkParty.outgoing _ num; trunkParty.reservedBy _ partyID; trunkParty.name _ description; -- starts out as name of trunk owner, changes on first use newPartyID _ Reseal[trunkParty]; }; <> <> <> <> <> <> GetRelatedParty: INTERNAL PROC[party: PartyData] RETURNS [nb: NB, relatedParty: PartyData_NIL] = { rAtom: ATOM; [nb,,rAtom]_GetRnameFromParty[party, $owner]; IF nb # $success THEN RETURN; -- VERY strange SELECT party.type FROM $individual, $telephone => { relatedParty _ NARROW[Triples.Select[$trunk, rAtom, -- trunk party --]]; <> IF ThPartyPrivate.PartyConflict[relatedParty, party] THEN nb _ $voiceTerminalBusy; }; $trunk => relatedParty _ NARROW[Triples.Select[$telephone, rAtom, -- telephone party --]]; ENDCASE; IF relatedParty = NIL THEN nb _ $noSuchParty2 ELSE IF relatedParty.numConvs#0 THEN nb _ $convStillActive -- busy ELSE IF ~relatedParty.enabled THEN nb _ $noSuchParty2; -- not available yet }; <> <> <> <> <> <> <> <<>> Register: PUBLIC PROC[ shh: SHHH_none, rName: ROPE_NIL, -- party identification type: Thrush.PartyType _ $individual, clonePartyID: PartyID, interface: ThParty.SmartsInterfaceName, -- interface properties: ThParty.SmartsProperties ] RETURNS [ nb: NB, credentials: ThParty.Credentials_[] ] = { [nb, credentials] _ DoRegister[rName, type, clonePartyID, interface, NIL, nullID, properties]; }; <> RegisterLocal: PUBLIC PROC[ shh: SHHH_none, rName: ROPE_NIL, -- party identification type: Thrush.PartyType _ $individual, clonePartyID: PartyID, interfaceRecord: ThParty.SmartsInterfaceRecord _ NIL, smartsID: Thrush.SmartsID, -- Use this one, if given. properties: ThParty.SmartsProperties ] RETURNS [ nb: NB, credentials: ThParty.Credentials_[] ] = { [nb, credentials] _ DoRegister[rName, type, clonePartyID, [ ], interfaceRecord, smartsID, properties]; }; <> DoRegister: PROC[ rName: ROPE_NIL, -- party identification type: Thrush.PartyType _ $individual, clonePartyID: PartyID, interface: ThParty.SmartsInterfaceName, -- interface interfaceRecord: ThParty.SmartsInterfaceRecord, smartsID: Thrush.SmartsID, -- Use this one, if given. properties: ThParty.SmartsProperties ] RETURNS [ nb: NB, credentials: ThParty.Credentials_[] ] = { hostAtom: ATOM = VoiceUtils.MakeAtom[ VoiceUtils.InstanceFromNetAddress[properties.netAddress, Atom.GetPName[type]]]; smarts: SmartsData_NARROW[Triples.Select[$SmartsForHost, hostAtom, -- smarts --]]; party: PartyData; partyToSmartsShh: SHHH _ none; IF clonePartyID#nullID THEN { [nb, credentials] _ RegisterClone[clonePartyID, hostAtom]; RETURN; }; IF smarts#NIL AND smarts.type#$service THEN { <> <> s: RefID.ID = Reseal[smarts]; nb_Deregister[none, s]; IF nb # $success THEN VoiceUtils.ProblemFR[ "Couldn't deregister smarts %g (%g); will attempt to reregister anyway.", $System, NIL, card[s], atom[nb]]; }; IF rName=NIL THEN RETURN[nb: $noIdentSupplied]; IF interfaceRecord=NIL THEN { partyToSmartsShh _ NamesRPC.StartConversation[ caller: serverInterfaceName.instance, callee: rName, key: serverPassword, level: --<>--CBCCheck ! RPC.AuthenticateFailed => { VoiceUtils.Problem["Authenticate failed", $System]; CONTINUE}]; IF partyToSmartsShh=NIL THEN RETURN[nb: $couldntAuthenticate]; interfaceRecord _ SmartsRpc.ImportNewInterface[ interfaceName: interface ! RPC.ImportFailed => { VoiceUtils.Problem["Import failed", $System]; CONTINUE}]; IF interfaceRecord=NIL THEN RETURN[nb: $couldntConnect]; }; { RegisterEntry: ENTRY PROC={ [nb, party] _ CreateParty[rName, type]; IF nb#$success THEN RETURN; <> smarts_NEW[ThPartyPrivate.SmartsBody_[ properties: properties, type: type, interface: interfaceRecord, shh: partyToSmartsShh, notifications: MBQueue.Create[], pupSocket: PupSocket.CreateEphemeral[remote: Pup.nullAddress] ]]; IF smartsID#nullID THEN { suppliedSmarts: SmartsData _ ThPartyPrivate.UnsealSmarts[smartsID]; suppliedSmarts^ _ smarts^; smarts _ suppliedSmarts; }; smarts.properties.netAddress.socket _ PupSocket.GetLocalAddress[smarts.pupSocket].socket; <> TU.MakeUnique[$Smarts, party, smarts]; IF type#$service THEN TU.MakeUnique[$SmartsForHost, hostAtom, smarts]; --Figure out how to do this stuff for services credentials.partyID _ RefID.Reseal[party]; credentials.smartsID _ RefID.Seal[smarts]; SetPoaching[party]; -- This is recomputed on every GetParty, included here for debugging ease. }; RegisterEntry[]; }; VoiceUtils.Report[IO.PutFR["Register[%g, %g, %g] -> [%g, %g]", rope[rName], atom[type], TU.RefAddr[ThPartyPrivate.UnsealParty[clonePartyID]], TU.RefAddr[party], TU.RefAddr[smarts]]]; }; <> <> <> <> <> <> <> <> AssignSmartsID: PUBLIC PROC RETURNS [smartsID: SmartsID] = { smarts: SmartsData _ NEW[ThPartyPrivate.SmartsBody _ [ properties: [], interface: NIL, shh: NIL, notifications: NIL]]; RETURN[RefID.Seal[smarts]]; }; CheckIn: PUBLIC PROC[shh: SHHH_none, credentials: ThParty.Credentials] RETURNS [nb: NB_$success] = { NULL; }; <> <> CreateParty: INTERNAL PROC[rName: Thrush.ROPE, type: Thrush.PartyType] RETURNS [nb: NB_$success, party: PartyData_NIL] = { <> partyRname: Thrush.ROPE = IF type # $service THEN rName ELSE MakeServiceRname[rName].serviceRname; partyID: PartyID_nullID; rAtom: ATOM=VoiceUtils.MakeAtom[partyRname]; extension: ATOM_NIL; IF partyRname=NIL THEN RETURN[nb: $noIdentSupplied]; IF type # $service THEN party_ NARROW[Triples.Select[type, rAtom, --party--]]; IF party#NIL THEN RETURN[$success, party]; party _ NEW[ThPartyPrivate.PartyBody _ [type: type, name: rName]]; SELECT type FROM $individual, $telephone, $service => { -- Local telephone number num: ROPE_GetNumbersForRName[rName: partyRname].number; party.outgoing _ num; IF num#NIL AND ~(([num,]_ThNet.HowToDial[num]).isLocalExtension) THEN num_NIL; IF num#NIL THEN extension _ VoiceUtils.MakeAtom[num]; }; ENDCASE; partyID _ RefID.Seal[party]; TU.MakeUnique[type, rAtom, party]; IF extension#NIL THEN TU.MakeUnique[$Extension, rAtom, extension]; <> IF type = $trunk THEN { telephoneParty: PartyData _ NARROW[Triples.Select[$telephone, rAtom, NIL]]; IF telephoneParty=NIL THEN ERROR; TU.MakeUnique[$Other, telephoneParty, party]; }; }; <> <> SetPoaching: PUBLIC INTERNAL PROC[party: PartyData, callingParty: PartyData_NIL] = { poacher, poachee: PartyData_NIL; tempParty: PartyData _ NIL; rName: ROPE _ party.name; smarts: REF; workstation: ATOM; wsName: ROPE; nb: NB; {SELECT party.type FROM $individual => { poacheeID: PartyID; poacher_party; IF (smarts _ Triples.Select[$Smarts, poacher, -- smarts --]) = NIL THEN { VoiceUtils.Problem["No smarts connection?"]; RETURN; }; IF (workstation _ NARROW[Triples.Select[$SmartsForHost,-- host--,smarts]]) = NIL THEN { VoiceUtils.Problem["No SmartsForHost?"]; RETURN; }; wsName _ Atom.GetPName[workstation]; -- $"3#456#individual" => wsName _ wsName.Substr[len: wsName.Length[]-Rope.Length["individual"]]; wsName _ wsName.Concat[larkRegistry]; -- "3#456#.lark" IF (rName _ NamesGV.GVGetAttribute[wsName, $owner, NIL]) = NIL THEN GOTO Done; poachee _ NARROW[Triples.Select[$telephone, VoiceUtils.MakeAtom[rName], --party--]]; IF poachee#NIL OR callingParty=NIL THEN GOTO Done; <> IF party.outgoing = NIL THEN GOTO Done; -- No phone number? [nb, poacheeID] _ GetPartyFromNumberInt[ partyID: Reseal[callingParty], phoneNumber: party.outgoing, description: rName, trunkRequired: TRUE ]; IF nb#$success OR poacheeID=nullID THEN GOTO Done; poachee _ ThPartyPrivate.UnsealParty[poacheeID]; }; $telephone => { wsRope: ROPE _ NamesGV.GVGetAttribute[rName.Concat[larkRegistry], $workstationhost, NIL]; workstation _ VoiceUtils.MakeAtom[wsRope.Concat["individual"]]; IF workstation = NIL THEN GOTO Done; poachee_party; IF (smarts _ Triples.Select[$SmartsForHost, workstation,-- smarts--])= NIL THEN GOTO Done; poacher _ NARROW[Triples.Select[$Smarts, -- party --, smarts]]; IF poacher = NIL THEN { VoiceUtils.Problem["No smarts connection?"]; RETURN; }; }; ENDCASE=>RETURN; EXITS Done => NULL; }; <> IF poachee#NIL THEN { tempParty _ NARROW[Triples.Select[$Poaching, Any, poachee]]; IF tempParty#NIL AND tempParty.enabled AND poacher=tempParty AND poacher.enabled THEN RETURN; -- Already set up as desired ThPartyPrivate.MakeSubstitution[oldParty: tempParty, newParty: poachee]; -- If tempParty#NIL Triples.Erase[$Poaching, Any, poachee]; }; IF poacher#NIL THEN { tempParty _ NARROW[Triples.Select[$Poaching, poacher, Any]]; ThPartyPrivate.MakeSubstitution[oldParty: poacher, newParty: tempParty]; -- See MakeSubstitution Triples.Erase[$Poaching, poacher, Any]; }; IF poacher=NIL OR poachee=NIL OR ~poacher.enabled OR ~poachee.enabled THEN RETURN; Triples.Make[$Poaching, poacher, poachee]; ThPartyPrivate.MakeSubstitution[oldParty: poachee, newParty: poacher]; }; RegisterClone: ENTRY PROC[clonePartyID: PartyID, hostAtom: ATOM] RETURNS [nb: NB_$success, credentials: ThParty.Credentials_[]] = { cloneParty: PartyData_ThPartyPrivate.UnsealParty[clonePartyID]; party: PartyData; cloneSmarts, smarts: SmartsData; rName: ROPE; rAtom: ATOM; [nb, rName,rAtom] _ GetRnameFromParty[cloneParty, $owner]; IF nb#$success THEN RETURN; IF cloneParty=NIL THEN RETURN[nb: $noSuchParty]; cloneSmarts _ NARROW[Triples.Select[$Smarts, cloneParty, -- Smarts --]]; IF cloneSmarts=NIL THEN { VoiceUtils.Problem["No Smarts for party?"]; RETURN[nb: $noSuchSmarts]; }; party _ NEW[ThPartyPrivate.PartyBody _ [type: cloneParty.type, name: cloneParty.name]]; smarts_NEW[ ThPartyPrivate.SmartsBody _ [ properties: cloneSmarts.properties, type: cloneSmarts.type, interface: cloneSmarts.interface, shh: cloneSmarts.shh, notifications: MBQueue.Create[], pupSocket: PupSocket.CreateEphemeral[remote: Pup.nullAddress] ]]; smarts.properties.netAddress.socket _ PupSocket.GetLocalAddress[smarts.pupSocket].socket; TU.MakeUnique[$Smarts, party, smarts]; IF party.type#$service THEN TU.MakeUnique[$SmartsForHost,hostAtom, smarts]; Triples.Make[cloneParty.type, rAtom, party]; credentials.partyID _ RefID.Seal[party]; credentials.smartsID _ RefID.Seal[smarts]; <<What should happen here anyhow, wrt SmartsForHost>> VoiceUtils.Report[IO.PutFR["RegisterClone[%g, %g] -> [%g, %g]", TU.RefAddr[party], TU.RefAddr[cloneParty], card[credentials.smartsID], TU.RefAddr[smarts] ], $Party, party]; }; <> <> <> <> Enable: PUBLIC ENTRY PROC[shh: SHHH_none, smartsID: SmartsID] RETURNS [nb: Thrush.NB] = { RETURN[Able[smartsID, TRUE]]; }; <> <<>> Disable: PUBLIC ENTRY PROC[shh: SHHH_none, smartsID: SmartsID] RETURNS [nb: Thrush.NB] = { RETURN[Able[smartsID, FALSE]]; }; <> Able: INTERNAL PROC[smartsID: SmartsID, enabling: BOOL] RETURNS[nb: Thrush.NB_$success] = { smarts: SmartsData=ThPartyPrivate.UnsealSmarts[smartsID]; party: PartyData; IF smarts=NIL THEN RETURN[$noSuchSmarts]; IF (party_NARROW[Triples.Select[$Smarts, --party--, smarts]])=NIL THEN RETURN[$noSuchParty]; party.enabled_ smarts.enablesParty_ enabling; SetPoaching[party]; }; <> <> <> GetPupSocket: PUBLIC PROC[partyID: PartyID] RETURNS [PupSocket.Socket_NIL] = { <> party: PartyData _ ThPartyPrivate.UnsealParty[partyID]; smarts: SmartsData = NARROW[Triples.Select[$Smarts, party, --smarts--]]; IF smarts=NIL THEN RETURN; IF smarts.properties.role=$voiceTerminal THEN RETURN[smarts.pupSocket]; party _ NARROW[Triples.Select[$Poaching, party, --poachee--]]; IF party#NIL THEN RETURN[GetPupSocket[Reseal[party]]]; }; <> <<>> Deregister: PUBLIC ENTRY PROC[shh: SHHH, smartsID: SmartsID] RETURNS [nb: NB _ $success] = { <> <> <<$Visiting implementations and hints for implementations are only suggestions, for now.>> ENABLE UNWIND => NULL; party: PartyData; smarts: SmartsData _ ThPartyPrivate.UnsealSmarts[smartsID]; IF smarts=NIL THEN RETURN[$noSuchSmarts]; party _ NARROW[Triples.Select[$Smarts, --party--, smarts]]; IF party#NIL THEN { IF party.type # $individual THEN IdleConversationsForParty[party, smartsID] ELSE { newParty: PartyData; SetPoaching[party]; newParty _ NARROW[Triples.Select[$Poaching, party, Triples.Any]]; IF newParty#NIL AND newParty.type=$telephone THEN ThPartyPrivate.MakeSubstitution[party, newParty]; }; <> EraseAsObjOrVal[party]; VoiceUtils.ReportFR[ "DeleteParty[%g, %g]", $System, NIL, card[Reseal[party]], TU.RefAddr[party]]; IF ~RefID.Release[Reseal[party]] THEN nb _ $noSuchParty; -- ?? } ELSE nb _ $noSuchParty; EraseAsObjOrVal[smarts]; smarts.failed _ TRUE; <> <> VoiceUtils.ReportFR[ "UnregisterSmarts[%g, %g]", $System, NIL, card[smartsID], TU.RefAddr[smarts]]; IF ~RefID.Release[smartsID] THEN ERROR; -- Having successfully dehandled above! }; <> <> <> IdleConversationsForParty: INTERNAL PROC[party: PartyData, smartsID: SmartsID] = { convState: ThPartyPrivate.ConvState_NIL; IdleOneConv: Triples.ForeachProc -- [trip: TripleRec] -- RETURNS [continue: BOOLEAN _ TRUE] -- ={ WITH trip.att SELECT FROM r: ThPartyPrivate.ConvState => convState _ r; ENDCASE => RETURN[TRUE]; IF convState.state<= Thrush.notReallyInConv THEN { convState _ NIL; RETURN[TRUE]; }; [--nb, convEvent; do your best, I guess.--] _ ThPartyPrivate.DoAdvance[ credentials: [ partyID: Reseal[party], smartsID: smartsID, -- This is very questionable convID: NARROW[trip.obj, ThPartyPrivate.ConversationData].convID, state: convState.state, stateID: convState.stateID ], state: $idle, reportToAll: TRUE, reason: $error, comment: "Other telephone failed", newInConv: FALSE ]; -- Idle party in conversation!! RETURN[FALSE]; }; IdleRelated: INTERNAL Triples.ForeachProc -- [trip: TripleRec] -- RETURNS [continue: BOOLEAN _ TRUE] -- = { IdleConversationsForParty[NARROW[trip.obj], smartsID]; }; <> DO convState_NIL; Triples.Foreach[Any, Any, party, IdleOneConv]; IF convState = NIL THEN EXIT; ENDLOOP; <> <<This is still bogus. It won't work as well as older systems at dealing properly with Finch dropping out. >> Triples.Foreach[$Poaching, Any, party, IdleRelated]; Triples.Foreach[$Visiting, Any, party, IdleRelated]; }; DescribeParty: PUBLIC PROC[partyID: Thrush.PartyID, nameReq: ThParty.NameReq] RETURNS[nb: NB, description: Thrush.ROPE, type: Thrush.PartyType_$telephone, partner: Thrush.PartyID_nullID, visitee: Thrush.PartyID_nullID, visitors: LIST OF Thrush.PartyID_NIL] = { a: ATOM; party: PartyData _ ThPartyPrivate.UnsealParty[partyID]; otherParty: PartyData; AddVisitor: Triples.ForeachProc -- [trip: TripleRec] -- RETURNS [continue: BOOLEAN _ TRUE] -- = { visitors _ CONS[Reseal[trip.obj], visitors]; }; [nb, description, a] _ GetRnameFromParty[ThPartyPrivate.UnsealParty[partyID], nameReq]; IF party=NIL THEN RETURN; type _ party.type; otherParty _ NARROW[Triples.Select[$Poaching, party, NIL]]; IF otherParty=NIL THEN otherParty _ NARROW[Triples.Select[$Poaching, NIL, party]]; IF otherParty#NIL THEN partner _ Reseal[otherParty]; otherParty _ NARROW[Triples.Select[$Visiting, party, NIL]]; IF otherParty#NIL THEN visitee _ Reseal[otherParty]; Triples.Foreach[$Visiting, NIL, party, AddVisitor]; }; <> <> <> <> noPassword: GVBasics.Password _ ALL[0]; Visit: PUBLIC ENTRY PROC[shh: SHHH_none, visitedParty: PartyID, visitingParty: PartyID, visitingPassword: GVBasics.Password] RETURNS [nb: Thrush.NB_$success] = { visitor, visitee: PartyData; visitorRname: Rope.ROPE; InterceptVisitingCalls: INTERNAL Triples.ForeachProc -- [trip: TripleRec] -- RETURNS [continue: BOOLEAN _ TRUE] -- = { convState: ThPartyPrivate.ConvState_NIL; credentials: Thrush.Credentials; originator: PartyData; conv: ThPartyPrivate.ConversationData; WITH trip.att SELECT FROM r: ThPartyPrivate.ConvState => convState _ r; ENDCASE => RETURN; SELECT convState.state FROM $ringing, $notified => NULL; ENDCASE=>RETURN; conv _ NARROW[trip.obj]; originator _ NARROW[Triples.Select[$Originator, conv, --originator--]]; IF originator=NIL THEN RETURN; credentials.partyID _ Reseal[originator]; credentials.smartsID _ Reseal[Triples.Select[$Smarts, originator, --smarts--]]; credentials.convID _ conv.convID; credentials.stateID _ ThPartyPrivate.GetConvState[conv, originator, FALSE].stateID; <> [] _ ThPartyPrivate.AlertOne[credentials, Reseal[visitee], Reseal[visitor]]; }; visitor _ ThPartyPrivate.UnsealParty[visitingParty]; visitee _ ThPartyPrivate.UnsealParty[visitedParty]; IF visitee = NIL THEN RETURN[$noSuchParty]; IF visitor = NIL THEN RETURN[$noSuchParty2]; IF visitor = visitee THEN RETURN[$narcissism]; SELECT visitee.type FROM $individual => NULL; ENDCASE => RETURN[$visitingIllegal]; SELECT visitor.type FROM $individual, $telephone => NULL; ENDCASE => RETURN[$visitingIllegal]; <> visitorRname _ GetRnameFromParty[visitor, $current].rName; IF visitorRname=NIL THEN { VoiceUtils.Problem["No rname for party"]; RETURN[$noSuchParty2]; }; IF visitingPassword = noPassword THEN { IF VoiceUtils.MakeAtom[NamesGV.GVGetAttribute[ visitorRname.Concat[larkRegistry], $visitpasswordrequired, "false"]] # $false THEN RETURN[$passwordNotValid]; } ELSE IF NamesGV.GVAuthenticate[visitorRname, visitingPassword] # $authentic THEN RETURN[$passwordNotValid]; []_UnvisitInternal[visitingParty]; Triples.Make[$Visiting, visitor, visitee]; <> <> Triples.Foreach[Any, Any, visitor, InterceptVisitingCalls]; }; <> <> <> <> <> <> <<>> Unvisit: PUBLIC ENTRY PROC[shh: SHHH_none, visitedParty: PartyID, visitingParty: PartyID, visitingPassword: GVBasics.Password] <> RETURNS [nb: Thrush.NB_$success] = { IF visitingParty=nullID THEN RETURN[$noSuchParty]; IF visitedParty=nullID THEN RETURN[$noSuchParty2]; RETURN[UnvisitInternal[visitedParty, visitingParty]]; }; <> <> <> <> <<>> UnvisitInternal: INTERNAL PROC[visitedParty: PartyID, visitingParty: PartyID_nullID] RETURNS[nb: Thrush.NB_$success] = { visitor, visitee: PartyData; visitor _ ThPartyPrivate.UnsealParty[visitingParty]; IF visitor=NIL THEN RETURN[$noSuchParty]; visitee _ NARROW[Triples.Select[$Visiting, visitor, -- visitee--]]; IF visitee=NIL THEN RETURN[$noSuchVisiting]; IF visitedParty#nullID AND Reseal[visitee]#visitedParty THEN RETURN[$noSuchVisiting]; Triples.Erase[$Visiting, visitor, Triples.Any]; <<ThPartyPrivate.MakeSubstitution[oldParty: visitor, newParty: visitee];>> <<Only workstations would care when visiting ended. At present, there's no direct way to tell them.>> }; <> <<>> DoDescribeParty: PUBLIC PROC[ party: PartyData, nameReq: ThParty.NameReq ] RETURNS[ description: Thrush.ROPE_NIL] = { type: Thrush.PartyType; DescribeTrunk: PROC RETURNS[des: ROPE] = INLINE { more: ROPE=IF party.outgoing#NIL THEN party.outgoing ELSE "outside line"; des _ IF party.name=NIL THEN more ELSE IO.PutFR["%s (%s)", IO.rope[party.name], IO.rope[more]]; }; Sel: PROC[type: Thrush.PartyType, party: PartyData] RETURNS [name: Thrush.ROPE] = { a: ATOM _ NARROW[Triples.Select[type, -- owner --, party]]; RETURN[IF a=NIL THEN NIL ELSE Atom.GetPName[a]]; }; IF party=NIL THEN RETURN; type _ party.type; IF nameReq=$none THEN RETURN; description _ SELECT type FROM $individual => (SELECT nameReq FROM $address => IF (party_NARROW[Triples.Select[$Poaching, party, NIL]])=NIL THEN NIL ELSE DoDescribeParty[party, $address], ENDCASE => party.name), $telephone => party.name, $trunk => (SELECT nameReq FROM $owner => Sel[$trunk, party], $description => DescribeTrunk[], $current => party.name, $address => party.outgoing, ENDCASE=>ERROR), $service => (SELECT nameReq FROM $description => IO.PutFR["%s service (%s)", rope[VoiceUtils.RnameToRspec[Sel[$service, party]].simpleName], rope[party.name]], $address => party.name, $owner, $current => Sel[$service, party], ENDCASE=>ERROR), ENDCASE => ERROR; }; <<>> GetRnameFromParty: PUBLIC PROC[party: PartyData, nameReq: ThParty.NameReq_$current] RETURNS [nb: NB_$success, rName: Thrush.ROPE_NIL, rAtom: ATOM_NIL] = { <> IF party=NIL THEN RETURN[nb: $noSuchParty]; rName _ DoDescribeParty[party, nameReq]; IF rName=NIL THEN nb _ $noNameAvailable ELSE rAtom _ VoiceUtils.MakeAtom[rName]; }; <> <> <> 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.SmartsID] RETURNS [nb: NB_$success, partyID: Thrush.PartyID_Thrush.nullID] = { smarts: REF _ ThPartyPrivate.UnsealSmarts[smartsID]; party: PartyData; p: PartyData; IF smarts=NIL THEN RETURN[nb: $noSuchSmarts]; party _ NARROW[Triples.Select[$Smarts, --party--, smarts]]; IF party#NIL AND ~party.enabled THEN RETURN[nb: $noSuchParty]; IF party=NIL THEN { VoiceUtils.Problem["No party?"]; RETURN[nb: $noSuchParty]; }; SetPoaching[party]; -- make sure $Poaching link is correct IF (p_NARROW[Triples.Select[$Poaching, -- poacher --, party]])#NIL AND p.enabled THEN party _ p; partyID _ Reseal[party]; }; <> <> <> EraseAsObjOrVal: INTERNAL PROC[ref: REF] = { Triples.Erase[Any, ref, Any]; Triples.Erase[Any, Any, ref]; }; <> <<>> ReportSystem: VoiceUtils.WhereProc = { s_NIL; }; ReportParty: VoiceUtils.WhereProc = { party: PartyData = NARROW[whereData]; smarts: SmartsData; IF party=NIL OR ( smarts _ NARROW[Triples.Select[$VoiceTerminal, party, Any]] ) = NIL THEN RETURN; SELECT smarts.properties.role FROM $voiceTerminal => s_Nice.LarkConLogStream[smarts.properties.netAddress]; ENDCASE; IF s=NIL OR s=Nice.LarkConLogStream[Thrush.noAddress] THEN s_VoiceUtils.FindWhere[$System, NIL]; }; WhitePagesInit: Commander.CommandProc = { treeName: ROPE; IF wpState#NIL AND ThNet.CloseWhitePagesDatabase[wpState] THEN wpState_NIL; treeName _ CommandTool.NextArgument[cmd]; IF wpState=NIL THEN wpState _ ThNet.InitWhitePagesDatabase[ treeName -- Optional, defaults supplied by WP impl., ]; }; 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>> <> <> <> <> <> <> < ID, NamesAndLog => VoiceUtils>> <> <<>> <<>> <> <> <> <<>> <<>> <<>> <> <> <> <<>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<>>