DIRECTORY
Atom USING [ GetPName, MakeAtom ],
Commander	USING [ CommandProc, Register ],
CommandTool USING [ NextArgument ],
ExplicitExport USING [ ExportExplicitly ],
IO,
LupineRuntime USING [ BindingError ],
NamesGV USING [ GVGetAttribute ],
NamesRPC USING [ StartConversation ],
Nice,
RefID USING [ ID, Release, Reseal, Seal ],
Rope USING [ Concat, Cat, Length, Substr ],
RPC USING [ AuthenticateFailed, EncryptionKey, ExportFailed, ImportFailed ],
ThNet USING [ CloseWhitePagesDatabase, HowToDial, InitWhitePagesDatabase, WhitePagesEntry, WPListing, WPState ],
ThParty USING [ Credentials, NameReq, SmartsInterfaceName, SmartsInterfaceRecord, SmartsProperties ],
ThPartyPrivate USING [ PartyBody, PartyData, SmartsBody, SmartsData, UnsealParty, UnsealSmarts ],
ThPartyRpcControl,
ThPartyRpcServerImpl,
ThPartyMonitorImpl,
Thrush USING [
ConversationID, NB, PartyID, PartyType, nullID, Machine, ROPE, SHHH, SmartsID, unencrypted ],
ThSmartsRpcControl USING [ ImportNewInterface ],
ThVersions USING [ ThrushVR ],
Triples USING [ Any, Erase, Item, Make, Select ],
TU USING [ MakeUnique, RefAddr ],
VoiceUtils USING [ CmdOrToken, CurrentPasskey, InstanceFromNetAddress, MakeAtom, Registrize, FindWhere, Problem, RegisterWhereToReport, Report, ReportFR, WhereProc ]
;

ThPartyInitImpl: CEDAR MONITOR LOCKS root
IMPORTS Atom,
IO,
Commander,
CommandTool,
ExplicitExport,
LupineRuntime,
NamesGV,
NamesRPC,
Nice,
root: ThPartyMonitorImpl,
RefID,
Rope,
RPC,
SmartsRpc: ThSmartsRpcControl,
ThNet,
ThPartyPrivate,
ThPartyRpcControl,
ThPartyRpcServerImpl,
ThVersions,
Triples,
TU,
VoiceUtils
EXPORTS ThParty, ThPartyPrivate
SHARES ThPartyMonitorImpl, ThPartyRpcServerImpl = {
OPEN IO;

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";

thPartyExported: BOOL_FALSE;
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;
rName _ ThNet.WhitePagesEntry [
wpState: wpState, name: feepNum, feep: TRUE, key: $officeNumber].fullRName;
IF rName=NIL THEN RETURN[nb: $noEntryFound];
[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;
};
GetRname: PUBLIC PROC[shh: SHHH_none, partyID: PartyID] 
RETURNS [nb: NB, Rname: Thrush.ROPE_NIL] = {
[nb, Rname,] _ GetRnameFromParty[ThPartyPrivate.UnsealParty[partyID]];
};

GetActiveParty: INTERNAL PROC[partyID: PartyID, rName: Thrush.ROPE,
type: Thrush.PartyType]
RETURNS [nb: NB_$success, newParty: PartyData_NIL] = {
number: ROPE;
listing: ThNet.WPListing;
wpRname: ROPE;
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];
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]; 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: $homeNumber, listing: listing].entry;
IF number=NIL THEN RETURN[nb: $noSuchParty2];
[nb, partyID] _ GetPartyFromNumberInt[partyID, number, rName, TRUE];
IF nb=$success THEN newParty _ ThPartyPrivate.UnsealParty[partyID];
};

GetIdleParty: INTERNAL PROC[partyID: PartyID, serviceName: ROPE]
RETURNS [nb: NB_$success, newParty: PartyData _ NIL] = {
ENABLE UNWIND=>NULL;
party: PartyData_ ThPartyPrivate.UnsealParty[partyID];
myName: ROPE;
rAtom: ATOM;
serviceNameAtom: ATOM = MakeServiceRname[serviceName].serviceRnameAtom;
IF party = NIL THEN RETURN[nb: $noSuchParty];
[nb, myName,] _ GetRnameFromParty[party];
IF nb # $success THEN RETURN[nb: nb, newParty: NIL];
rAtom _ VoiceUtils.MakeAtom[Rope.Cat[myName, ".", serviceName]];
newParty _ NARROW[Triples.Select[$service, serviceNameAtom, --party--]];
IF newParty = NIL OR ~newParty.enabled THEN nb_$noSuchParty2 -- e.g., all channels busy
ELSE TU.MakeUnique[$service, rAtom, newParty]; -- $service[$"myself.pa.t..."] = <party>
};

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];
IF nb=$success THEN RETURN[newPartyID: Reseal[newParty]];
};
};
[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];
IF nb # $success THEN RETURN; -- VERY strange
SELECT party.type FROM
$individual, $telephone =>
relatedParty _ NARROW[Triples.Select[$trunk, rAtom, -- trunk party --]];
ENDCASE; -- $trunk case will be added as needed for incoming calls.
IF relatedParty = NIL THEN nb _ $noTrunkParty
ELSE IF relatedParty.numConvs#0 THEN nb _ $convStillActive -- busy
ELSE IF ~relatedParty.enabled THEN nb _ $noSuchParty2; -- not available yet
IF nb # $success THEN relatedParty _ NIL;
};

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, properties];
};

