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, QueueClientAction ], NameDB USING [ Authenticate, GetAttribute ], 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, SetMaxTransmissions ], ThNet USING [ CloseWhitePagesDatabase, HowToDial, InitWhitePagesDatabase, pd, WhitePagesEntry, WPListing, WPState ], ThParty USING [ Credentials, NameReq, SmartsInterfaceName, SmartsInterfaceRecord, SmartsProperties ], ThPartyPrivate USING [ AlertOne, DoAdvance, ConversationData, ConvState, DoPoll, GetConvState, MakeSubstitution, PartyBody, PartyData, PollSpecBody, SmartsBody, SmartsData, UnsealParty, UnsealSmarts, VoiceParty ], ThPartyRpcControl, ThPartyMonitorImpl, Thrush USING [ ConversationID, Credentials, NB, NetAddress, noAddress, notReallyInConv, nullConvID, nullID, PartyID, PartyType, ROPE, SHHH, SmartsID, unencrypted ], ThSmarts USING [ noneScheduled ], 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, NameDB, 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 = { 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 => { otherParty: PartyData_NIL; relatedParty _ NARROW[Triples.Select[$trunk, rAtom, -- trunk party --]]; IF relatedParty#NIL THEN otherParty _ NARROW[Triples.Select[$Other, NIL, relatedParty]]; IF otherParty#NIL AND otherParty.partyEngaged#Thrush.nullConvID AND otherParty#ThPartyPrivate.VoiceParty[party] THEN nb _ $voiceTerminalBusy; }; $telephone => relatedParty _ NARROW[Triples.Select[$trunk, rAtom, -- trunk party --]]; $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]; RPC.SetMaxTransmissions[ conversation: partyToSmartsShh, maxTransmissions: ThNet.pd.toSmartsTimeout, timeoutEnable: IF ThNet.pd.toSmartsTimeoutOK THEN always ELSE never]; 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]]; }; 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"]]; -- "3#456#" IF (rName _ NameDB.GetAttribute[wsName, $rname, NIL, $workstationhost]) = NIL THEN GOTO Done; -- rName is the name of the workstation's owner. poachee _ NARROW[Triples.Select[$telephone, VoiceUtils.MakeAtom[rName], --party--]]; IF poachee#NIL OR callingParty=NIL THEN GOTO Done; SELECT callingParty.type FROM $individual, $telephone => { num: ROPE _ GetNumbersForRName[rName: rName].number; IF num = NIL THEN GOTO Done; -- No phone number? [nb, poacheeID] _ GetPartyFromNumberInt[ partyID: Reseal[callingParty], phoneNumber: num, description: rName, trunkRequired: TRUE ]; IF nb#$success OR poacheeID=nullID THEN GOTO Done; poachee _ ThPartyPrivate.UnsealParty[poacheeID]; }; $trunk => { trunkOwner: REF _ Triples.Select[$trunk, NIL, callingParty]; IF trunkOwner#NIL THEN poachee _ NARROW[Triples.Select[$telephone, trunkOwner, NIL]]; }; ENDCASE => NULL; -- Give up. }; $telephone => { wsRope: ROPE _ NameDB.GetAttribute[rName, $workstationhost]; 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 OR tempParty.numConvs#0) THEN RETURN; -- Already set up as desired, or can't break poaching relation now. 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]]; IF tempParty#NIL AND poacher.enabled AND poacher.numConvs#0 THEN RETURN; -- Can't break poaching relation now. 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]; 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; smarts.notifications.QueueClientAction[ThPartyPrivate.DoPoll, NEW[ThPartyPrivate.PollSpecBody_[smarts, Reseal[party], IF enabling THEN $welcome ELSE $goodbye, IF enabling THEN 120 ELSE ThSmarts.noneScheduled]]]; -- 120 is an opening guess. 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] = { 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; newParty _ NARROW[Triples.Select[$Poaching, party, Triples.Any]]; IF newParty#NIL THEN ThPartyPrivate.MakeSubstitution[party, newParty] ELSE IdleConversationsForParty[party, smartsID]; }; [] _ Able[smartsID: smartsID, enabling: FALSE]; 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]; 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: "Etherphone failed", newInConv: FALSE, checkConflict: 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; 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[NameDB.GetAttribute[ visitorRname, $visitpasswordrequired, "false"]] # $false THEN RETURN[$passwordNotValid]; } ELSE IF NameDB.Authenticate[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]; }; 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"]; }. -~ThPartyInitImpl.mesa Copyright Σ 1985, 1986, 1987 by Xerox Corporation. All rights reserved. Last modified by D. Swinehart, July 8, 1987 1:05:51 pm PDT Polle Zellweger (PTZ) March 2, 1987 7:15:41 pm PST Definitions Functions for obtaining existing parties, or phone numbers GetParty.nb~$success GetParty.nb~$noIdentSupplied GetParty.nb~$noSuchParty2 GetParty.nb~$noSuchParty GetParty.GetRelatedParty.nb~~$voiceTerminalBusy GetParty.GetRelatedParty.nb~~$convStillActive GetParty.GetIdleParty/GetRnameFromParty.nb~~$noNameAvailable GetPartyFromFeepNum.nb~$success GetPartyFromFeepNum.nb~$noSuchParty2 GetPartyFromFeepNum.GetActiveParty.nb~~$noIdentSupplied GetPartyFromFeepNum.GetActiveParty.nb~~$noSuchParty2 GetPartyFromFeepNum.GetActiveParty.nb~~$noSuchParty GetPartyFromFeepNum.GetRelatedParty.nb~~$voiceTerminalBusy GetPartyFromFeepNum.GetRelatedParty.nb~~$convStillActive GetPartyFromFeepNum.GetIdleParty/GetRnameFromParty.nb~~$noNameAvailable GetPartyFromNumber.nb~$success GetPartyFromNumber.nb~$noSuchParty GetPartyFromNumber.GetRelatedParty.nb~~$voiceTerminalBusy GetPartyFromNumber.GetRelatedParty.nb~~$noSuchParty2 GetPartyFromNumber.GetRelatedParty.nb~~$convStillActive GetPartyFromNumber.GetRnameFromParty.nb~~$noNameAvailable Obtains a $service, $individual, or $telephone party, if available It is important to allow one to get any party; attempts to connect a party to itself are caught by ThParty.Alert. Problem solved: couldn't call visitor from visitor's phone. (Poacher's home phone has already been substituted for poacher's when that's appropriate.) GetActiveParty.nb~$success GetActiveParty.nb~$noIdentSupplied GetActiveParty.nb~$noSuchParty2 GetActiveParty.nb~$noSuchParty GetActiveParty.GetRelatedParty.nb~~$voiceTerminalBusy GetActiveParty.GetRelatedParty.nb~~$convStillActive GetActiveParty.GetIdleParty/GetRnameFromParty.nb~~$noNameAvailable nb=$success,type=$individual No active party; try to get a trunk line for that individual GetActivePartyDo.nb~$ See GetActiveParty Locates an idle $service party, if available e.g., $"text-to-speech.lark" [trip: Triples.TripleRec] RETURNS [continue: BOOLEAN _ TRUE] A service party is available if it is enabled, not involved in a conversation, and either has never been reserved or the guaranteed reservation period has expired. e.g., $"myself.pa" Reserve the party GetIdleParty.nb~$success GetIdleParty.nb~$noSuchParty GetIdleParty.nb~$noSuchParty2 GetIdleParty.GetRnameFromParty.nb~~$noNameAvailable Always successful, since optional ReleaseParty.nb~$success Party description has not yielded a party, so phone number will have to do. Perhaps the phone number is an extension of an active $individual; if not, try to find an idle outgoing trunk. myExt is used as Intelnet authorization code. IF phoneNumber = NIL THEN RETURN[$noIdentSupplied, nullID]; A null phoneNumber is a request for a direct outside line connection. Get Trunk Party GetPartyFromNumberInt.nb~$success GetPartyFromNumberInt.nb~$noSuchParty GetPartyFromNumberInt.GetRelatedParty.nb~~$voiceTerminalBusy GetPartyFromNumberInt.GetRelatedParty.nb~~$noSuchParty2 GetPartyFromNumberInt.GetRelatedParty.nb~~$convStillActive GetPartyFromNumberInt.GetRnameFromParty.nb~~$noNameAvailable Make sure there's no "Front door/back door" conflict for trunk. GetRelatedParty.nb~$success GetRelatedParty.nb~$voiceTerminalBusy GetRelatedParty.nb~$noSuchParty2 GetRelatedParty.nb~$convStillActive GetRelatedParty.GetRnameFromParty.nb~~$noSuchParty GetRelatedParty.GetRnameFromParty.nb~~$noNameAvailable Making new parties and smartses Register.nb~$ See DoRegister RegisterLocal.nb~$ See DoRegister Deregister any previous smarts from this machine. When cloning to produce multiple smarts and parties for a multi-channel service, obviously don't want to do this. Multi-channel services must deregister explicitly, since this won't handle it. Includes setting RName access, poaching and visiting designations. But IMPORTANT!! Net and host remain those supplied by the client. We're only providing a socket ID here that is in the range available to Bluejay. See ThPartyPrivate DoRegister.nb~$success DoRegister.nb~$noIdentSupplied DoRegister.nb~$couldntAuthenticate DoRegister.nb~$couldntConnect DoRegister.GetRnameFromParty.nb~~$noSuchParty -- $clone creation only DoRegister.GetRnameFromParty.nb~~$noSuchSmarts -- $clone creation only DoRegister.GetRnameFromParty.nb~~$noNameAvailable -- $clone creation only DoRegister.CreateParty.nb~~$noIdentSupplied rName is a misnomer; it's the name of a service, without registry, if type is service. This next is very type-specific. Too bad it has to be here.  CreateParty.nb~$success CreateParty.nb~$noIdentSupplied There's no voice path for this individual. Try to create one using calling party's trunk, calling the phone number for the workstation owner (rName) The callee has no $telephone adjacent to the $individual location, but must have one elsewhere (or we wouldn't be here; the callingParty must be the callee's own trunk). As a final attempt, try to find it and use it. Splice in poachees for poachers in any relationships that are ending, poachers for poachees in any that are beginning. What should happen here anyhow, wrt SmartsForHost RegisterClone.nb~$success RegisterClone.nb~$noSuchParty RegisterClone.nb~$noSuchSmarts RegisterClone.GetRnameFromParty.nb~~$noNameAvailable Enable.nb~$ See Able Enable.nb~$ See Able Able.nb~success Able.nb~noSuchSmarts Able.nb~noSuchParty Find socket structure associated with the voiceTerminal smarts currently associated with this party. See ThPartyPrivate discussion of sockets. Deregister Smarts from Party, destroy both There's but one party per smarts, these days. When the Smarts goes, so goes the party. For the most part, we can (after idling all the conversations in which the party participates) merely erase all the interconnections between the party and smarts and other objects, and release their ID's. Don't hang up calls for $individual parties with valid poachees, since a poachee can carry on a conversation by itself. $Visiting implementations and hints for implementations are only suggestions, for now. SetPoaching[party]; June 18, 1987 9:45:14 pm PDT, DCS, not interested in disturbing existing poaching relationships at this time? Since second argument isn't/can't be provided, it's unlikely this will produce the right answer anyway. I am worried about what situation this was placed here to correct, though! June 18, 1987 9:47:05 pm PDT; An individual, with no valid poachee, being deleted. Such an individual possibly should not be in a conversation, but if it is, this is the only chance to get it out. Transfer $Visitors from authenticated to assumed. smarts.failed _ TRUE; -- Failure is recorded only when communications to Smarts (ex-smarts) fails. smarts.notifications.Flush[]; -- Abandon progress-report queue They have to be allowed to empty themselves, so that the "reports outstanding" counts will be maintained correctly. Deregister.nb~$success Deregister.nb~$noSuchSmarts Deregister.nb~$noSuchParty -- really a quite unreasonable error case! Idle any conversations in which the party is involved. Outside loop avoids Foreach/Erase conflicts. Apply the process recursively to poachers and visitors: if a poacher is in a conversation, the poachee is not recorded as being in the same one; similarly for visitors, at least at present. But the poachee or visitee is always vital to the conversation's existence, so out it goes. This is still bogus. It won't work as well as older systems at dealing properly with Finch dropping out.  DescribeParty.nb~$success DescribeParty.GetRnameFromParty.nb~$noSuchParty DescribeParty.GetRnameFromParty.nb~$noNameAvailable Visiting Registration/Deregistration This would be bogus, except that Alert does not change the stateID of the caller. Password protection Worry about Patching the visitee into a ringing call! If visitor is in a conversation, in state notified or ringing, simulate having the originator notify the visitee. Visit.nb~$success Visit.nb~$noSuchParty Visit.nb~$noSuchParty2 Visit.nb~$narcissism Visit.nb~$visitingIllegal Visit.nb~$passwordNotValid Password not presently required or heeded. Unvisit.nb~$success Unvisit.nb~$noSuchParty -- Visiting Party ID null or invalid Unvisit.nb~$noSuchParty2 -- Visitee Party ID null or invalid Unvisit.nb~$noSuchVisiting -- specified visiting relationship did not hold. ThPartyPrivate.MakeSubstitution[oldParty: visitor, newParty: visitee]; Only workstations would care when visiting ended. At present, there's no direct way to tell them. ThPartyPrivate Implementations Returns (bogus) service name if type = $service GetRnameFromParty.nb~$success GetRnameFromParty.nb~$noSuchParty GetRnameFromParty.nb~$noNameAvailable Which party should we be when initiating calls? GetCurrentParty.nb~$success GetCurrentParty.nb~$noSuchSmarts GetCurrentParty.nb~$noSuchParty Initialization Log Swinehart, May 15, 1985 10:24:49 am PDT Cedar 6.0 changes to: larkRegistry, GetParty, GetActiveParty, GeIdleParty, CreateParty, NewParty Swinehart, May 16, 1985 9:18:28 am PDT CommandToolExtras zapped. Swinehart, May 22, 1985 12:01:41 pm PDT ServiceName changes recording => service Jay => serviceName, to generalize individual and service treated similarly in places changes to: GetIdleParty, CreateParty, NewParty, DoDescribeParty, MakeServiceRname changes to: CreateParty, ExistingParty, NewParty, DoDescribeParty changes to: GetIdleParty changes to: CreateParty, ExistingParty, NewParty Swinehart, October 25, 1985 6:45:23 pm PDT Handle => ID, NamesAndLog => VoiceUtils changes to: DIRECTORY, ThPartyInitImpl, GetActiveParty, GetIdleParty, CreateParty, ExistingParty, DoRegister, RegisterClone, Deregister, EnterSmarts, MakeServiceRname, GetStdRingInfo, ReportSystem, ReportParty, ThPartyInit, ConversationID, PartyID, SmartsID, nullID, Reseal, GetParty, GetPartyFromFeepNum, GetPartyFromNumber, GetPartyFromNumberInt, GetTrunkParty, GetRname, Register, RegisterLocal, EnterSmartsE (local of EnterSmarts), Enable, Disable, DescribeParty, DoDescribeParty, GetCurrentParty, GetPartySmarts, SetStdRingInfo, SetRingEnable, NewParty Swinehart, October 29, 1985 5:55:37 pm PST Major revisions for visiting and poaching. See design document. changes to: CreateParty, Register, RegisterLocal, DoRegister, EnterSmarts, Able, Deregister, PrepareRingTune, GetPartyFromNumber, GetRname, DescribeParty, GetActiveParty, GetIdleParty, GetPartyFromNumberInt, GetTrunkParty, Deregister, Deregister, EraseAsObj, Deregister Swinehart, December 11, 1985 3:29:30 pm PST Just improve comments changes to: GetRelatedParty, SetPoaching Swinehart, May 17, 1986 5:38:13 pm PDT Cedar 6.1 changes to: DIRECTORY, DoRegister, ReportParty Swinehart, June 29, 1986 11:40:51 am PDT Pull PartyAvailable stuff, counting on Smarts to get it right. changes to: VoiceParty, GetActivePartyDo Swinehart, August 8, 1986 10:26:54 am PDT Visiting. changes to: Visit, Unvisit, UnvisitInternal, EraseAsObjOrVal Swinehart, December 22, 1986 1:43:29 pm PST Eliminated narcissism check in GetParty, moved VoiceParty to ThPartyOpsImpl, to improve Alert. changes to: GetActiveParty Swinehart, January 1, 1987 10:56:03 pm PST Still working on Front door/back door conflict tests. changes to: DIRECTORY, GetRelatedParty Swinehart, January 21, 1987 7:24:51 pm PST Record uses of NB changes to: GetParty, GetPartyFromFeepNum, GetPartyFromNumber, GetActiveParty, GetActivePartyDo, GetIdleParty, GetPartyFromNumberInt, GetRelatedParty, Register, RegisterLocal, DoRegister, RegisterClone, Enable, Disable, DescribeParty Κ&—˜Jšœ™šœH™HJšœ:™:Icode™2J˜—šΟk ˜ Jšœœ˜"Jšœ œ*˜9Jšœ œ˜*Jšœ œ˜#Jšœ œ˜J˜Jšœœ˜%Jšœœ˜,Jšœœ ˜,Jšœ œ˜%J˜J˜Jšœ œ.˜=Jšœœœ˜*Jšœœœ ˜1JšœœX˜aJšœœœf˜tJšœe˜eJšœœΑ˜ΥJšœ˜J˜šœœ˜JšœœRœœ˜•—Jšœ œ˜!Jšœœ˜0J˜Jšœœ:˜GJšœœ˜!Jšœ œΉ˜ΙJ˜J˜—šœ œœ˜)šœ˜ J˜ Jšœ ˜ Jšœ ˜ J˜J˜J˜Jšœ˜Jšœ ˜ J˜J˜ J˜J˜J˜Jšœ˜J˜J˜J˜J˜J˜ J˜Jšœ˜J˜ —Jšœ˜šœ˜Jšœœ˜—J˜—šœ ™ Jšœœ œ˜Jšœ œΟc˜;šœ œž˜%J˜ —Jšœœ œ˜Jšœ œž˜=Jšœ œž˜'šœœ œ˜Jšœœ˜ —JšΟnœœœœœœœ˜LJ˜ J˜Jšœœ ˜J˜J˜J˜—™:J˜š Ÿœœ œœœ˜˜Jšœ[œœœ˜‘—šœ/˜/Jšœœ˜.Jšœ0œ˜;—Jšœœœœ˜8J˜—JšΟbΠbn ‘Πbk‘£‘˜Jšœ'˜'šœ œœ˜JšœB™B—šœœ˜&Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ ˜ J˜=Jšœ˜—šœœ˜J˜CJ˜J˜J˜—˜YJšœ£ œRœ1Οtœ€™¨—Jšœ$˜&Jš œœœ/žΠctž,₯˜wJ˜*J˜*JšœžJ˜_Jš‘˜šœ>˜>J˜NJ˜(—J˜Jšœ™Jšœ™Jšœ"™"Jšœ™JšœE™EJšœF™FJšœI™IJšœ+™+J˜—šŸœœœœ˜<šœœ˜6Jšœœœœ˜?—Jšœ˜J˜J˜—šŸ œœœœ˜FJšœœœ˜3J™VJš œœœœœ&˜bJ˜Jšœœ!˜,Jšœ œœ˜Jšœ œœœ˜4Jšœœœž œ˜NJšœœœœ˜*Jšœœ7˜Bšœ˜šœ'ž˜@Jšœœ.˜7Jšœ˜Jš œœœ3œœ˜NJšœœœ&˜5J˜—Jšœ˜—J˜Jšœ ˜"Jšœ œœœ*˜BJšœ=€™?šœœ˜Jšœœ#œ˜KJšœœœœ˜!Jšœ+˜-J˜—Jšœ˜J™Jšœ™J˜—šŸ œœœ+œ˜TJšœœ˜ Jšœœ˜Jšœœ˜Jšœœ˜ Jšœ œ˜Jšœœ˜ Jšœœ˜š‘œ ˜˜J˜J˜šœ,ž œœœ˜IJšœ-œ˜7—š œœž œ œœ˜WJšœ)œ˜3—Jšœ%ž˜>šœ˜Jšœ?ž ˜J—šœ.œ˜MJšœœž0˜@—Jšœ œ8ž œ˜TJš œ œœ œœ˜2Jš€*™*šœ˜˜Jš€j™jJšœœ+˜4Jš œœœœž˜2˜(J˜Jšœ˜J˜Jšœ˜Jšœ˜—Jšœ œœœ˜2J˜0J˜—˜ J™ΩJšœ œœ˜<šœ œœ ˜ Jšœ(œ˜4—J˜—Jšœœž ˜—J˜—˜šœœ˜Jšœ-˜-—J˜?Jšœœœœ˜$J˜Jš œ6ž œœœœ˜ZJšœ œ/˜?Jšœ œœ0œ˜OJ˜—Jšœœ˜—Jšœ‘œœ‘œ˜™všœ œœ˜Jšœ œ*˜<šœ œœ˜*šœœ˜,JšœœžC˜P——JšœIž˜\J˜'J˜—šœ œœ˜Jšœ œ*˜<šœ œœœ˜;Jšœœž&˜3—JšœIž˜`J˜'J˜—Jšœ œœ œœœœœ˜RJ˜*J˜F—J˜J˜—šŸ œœœ"œ˜@Jšœœ3˜BJšœ?˜?Jšœ˜J˜ Jšœœ œ˜Jšœ:˜:Jšœ œœ˜Jšœ œœœ˜0Jšœœ%ž œ˜Hšœ œœ˜Jšœ,œ˜I—JšœœL˜Wšœœ˜)J˜$J˜J˜!J˜Jšœ ˜ J˜=J˜—J˜YJšœ$˜&Jšœœœ-˜KJšœ,˜,J˜(J˜*Jš€œ1€™3šœ?˜?Jšœœ2œ'˜p—Jšœ˜J™Jšœ™Jšœ™Jšœ4™4J˜—š Ÿœœœœœ˜=Jšœ œœœ˜Jšœ œœœ˜=J™—J˜šŸœœœœ˜7Jšœ œ˜#J˜9J˜Jšœœœœ˜)šœœž œ ˜AJšœœ˜—Jšœ-˜-šœ=˜=šœ4˜7Jšœ œ œ ˜)Jšœ œœž˜P——Jšœ˜J˜Jšœ™Jšœ™Jšœ™J˜—š Ÿ œœœœœ˜NJšœf€œ)€™‘Jšœ7˜7Jšœœ ž œ˜HJšœœœœ˜Jšœ'œœ˜GJšœœ"ž œ˜>Jšœœœœ˜6J˜J˜——™*J™š Ÿ œœœœœ˜J˜—Jšœ˜Jšœ˜JšœœN™bšœž ™>J™s—šœ˜Jšœ%œœ˜N—Jšœœœž'˜OJ˜J™Jšœ™JšœE™EJ˜—šŸœœœ*˜RJšœ$œ˜(šŸ œ ˜5Jš (œ˜,Jš œ œœ/œœœ˜`Jš œ*œœœœ˜Tšœž+œ˜J˜J˜Jšœž Ρbcež œ˜2Jšœœ3˜AJ˜J˜J˜—J˜ Jšœ œ˜Jšœ˜J˜Jšœ ˜Jšœ˜Jšœž˜"—Jšœœ˜J˜—šŸ œœ ˜>Jš (œ˜.Jšœœ˜6J˜—J™dš˜Jšœ œ˜J˜.Jšœ œœœ˜Jšœ˜—J™šJ™lJšœ4˜4Jšœ4˜4J˜J˜—šŸ œ œ3˜Mšœœ˜)Jšœb˜bJšœ œœœ˜)—Jšœœ˜J˜7J˜šŸ œž˜4Jšž(œ˜.Jšœ œ˜,J˜—J˜WJšœœœœ˜J˜Jšœ œ"œ˜;Jš œ œœœœ ˜RJšœ œœ˜4Jšœ œ"œ˜;Jšœ œœ˜4Jšœœ˜3J˜J™Jšœ/™/Jšœ3™3J˜——šœ$™$J˜Jšœ œ˜'J˜š Ÿœœœœœ5˜WJšœ$˜$Jšœ œ˜$Jšœ˜Jšœœ˜šŸœœ ˜IJš (œ˜.Jšœ$œ˜(J˜ J˜J˜&šœ œ˜Jšœ.œœ˜@—Jš œœœœœ˜IJšœœ ˜Jšœ œ#žœ˜GJšœ œœœ˜J˜)JšœBž œ˜OJ˜!šœDœ ˜SJ™Q—J˜LJ˜—Jšœ4˜4Jšœ3˜3Jšœ œœœ˜+Jšœ œœœ˜,Jšœœœ˜.Jš œœœœœ˜Ršœ˜Jšœœœœ˜E—J™J˜:Jšœœœ-œ˜^šœœ˜'šœ)˜+šœ8˜8Jšœœ˜——J˜—šœœB˜MJšœ˜—Jšœ"˜"Jšœ*˜*šœ5™5Jšœq™q—J˜;Jšœ˜J™Jšœ™Jšœ™Jšœ™Jšœ™Jšœ™J™—šŸœœœ5˜YJšœ$˜$J™*Jšœ œ˜$Jšœœœ˜2Jšœœœ˜2Jšœ/˜5Jšœ˜J™J™Jšœœœ$œ˜QJšœž&˜:šœœž œ ˜BJšœ œ ˜—J˜J˜J™Jšœ ™ Jšœ™—J˜šŸœœœœ˜,J˜J˜J˜J˜——™J™šŸ œœ˜0J˜—šŸ œ˜%Jšœœ ˜%Jšœ˜šœœ˜Jš œ œ1œœœ˜R—šœ˜"JšœHœ˜Q—Jš œœœ+œ!œ˜`J˜—J˜šŸœ˜)Jšœ œ˜Jš œ œœ(œ œ˜KJšœ)˜)šœ œœ(˜;Jšœ ž+˜4J˜—J˜—J˜Jšœ5˜5Jšœœ˜"J˜šŸ œ˜&š˜JšœIœ ˜[—šœ œ+˜9JšœX˜X—šœ˜J˜Jšœ˜J˜—šœA˜AJ˜;—Jšœ8˜8Jšœ6˜6JšœAœ˜Kšœ"˜"Jšœ#˜#Jšœ˜J˜—Jšœ9œ&˜bš˜JšœKœ˜U—J˜—J˜Jšœ}˜}šœ*˜*Jšœ[˜[—J˜K™—™™'K™ Kšœ ΟrJ™V—™&K™—™'K™K™K™!K™2Kšœ §F™RKšœ §5™AKšœ § ™Kšœ §$™0—™*K™'Kšœ §œ§{™­K™K™—™*K™@Kšœ §™—K™K™—K™™+K™Kšœ §™(—K™™&K™ Kšœ §"™.—™(K™>Kšœ §™(—™)K™ Kšœ §0™<—™+K™^Kšœ §™—™*K™5Kšœ §™&—™*K™Kšœ §έ™ι—K™—…—| Π