RegisterLocal: PUBLIC PROC[
shh: SHHH_none,
rName: ROPE_NIL, -- party identification
type: Thrush.PartyType _ $individual,
clonePartyID: PartyID,
interfaceRecord: ThParty.SmartsInterfaceRecord _ NIL,
properties: ThParty.SmartsProperties
] RETURNS [
nb: NB, credentials: ThParty.Credentials_[] ] = {
[nb, credentials] _ DoRegister[rName, type, clonePartyID, [ ], interfaceRecord, properties];
};

DoRegister: PROC[
rName: ROPE_NIL, -- party identification
type: Thrush.PartyType _ $individual,
clonePartyID: PartyID,
interface: ThParty.SmartsInterfaceName, -- interface
interfaceRecord: ThParty.SmartsInterfaceRecord,
properties: ThParty.SmartsProperties
] RETURNS [
nb: NB, credentials: ThParty.Credentials_[] ] = {
hostAtom: ATOM = VoiceUtils.MakeAtom[
VoiceUtils.InstanceFromNetAddress[properties.machine, Atom.GetPName[type]]];
smarts: SmartsData_NARROW[Triples.Select[$SmartsForHost, hostAtom, -- smarts --]];
party: PartyData;
partyToSmartsShh: SHHH _ none;
IF clonePartyID#nullID THEN [nb, credentials] _ RegisterClone[clonePartyID];
IF smarts#NIL THEN {
nb_Deregister[none, Reseal[smarts]];
IF nb # $success THEN RETURN;
};
IF rName=NIL THEN RETURN[nb: $noIdentSupplied];
IF interfaceRecord=NIL THEN {
partyToSmartsShh _ NamesRPC.StartConversation[
caller: serverInterfaceName.instance,
callee: rName,
key: serverPassword,
level: --<<ECB>>--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
]];
TU.MakeUnique[$Smarts, party, smarts];
TU.MakeUnique[$SmartsForHost, hostAtom, smarts];
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]]];
};

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];
};

SetPoaching: INTERNAL PROC[party: PartyData, callingParty: PartyData_NIL] = {
poacher, poachee: 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 RETURN;
poachee _ NARROW[Triples.Select[$telephone, VoiceUtils.MakeAtom[rName], --party--]];
IF poachee#NIL THEN GOTO Done ELSE IF callingParty=NIL THEN RETURN;
IF party.outgoing = NIL THEN RETURN; -- No phone number?
[nb, poacheeID] _ GetPartyFromNumberInt[
partyID: Reseal[callingParty],
phoneNumber: party.outgoing,
description: rName,
trunkRequired: TRUE
];
IF nb#$success OR poacheeID=nullID THEN RETURN;
poachee _ ThPartyPrivate.UnsealParty[poacheeID];
};
$telephone => {
wsRope: ROPE _
NamesGV.GVGetAttribute[rName.Concat[larkRegistry], $workstationhost, NIL];
workstation _ VoiceUtils.MakeAtom[wsRope.Concat["individual"]];
IF workstation = NIL THEN RETURN;
poachee_party;
IF (smarts _ Triples.Select[$SmartsForHost, workstation,-- smarts--])= NIL THEN RETURN;
poacher _ NARROW[Triples.Select[$Smarts, -- party --, smarts]];
IF poacher = NIL THEN { VoiceUtils.Problem["No smarts connection?"]; RETURN; };
};
ENDCASE=>RETURN;
EXITS Done => NULL; };
Triples.Erase[$Poaching, Any, poachee];
Triples.Erase[$Poaching, poacher, Any];
Triples.Make[$Poaching, poacher, poachee];
};

RegisterClone: ENTRY PROC[clonePartyID: PartyID]
RETURNS [nb: NB, credentials: ThParty.Credentials_[]] = {
cloneParty: PartyData_ThPartyPrivate.UnsealParty[clonePartyID];
party: PartyData _
NEW[ThPartyPrivate.PartyBody _ [type: cloneParty.type, name: cloneParty.name]];
cloneSmarts, smarts: SmartsData;
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]; };
smarts_NEW[ ThPartyPrivate.SmartsBody _ [
properties: cloneSmarts.properties, 
type: cloneSmarts.type,
interface: cloneSmarts.interface,
shh: cloneSmarts.shh
]];
Triples.Make[$Smarts, party, smarts];
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;
};

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 {
IdleConversationsForParty[party];
EraseAsObj[party,
LIST[ $Smarts, $AssumedParty, $AuthenticatedParty, $TrunkParty, $Poaching ] ];
Triples.Erase[$Poaching, Any, 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;
Triples.Erase[$SmartsForHost, Any, 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] = {
};

DescribeParty: PUBLIC PROC[partyID: Thrush.PartyID, nameReq: ThParty.NameReq]
RETURNS[nb: NB, description: Thrush.ROPE] = {
a: ATOM;
[nb, description, a] _ GetRnameFromParty[ThPartyPrivate.UnsealParty[partyID], nameReq];
};

DoDescribeParty: PUBLIC PROC[ party: PartyData, nameReq: ThParty.NameReq ]
RETURNS[ description: Thrush.ROPE_NIL] = {
type: Thrush.PartyType _ party.type;
DescribeTrunk: PROC RETURNS[des: ROPE] = INLINE {
more: ROPE=IF party.outgoing#NIL THEN party.outgoing ELSE "outside line";
des _ IF description=NIL THEN more
ELSE IO.PutFR["%s (%s)", IO.rope[description], IO.rope[more]];
};
Sel: PROC[type: Thrush.PartyType, party: PartyData] RETURNS [name: Thrush.ROPE] = {
a: ATOM _ NARROW[Triples.Select[$trunk, -- owner --, party]];
RETURN[IF a=NIL THEN NIL ELSE Atom.GetPName[a]];
};
IF nameReq=$none THEN RETURN;
description _ SELECT type FROM
$individual, $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
$owner => party.name.Concat[larkRegistry],
$description => IO.PutFR["%g service", rope[party.name]],
$current, $address => Sel[$service, party],
ENDCASE=>ERROR),
ENDCASE => ERROR;
};
GetRnameFromParty: 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];
};

EraseAsObj: INTERNAL PROC[obj: REF, attributes: LIST OF ATOM] = {
FOR attr: LIST OF ATOM _ attributes, attr.rest WHILE attr#NIL DO
Triples.Erase[attr.first, obj, Any];
ENDLOOP;
};

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.machine]; ENDCASE;
IF s=NIL OR s=Nice.LarkConLogStream[[[0],[0]]] THEN s_VoiceUtils.FindWhere[$System, NIL];
};

WhitePagesInit: Commander.CommandProc = {
treeName, intExtName: ROPE;
IF wpState#NIL AND ThNet.CloseWhitePagesDatabase[wpState] THEN wpState_NIL;
treeName _ CommandTool.NextArgument[cmd];
intExtName _ CommandTool.NextArgument[cmd];
IF wpState=NIL THEN wpState _ ThNet.InitWhitePagesDatabase[
treeName, -- Optional, defaults supplied by WP impl.,
intExtName, --  so NILs are OK here
$write];
};

serverInterfaceName: ThPartyRpcControl.InterfaceName;
serverPassword: RPC.EncryptionKey;

ThPartyInit: Commander.CommandProc = {
ENABLE
RPC.ExportFailed => { VoiceUtils.Problem["ThParty export failed", $System]; GOTO Failed; };
serverInterfaceName _ [
type: "ThParty.Lark",
instance: VoiceUtils.CmdOrToken[cmd: cmd, key: "ThrushServerInstance", default: "Morley.Lark"],
version: ThVersions.ThrushVR];
serverPassword _ VoiceUtils.CurrentPasskey[VoiceUtils.CmdOrToken[
cmd: cmd, key: "ThrushServerPassword", default: "MFLFLX"]];
IF thPartyExported THEN RETURN;
VoiceUtils.RegisterWhereToReport[ReportSystem, $System];
VoiceUtils.RegisterWhereToReport[ReportParty, $Party];
ThPartyRpcControl.ExportInterface[
interfaceName: serverInterfaceName,
user: serverInterfaceName.instance,
password: serverPassword];
ExplicitExport.ExportExplicitly["ThParty.Lark", serverInterfaceName.instance, ThVersions.ThrushVR, ThPartyRpcControl.LupineProtocolVersion, ThPartyRpcServerImpl.ServerDispatcher];
VoiceUtils.ReportFR["Export[ThParty.Lark, %s]", $System, NIL, rope[serverInterfaceName.instance]];
thPartyExported _ TRUE;
EXITS
Failed => ThPartyRpcControl.UnexportInterface[!LupineRuntime.BindingError=>CONTINUE];
};

Commander.Register["WhitePages", WhitePagesInit, "WhitePages <TreeName (opt.) <IntExtMapFileName (opt)>> -- Initialize and Open White Pages Database"];
Commander.Register["ThParty", ThPartyInit,
"ThParty <ExportInstance(opt.) <ServerPassword (opt)>> -- Initialize and Export ThParty"];
}.
�����ThPartyInitImpl.mesa
Copyright c 1985 by Xerox Corporation.  All rights reserved.
Last modified by D. Swinehart, December 26, 1985 10:51:47 am PST
Definitions	
Functions for obtaining existing parties, or phone numbers

Obtains a $service, $individual, or $telephone party, if available
No active party; try to get a trunk line for that individual
Locates an idle $service party, if available
<<This still needs some work.>>
e.g., $"text-to-speech.lark"
e.g., $"myself.pa"
e.g., $"myself.pa.text-to-speech"
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
Making new parties and smartses

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.
rName is a misnomer; it's the name of a service, without registry, if type is service.
There's no voice path for this individual.  Try to create one using calling party's trunk.
No HostForSmarts entry is made for clones.  What should happen here anyhow?

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.
$Visiting implementations and hints for implementations are only suggestions, for now.
Transfer $Visitors from authenticated to assumed.
This is now bogus.  Fix it a bit later.
IdleOneConv: Triples.ForeachProc -- [trip: TripleRec]
-- RETURNS [continue: BOOLEAN _ TRUE] --  ={
WITH trip.att SELECT FROM r: ThPartyPrivate.ConvState => cfRef _ r; ENDCASE => RETURN[TRUE];
IF cfRef.event.state=idle THEN RETURN[FALSE];
[] _ ThPartyPrivate.DoAdvance[ 
smartsID: smartsID,
party: party,
conv: NARROW[trip.obj],
cfRef: cfRef,
state: $idle,
reason: $terminating]; -- Idle party in conversation!!
RETURN[FALSE];
};
IdleRelated: Triples.ForeachProc -- [trip: TripleRec]
-- RETURNS [continue: BOOLEAN _ TRUE] --  = { 
IdleConversationsForParty[NARROW[trip.obj]];
};
Idle any conversations in which the party is involved.  Outside loop avoids Foreach/Erase conflicts.
DO
cfRef_NIL;
Foreach[Any, Any, party, IdleOneConv];
IF cfRef = NIL THEN EXIT;
ENDLOOP;
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.
Triples.Foreach[$Poaching, Any, party, IdleRelated];
Triples.Foreach[$Visiting, Any, party, IdleRelated];
ThPartyPrivate Implementations


Returns (bogus) service name if type = $service
Which party should we be when initiating calls?
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


�澥X����J�����
蟤�1�<J�淍橜J����蟢	�	J�����"J��
���*J�����#J�����*J�J�����%J�����!J��	���%J�J�������*J����!�+J����淐楲J�����渂榩J�渆榚J���淢榓J���J�J������J����'����榏桱�����0J�J����$�1J������!J���湑槬J�J������
����)����
J�J��
�
J���J�J�J�J��	�	J�J�J�J�J����J�J�J�J�J�J�J�J����J�
桱�������-�3J������桱������J�����
���J����蟘�;��	����%J� 桱�����
���J������=J��
����'�����
���J����� 桱�蟦�������������楲J� J��J����	�J��J�������J�J����:J���
�����
������<J�������/J������)J��������J�J�𶴔J�J���J���������
��'J����)�����;J�����*�9J��������J�����J��������J��'�� 楰桱����������,J��-���2J�J���J�������������&J����(���6J��
���J������)J��������J�淟��楽J�J����
����������楥J���
��
�����=J��𶠧J�𵹍����	����$�7J�?桱�J�������������8J����������,J�淔楩J���J����������!��楥J���J��������6J橞J�����
J�J��	���J�
�����������0�������+�=J�=J�����=楻J����J�������J��=��楪J������)J��
����(J������������J����2��榁桱�
��
��������楳J��������4J�<�𶌺J�𵹍桱���	����	�������=�������!�3J�淚業桱����������-J��>��楧J���
��0楥J�J���������� ��楡J�����!���8J�,J�J��������J�𶴔J�����
J���������2楪J�桱���	�������-��)�)J�桱����������4�淍楡J�!桱����+�	�楬J�	���������榃J�����(�(榃J���J�������
��%J��&���+J��
�����'J�����*�9J櫥J�������J�������J�7J�J�����J�����J�-J����������/J業��
��������&J�����'��楧桱������������楡����������;J橢桱�𵹍��������"J������1J�
���������	�	楳��������J�J��=��楤J���
�����9J���桱���桱�J��+�/榋������J����"���?J�������+�<J�桱��������J���J�� � J���:榊J�� � J�J�����������0J��������1J�����J��%�%J���������-��������J������楬桱����<楨桱��������-J��������楤J��������楰J��������)J���J��棗�J����������J�����J��������(J�%J�J��(��4J�$J�����J����+�1J�淓��榁J�J�����
������J�����J��������(J�%J�J��1���5J�$J�����J����+�1J�淺榎J�J�����
����J��������(J�%J�J��(��4J��/�/J�$J�����J����+�1��
���%J�淟楲桱����*��楻J�J�����J�����1楲��������J櫎J橬J��$�$J��������J�桱����������/����������.�.J��%�%J���J���J�������6J��6��楢桱����������>��/�/J�����.J��0���;桱����������8J�桱�蟗衎n
�衎k����J��'�'���
�����J�淏橞������&J���J���J���J���J���桱���$�&J���.�0J�*J�*J��烰榑J�����>�>J楴J�(桱�J������������楩J��������3J橵J�	���������&榖J�J����!�,J�������J����������4J��������	�楴J����������*J����7楤�������'�楡J����.�7J���J�
������3����楴J�������&�5J�桱����桱�J��� �"J���������*楤J���J����������+��楳J����� J�����J�����J��
���J�����
J������������J�J����,������業J��-���7��
�����	�
����榃J��)���3桱��%��>J楪J��(��8J�
��1��������楰J��
��8�	�楾J���	����������������楥J�蟭Z橺J�	����������:�(J�J�J�J����J���桱���
�������/J�0J������J�淓��楯桱�?J����������!J�J�
��6��������榃J��
��/�?J�������0��極J�桱������桱����������J�'J�'J�*J�J�����
������0J�����*�9J��?�?���J��淟極桱� J����������0J����%��楬���
�����J��,��業������)J�$J�J�!J�J�桱��%�%J�(J�*J橩��?�?J�����2��'榩桱���J����
�����������=J���
�������<J����
�����������>J���
�������=桱������������7J������#J�9J�J����������)������	��楢J������桱��-�-J�J��棗�*J���
�
����������<J������J櫏J橵J��������J�J�;J����������)J�����	��;��������J�!J�1�J��淛楴桱�%���J�� ����楳桱�������>J�桱����J�+J��������J��%����楴桱��������'極J�J�����������>J�'���衏k�5J��(��,J���
����+������橽J����������-���J���J��
�
J�����J��
�
J�
J����6桱������J�������5J��(��.J�����,J�桱檇���J�����
J�&J���	�������J����桱櫄J�𶍄J�𶍄J�J�����
���3楳J��������-J�����J榃J�J��棗�J���������.楯J��������*J�$�
�
����������1J������������業����
�����"J���������
�>桱�������+����楽J��������
�=J��������������0J�桱��������������J�&����	��J���J� J���J�J���������
��	�� J�*J����'�9J��+�+J������桱������桱�J��������5楲J���������	����楩J�/J����������+J�(J��������'J���$�(J���桱������������7J��������?J�𵹍J�𶠧J���桱��J�/������
�
��!楾J�����5楧J����)�4J�J��
�
J����������-J�����	��;J�
�����������>J�������$��楺J���&�:������
��楤J������桱�J�桱����
�������������楢�
�������������楡J�$J����桱�J��棗�J��������0J�������%J�����%J����������J�	���1������楻������"J�淓��楴桱�
������$��!��榊J�桱������)J�����J�
��	����(��	��楰J��)�)J��+�+���	����(�;J��
�+�5J����#J�桱�桱��J�𶠧J�����"J������&���J��淚��榌����J�J�淿榑J���淎楢J�;桱��������J�𷛮J�𶴔�"J��#�#J��#�#J�桱�湷槼J��9��&榖J��������J�淜��楿桱�桱��J�湕槜��*�*J�淶榋桱�Icode�����'K�	K��蟫J橵��&K���'K�K�K�!K�2K��橰K���5橝K����K���$�0��*K�'K���櫗K��K����*K橜K��檷桲��K��桲���+K�K����(桲��K����厳����S>��€D��