DIRECTORY Atom USING[ GetPName ], AuthenticationP14V2 USING[ Problem, CallProblem ], BasicTime USING[ FromNSTime, GetClockPulses, GMT, Now, Period, Pulses, PulsesToSeconds, ToNSTime, UnpackedPeriod, UnpackPeriod ], CHEntriesP0V0, CHOpsP2V3 USING [ItemObject], Convert USING[ CardFromRope, Error, RopeFromCard, RopeFromInt, RopeFromTime, RopeFromXNSAddress, XNSAddressFromRope, XNSHostFromRope ], GluePruneHacks USING [CacheEntry, DomainsForServer, ExposeErrorCode, RopeList], IO USING[ PutChar, PutF, PutF1, PutRope ], MaintainDefs USING[ CmdButton, Level, MyData, MyDataObject, RetrieveProc, What ], MaintainProcs USING[ ChangeLooksProc ], MaintainMisc USING[ CallForItems, EnumGroups, EnumIndividuals, Plural, Verify ], MaintainNS, Process USING[Pause, SecondsToTicks], Real USING[Fix], Rope USING[ Cat, Concat, Equal, Find, FindBackward, Index, Length, Replace, ROPE, Substr ], XNS USING[ Address, Host, unknownAddress, unknownHost, unknownSocket ], XNSAuth USING[ AuthenticationError, CallError, ChangeMyPasswords, CreateSimpleKey, CreateStrongKey, DeleteSimpleKey, DeleteStrongKey, GetIdentityDetails, Identity, SimpleKeyFromPassword, StrongKeyFromPassword], XNSCH USING[ Add, AddAlias, AddGroupProperty, AddItemProperty, AddMember, AddSelf, ChangeItemProperty, Conversation, Delete, DeleteAlias, DeleteMember, DeleteProperty, DeleteSelf, ElementStreamProc, Enumerate, EnumerateDomains, EnumerateOrganizations, Error, ErrorCode, GetProperties, InitiateConversation, IsMember, IsMemberClosure, Item, ListAliases, ListMembers, Lookup, LookupAddressFromRope, LookupItemProperty, Name, Properties, RopeFromItem, TerminateConversation, Which], XNSCHACL USING[ AccessList, AddMemberToPropertyACL, DeleteMemberFromPropertyACL, ListMembersOfPropertyACL], XNSCHItemOps USING[AddressListFromItem, BoolFromItem, Card32FromItem, Card16IntoItem, Card32IntoItem, CreateNewItem, Error, ItemFromAddressList, ItemFromCard16, ItemFromName, ItemFromRope, NameListFromItem, NameFromItem, NameIntoItem], XNSCHName USING[ FieldTooLong, NameFromRope, RopeFromName]; MaintainNSImpl: CEDAR MONITOR LOCKS d USING d: MyData IMPORTS Atom, BasicTime, Convert, GluePruneHacks, IO, MaintainDefs, MaintainMisc, Process, Real, Rope, XNSAuth, XNSCH, XNSCHACL, XNSCHItemOps, XNSCHName EXPORTS MaintainNS = { OPEN CHEntriesP0V0; ROPE: TYPE = Rope.ROPE; MyData: TYPE ~ MaintainDefs.MyData; MyDataObject: TYPE ~ MaintainDefs.MyDataObject; What: TYPE ~ MaintainDefs.What; Name: TYPE = XNSCH.Name; NameRef: TYPE = REF Name; NameList: TYPE = LIST OF NameRef; Item: TYPE = XNSCH.Item; AccessList: TYPE = XNSCHACL.AccessList; Identity: TYPE = XNSAuth.Identity; PropType: TYPE = {unknown, group, rope, name, addressList, authenticationLevel, userData, mailboxInfo}; Property: TYPE = CARD32; DoCommand: PUBLIC PROC [d: MyData, cb: MaintainDefs.CmdButton, name, args: ROPE] = { SELECT cb.cmd FROM type, isMember => DoTypeCommand[d, cb, name, args]; set, add, remove, misc => DoUpdateCommand[d, cb, name, args]; ENDCASE => ERROR; }; DoEnumerate: PUBLIC PROC [d: MyData, cb: MaintainDefs.CmdButton, pattern, args: ROPE] = { Enumerate: PROC [d: MyData, cb: MaintainDefs.CmdButton, pattern, args: ROPE] = { AddNameToOrgList: PROC [name: Name] = { nr: NameRef ฌ NEW[Name ฌ name]; orgNameList ฌ CONS[nr, orgNameList]; nOrganizations ฌ SUCC[nOrganizations]; IF d.verbose THEN d.out.PutChar['.]; }; AddNameToDomainList: PROC [name: Name] = { nr: NameRef ฌ NEW[Name ฌ name]; domainNameList ฌ CONS[nr, domainNameList]; nDomains ฌ SUCC[nDomains]; IF d.verbose THEN d.out.PutChar['.]; }; AddNameToFullList: PROC [name: Name] = { nr: NameRef ฌ NEW[Name ฌ name]; fullNameList ฌ CONS[nr, fullNameList]; nNames ฌ SUCC[nNames]; }; ReverseList: PROC [original: NameList] RETURNS[newList: NameList]= { FOR list: NameList ฌ original, list.rest UNTIL list = NIL DO newList ฌ CONS[list.first, newList]; ENDLOOP; }; startTime: BasicTime.GMT; patternName, pat: Name; wildcardInAnyField, wildcardInObject, wildcardInDomain, wildcardInOrganization: BOOL; orgNameList, domainNameList, fullNameList: NameList ฌ NIL; eC: XNSCH.ErrorCode; eW: XNSCH.Which; problems: BOOL ฌ FALSE; fieldTooLong: BOOL ฌ FALSE; patternName ฌ XNSCHName.NameFromRope[pattern ! XNSCHName.FieldTooLong => {fieldTooLong ฌ TRUE; RESUME;}]; wildcardInObject ฌ Rope.Find[patternName.object, "*"] >= 0; wildcardInDomain ฌ Rope.Find[patternName.domain, "*"] >= 0; wildcardInOrganization ฌ Rope.Find[patternName.organization, "*"] >= 0; wildcardInAnyField ฌ wildcardInObject OR wildcardInDomain OR wildcardInOrganization; IF ~wildcardInAnyField AND cb.what # matches THEN { DoCommand[d, cb, pattern, args]; RETURN; }; pat ฌ patternName; startTime ฌ BasicTime.Now[]; IF d.verbose THEN d.out.PutF1["\n[Starting name expansion of \"%g\"]", [rope[pattern]]]; IF ~wildcardInOrganization THEN AddNameToOrgList[pat] ELSE { XNSCH.EnumerateOrganizations[c, pat, AddNameToOrgList ! XNSCH.Error => { eC ฌ code; eW ฌ which; problems ฌ TRUE; d.out.PutF["\n=> Error: %g (\"%g\")\n", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]], [rope[XNSCHName.RopeFromName[pat]]]]; CONTINUE; }]; orgNameList ฌ ReverseList[orgNameList]; }; FOR thisOrg: NameList ฌ orgNameList, thisOrg.rest UNTIL (thisOrg = NIL OR d.stop) DO domainNameList ฌ NIL; pat.organization ฌ thisOrg.first.organization; pat.domain ฌ patternName.domain; IF d.verbose THEN d.out.PutF1["\n[Org: \"%g\"]", [rope[pat.organization]]]; IF ~wildcardInDomain THEN AddNameToDomainList[pat] ELSE { XNSCH.EnumerateDomains[c, pat, AddNameToDomainList ! XNSCH.Error => { nOrganizationProblems ฌ nOrganizationProblems+1; eC ฌ code; eW ฌ which; problems ฌ TRUE; d.out.PutF["\n=> Error: %g (\"%g\")\n", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]], [rope[XNSCHName.RopeFromName[pat]]]]; CONTINUE; }]; domainNameList ฌ ReverseList[domainNameList]; }; FOR thisDomain: NameList ฌ domainNameList, thisDomain.rest UNTIL (thisDomain = NIL OR d.stop) DO firstObjectInDomain: BOOL ฌ TRUE; prop: Property; host: XNS.Host; atom: ATOM; IF cb.what = matches THEN atom ฌ NARROW[d.cmdData]; fullNameList ฌ NIL; pat.organization ฌ thisDomain.first.organization; pat.domain ฌ thisDomain.first.domain; pat.object ฌ patternName.object; IF d.verbose THEN d.out.PutF["\n [Domain: \"%g:%g\"]", [rope[pat.domain]], [rope[pat.organization]]]; IF cb.what = matches THEN prop ฌ PropertyFromAtom[atom, d]; IF cb.what = matches AND atom # $GroupsAndUsers THEN { d.out.PutF1["(matching: %g) ... ", [rope[GetPropertyInfo[prop].shortLabel]]]; XNSCH.Enumerate[c, pat, prop, AddNameToFullList ! XNSCH.Error => { nDomainProblems ฌ nDomainProblems+1; eC ฌ code; eW ฌ which; problems ฌ TRUE; d.out.PutF["\n=> Error: %g (\"%g\")\n", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]], [rope[XNSCHName.RopeFromName[pat]]]]; CONTINUE; }]; } ELSE { IF MaintainMisc.EnumGroups[cb] THEN XNSCH.Enumerate[c, pat, userGroup, AddNameToFullList ! XNSCH.Error => { nDomainProblems ฌ nDomainProblems+1; eC ฌ code; eW ฌ which; problems ฌ TRUE; d.out.PutF["\n=> Error: %g (\"%g\")\n", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]], [rope[XNSCHName.RopeFromName[pat]]]]; CONTINUE; }]; IF MaintainMisc.EnumIndividuals[cb] THEN XNSCH.Enumerate[c, pat, user, AddNameToFullList ! XNSCH.Error => { nDomainProblems ฌ nDomainProblems+1; eC ฌ code; eW ฌ which; problems ฌ TRUE; d.out.PutF["\n=> Error: %g (\"%g\")\n", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]], [rope[XNSCHName.RopeFromName[pat]]]]; CONTINUE; }]; }; fullNameList ฌ ReverseList[fullNameList]; IF cb.what = matches AND prop = addressList THEN host ฌ HostAddressFromSelection[d]; FOR thisName: NameList ฌ fullNameList, thisName.rest UNTIL (thisName = NIL OR d.stop) DO IF cb.what = matches THEN { name: Name ฌ thisName.firstญ; IF prop # addressList OR host = XNS.unknownHost OR MatchHostAddressForObject[host, name] THEN { IF ~d.firstTime THEN d.out.PutRope[", "] ELSE d.firstTime ฌ FALSE; d.out.PutF1["\"%g\"", [rope[XNSCHName.RopeFromName[name]]]]; }; LOOP; }; IF firstObjectInDomain THEN { IF d.verbose THEN d.out.PutChar['\n]; firstObjectInDomain ฌ FALSE; }; DoCommand[d, cb, XNSCHName.RopeFromName[thisName.firstญ], args]; ENDLOOP; ENDLOOP; ENDLOOP; IF nNames > 1 OR nDomains > 1 OR nOrganizations > 1 THEN { d.out.PutF["\n(%g %g", [cardinal[nNames]], [rope[MaintainMisc.Plural["name", "names", nNames]]]]; IF nDomains > 1 OR nOrganizations > 1 THEN d.out.PutF[" in %g %g", [cardinal[nDomains]], [rope[MaintainMisc.Plural["domain", "domains", nDomains]]]]; IF nOrganizations > 1 THEN d.out.PutF[" in %g %g", [cardinal[nOrganizations]], [rope[MaintainMisc.Plural["organization", "organizations", nOrganizations]]]]; d.out.PutRope[" enumerated."]; IF nDomainProblems > 0 OR nOrganizationProblems > 0 THEN { d.out.PutRope[" Problems with "]; d.out.PutF["%g %g and", [cardinal[nDomainProblems]], [rope[MaintainMisc.Plural["domain", "domains", nDomainProblems]]]]; d.out.PutF[" %g %g.)", [cardinal[nOrganizationProblems]], [rope[MaintainMisc.Plural["organization", "organizations", nOrganizationProblems]]]]; } ELSE d.out.PutChar[')]; }; IF d.verbose THEN d.out.PutF["\n[Start: %g, Stop: %g]", [rope[Convert.RopeFromTime[startTime]]], [rope[Convert.RopeFromTime[BasicTime.Now[]]]]]; }; c: XNSCH.Conversation ฌ XNSCH.InitiateConversation[d.identity, d.conversationAddress]; nNames, nDomains, nOrganizations: CARD ฌ 0; nDomainProblems, nOrganizationProblems: CARD ฌ 0; Enumerate[d, cb, pattern, args]; XNSCH.TerminateConversation[c]; }; DoForEachArgument: PROC [d: MyData, c: XNSCH.Conversation, proc: PROC[Name, ROPE, Name, ROPE, BOOL], check: BOOL ฌ TRUE, doDisting: BOOL ฌ TRUE, leader: ROPE ฌ "\n"] = { FOR argList: LIST OF ROPE ฌ d.argList, argList.rest WHILE argList # NIL DO name, distingName: Name; nameRope, distingNameRope, thisArg: ROPE; problems: BOOL; thisArg ฌ argList.first; name ฌ XNSCHName.NameFromRope[thisArg ! XNSCHName.FieldTooLong => RESUME]; nameRope ฌ XNSCHName.RopeFromName[name]; IF doDisting THEN [distingName, problems, , ] ฌ DistinguishedName[name, c] ELSE problems ฌ TRUE; IF ~problems THEN distingNameRope ฌ XNSCHName.RopeFromName[distingName]; IF check AND ~CheckName[name] THEN { IF MaintainMisc.Verify[d] THEN { d.out.PutF1["\n\"%g\" is NOT a valid name.", [rope[nameRope]]]; RETURN; }; d.out.PutF["%g(\"%g\" is an invalid name) ... ", [rope[leader]], [rope[nameRope]]]; }; proc[name, nameRope, distingName, distingNameRope, ~problems]; ENDLOOP; }; DoUpdateCommand: PROC [d: MyData, cb: MaintainDefs.CmdButton, name, args: ROPE] = { UpdatePassword: PROC = { SELECT cb.cmd FROM set => { type: ROPE ฌ IF cb.what = password THEN "strong" ELSE "simple"; idName, dIdName, dParmName: Name; sameName: BOOL; dIdNameRope, dParmNameRope: ROPE; idName ฌ XNSAuth.GetIdentityDetails[d.identity].name; [dIdName, problems, , ] ฌ DistinguishedName[idName, c]; dIdNameRope ฌ XNSCHName.RopeFromName[dIdName]; IF problems THEN { d.out.PutF1["\nSorry, unable to discern logged-in user: \"%g\"? Password not changed.", [rope[dIdNameRope]]]; RETURN; }; [dParmName, problems, , ] ฌ DistinguishedName[nsName, c]; dParmNameRope ฌ XNSCHName.RopeFromName[dParmName]; IF problems THEN { d.out.PutF1["\nSorry, unable to distinguish name: \"%g\". Password not changed.", [rope[dParmNameRope]]]; RETURN; }; sameName ฌ Rope.Equal[dIdNameRope, dParmNameRope, FALSE]; SELECT TRUE FROM sameName, d.level = normal, d.level = owner => { IF ~sameName THEN { d.out.PutF["\nSorry, in Normal and Owner level the provided name must be the same as the logged in user (\"%g\" # \"%g\"). Password not changed.", [rope[dParmNameRope]], [rope[dIdNameRope]]]; RETURN; }; d.out.PutF["\nChanging my (%g) password (%g) ...", [rope[type]], [rope[dIdNameRope]]]; [] ฌ XNSAuth.ChangeMyPasswords[d.identity, args, cb.what = password, cb.what = simple ! XNSAuth.AuthenticationError => { d.out.PutF1[" Error: %g.", [rope[ExposeAuthProblem[problem]]]]; problems ฌ TRUE; CONTINUE }; XNSAuth.CallError => { d.out.PutF1[" Error: %g.", [rope[ExposeAuthCallProblem[problem]]]]; problems ฌ TRUE; CONTINUE }; ]; }; ENDCASE => { d.out.PutF["\nChanging (%g) password for \"%g\" ...", [rope[type]], [rope[name]]]; d.out.PutF1[" deleting existing %g key ...", [rope[type]]]; IF cb.what = password THEN [] ฌ XNSAuth.DeleteStrongKey[d.identity, nsName ! XNSAuth.AuthenticationError => { d.out.PutF1[" %g ...", [rope[ExposeAuthProblem[problem]]]]; problems ฌ TRUE; CONTINUE }; XNSAuth.CallError => { d.out.PutF1[" %g ...", [rope[ExposeAuthCallProblem[problem]]]]; problems ฌ TRUE; CONTINUE }; ] ELSE [] ฌ XNSAuth.DeleteSimpleKey[d.identity, nsName ! XNSAuth.AuthenticationError => { d.out.PutF1[" %g ...", [rope[ExposeAuthProblem[problem]]]]; problems ฌ TRUE; CONTINUE }; XNSAuth.CallError => { d.out.PutF1[" %g ...", [rope[ExposeAuthCallProblem[problem]]]]; problems ฌ TRUE; CONTINUE }; ]; problems ฌ FALSE; d.out.PutF1[" creating new %g key ...", [rope[type]]]; IF cb.what = password THEN [] ฌ XNSAuth.CreateStrongKey[d.identity, nsName, XNSAuth.StrongKeyFromPassword[args] ! XNSAuth.AuthenticationError => { d.out.PutF1[" %g ...", [rope[ExposeAuthProblem[problem]]]]; problems ฌ TRUE; CONTINUE }; XNSAuth.CallError => { d.out.PutF1[" %g ...", [rope[ExposeAuthCallProblem[problem]]]]; problems ฌ TRUE; CONTINUE }; ] ELSE [] ฌ XNSAuth.CreateSimpleKey[d.identity, nsName, XNSAuth.SimpleKeyFromPassword[args] ! XNSAuth.AuthenticationError => { d.out.PutF1[" %g ...", [rope[ExposeAuthProblem[problem]]]]; problems ฌ TRUE; CONTINUE }; XNSAuth.CallError => { d.out.PutF1[" %g ...", [rope[ExposeAuthCallProblem[problem]]]]; problems ฌ TRUE; CONTINUE }; ]; }; IF ~problems THEN d.out.PutRope[" ok."] ELSE d.out.PutRope[" errors."]; }; ENDCASE => NotImplementedYet[d]; }; UpdateAlias: PROC = { UpdateOneAlias: PROC [thisName: Name, thisNameRope: ROPE, distingName: Name, distingNameRope: ROPE, disting: BOOL] = { SELECT cb.cmd FROM add => { IF disting THEN d.out.PutF1["\nSorry, that alias/name (%g) is already taken.", [rope[thisNameRope]]] ELSE { d.out.PutF["\nAdding alias \"%g\" for \"%g\" ...", [rope[thisNameRope]], [rope[name]]]; [] ฌ XNSCH.AddAlias[c, nsName, thisName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; }; remove => { IF ~disting THEN d.out.PutF1["\nSorry, that alias (%g) is not currently assigned.", [rope[thisNameRope]]] ELSE { d.out.PutF1["\nRemoving alias \"%g\" ...", [rope[thisNameRope]]]; [] ฌ XNSCH.DeleteAlias[c, thisName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; }; ENDCASE => NotImplementedYet[d]; IF problems THEN { d.out.PutF1[" Error => %g", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]]]; problems ฌ FALSE; } ELSE d.out.PutRope[" ok."]; }; DoForEachArgument[d, c, UpdateOneAlias, FALSE, TRUE]; }; RemoveProperty: PROC [prop: Property] = { d.out.PutF["\nRemoving %g property from \"%g\" ...", [rope[ExposePropertyCode[prop]]], [rope[name]]]; [] ฌ XNSCH.DeleteProperty[c, nsName, prop ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; UpdateRemark: PROC = { SELECT cb.what FROM individualRemark => { d.out.PutF["\nSetting remark for \"%g\" to \"%g\" ...", [rope[name]], [rope[args]]]; [] ฌ XNSCH.ChangeItemProperty[c, nsName, user, XNSCHItemOps.ItemFromRope[args].newItem ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; groupRemark => { d.out.PutF["\nSetting remark for \"%g\" to \"%g\" ...", [rope[name]], [rope[args]]]; [] ฌ XNSCH.ChangeItemProperty[c, nsName, userGroup, XNSCHItemOps.ItemFromRope[args].newItem ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; ENDCASE => NotImplementedYet[d]; IF problems THEN { d.out.PutF1[" Error => %g", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]]]; problems ฌ FALSE; } ELSE { d.out.PutRope[" ok."]; }; }; UpdateProperties: PROC = { atom: ATOM ฌ NARROW[d.cmdData]; SetRopeProperty: PROC [prop: Property, type, value: ROPE] = { d.out.PutF["\nSetting %g for \"%g\" to \"%g\" ...", [rope[type]], [rope[name]], [rope[value]]]; problems ฌ FALSE; [] ฌ XNSCH.AddItemProperty[c, nsName, prop, XNSCHItemOps.ItemFromRope[value].newItem ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF problems THEN { problems ฌ FALSE; [] ฌ XNSCH.ChangeItemProperty[c, nsName, prop, XNSCHItemOps.ItemFromRope[value].newItem ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; }; SetNamePropertyFromRope: PROC [prop: Property, type, value: ROPE] = { item: Item; thisName: Name; d.out.PutF["\nSetting %g for \"%g\" to \"%g\" ...", [rope[type]], [rope[name]], [rope[value]]]; thisName ฌ XNSCHName.NameFromRope[value ! XNSCHName.FieldTooLong => {RESUME}]; item ฌ XNSCHItemOps.ItemFromName[thisName].newItem; problems ฌ FALSE; [] ฌ XNSCH.AddItemProperty[c, nsName, prop, item ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF problems THEN { problems ฌ FALSE; [] ฌ XNSCH.ChangeItemProperty[c, nsName, prop, item ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; }; SetListOfCard16PropertyFromRope: PROC [prop: Property, type, value: ROPE] = { item: Item ฌ ItemFromListOfCard16Rope[value]; problems ฌ FALSE; IF item = NIL THEN { d.out.PutF1["\n\"%g\" is not a valid list of CARD16.", [rope[value]]]; RETURN; }; d.out.PutF["\nSetting %g for \"%g\" to \"%g\" ...", [rope[type]], [rope[name]], [rope[value]]]; [] ฌ XNSCH.AddItemProperty[c, nsName, prop, item ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF problems THEN { problems ฌ FALSE; [] ฌ XNSCH.ChangeItemProperty[c, nsName, prop, item ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; }; SELECT atom FROM $UserFileService => NotImplementedYet[d]; $AddressList => { addresses: LIST OF XNS.Address; addresses ฌ AddressListFromRopeList[args ! Convert.Error => {problems ฌ TRUE; CONTINUE}]; IF problems THEN { d.out.PutF1["\nSorry, but you have given an invalid address: \"%g\"", [rope[args]]]; GOTO someProblems; }; d.out.PutRope["\nSetting the Address List property to: "]; PutAddressList[d, addresses]; d.out.PutRope[" ..."]; problems ฌ FALSE; [] ฌ XNSCH.ChangeItemProperty[c, nsName, addressList, XNSCHItemOps.ItemFromAddressList[addresses].newItem ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF problems THEN { problems ฌ FALSE; [] ฌ XNSCH.AddItemProperty[c, nsName, addressList, XNSCHItemOps.ItemFromAddressList[addresses].newItem ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; d.out.PutF1[" %g.", [rope[IF ~problems THEN "ok" ELSE "errors"]]]; EXITS someProblems => NULL; }; $PrintService => SetRopeProperty[printService, "print service remark", args]; $FileSerivce => SetRopeProperty[fileService, "file service remark", args]; $CHService => SetRopeProperty[clearinghouseService, "clearingHouse service remark", args]; $UserRemark => SetRopeProperty[user, "user remark", args]; $GroupRemark => SetRopeProperty[userGroup, "group remark", args]; $AssociatedWorkstation => SetNamePropertyFromRope[associatedWorkstation, "Associated Workstation name", args]; $RopePropFromSelection => SetRopeProperty[PropertyFromSelection[d], ExposePropertyCode[PropertyFromSelection[d]], args]; $NamePropFromSelection => SetNamePropertyFromRope[PropertyFromSelection[d], ExposePropertyCode[PropertyFromSelection[d]], args]; $ListOfCard16PropFromSelection => SetListOfCard16PropertyFromRope[PropertyFromSelection[d], ExposePropertyCode[PropertyFromSelection[d]], args]; ENDCASE => NotImplementedYet[d]; IF problems THEN { d.out.PutF1[" Error => %g", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]]]; problems ฌ FALSE; } ELSE { d.out.PutRope[" ok."]; }; }; UpdateMember: PROC = { UpdateOneMember: PROC [thisName: Name, thisNameRope: ROPE, distingName: Name, distingNameRope: ROPE, disting: BOOL]= { myGroup: Name ฌ nsName; myGroupRope: ROPE ฌ name; changed: BOOL ฌ FALSE; IF d.level IN [normal..owner] THEN [myGroup, myGroupRope, changed] ฌ GetPossibleSubList[nsName]; SELECT cb.cmd FROM add => { IF disting THEN { d.out.PutF["\nAdding member \"%g\" to \"%g\" ...", [rope[distingNameRope]], [rope[name]]]; IF changed THEN d.out.PutF1[" split-list, using \"%g\" sub-list ...", [rope[myGroupRope]]]; [] ฌ XNSCH.AddMember[c, myGroup, prop, distingName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; } ELSE { d.out.PutF["\nAdding (possibly not distinguished) member \"%g\" to \"%g\" ...", [rope[thisNameRope]], [rope[name]]]; IF changed THEN d.out.PutF1[" split-list, using \"%g\" sub-list ...", [rope[myGroupRope]]]; [] ฌ XNSCH.AddMember[c, myGroup, prop, thisName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; }; remove => { d.out.PutF["\nRemoving member \"%g\" from \"%g\" ...", [rope[thisNameRope]], [rope[name]]]; IF changed THEN d.out.PutF1[" split-list, using \"%g\" sub-list ...", [rope[myGroupRope]]]; [] ฌ XNSCH.DeleteMember[c, myGroup, prop, thisName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF problems THEN { [distingName, problems, , ] ฌ DistinguishedName[thisName, c]; IF ~problems THEN { distingNameRope ฌ XNSCHName.RopeFromName[distingName]; d.out.PutF1[" trying distinguished name \"%g\" ...", [rope[distingNameRope]]]; [] ฌ XNSCH.DeleteMember[c, myGroup, prop, distingName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; }; }; ENDCASE => NotImplementedYet[d]; IF problems THEN { d.out.PutF1[" Error => %g", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]]]; problems ฌ FALSE; } ELSE d.out.PutRope[" ok."]; }; DoForEachArgument[d, c, UpdateOneMember, cb.cmd = add, cb.cmd = add]; }; UpdateOwner: PROC = { UpdateOneOwner: PROC [thisName: Name, thisNameRope: ROPE, distingName: Name, distingNameRope: ROPE, disting: BOOL]= { SELECT cb.cmd FROM add => { IF disting THEN { d.out.PutF["\nAdding owner (administrator) \"%g\" to \"%g\" ...", [rope[distingNameRope]], [rope[name]]]; [] ฌ XNSCHACL.AddMemberToPropertyACL[c, nsName, members, administrators, distingName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; } ELSE { d.out.PutF["\nAdding (possibly not distinguished) owner (administrator) \"%g\" to \"%g\" ...", [rope[thisNameRope]], [rope[name]]]; [] ฌ XNSCHACL.AddMemberToPropertyACL[c, nsName, members, administrators, thisName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; }; remove => { d.out.PutF["\nRemoving owner (administrator) \"%g\" from \"%g\" ...", [rope[thisNameRope]], [rope[name]]]; [] ฌ XNSCHACL.DeleteMemberFromPropertyACL[c, nsName, members, administrators, thisName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF problems THEN { [distingName, problems, , ] ฌ DistinguishedName[thisName, c]; IF ~problems THEN { d.out.PutF1[" trying distinguished name \"%g\" ...", [rope[distingNameRope]]]; [] ฌ XNSCHACL.DeleteMemberFromPropertyACL[c, nsName, members, administrators, distingName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; }; }; ENDCASE => NotImplementedYet[d]; IF problems THEN { d.out.PutF1[" Error => %g", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]]]; problems ฌ FALSE; } ELSE d.out.PutRope[" ok."]; }; DoForEachArgument[d, c, UpdateOneOwner, cb.cmd = add, cb.cmd = add]; }; UpdateFriend: PROC = { UpdateOneFriend: PROC [thisName: Name, thisNameRope: ROPE, distingName: Name, distingNameRope: ROPE, disting: BOOL]= { SELECT cb.cmd FROM add => { IF disting THEN { d.out.PutF["\nAdding friend (self-controller) \"%g\" to \"%g\" ...", [rope[distingNameRope]], [rope[name]]]; [] ฌ XNSCHACL.AddMemberToPropertyACL[c, nsName, members, selfControl, distingName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; } ELSE { d.out.PutF["\nAdding (possibly not distinguished) friend (self-controller) \"%g\" to \"%g\" ...", [rope[thisNameRope]], [rope[name]]]; [] ฌ XNSCHACL.AddMemberToPropertyACL[c, nsName, members, selfControl, thisName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; }; remove => { d.out.PutF["\nRemoving friend (self-controller) \"%g\" from \"%g\" ...", [rope[thisNameRope]], [rope[name]]]; [] ฌ XNSCHACL.DeleteMemberFromPropertyACL[c, nsName, members, selfControl, thisName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF problems THEN { [distingName, problems, , ] ฌ DistinguishedName[thisName, c]; IF ~problems THEN { d.out.PutF1[" trying distinguished name \"%g\" ...", [rope[distingNameRope]]]; [] ฌ XNSCHACL.DeleteMemberFromPropertyACL[c, nsName, members, selfControl, distingName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; }; }; ENDCASE => NotImplementedYet[d]; IF problems THEN { d.out.PutF1[" Error => %g", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]]]; problems ฌ FALSE; } ELSE d.out.PutRope[" ok."]; }; DoForEachArgument[d, c, UpdateOneFriend, FALSE, cb.cmd = add]; }; UpdateSelf: PROC = { myGroup: Name; myGroupRope: ROPE; changed: BOOL ฌ FALSE; [myGroup, myGroupRope, changed] ฌ GetPossibleSubList[nsName]; SELECT cb.cmd FROM add => { d.out.PutF1["\nAdding self to \"%g\" ...", [rope[name]]]; IF changed THEN d.out.PutF1[" split-list, using \"%g\" sub-list ...", [rope[myGroupRope]]]; [] ฌ XNSCH.AddSelf[c, myGroup, members ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; remove => { d.out.PutF1["\nRemoving self from \"%g\" ...", [rope[name]]]; IF changed THEN d.out.PutF1[" split-list, using \"%g\" sub-list ...", [rope[myGroupRope]]]; [] ฌ XNSCH.DeleteSelf[c, myGroup, members ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; ENDCASE => NotImplementedYet[d]; IF problems THEN { d.out.PutF1[" Error => %g", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]]]; problems ฌ FALSE; } ELSE { d.out.PutRope[" ok."]; }; }; UpdateOccurrences: PROC = { SELECT cb.cmd FROM add => NotImplementedYet[d]; remove => NotImplementedYet[d]; ENDCASE => NotImplementedYet[d]; }; UpdateMailbox: PROC = { properties: XNSCH.Properties ฌ GetProperties[nsName, c]; item: Item; mbl: LIST OF ROPE ฌ NIL; existingMailboxes: LIST OF ROPE ฌ NIL; IF HasProperty[mailboxes, properties] THEN existingMailboxes ฌ GetExistingMailboxes[nsName, c] ELSE { d.out.PutRope[" creating mailboxes property ..."]; [] ฌ XNSCH.AddItemProperty[c, nsName, mailboxes, MailboxItemFromList[NIL] ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; FOR l: LIST OF ROPE ฌ d.argList, l.rest WHILE l # NIL DO distingName: Name; problems: BOOL; [distingName, problems, , ] ฌ DistinguishedNameFromRope[l.first]; IF problems THEN distingName ฌ XNSCHName.NameFromRope[l.first]; mbl ฌ CONS[XNSCHName.RopeFromName[distingName], mbl]; ENDLOOP; SELECT cb.cmd FROM add => { existingMailboxes ฌ AddRopesToList[existingMailboxes, mbl]; }; remove => { existingMailboxes ฌ RemoveRopesFromList[existingMailboxes, d.argList]; existingMailboxes ฌ RemoveRopesFromList[existingMailboxes, mbl]; }; ENDCASE => { NotImplementedYet[d]; RETURN; }; IF existingMailboxes = NIL THEN { d.out.PutRope[" no more mailboxes, deleting mailboxes property ..."]; [] ฌ XNSCH.DeleteProperty[c, nsName, mailboxes ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; } ELSE { d.out.PutRope[" changing mailboxes property to: "]; PutRopeList[d, existingMailboxes]; d.out.PutRope[" ..."]; item ฌ MailboxItemFromList[existingMailboxes]; [] ฌ XNSCH.ChangeItemProperty[c, nsName, mailboxes, item ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; }; UpdateForwarding: PROC = { DoNothingEnumeratorProc: PROC [XNSCH.ElementStreamProc] = {RETURN}; GetProperties: PROC [name: Name] RETURNS [XNSCH.Properties] = { [, properties] ฌ XNSCH.GetProperties[c, name ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; RETURN[properties]; }; HasAtLeastOneMember: PROC [group: Name] RETURNS[BOOL] = { BumpCount: PROC [name: Name] = { count ฌ count + 1; }; count: CARD ฌ 0; [] ฌ XNSCH.ListMembers[c, group, members, BumpCount ! XNSCH.Error => {CONTINUE}]; RETURN[count > 0]; }; properties: XNSCH.Properties ฌ GetProperties[nsName]; d.out.PutF[" %g forwarding %g \"%g\"...", [rope[SELECT cb.cmd FROM add => "adding", remove => "removing", ENDCASE => "???"]], [rope[SELECT cb.cmd FROM add => "to", remove => "from", ENDCASE => "???"]], [rope[name]]]; IF ~HasProperty[user, properties] THEN { d.out.PutRope[" must be a user."]; RETURN; }; SELECT cb.cmd FROM add => { IF HasProperty[mailboxes, properties] THEN { d.out.PutRope[" This user presently has Mailboxes, they should be removed ..."]; }; IF HasProperty[userGroup, properties] THEN d.out.PutRope[" group property already exists ..."] ELSE { d.out.PutRope[" adding group property ..."]; [] ฌ XNSCH.AddItemProperty[c, nsName, userGroup, XNSCHItemOps.ItemFromRope[args].newItem ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; IF HasProperty[members, properties] THEN d.out.PutRope[" members property already exists ..."] ELSE { d.out.PutRope[" adding members property ..."]; [] ฌ XNSCH.AddGroupProperty[c, nsName, members, DoNothingEnumeratorProc ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; d.out.PutRope["\n(Note: use Add Member to actually add names to the \"forwarding\" list.)"]; }; remove => { IF ~HasProperty[members, properties] THEN d.out.PutRope[" no members property to remove ..."] ELSE { IF HasAtLeastOneMember[nsName] THEN { d.out.PutRope[" still forwarding to at least one member, remove members first ..."]; RETURN; } ELSE { d.out.PutRope[" removing members property ..."]; [] ฌ XNSCH.DeleteProperty[c, nsName, members ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; }; IF ~HasProperty[userGroup, properties] THEN d.out.PutRope[" no group property to remove ..."] ELSE { d.out.PutRope[" removing group property ..."]; [] ฌ XNSCH.DeleteProperty[c, nsName, userGroup ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; d.out.PutRope["\n(Note: use Remove Member to actually remove names from the \"forwarding\" list.)"]; }; ENDCASE => NotImplementedYet[d]; }; UpdateCreate: PROC = { CreateUserData: PROC [name: Name] = { fsName: Name ฌ [name.organization, name.domain, "UNKNOWN"]; item: Item ฌ XNSCHItemOps.ItemFromCard16[Rope.FindBackward[name.object, " "]+1].newItem; [] ฌ XNSCHItemOps.NameIntoItem[item, fsName, 1]; [] ฌ XNSCH.AddItemProperty[c, name, userData, item ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; SELECT cb.what FROM createIndividual => { atom: ATOM ฌ NARROW[d.cmdData]; SELECT atom FROM $CreateCHS => { address: XNS.Address; address ฌ Convert.XNSAddressFromRope[args ! Convert.Error => {problems ฌ TRUE; CONTINUE}]; IF problems THEN { d.out.PutF1["\nYou must provide a valid address; \"%g\" is invalid.", [rope[args]]]; GOTO someProblems; }; address.socket ฌ XNS.unknownSocket; nsName ฌ [object: nsName.object, domain: "CHServers", organization: "CHServers"]; name ฌ XNSCHName.RopeFromName[nsName]; d.out.PutF1["\nCreating CHS entry for \"%g\" ...", [rope[name]]]; [] ฌ XNSCH.Add[c, nsName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF problems THEN GOTO someProblems; d.out.PutF1[" adding address = %g ...", [rope[Convert.RopeFromXNSAddress[address]]]]; [] ฌ XNSCH.AddItemProperty[c, nsName, addressList, XNSCHItemOps.ItemFromAddressList[LIST[address]].newItem ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF problems THEN GOTO someProblems; d.out.PutRope[" adding mailboxes property ..."]; [] ฌ XNSCH.AddItemProperty[c, nsName, mailboxes, MailboxItemFromList[LIST[name]] ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF problems THEN GOTO someProblems; d.out.PutRope[" adding clearinghouseService property ..."]; [] ฌ XNSCH.AddItemProperty[c, nsName, clearinghouseService, NEW[CHOpsP2V3.ItemObject[0]] ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; d.out.PutF1[" %g.", [rope[IF ~problems THEN "ok" ELSE "errors"]]]; EXITS someProblems => NULL; }; $CreateObject => { d.out.PutF1["\nCreating object \"%g\" ...", [rope[name]]]; [] ฌ XNSCH.Add[c, nsName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; ENDCASE => { d.out.PutF1["\nCreating individual \"%g\" ...", [rope[name]]]; [] ฌ XNSCH.Add[c, nsName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF ~problems THEN [] ฌ XNSCH.AddItemProperty[c, nsName, user, XNSCHItemOps.ItemFromRope[args].newItem ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF ~problems THEN CreateUserData[nsName]; }; }; createGroup => { DoNothingEnumeratorProc: PROC [XNSCH.ElementStreamProc] = {RETURN}; d.out.PutF1["\nCreating group \"%g\" ...", [rope[name]]]; [] ฌ XNSCH.Add[c, nsName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF ~problems THEN [] ฌ XNSCH.AddItemProperty[c, nsName, userGroup, XNSCHItemOps.ItemFromRope[args].newItem ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF ~problems THEN [] ฌ XNSCH.AddGroupProperty[c, nsName, members, DoNothingEnumeratorProc ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; }; ENDCASE => NotImplementedYet[d]; IF problems THEN { d.out.PutF1[" Error => %g", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]]]; problems ฌ FALSE; } ELSE { d.out.PutRope[" ok."]; }; }; UpdateDelete: PROC = { d.out.PutF1["\nDeleting object \"%g\" ...", [rope[name]]]; [] ฌ XNSCH.Delete[c, nsName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF problems THEN { d.out.PutF1[" Error => %g", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]]]; problems ฌ FALSE; } ELSE { d.out.PutRope[" ok."]; }; }; c: XNSCH.Conversation ฌ XNSCH.InitiateConversation[d.identity, d.conversationAddress]; eC: XNSCH.ErrorCode; eW: XNSCH.Which; problems: BOOL ฌ FALSE; nsName, argAsName: Name; prop: Property; atom: ATOM; IF ISTYPE[d.cmdData, ATOM] THEN { atom ฌ NARROW[d.cmdData]; prop ฌ PropertyFromAtom[atom, d]; }; nsName ฌ XNSCHName.NameFromRope[name ! XNSCHName.FieldTooLong => {RESUME;}]; argAsName ฌ XNSCHName.NameFromRope[args ! XNSCHName.FieldTooLong => {RESUME;}]; IF cb.what # createIndividual AND cb.what # createGroup AND ~CheckName[nsName, c] THEN { d.out.PutF1["\"%g\" is an invalid name.", [rope[XNSCHName.RopeFromName[nsName]]]]; XNSCH.TerminateConversation[c]; RETURN; }; SELECT cb.what FROM password, simple => UpdatePassword[]; alias => { IF cb.cmd = remove THEN { IF atom = $RemoveAlias THEN UpdateAlias[] ELSE RemoveProperty[PropertyFromSelection[d]]; } ELSE UpdateAlias[]; }; self => UpdateSelf[]; individualRemark, groupRemark => UpdateRemark[]; fileService => UpdateProperties[]; member => UpdateMember[]; owner => UpdateOwner[]; friend => UpdateFriend[]; mailbox => UpdateMailbox[]; forwarding => UpdateForwarding[]; occurrences => UpdateOccurrences[]; createIndividual, createGroup => UpdateCreate[]; delete => UpdateDelete[]; ENDCASE => NotImplementedYet[d]; XNSCH.TerminateConversation[c]; }; HasProperty: PROC [prop: Property, properties: XNSCH.Properties] RETURNS [BOOL] = { FOR pi: CARDINAL IN [0..properties.length) DO IF properties[pi] = prop THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; DoTypeCommand: PROC [d: MyData, cb: MaintainDefs.CmdButton, name, args: ROPE] = { PutOneFink: PROC [fink: Name, allDownList: LIST OF ROPE, c: XNSCH.Conversation, doDelete: BOOL ฌ FALSE, group: Name ฌ [NIL, NIL, NIL]] RETURNS [LIST OF ROPE] = { RopeInList: PROC [r: ROPE, l: LIST OF ROPE] RETURNS [BOOL] = { FOR l ฌ l, l.rest WHILE l # NIL DO IF Rope.Equal[r, l.first, FALSE] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; deleteThisFink: BOOL ฌ FALSE; nr: ROPE = XNSCHName.RopeFromName[fink]; IF d.verbose THEN { problems: BOOL ฌ FALSE; eC: XNSCH.ErrorCode; eW: XNSCH.Which; properties: XNSCH.Properties; domainOrganization: ROPE ฌ Rope.Cat[fink.domain, ":", fink.organization]; IF RopeInList[domainOrganization, allDownList] THEN {eC ฌ allDown; problems ฌ TRUE} ELSE [, properties] ฌ XNSCH.GetProperties[c, fink ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; SELECT TRUE FROM problems => SELECT eC FROM illegalPropertyID, illegalOrganizationName, illegalDomainName, illegalObjectName, noSuchOrganization, noSuchDomain, noSuchObject => { IF ExternalMailName[fink, c] THEN { d.out.PutF1["\n \"%g\" => possible external mail name", [rope[nr]]]; } ELSE { d.out.PutF["\n \"%g\" => %g", [rope[nr]], [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]]]; deleteThisFink ฌ TRUE; }; }; ENDCASE => { IF eC = allDown THEN IF ~RopeInList[domainOrganization, allDownList] THEN allDownList ฌ CONS[domainOrganization, allDownList]; d.out.PutF["\n \"%g\" => %g", [rope[nr]], [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]]]; }; (~HasProperty[mailboxes, properties] AND ~HasProperty[members, properties]) => d.out.PutF1["\n \"%g\" => no mailbox or members.", [rope[nr]]]; ENDCASE; }; IF deleteThisFink AND doDelete THEN DO { problems ฌ FALSE; d.out.PutRope[" => Deleting ..."]; [] ฌ XNSCH.DeleteMember[c, group, members, fink ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF problems THEN { d.out.PutF1[" Error => %g", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]]]; IF eC = overflowOfDataBase THEN { d.out.PutRope["; Pausing for 5 minutes and then I'll try again ..."]; FOR i: CARD IN [0..(5*60)) WHILE ~d.stop DO Process.Pause[Process.SecondsToTicks[1]]; ENDLOOP; IF d.stop THEN EXIT; LOOP; }; } ELSE d.out.PutRope[" deleted."]; EXIT; } ENDLOOP; count ฌ SUCC[count]; RETURN[allDownList]; }; PutOneName: PROC [name: Name] = { IF ~firstName OR (cb.what = matches AND ~d.firstTime) THEN d.out.PutRope[", "] ELSE {firstName ฌ FALSE; d.firstTime ฌ FALSE}; count ฌ SUCC[count]; d.out.PutF1["\"%g\"", [rope[XNSCHName.RopeFromName[name]]]]; }; PutOneDomainName: PROC [name: Name] = { IF firstName THEN firstName ฌ FALSE ELSE d.out.PutRope[", "]; count ฌ SUCC[count]; d.out.PutF["\"%g:%g\"", [rope[name.domain]], [rope[name.organization]]]; }; PutOneOrganizationName: PROC [name: Name] = { IF firstName THEN firstName ฌ FALSE ELSE d.out.PutRope[", "]; count ฌ SUCC[count]; d.out.PutF1["\"%g\"", [rope[name.organization]]]; }; PutItemAsRope: PROC [item: Item] = { rope: ROPE ฌ XNSCH.RopeFromItem[item]; d.out.PutF1["\"%g\"", [rope[rope]]]; }; PutItemAsName: PROC [item: Item, offset: INT ฌ 0] = { name: Name ฌ XNSCHItemOps.NameFromItem[item, offset].name; rope: ROPE ฌ XNSCHName.RopeFromName[name]; d.out.PutF1["\"%g\"", [rope[rope]]]; }; PutItemAsItem: PROC [item: Item, full: BOOL ฌ FALSE] = { d.out.PutF1["length = %g", [cardinal[item.length]]]; IF ~full OR item.length = 0 THEN RETURN; d.out.PutRope[": "]; FOR i: CARDINAL IN [0..item.length) DO IF i # 0 THEN d.out.PutRope[", "]; d.out.PutF["%g (%g)", [cardinal[item[i]]], [rope[Convert.RopeFromCard[item[i], 8]]]]; ENDLOOP; }; MaybePutItemAsRope: PROC [item: Item, full: BOOL ฌ FALSE] = { IF item # NIL AND item.length > 0 AND item.length = LOOPHOLE[1 + (item[0]+1)/2, CARDINAL] THEN PutItemAsRope[item] ELSE PutItemAsItem[item, full]; }; PutUserDataInfo: PROC [item: Item, user: ROPE, details: BOOL, leadingSpace: ROPE ฌ ""] = { name: Name; IF details THEN d.out.PutF["\n%gLast Name: \"%g\"", [rope[leadingSpace]], [rope[IF item.length <= 0 OR item[0] > Rope.Length[user] THEN "(Invalid Index)" ELSE Rope.Substr[user, item[0]]]]]; name ฌ XNSCHItemOps.NameFromItem[item, 1].name; d.out.PutF["\n%gUsers's File Service: \"%g\"", [rope[leadingSpace]], [rope[XNSCHName.RopeFromName[name]]]]; }; PutMailboxInfo: PROC [item: Item, leadingSpace: ROPE ฌ "", printTime: BOOL] = { ENABLE XNSCHItemOps.Error => { d.out.PutRope["\nMailboxes: malformed property!"]; GOTO OutOfHere; }; pos: INT ฌ 3; mailboxList: LIST OF Name; IF printTime THEN d.out.PutF["\n%gTime: %g", [rope[leadingSpace]], [rope[Convert.RopeFromTime[BasicTime.FromNSTime[NSTimeFromItem[item]]]]]]; d.out.PutF1["\n%gMailboxes: ", [rope[leadingSpace]]]; mailboxList ฌ XNSCHItemOps.NameListFromItem[item, 2].names; WHILE mailboxList # NIL DO d.out.PutF1["\"%g\"", [rope[XNSCHName.RopeFromName[mailboxList.first]]]]; IF mailboxList.rest # NIL THEN d.out.PutRope[", "]; mailboxList ฌ mailboxList.rest; ENDLOOP; EXITS OutOfHere => RETURN; }; PutItemAsAddressList: PROC [item: Item] = { addresses: LIST OF XNS.Address; addresses ฌ XNSCHItemOps.AddressListFromItem[item ! XNSCHItemOps.Error => {addresses ฌ NIL; CONTINUE}].addresses; PutAddressList[d, addresses]; }; PutItemAsAuthenticationLevel: PROC [item: Item] = { ENABLE XNSCHItemOps.Error => { d.out.PutRope["I don't understand: "]; PutItemAsItem[item, d.verbose]; GOTO OutOfHere; }; simpleSupported: BOOL = XNSCHItemOps.BoolFromItem[item, 0].bool; strongSupported: BOOL = XNSCHItemOps.BoolFromItem[item, 1].bool; IF simpleSupported THEN d.out.PutRope["simpleSupported"]; IF strongSupported THEN d.out.PutF1["%gstrongSupported", [rope[IF simpleSupported THEN ", " ELSE ""]]]; EXITS OutOfHere => RETURN; }; CountMembers: PROC [group: Name, prop: Property] RETURNS[CARD] = { BumpCount: PROC [name: Name] = { count ฌ count + 1; }; count: CARD ฌ 0; [] ฌ XNSCH.ListMembers[c, group, prop, BumpCount ! XNSCH.Error => {CONTINUE}]; RETURN[count]; }; GiveNSInfo: PROC [level: What] = { OPEN CHEntriesP0V0; giveDetails: BOOL ฌ FALSE; properties: XNSCH.Properties ฌ NIL; [distingName , properties] ฌ XNSCH.GetProperties[c, pattern ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF d.stop THEN RETURN; IF problems THEN SELECT eC FROM noSuchObject, allDown, wasUpNowDown => RETURN; ENDCASE => IF ExternalMailName[pattern, c] THEN NULL ELSE RETURN; IF d.firstTime THEN d.firstTime ฌ FALSE ELSE d.out.PutChar['\n]; IF problems THEN { d.out.PutF1["\nName: \"%g\"", [rope[name]]]; d.out.PutF["\nThe domain \"%g:%g\" appears to be connected via an External Mail Gateway. (An External Mail Gateway is a \"controlled\" gateway that passes mail only -- it does not allow access to file servers, clearinghouses, etc.) The name \"%g\" may or may not be valid in that domain -- it is impossible to determine that from here. Individuals within such a domain are most likely not Xerox employes. Caution should be exercised when sending sensitive data to this name or before adding such a name to a DL.", [rope[pattern.domain]], [rope[pattern.organization]], [rope[pattern.object]]]; problems ฌ FALSE; RETURN; }; d.out.PutF1["\nDistinguished name: \"%g\"", [rope[XNSCHName.RopeFromName[distingName]]]]; IF d.stop OR NARROW[d.cmdData, ATOM] = $DistingOnly THEN RETURN; d.out.PutRope["\nAliases: "]; [] ฌ XNSCH.ListAliases[c, pattern, PutOneName ! XNSCH.Error => {eC ฌ code; eW ฌ which; CONTINUE}]; IF count = 0 THEN d.out.PutRope["(none)"]; IF d.stop THEN RETURN; giveDetails ฌ cb.what = details; FOR pi: CARDINAL IN [0..properties.length) DO clProc: MaintainProcs.ChangeLooksProc ฌ LOOPHOLE[MaintainDefs.RetrieveProc[d.flavor, $ChangeLooks]]; unknown: BOOL; propInfo: PropertyInfoObject; propType: PropType; prop: Property ฌ properties.body[pi]; IF d.stop THEN RETURN; propInfo ฌ GetPropertyInfo[prop]; unknown ฌ propInfo.codeLabel = NIL; IF (unknown AND ~giveDetails) OR propInfo.detailed AND ~giveDetails THEN LOOP; IF ~propInfo.subLabels THEN d.out.PutF1["\n%g: ", [rope[IF unknown THEN Rope.Concat["Property ", propInfo.label] ELSE propInfo.label]]]; propType ฌ propInfo.propType; SELECT propType FROM group => { atom: ATOM = NARROW[d.cmdData, ATOM]; firstName ฌ TRUE; IF d.verbose THEN [] ฌ XNSCH.ListMembers[c, pattern, prop, PutOneName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}] ELSE d.out.PutF1["%g", [cardinal[CountMembers[pattern, prop]]]]; IF atom # $NoACLInfo THEN { FOR acl: AccessList IN [readAccess..selfControl] DO n: CARD ฌ count; IF d.stop THEN RETURN; IF clProc # NIL THEN clProc[d, 'i]; SELECT acl FROM readAccess => {d.out.PutRope["\nReaders: "];}; administrators => {d.out.PutRope["\nAdministrators: "];}; selfControl => {d.out.PutRope["\nSelfControllers: "];}; ENDCASE => LOOP; IF clProc # NIL THEN clProc[d, ' ]; firstName ฌ TRUE; [] ฌ XNSCHACL.ListMembersOfPropertyACL[c, pattern, prop, acl, PutOneName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF n = count THEN d.out.PutRope["(none)"]; ENDLOOP; }; }; ENDCASE => { item: Item; [, item] ฌ XNSCH.LookupItemProperty[c, pattern, prop ! XNSCH.Error => {CONTINUE}]; IF item # NIL THEN SELECT propType FROM unknown => MaybePutItemAsRope[item, d.verbose]; rope => PutItemAsRope[item]; name => PutItemAsName[item]; authenticationLevel => PutItemAsAuthenticationLevel[item]; addressList => PutItemAsAddressList[item]; userData => PutUserDataInfo[item, distingName.object, giveDetails, ""]; mailboxInfo => PutMailboxInfo[item, "", giveDetails]; ENDCASE => d.out.PutRope["???"]; }; ENDLOOP; }; c: XNSCH.Conversation ฌ XNSCH.InitiateConversation[d.identity, d.conversationAddress]; pattern: Name ฌ XNSCHName.NameFromRope[name ! XNSCHName.FieldTooLong => {RESUME;}]; distingName: Name; fullName: ROPE; problems: BOOL ฌ FALSE; firstName: BOOL ฌ TRUE; count: CARD ฌ 0; eC: XNSCH.ErrorCode; eW: XNSCH.Which; fullName ฌ XNSCHName.RopeFromName[pattern]; SELECT cb.what FROM members => { prop: Property; atom: ATOM ฌ NARROW[d.cmdData]; prop ฌ PropertyFromAtom[atom, d]; IF ~d.firstTime THEN d.out.PutChar['\n]; d.out.PutF1["\nMembers of \"%g\":\n", [rope[fullName]]]; [] ฌ XNSCH.ListMembers[c, pattern, prop, PutOneName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF ~problems THEN d.out.PutF["\n(%g %g)", [cardinal[count]], [rope[MaintainMisc.Plural["member", "members", count]]]]; }; finks => { start, finish: BasicTime.GMT; startThisFink, finishThisFink: BasicTime.Pulses; membersList: LIST OF Name; allDownList: LIST OF ROPE; AddNameToList: PROC [name: Name] = { membersList ฌ CONS[name, membersList]; count ฌ count + 1; }; ReverseList: PROC [original: LIST OF Name] RETURNS[newList: LIST OF Name]= { FOR list: LIST OF Name ฌ original, list.rest UNTIL list = NIL DO newList ฌ CONS[list.first, newList]; ENDLOOP; }; IF d.firstTime THEN d.firstTime ฌ FALSE ELSE d.out.PutChar['\n]; d.out.PutF1["\nFinks in group \"%g\":", [rope[fullName]]]; start ฌ BasicTime.Now[]; IF d.debugSwitch THEN d.out.PutF1[" [%g]", [time[BasicTime.Now[]]]]; [] ฌ XNSCH.ListMembers[c, pattern, members, AddNameToList ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; d.out.PutF[" (%g %g)", [cardinal[count]], [rope[MaintainMisc.Plural["member", "members", count]]]]; IF ~problems THEN { membersList ฌ ReverseList[membersList]; FOR m: LIST OF Name ฌ membersList, m.rest WHILE m # NIL AND ~d.stop DO IF d.debugSwitch THEN d.out.PutF["\n ... \"%g\" [%g] ...", [rope[XNSCHName.RopeFromName[m.first]]], [time[BasicTime.Now[]]]]; startThisFink ฌ BasicTime.GetClockPulses[]; allDownListฌ PutOneFink[m.first, allDownList, c, d.autoDelete, pattern]; finishThisFink ฌ BasicTime.GetClockPulses[]; IF d.debugSwitch THEN d.out.PutF1[" (took: %g)", [rope[RealSecsToRope[BasicTime.PulsesToSeconds[finishThisFink-startThisFink]]]]]; ENDLOOP; finish ฌ BasicTime.Now[]; IF d.debugSwitch THEN d.out.PutF1["\n (Total: %g)", [rope[PeriodToRope[BasicTime.Period[start, finish]]]]]; }; }; matches => PutOneName[pattern]; summary => GiveNSInfo[summary]; details => GiveNSInfo[details]; domains => { orgNotProvided: ROPE = "orgNotProvided"; pattern ฌ XNSCHName.NameFromRope[name, orgNotProvided ! XNSCHName.FieldTooLong => {RESUME;}]; IF ~Rope.Equal[pattern.domain, orgNotProvided] THEN pattern.organization ฌ pattern.domain; pattern.domain ฌ pattern.object; pattern.object ฌ NIL; d.out.PutF1["\nDomains matching \"%g\": ", [rope[name]]]; XNSCH.EnumerateDomains[c, pattern, PutOneDomainName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF ~problems THEN d.out.PutF["\n(%g %g)", [cardinal[count]], [rope[MaintainMisc.Plural["match", "matches", count]]]]; }; organizations => { pattern.organization ฌ pattern.object; pattern.domain ฌ NIL; pattern.object ฌ NIL; XNSCH.EnumerateOrganizations[c, pattern, PutOneOrganizationName ! XNSCH.Error => {eC ฌ code; eW ฌ which; problems ฌ TRUE; CONTINUE}]; IF ~problems THEN d.out.PutF["\n(%g %g)", [cardinal[count]], [rope[MaintainMisc.Plural["match", "matches", count]]]]; }; domainsServed => { n: CARD ฌ 0; ce: GluePruneHacks.CacheEntry; d.out.PutF1["\n\nDomains served by \"%g\": ", [rope[name]]]; ce ฌ GluePruneHacks.DomainsForServer[name]; d.out.PutF1["\nKey: \"%g\"", [rope[ce.key]]]; d.out.PutF1[", ok: %g", [rope[IF ce.ok THEN "TRUE" ELSE "FALSE"]]]; IF ~ce.ok THEN d.out.PutF1[", reason = %g", [rope[GluePruneHacks.ExposeErrorCode[ce.reason]]]]; d.out.PutRope["\nValue: "]; FOR rl: GluePruneHacks.RopeList ฌ ce.value, rl.rest WHILE rl # NIL DO d.out.PutF["%g\"%g\"", [rope[IF n = 0 THEN "" ELSE ", "]], [rope[rl.first]]]; n ฌ n + 1; ENDLOOP; d.out.PutF1["\nTotal Domains served: %g", [cardinal[n]]]; }; direct, occurrences => { FOR argList: LIST OF ROPE ฌ d.argList, argList.rest WHILE argList # NIL DO memberRopeToCheck: ROPE ฌ argList.first; memberToCheck: Name; yes: BOOL ฌ FALSE; memberToCheck ฌ XNSCHName.NameFromRope[memberRopeToCheck ! XNSCHName.FieldTooLong => {RESUME;}]; problems ฌ FALSE; [ , yes] ฌ XNSCH.IsMember[c, pattern, members, memberToCheck ! XNSCH.Error => {CONTINUE}]; IF ~problems THEN { IF cb.what = direct THEN d.out.PutF["\n\"%g\" IS%g a direct member of \"%g\".", [rope[memberRopeToCheck]], IF yes THEN [rope[""]] ELSE [rope[" NOT"]], [rope[XNSCHName.RopeFromName[pattern]]]] ELSE IF yes THEN { IF d.firstTime THEN d.firstTime ฌ FALSE ELSE d.out.PutRope[", "]; d.out.PutF1["\"%g\"", [rope[name]]]; }; }; ENDLOOP; }; extended => { FOR argList: LIST OF ROPE ฌ d.argList, argList.rest WHILE argList # NIL DO memberRopeToCheck: ROPE ฌ argList.first; memberToCheck: Name; yes: BOOL ฌ FALSE; memberToCheck ฌ XNSCHName.NameFromRope[memberRopeToCheck ! XNSCHName.FieldTooLong => {RESUME;}]; problems ฌ FALSE; [ , yes] ฌ XNSCH.IsMemberClosure[c, pattern, members, memberToCheck ! XNSCH.Error => {CONTINUE}]; IF ~problems THEN d.out.PutF["\n\"%g\" IS%g a member of \"%g\".", [rope[memberRopeToCheck]], IF yes THEN [rope[""]] ELSE [rope[" NOT"]], [rope[XNSCHName.RopeFromName[pattern]]]]; ENDLOOP; }; ENDCASE => NotImplementedYet[d]; IF problems THEN { d.out.PutF1["\nError: %g", [rope[ExposeErrorCode[eC, LOOPHOLE[eW]]]]]; }; XNSCH.TerminateConversation[c]; IF d.stop THEN { IO.PutRope[d.out, "\nStopping...\n"]}; }; NSTimeFromItem: PROC [item: Item, index: CARD ฌ 0] RETURNS [CARD] = { RETURN[ XNSCHItemOps.Card32FromItem[item, index ! XNSCHItemOps.Error => {GOTO OutOfHere}].card ]; EXITS OutOfHere => RETURN[BasicTime.ToNSTime[BasicTime.Now[]]]; }; GetProperties: PROC [name: Name, c: XNSCH.Conversation ฌ NIL] RETURNS [XNSCH.Properties] = { newConversation: BOOL ฌ c = NIL; properties: XNSCH.Properties; IF newConversation THEN c ฌ XNSCH.InitiateConversation[]; [, properties] ฌ XNSCH.GetProperties[c, name ! XNSCH.Error => {CONTINUE}]; IF newConversation THEN XNSCH.TerminateConversation[c]; RETURN[properties]; }; GetExistingMailboxes: PROC [name: Name, c: XNSCH.Conversation ฌ NIL] RETURNS [ropes: LIST OF ROPE] = { newConversation: BOOL ฌ c = NIL; lastRope: LIST OF ROPE; names: LIST OF Name; item: Item; IF newConversation THEN c ฌ XNSCH.InitiateConversation[]; [, item] ฌ XNSCH.LookupItemProperty[c, name, mailboxes ! XNSCH.Error => {CONTINUE}]; names ฌ XNSCHItemOps.NameListFromItem[item, 2 ! XNSCHItemOps.Error => { IF newConversation THEN XNSCH.TerminateConversation[c]; GOTO OutOfHere; }].names; WHILE names # NIL DO IF ropes = NIL THEN lastRope ฌ ropes ฌ LIST[XNSCHName.RopeFromName[name]] ELSE lastRope ฌ lastRope.rest ฌ LIST[XNSCHName.RopeFromName[name]]; names ฌ names.rest; ENDLOOP; IF newConversation THEN XNSCH.TerminateConversation[c]; EXITS OutOfHere => RETURN[NIL]; }; MailboxItemFromList: PROC [list: LIST OF ROPE, time: CARD ฌ BasicTime.ToNSTime[BasicTime.Now[]]] RETURNS [item: Item] = { names: CARD16 ฌ 0; thisName: Name; pos: CARD ฌ 3; FOR l: LIST OF ROPE ฌ list, l.rest WHILE l # NIL DO thisName ฌ XNSCHName.NameFromRope[l.first]; [item, pos] ฌ XNSCHItemOps.ItemFromName[thisName, pos, item]; names ฌ names + 1; ENDLOOP; [] ฌ XNSCHItemOps.Card32IntoItem[item, time, 0]; [] ฌ XNSCHItemOps.Card16IntoItem[item, names, 2]; }; ItemFromListOfCard16Rope: PROC [list: ROPE] RETURNS [item: Item] = { count: INT ฌ 0; number: CARD16; errors: BOOL ฌ FALSE; BumpCount: PROC [r: ROPE] = { number ฌ Convert.CardFromRope[r ! Convert.Error => {errors ฌ TRUE; CONTINUE}]; count ฌ count+1; }; FillInItem: PROC [r: ROPE] = { number ฌ Convert.CardFromRope[r]; item[count] ฌ number; count ฌ count+1; }; MaintainMisc.CallForItems[NEW[MyDataObject], list, BumpCount]; IF errors THEN RETURN[NIL]; item ฌ XNSCHItemOps.CreateNewItem[count]; count ฌ 0; MaintainMisc.CallForItems[NEW[MyDataObject], list, FillInItem]; RETURN[item]; }; AddressListFromRopeList: PROC [list: ROPE] RETURNS [addresses: LIST OF XNS.Address] = { lastAddress: LIST OF XNS.Address; AddAddress: PROC [r: ROPE] = { address: XNS.Address ฌ Convert.XNSAddressFromRope[r]; IF addresses = NIL THEN lastAddress ฌ addresses ฌ LIST[address] ELSE lastAddress ฌ lastAddress.rest ฌ LIST[address]; }; MaintainMisc.CallForItems[NEW[MyDataObject], list, AddAddress]; }; AddRopesToList: PROC [a, b: LIST OF ROPE] RETURNS [LIST OF ROPE] = { new: LIST OF ROPE; FOR a ฌ a, a.rest WHILE a # NIL DO new ฌ CONS[a.first, new]; ENDLOOP; FOR b ฌ b, b.rest WHILE b # NIL DO new ฌ CONS[b.first, new]; ENDLOOP; RETURN[new]; }; RemoveRopesFromList: PROC [a, b: LIST OF ROPE] RETURNS [LIST OF ROPE] = { new: LIST OF ROPE; FOR a ฌ a, a.rest WHILE a # NIL DO IF ~RopeInList[a.first, b] THEN new ฌ CONS[a.first, new]; ENDLOOP; RETURN[new]; }; PutRopeList: PROC [d: MyData, l: LIST OF ROPE] = { first: BOOL ฌ TRUE; FOR l ฌ l, l.rest WHILE l # NIL DO IF first THEN first ฌ FALSE ELSE d.out.PutRope[", "]; d.out.PutF1["%g", [rope[l.first]]]; ENDLOOP; }; PutAddressList: PROC [d: MyData, addressList: LIST OF XNS.Address] = { first: BOOL ฌ TRUE; WHILE addressList # NIL DO d.out.PutF["%g%g", [rope[IF first THEN "" ELSE ", "]], [rope[Convert.RopeFromXNSAddress[addressList.first]]]]; first ฌ FALSE; addressList ฌ addressList.rest; ENDLOOP; }; RopeInList: PROC [r: ROPE, l: LIST OF ROPE] RETURNS [BOOL] = { FOR l ฌ l, l.rest WHILE l # NIL DO IF Rope.Equal[r, l.first, FALSE] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; DoSetServer: PUBLIC PROC [d: MyData, name: ROPE] = { tryCHS: BOOL ฌ FALSE; adr: XNS.Address ฌ XNS.unknownAddress; IF name = NIL OR Rope.Length[name] <= 1 THEN { d.out.PutRope["\nSetting NS server to ALL servers ... ok.\n"]; d.conversationAddress ฌ XNS.unknownAddress; RETURN; }; d.out.PutF1["\nSetting NS server to: %g ... ", [rope[name]]]; adr ฌ Convert.XNSAddressFromRope[name ! Convert.Error => {tryCHS ฌ TRUE; CONTINUE}]; IF tryCHS THEN [, adr] ฌ XNSCH.LookupAddressFromRope[name]; d.out.PutF1[" (%g) ok.\n", [rope[Convert.RopeFromXNSAddress[adr]]]]; d.conversationAddress ฌ adr; }; DistinguishedNameFromRope: PROC [name: ROPE, c: XNSCH.Conversation ฌ NIL] RETURNS [distingName: Name, problems: BOOL, errorCode: XNSCH.ErrorCode, which: XNSCH.Which, tooLong: BOOL] = { [distingName, problems, errorCode, which] ฌ DistinguishedName[XNSCHName.NameFromRope[name ! XNSCHName.FieldTooLong => {tooLong ฌ TRUE; RESUME;}], c]; RETURN; }; DistinguishedName: PROC [name: Name, c: XNSCH.Conversation ฌ NIL] RETURNS [distingName: Name ฌ [NIL, NIL, NIL], problems: BOOL ฌ FALSE, errorCode: XNSCH.ErrorCode ฌ unknown, which: XNSCH.Which ฌ first] = { newConversation: BOOL ฌ c = NIL; IF newConversation THEN c ฌ XNSCH.InitiateConversation[]; distingName ฌ XNSCH.Lookup[c, name ! XNSCH.Error => {errorCode ฌ code; which ฌ which; problems ฌ TRUE; CONTINUE}]; IF newConversation THEN XNSCH.TerminateConversation[c]; }; ExternalMailName: PROC [name: Name, c: XNSCH.Conversation ฌ NIL] RETURNS [BOOL] = { errorCode: XNSCH.ErrorCode; problems: BOOL ฌ FALSE; newConversation: BOOL ฌ c = NIL; dots: ROPE = "..."; obj: ROPE = Rope.Concat[name.domain, name.organization]; IF Rope.Length[obj] > 40 THEN RETURN[FALSE]; IF newConversation THEN c ฌ XNSCH.InitiateConversation[]; [] ฌ XNSCH.Lookup[c, [dots, dots, obj] ! XNSCH.Error => {errorCode ฌ code; problems ฌ TRUE; CONTINUE}]; IF newConversation THEN XNSCH.TerminateConversation[c]; RETURN[~(problems AND errorCode = noSuchObject)]; }; IsGVName: PROC [name: ROPE] RETURNS[BOOLEAN] = { RETURN[name # NIL AND (Rope.Find[name, "@"] < 0)]; }; IsNsName: PROC [name: ROPE] RETURNS [BOOL] = { RETURN[name # NIL]; }; GetPossibleSubList: PROC [dl: Name] RETURNS [name: Name, nameRope: ROPE, changed: BOOL] = { changed ฌ FALSE; nameRope ฌ XNSCHName.RopeFromName[dl]; RETURN[dl, nameRope, changed]; }; NeedsQuotes: PROC [r: ROPE] RETURNS [BOOL] ~ { IF Rope.Length[r] = 0 OR (Rope.Find[r, "\""] < 0 AND Rope.Find[r, " "] >= 0) THEN RETURN [TRUE]; RETURN[FALSE]; }; QuoteIfNeeded: PROC [r: ROPE] RETURNS [ROPE] ~ { IF NeedsQuotes[r] THEN RETURN [Rope.Cat["\"", r, "\""]]; RETURN[r]; }; EndsWith: PROC [a, b: ROPE] RETURNS [BOOL] = { bLength: INT = Rope.Length[b]; aLength: INT = Rope.Length[a]; IF aLength < bLength THEN RETURN[FALSE]; RETURN[Rope.Equal[Rope.Substr[a, aLength - bLength, bLength], b, FALSE]]; }; CheckName: PROC [name: Name, c: XNSCH.Conversation ฌ NIL] RETURNS[BOOL] = { ok: BOOL ฌ TRUE; checkForeign: BOOL ฌ FALSE; newConversation: BOOL ฌ c = NIL; IF newConversation THEN c ฌ XNSCH.InitiateConversation[]; [] ฌ XNSCH.Lookup[c, name ! XNSCH.Error => { SELECT code FROM illegalOrganizationName, illegalDomainName, illegalObjectName, noSuchObject => ok ฌ FALSE; noSuchOrganization, noSuchDomain => checkForeign ฌ TRUE; ENDCASE; CONTINUE}]; IF checkForeign THEN ok ฌ ExternalMailName[name, c]; IF newConversation THEN XNSCH.TerminateConversation[c]; RETURN[ok]; }; CheckNameRope: PROC [name: ROPE, c: XNSCH.Conversation ฌ NIL] RETURNS[BOOL] = { RETURN[CheckName[XNSCHName.NameFromRope[name ! XNSCHName.FieldTooLong => {RESUME;}], c]]; }; PropertyInfoList: TYPE = LIST OF PropertyInfoObject; PropertyInfoObject: TYPE = RECORD [ prop: Property, label: ROPE, shortLabel: ROPE, codeLabel: ROPE, propType: PropType ฌ unknown, subLabels: BOOL ฌ FALSE, detailed: BOOL ฌ FALSE ]; all: Property = 0; alias: Property = 1; aliases: Property = 2; simpleKey: Property = 7; server: Property = 10024; nullProperty: Property = 37777777777B; propertyInfoList: PropertyInfoList ฌ LIST[ [addressList, "Address List", "AddressList", "addressList", addressList], [adobeService, "Adobe Service", "Adobe", "adobeService"], [alias, "Alias", "Alias", "alias"], [aliases, "Aliases", "Aliases", "aliases"], [all, "Any Object", "Any", "all"], [associatedWorkstation, "Associated Workstation", "AWKS", "associatedWorkstation", name], [authenticationLevel, "Authentication Level", "AuthLevel", "authenticationLevel", authenticationLevel, FALSE, TRUE], [authenticationService, "Authentication Service", "Auth", "authenticationService"], [authKeys, "Auth Keys", "AuthKeys", "authKeys", unknown, FALSE, TRUE], [clearinghouseService, "Clearinghouse Service", "CHS", "clearinghouseService"], [communicationsInterfaceUnit, "Communications Interface Unit", "CIU", "communicationsInterfaceUnit"], [externalCommunicationsService, "External Communications Service", "ECS", "externalCommunicationsService"], [fetchService, "Fetch Service", "Fetch", "fetchService"], [fileService, "File Service", "FS", "fileService", rope], [foreignMailSystemName, "Foreign Mail System Name", "ForeignMailSystemName", "foreignMailSystemName", name], [gatewayService, "Gateway Service", "GWS", "gatewayService"], [interactiveTerminalService, "Interactive Terminal Service", "ITS", "interactiveTerminalService"], [internetworkRoutingService, "Internetwork Routing Service", "IRS", "interactiveTerminalService"], [librarianService, "Librarian Service", "Librarian", "librarianService"], [mailboxes, "Mailboxes", "MBX", "mailboxes", mailboxInfo, TRUE], [mailGateway, "Mail Gateway", "MGW", "mailGateway", rope], [mailGatewayRouteData, "Mail Gateway Route Data", "MailGatewayRouteData", "mailGatewayRouteData", group], [mailService, "Mail Service", "MS", "mailService"], [members, "Members", "Members", "members", group], [network, "Network", "Net", "network"], [networkServers, "Network Servers", "NetworkServers", "networkServers"], [nullProperty, "Null Property", "NullProperty", "nullProperty"], [printService, "Print Service", "PS", "printService"], [remoteBatchService, "Remote Batch Service", "RBS", "remoteBatchService"], [rs232CData, "RS232CData", "RS232CData", "rs232CData"], [rs232CPort, "RS232CPort", "RS232CPort", "rs232CPort"], [server, "Server", "Server", "server"], [services, "Services", "Services", "services"], [simpleKey, "Simple Key", "SimpleKey", "simpleKey", unknown, FALSE, TRUE], [user, "User Remark", "User", "user", rope], [userData, "User Data", "UserData", "userData", userData, TRUE], [userGroup, "Group Remark", "Group", "userGroup", rope], [userPassword, "User Password", "Password", "userPassword", rope, FALSE, TRUE], [workstation, "Workstation", "Workstation", "workstation"] ]; HostAddressFromRope: PROC [r: ROPE] RETURNS[XNS.Host] = { RETURN[Convert.XNSHostFromRope[r ! Convert.Error => CONTINUE]]; RETURN[Convert.XNSAddressFromRope[r ! Convert.Error => CONTINUE].host]; RETURN[XNS.unknownHost]; }; HostAddressFromSelection: PROC [d: MyData] RETURNS[XNS.Host] = { sel: ROPE ฌ NARROW[d.moreData]; RETURN[HostAddressFromRope[sel]]; }; MatchHostAddressForObject: PROC [host: XNS.Host, obj: Name] RETURNS[BOOL] = { item: Item; addresses: LIST OF XNS.Address; error: BOOL ฌ FALSE; c: XNSCH.Conversation ฌ XNSCH.InitiateConversation[]; [, item] ฌ XNSCH.LookupItemProperty[c, obj, addressList ! XNSCH.Error => {error ฌ TRUE; CONTINUE}]; XNSCH.TerminateConversation[c]; IF error THEN RETURN[FALSE]; IF item.length > 0 THEN addresses ฌ XNSCHItemOps.AddressListFromItem[item ! XNSCHItemOps.Error => {error ฌ TRUE; CONTINUE}].addresses; IF error THEN RETURN[FALSE]; FOR addresses ฌ addresses, addresses.rest WHILE addresses # NIL DO IF host = addresses.first.host THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; PropertyFromRope: PROC [r: ROPE] RETURNS[Property] = { prop: Property; isNumber: BOOL ฌ TRUE; prop ฌ Convert.CardFromRope[r ! Convert.Error => {isNumber ฌ FALSE; CONTINUE}]; IF isNumber THEN RETURN[prop]; IF HostAddressFromRope[r] # XNS.unknownHost THEN RETURN[addressList]; FOR pi: PropertyInfoList ฌ propertyInfoList, pi.rest WHILE pi # NIL DO IF Rope.Equal[pi.first.label, r, FALSE] OR Rope.Equal[pi.first.shortLabel, r, FALSE] OR Rope.Equal[pi.first.codeLabel, r, FALSE] THEN RETURN[pi.first.prop]; ENDLOOP; r ฌ ReplaceThisRope[r, " ", ""]; r ฌ Rope.Substr[r, 0, Rope.Index[r, 0, "serv", FALSE]]; FOR pi: PropertyInfoList ฌ propertyInfoList, pi.rest WHILE pi # NIL DO IF Rope.Find[pi.first.label, r, 0, FALSE] # -1 OR Rope.Find[pi.first.codeLabel, r, 0, FALSE] # -1 THEN RETURN[pi.first.prop]; ENDLOOP; RETURN[nullProperty]; }; PropertyFromSelection: PROC [d: MyData] RETURNS[Property] = { sel: ROPE ฌ NARROW[d.moreData]; RETURN[PropertyFromRope[sel]]; }; PropertyFromAtom: PROC [atom: ATOM, d: MyData] RETURNS[Property] = { IF atom = $FromSelection THEN RETURN[PropertyFromSelection[d]]; RETURN[PropertyFromRope[Atom.GetPName[atom]]]; }; GetPropertyInfo: PROC [prop: Property] RETURNS [PropertyInfoObject] = { FOR pi: PropertyInfoList ฌ propertyInfoList, pi.rest WHILE pi # NIL DO IF prop = pi.first.prop THEN RETURN[pi.first]; ENDLOOP; RETURN[[prop, Convert.RopeFromCard[prop], Convert.RopeFromCard[prop], NIL, unknown, FALSE, FALSE]]; }; ExposePropertyCode: PROC [prop: Property] RETURNS [ROPE] = { pi: PropertyInfoObject ฌ GetPropertyInfo[prop]; IF pi.codeLabel # NIL THEN RETURN[pi.codeLabel] ELSE RETURN[Rope.Concat["property", pi.shortLabel]]; }; ExposeAuthCallProblem: PROC [problem: AuthenticationP14V2.CallProblem] RETURNS[ROPE] = { RETURN[SELECT problem FROM tooBusy => "tooBusy", accessRightsInsufficient => "accessRightsInsufficient", keysUnavailable => "keysUnavailable", strongKeyDoesNotExist => "strongKeyDoesNotExist", simpleKeyDoesNotExist => "simpleKeyDoesNotExist", strongKeyAlreadyRegistered => "strongKeyAlreadyRegistered", simpleKeyAlreadyRegistered => "simpleKeyAlreadyRegistered", domainForNewKeyUnavailable => "domainForNewKeyUnavailable", domainForNewKeyUnknown => "domainForNewKeyUnknown", badKey => "badKey", badName => "badName", databaseFull => "databaseFull", other => "other", ENDCASE => "UNKNOWN" ]; }; ExposeAuthProblem: PROC [problem: AuthenticationP14V2.Problem] RETURNS[ROPE] = { RETURN[SELECT problem FROM credentialsInvalid => "credentialsInvalid", verifierInvalid => "verifierInvalid", verifierExpired => "verifierExpired", verifierReused => "verifierReused", credentialsExpired => "credentialsExpired", inappropriateCredentials => "inappropriateCredentials", ENDCASE => "UNKNOWN" ]; }; ExposeErrorCode: PROC [arg: XNSCH.ErrorCode, level: NAT] RETURNS [res: ROPE] ~ { SELECT arg FROM notAllowed => res ฌ "notAllowed"; allDown => res ฌ "allDown"; wasUpNowDown => res ฌ "wasUpNowDown"; protocolError => res ฌ "protocolError"; cantConnectToServer => res ฌ "cantConnectToServer"; communicationFailure => res ฌ "communicationFailure"; serverTooBusy => res ฌ "serverTooBusy"; serviceNotExported => res ฌ "serviceNotExported"; illegalPropertyID => res ฌ "illegalPropertyID"; illegalOrganizationName => res ฌ "illegalOrganizationName"; illegalDomainName => res ฌ "illegalDomainName"; illegalObjectName => res ฌ "illegalObjectName"; noSuchOrganization => res ฌ "noSuchOrganization"; noSuchDomain => res ฌ "noSuchDomain"; noSuchObject => res ฌ "noSuchObject"; propertyIDNotFound => res ฌ "propertyIDNotFound"; wrongPropertyType => res ฌ "wrongPropertyType"; noChange => res ฌ "noChange"; outOfDate => res ฌ "outOfDate"; overflowOfName => res ฌ "overflowOfName"; overflowOfDataBase => res ฌ "overflowOfDataBase"; credentialsInvalid => res ฌ "credentialsInvalid"; credentialsTooWeak => res ฌ "credentialsTooWeak"; inappropriateConversation => res ฌ "inappropriateConversation"; unknown => res ฌ "unknown"; ENDCASE => ERROR }; ExposeWhich: PROC [arg: XNSCH.Which, level: NAT] RETURNS [res: ROPE] ~ { SELECT arg FROM first => res ฌ "first"; second => res ฌ "second"; ENDCASE => ERROR }; ReplaceThisRope: PROC [r, d, n: ROPE, all: BOOL ฌ TRUE, case: BOOL ฌ TRUE, pos: INT ฌ 0] RETURNS [ROPE] = { ld: INT ฌ Rope.Length[d]; ln: INT ฌ Rope.Length[n]; DO l: INT ฌ Rope.Length[r]; fi: INT ฌ Rope.Find[r, d, pos, case]; IF fi = -1 THEN EXIT; r ฌ Rope.Replace[r, fi, ld, n]; pos ฌ fi + ln; IF ~all THEN EXIT; ENDLOOP; RETURN[r]; }; NotImplementedYet: PROC [d: MyData] = { d.out.PutRope["\nSorry, that function not implemented yet..."]; }; RealSecsToRope: PROC [seconds: REAL] RETURNS [r: ROPE] = { intSeconds: INT ฌ Real.Fix[seconds]; mSecs: INT ฌ Real.Fix[(seconds - REAL[intSeconds]) * 1000]; r ฌ PeriodToRope[intSeconds]; r ฌ Rope.Cat[ r, ", ", Convert.RopeFromInt[mSecs], MaintainMisc.Plural[" mSec", " mSecs", mSecs]]; }; PeriodToRope: PROC [period: INT] RETURNS [r: ROPE] = { up: BasicTime.UnpackedPeriod ฌ BasicTime.UnpackPeriod[period]; IF up.hours > 0 THEN r ฌ Rope.Concat[ Convert.RopeFromInt[up.hours], MaintainMisc.Plural[" hour,", " hours,", up.hours]]; IF up.hours > 0 OR up.minutes > 0 THEN r ฌ Rope.Cat[ r, " ", Convert.RopeFromInt[up.minutes], MaintainMisc.Plural[" minute,", " minutes,", up.minutes]]; r ฌ Rope.Cat[ r, " ", Convert.RopeFromInt[up.seconds], MaintainMisc.Plural[" second", " seconds", up.seconds]]; }; }. VMaintainNSImpl.mesa -- From MaintainImpl.mesa Copyright ำ 1987, 1989, 1990, 1991, 1992 by Xerox Corporation. All rights reserved. Wes Irish, July 21, 1989 12:13:18 pm PDT Bill Jackson (bj) January 22, 1990 6:30:11 pm PST Bryan Lyles, November 8, 1990 1:03 pm PST Philip James, September 12, 1991 11:36 am PDT Willie-s, January 7, 1992 4:48 pm PST Michael Plass, March 11, 1992 1:50 pm PST Doug Wyatt, May 15, 1992 1:08 pm PDT ******** Enquiry operations ******** -- This is the simple case, no wildcards Wildcards present, enumerate all names... -- we set check to FALSE because friends should be able to be things like "*:*:Xerox" Notice that we don't automatically distinguish the name... Notice that we don't automatically distinguish the name... Returns FALSE iff the name could not possibly be a valid ExternalMailName. In other words, a returned value of TRUE does not guarantee that the name is a valid ExternalMailName. IF ~EndsWith[dl.object, "-NS"] THEN { tempName: Name _ dl; tempName.object _ Rope.Cat[dl.object, "-NS"]; IF CheckName[tempName] THEN { dl _ tempName; changed _ TRUE; }; }; สGต•NewlineDelimiter –(cedarcode) style˜codešœฯc™-Kšœ ฯeœH™TKšœ(™(K™1K™*K™-K™%K™)K™$K™—šฯk ˜ KšœŸœ ˜KšœŸœ˜2Kšœ ŸœŸœQ˜Kšœ˜Kšœ Ÿœ˜KšœŸœz˜‡KšœŸœ;˜OKšŸœŸœ"˜*Kšœ Ÿœ?˜QKšœŸœ˜'Kšœ Ÿœ>˜PKšœ ˜ KšœŸœ˜%KšœŸœ˜KšœŸœBŸœ ˜[KšŸœŸœ>˜GKšœŸœล˜าKšŸœŸœิ˜฿KšŸœŸœ]˜kKšœ Ÿœู˜๋Kšœ Ÿœ,˜;—K˜š ฯnœŸœŸœŸœŸœ ˜5KšŸœ+Ÿœ<ŸœŸœ˜˜KšŸœ˜KšŸœ˜KšŸœŸœŸœ˜K˜Kšœ#˜#Kšœ/˜/Kšœ˜K˜KšœŸœŸœ˜Kšœ ŸœŸœ˜Kšœ ŸœŸœŸœ ˜!K˜KšœŸœŸœ˜Kšœ ŸœŸœ ˜'Kšœ Ÿœ˜"K˜Kšœ ŸœY˜gK˜Kšœ ŸœŸœ˜—K˜Kšœ%™'˜š  œŸœŸœ5Ÿœ˜TšŸœŸ˜Kšœ3˜3Kšœ=˜=KšŸœŸœ˜—K˜K˜—š  œŸœŸœ8Ÿœ˜Yšะbn œŸœ8Ÿœ˜PšกœŸœ˜'KšœŸœ˜KšœŸœ˜$KšœŸœ˜&KšŸœ Ÿœ˜$Kšœ˜—šกœŸœ˜*KšœŸœ˜KšœŸœ˜*Kšœ Ÿœ ˜KšŸœ Ÿœ˜$Kšœ˜—šกœŸœ˜(KšœŸœ˜KšœŸœ˜&Kšœ Ÿœ ˜Kšœ˜—šก œŸœŸœ˜DšŸœ&ŸœŸœŸ˜˜>KšŸœ˜—K˜—K˜š œŸœ5Ÿœ˜Sš œŸœ˜šŸœŸ˜˜Kš œŸœŸœŸœ Ÿœ ˜?Kšœ!˜!Kšœ Ÿœ˜KšœŸœ˜!K˜5K˜7K˜.šŸœ Ÿœ˜K˜nKšŸœ˜K˜—K˜9K˜2šŸœ Ÿœ˜K˜jKšŸœ˜K˜—Kšœ3Ÿœ˜:šŸœŸœŸ˜šœ0˜0šŸœ Ÿœ˜Kšœภ˜ภKšŸœ˜K˜—KšœV˜V˜Ušœ"˜"K˜?Kšœ Ÿœ˜KšŸ˜Kšœ˜—šœ˜K˜CKšœ Ÿœ˜KšŸ˜Kšœ˜—Kšœ˜—K˜—šŸœ˜ K˜RK˜;šŸœ˜šŸœ0˜4šœ"˜"K˜;Kšœ Ÿœ˜KšŸ˜Kšœ˜—šœ˜K˜?Kšœ Ÿœ˜KšŸ˜Kšœ˜—Kšœ˜—šŸœ0˜4šœ"˜"K˜;Kšœ Ÿœ˜KšŸ˜Kšœ˜—šœ˜K˜?Kšœ Ÿœ˜KšŸ˜Kšœ˜—K˜——Kšœ Ÿœ˜K˜6šŸœ˜šŸœU˜Yšœ"˜"K˜;Kšœ Ÿœ˜KšŸ˜Kšœ˜—šœ˜K˜?Kšœ Ÿœ˜KšŸ˜Kšœ˜—K˜—šŸœU˜Yšœ"˜"K˜;Kšœ Ÿœ˜KšŸ˜Kšœ˜—šœ˜K˜?Kšœ Ÿœ˜KšŸ˜Kšœ˜—K˜——K˜——šŸœ ˜ KšŸœ˜KšŸœ˜—K˜—KšŸœ˜!—K˜—š  œŸœ˜š  œŸœ Ÿœ&Ÿœ Ÿœ˜všŸœŸ˜˜šŸœ Ÿ˜K˜T—šŸœ˜KšœW˜WšœŸœ˜'KšœŸœ-ŸœŸœ˜E—K˜—K˜—˜ šŸœ Ÿ˜K˜X—šŸœ˜K˜AšœŸœ˜"KšœŸœ-ŸœŸœ˜E—K˜—K˜—KšŸœ˜ —šŸœ Ÿœ˜Kšœ6Ÿœ ˜GKšœ Ÿœ˜K˜—KšŸœ˜K˜—Kšœ(ŸœŸœ˜5K˜K˜—šกœŸœ˜)Kšœe˜ešœŸœ˜)KšœŸœ-ŸœŸœ˜E—K˜K˜—šก œŸœ˜šŸœ Ÿ˜šœ˜KšœT˜TšœŸœL˜VKšœŸœ-ŸœŸœ˜E—K˜—šœ˜KšœT˜TšœŸœQ˜[KšœŸœ-ŸœŸœ˜E—K˜—KšŸœ˜ —šŸœ Ÿœ˜Kšœ6Ÿœ ˜GKšœ Ÿœ˜K˜—šŸœ˜K˜K˜—K˜—šกœŸœ˜KšœŸœŸœ ˜šกœŸœŸœ˜=Kšœ_˜_Kšœ Ÿœ˜šœŸœJ˜TKšœŸœ-ŸœŸœ˜E—šŸœ Ÿœ˜Kšœ Ÿœ˜šœŸœM˜WKšœŸœ-ŸœŸœ˜E—K˜—K˜—šกœŸœŸœ˜EKšœ ˜ K˜Kšœ_˜_˜'KšœŸœ˜&—K˜3Kšœ Ÿœ˜šœŸœ&˜0KšœŸœ-ŸœŸœ˜E—šŸœ Ÿœ˜Kšœ Ÿœ˜šœŸœ)˜3KšœŸœ-ŸœŸœ˜E—K˜—K˜—šก  กœŸœŸœ˜MK˜-Kšœ Ÿœ˜šŸœŸœŸœ˜K˜FKšŸœ˜K˜—Kšœ_˜_šœŸœ&˜0KšœŸœ-ŸœŸœ˜E—šŸœ Ÿœ˜Kšœ Ÿœ˜šœŸœ)˜3KšœŸœ-ŸœŸœ˜E—K˜—K˜—šŸœŸ˜Kšœ)˜)˜Kšœ ŸœŸœŸœ ˜˜(KšœŸœŸœ˜0—šŸœ Ÿœ˜K˜TKšŸœ˜Kšœ˜—K˜:Kšœ˜K˜Kšœ Ÿœ˜šœŸœ_˜iKšœŸœ-ŸœŸœ˜E—šŸœ Ÿœ˜Kšœ Ÿœ˜šœŸœ\˜fKšœŸœ-ŸœŸœ˜E—K˜—KšœŸœ ŸœŸœ ˜BšŸ˜KšœŸœ˜—K˜—K˜MK˜JK˜ZK˜:K˜AKšœn˜nKšœx˜xKšœ€˜€Kšœ˜KšŸœ˜ —šŸœ Ÿœ˜Kšœ6Ÿœ ˜GKšœ Ÿœ˜K˜—šŸœ˜K˜K˜—K˜—šก œŸœ˜š กœŸœ Ÿœ&Ÿœ Ÿœ˜vK˜Kšœ Ÿœ˜Kšœ ŸœŸœ˜šŸœ ŸœŸ˜"K˜=—šŸœŸ˜˜šŸœ Ÿœ˜KšœZ˜ZKšŸœ ŸœL˜[šœŸœ(˜2KšœŸœ-ŸœŸœ˜E—K˜—šŸœ˜Kšœt˜tKšŸœ ŸœL˜[šœŸœ%˜/KšœŸœ-ŸœŸœ˜E—K˜—K˜—˜ Kšœ[˜[KšŸœ ŸœL˜[šœŸœ(˜2KšœŸœ-ŸœŸœ˜E—šŸœ Ÿœ˜K˜=šŸœ Ÿœ˜K˜6K˜NšœŸœ+˜5KšœŸœ-ŸœŸœ˜E—K˜—K˜—K˜—KšŸœ˜ —šŸœ Ÿœ˜Kšœ6Ÿœ ˜GKšœ Ÿœ˜K˜—KšŸœ˜K˜—KšœE˜EK˜—šก œŸœ˜š กœŸœ Ÿœ&Ÿœ Ÿœ˜ušŸœŸ˜˜šŸœ Ÿœ˜Kšœi˜išœŸœG˜TKšœŸœ-ŸœŸœ˜E—K˜—šŸœ˜Kšœƒ˜ƒšœŸœD˜QKšœŸœ-ŸœŸœ˜E—K˜—K˜—˜ Kšœj˜jšœŸœI˜VKšœŸœ-ŸœŸœ˜E—šŸœ Ÿœ˜K˜=šŸœ Ÿœ˜K˜NšœŸœL˜YKšœŸœ-ŸœŸœ˜E—K˜—K˜—K˜—KšŸœ˜ —šŸœ Ÿœ˜Kšœ6Ÿœ ˜GKšœ Ÿœ˜K˜—KšŸœ˜K˜—KšœD˜DK˜—šก œŸœ˜š กœŸœ Ÿœ&Ÿœ Ÿœ˜všŸœŸ˜˜šŸœ Ÿœ˜Kšœl˜lšœŸœD˜QKšœŸœ-ŸœŸœ˜E—K˜—šŸœ˜Kšœ†˜†šœŸœA˜NKšœŸœ-ŸœŸœ˜E—K˜—K˜—˜ Kšœm˜mšœŸœF˜SKšœŸœ-ŸœŸœ˜E—šŸœ Ÿœ˜K˜=šŸœ Ÿœ˜K˜NšœŸœI˜VKšœŸœ-ŸœŸœ˜E—K˜—K˜—K˜—KšŸœ˜ —šŸœ Ÿœ˜Kšœ6Ÿœ ˜GKšœ Ÿœ˜K˜—KšŸœ˜K˜—Kšะcs=™UKšœ)Ÿœ˜>K˜—šก œŸœ˜Kšœ˜Kšœ Ÿœ˜Kšœ ŸœŸœ˜K˜=šŸœŸ˜˜K˜9KšŸœ ŸœL˜[šœŸœ˜&KšœŸœ-ŸœŸœ˜E—K˜—˜ K˜=KšŸœ ŸœL˜[šœŸœ˜)KšœŸœ-ŸœŸœ˜E—K˜—KšŸœ˜ —šŸœ Ÿœ˜Kšœ6Ÿœ ˜GKšœ Ÿœ˜K˜—šŸœ˜K˜K˜—K˜—šกœŸœ˜šŸœŸ˜Kšœ˜Kšœ˜KšŸœ˜ —K˜—šก œŸœ˜Kšœ Ÿœ'˜8Kšœ ˜ Kš œŸœŸœŸœŸœ˜Kš œŸœŸœŸœŸœ˜&šŸœ#˜%KšŸœ4˜8—šŸœ˜K˜2šœŸœ;Ÿœ˜IKšœŸœ-ŸœŸœ˜E—K˜—š ŸœŸœŸœŸœŸœŸœŸ˜8Kšœ˜Kšœ Ÿœ˜K˜AšŸœ Ÿ˜K˜.—KšœŸœ+˜5KšŸœ˜—šŸœŸ˜šœ˜K˜;Kšœ˜—šœ ˜ K˜FK˜@K˜—šŸœ˜ Kšœ˜KšŸœ˜K˜——šŸœŸœŸœ˜!K˜EšœŸœ$˜.KšœŸœ-ŸœŸœ˜EK˜——šŸœ˜K˜3Kšœ"˜"K˜K˜.šœŸœ.˜8KšœŸœ-ŸœŸœ˜E—K˜—K˜—šกœŸœ˜Kš œŸœŸœŸœ˜Cš  œŸœŸœŸœ˜?šœŸœ˜,KšœŸœ-ŸœŸœ˜E—KšŸœ ˜K˜—šกœŸœŸœŸœ˜9šก œŸœ˜ K˜Kšœ˜—KšœŸœ˜šœŸœ)˜3KšœŸœ Ÿœ˜—KšŸœ ˜Kšœ˜—Kšœ Ÿœ$˜5šœ)˜)šœŸœŸ˜Kšœ˜K˜KšŸœ ˜—šœŸœŸ˜Kšœ ˜ K˜KšŸœ ˜—Kšœ˜—šŸœ Ÿœ˜(K˜"KšŸœ˜K˜—šŸœŸ˜šœ˜šŸœ$Ÿœ˜,K˜PKšœ˜—šŸœ#˜%KšŸœ4˜8—šŸœ˜K˜,šœŸœN˜XKšœŸœ-ŸœŸœ˜E—Kšœ˜—šŸœ!˜#KšŸœ6˜:—šŸœ˜K˜.šœŸœ=˜GKšœŸœ-ŸœŸœ˜E—K˜—K˜\Kšœ˜—šœ ˜ šŸœ"˜$KšŸœ4˜8šŸœ˜šŸœ˜šŸœ˜K˜TKšŸœ˜K˜—šŸœ˜K˜0šœŸœ"˜,KšœŸœ-ŸœŸœ˜E—Kšœ˜—K˜———šŸœ$˜&KšŸœ2˜6šŸœ˜K˜.šœŸœ$˜.KšœŸœ-ŸœŸœ˜E—Kšœ˜——K˜dKšœ˜—KšŸœ˜ —K˜—šก œŸœ˜šกœŸœ˜%K˜;K˜XK˜0šœŸœ(˜2KšœŸœ-ŸœŸœ˜E—K˜—šŸœ Ÿ˜šœ˜KšœŸœŸœ ˜šŸœŸ˜˜Kšœ Ÿœ ˜˜)KšœŸœŸœ˜0—šŸœ Ÿœ˜K˜TKšŸœ˜Kšœ˜—KšœŸœ˜#K˜QK˜&K˜AšœŸœ˜KšœŸœ-ŸœŸœ˜E—KšŸœ ŸœŸœ˜#K˜UšœŸœJŸœ˜jKšœŸœ-ŸœŸœ˜E—KšŸœ ŸœŸœ˜#K˜0šœŸœ;Ÿœ˜PKšœŸœ-ŸœŸœ˜E—KšŸœ ŸœŸœ˜#K˜;šœŸœ2Ÿœ˜XKšœŸœ-ŸœŸœ˜E—KšœŸœ ŸœŸœ ˜BšŸ˜KšœŸœ˜—K˜—šœ˜K˜:šœŸœ˜KšœŸœ-ŸœŸœ˜E—K˜—šŸœ˜ K˜>šœŸœ˜KšœŸœ-ŸœŸœ˜E—šŸœ ŸœŸœI˜eKšœŸœ-ŸœŸœ˜E—KšŸœ Ÿœ˜)K˜——K˜—˜Kš œŸœŸœŸœ˜CK˜9šœŸœ˜KšœŸœ-ŸœŸœ˜E—šŸœ ŸœŸœN˜jKšœŸœ-ŸœŸœ˜E—šŸœ ŸœŸœ=˜YKšœŸœ-ŸœŸœ˜E—K˜—KšŸœ˜ —šŸœ Ÿœ˜Kšœ6Ÿœ ˜GKšœ Ÿœ˜K˜—šŸœ˜K˜K˜—K˜—šก œŸœ˜K˜:šœŸœ˜KšœŸœ-ŸœŸœ˜E—šŸœ Ÿœ˜Kšœ6Ÿœ ˜GKšœ Ÿœ˜K˜—šŸœ˜K˜K˜—K˜—KšœŸœŸœ9˜VKšœŸœ ˜KšœŸœ˜Kšœ ŸœŸœ˜Kšœ˜K˜KšœŸœ˜ šŸœŸœ ŸœŸœ˜!KšœŸœ ˜K˜!K˜—˜$KšœŸœ˜'—˜'KšœŸœ˜'—šŸœŸœŸœŸœ˜XK˜RKšŸœ˜KšŸœ˜Kšœ˜—šŸœ Ÿ˜K˜%˜ šŸœ˜šŸœ˜šŸœ˜KšŸœ˜KšŸœ*˜.—K˜—KšŸœ˜—K˜—K˜Kšœ0˜0Kšœ"˜"K˜K˜K˜Kšœ˜Kšœ!˜!Kšœ#˜#Kšœ0˜0K˜KšŸœ˜ —KšŸœ˜K˜K˜—š ก œŸœŸœ ŸœŸœ˜SšŸœŸœŸœŸ˜-KšŸœŸœŸœŸœ˜+KšŸœ˜—KšŸœŸœ˜K˜K˜—š  œŸœ5Ÿœ˜Qš  œŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜กšก œŸœŸœŸœŸœŸœŸœŸœ˜>šŸœŸœŸœŸ˜"Kš ŸœŸœŸœŸœŸœ˜3KšŸœ˜—KšŸœŸœ˜Kšœ˜—KšœŸœŸœ˜KšœŸœ ˜(šŸœ Ÿœ˜Kšœ ŸœŸœ˜KšœŸœ ˜KšœŸœ˜Kšœ Ÿœ ˜KšœŸœ1˜IšŸœ,˜.KšŸœŸœ˜$šŸœŸœ˜1KšœŸœ-ŸœŸœ˜E——šŸœŸœŸ˜šœ ŸœŸ˜K˜>˜FšŸœ˜šŸœ˜˜8Kšœ ˜ —K˜—šŸœ˜šœ˜Kšœ ˜ KšœŸœ ˜+—KšœŸœ˜K˜——K˜—šŸœ˜ šŸœŸ˜KšŸœ.ŸœŸœ"˜i—šœ˜Kšœ ˜ KšœŸœ ˜+—K˜——šœ$˜$šŸœ&˜)K˜@——KšŸœ˜—K˜—šŸœŸœ ŸœŸœ˜(Kšœ Ÿœ˜K˜"šœŸœ%˜/KšœŸœ-ŸœŸœ˜E—šŸœ Ÿœ˜Kšœ6Ÿœ ˜GšŸœŸœ˜!K˜Eš ŸœŸœŸœ Ÿœ Ÿ˜+Kšœ)˜)KšŸœ˜—KšŸœŸœŸœ˜KšŸœ˜Kšœ˜—Kšœ˜KšŸœ˜ —KšŸœ˜KšœŸœ˜ —KšœŸœ˜KšŸœ˜Kšœ˜—š  œŸœ˜!šŸœ ŸœŸœ˜5KšŸœ˜KšŸœŸœŸœ˜.—KšœŸœ˜K˜KšŸœŸœŸœŸœ˜K˜)K˜ KšœŸœ"˜?KšŸœ˜ Kšœ˜K˜—šกœŸœŸœŸœ ŸœŸœŸœ ˜WKšœ ŸœŸœŸœ ˜!šก œŸœŸœ˜Kšœ Ÿœ)˜5šŸœ Ÿ˜KšŸœŸœ ˜,KšŸœ"Ÿœ ˜4—K˜—KšœŸœ"˜?Kšœ˜K˜—šกœŸœŸœŸœŸœŸœŸœŸœŸœ˜DKšœŸœŸœŸœ˜šŸœŸœŸœŸ˜"KšœŸœ˜KšŸœ˜—šŸœŸœŸœŸ˜"KšœŸœ˜KšŸœ˜—KšŸœ˜ Kšœ˜K˜—šกœŸœŸœŸœŸœŸœŸœŸœŸœ˜IKšœŸœŸœŸœ˜šŸœŸœŸœŸ˜"KšŸœŸœŸœ˜9KšŸœ˜—KšŸœ˜ Kšœ˜K˜—š ก œŸœŸœŸœŸœ˜2KšœŸœŸœ˜šŸœŸœŸœŸ˜"šŸœ˜KšŸœ Ÿ˜KšŸœ˜—K˜#KšŸœ˜—Kšœ˜K˜—š กœŸœŸœŸœŸœ ˜FKšœŸœŸœ˜šŸœŸœŸ˜KšœŸœŸœŸœ@˜nKšœŸœ˜K˜KšŸœ˜—Kšœ˜K˜—šก œŸœŸœŸœŸœŸœŸœŸœ˜>šŸœŸœŸœŸ˜"Kš ŸœŸœŸœŸœŸœ˜3KšŸœ˜—KšŸœŸœ˜Kšœ˜K˜—š  œŸœŸœŸœ˜4KšœŸœŸœ˜KšœŸœ Ÿœ˜&šŸœŸœŸœŸœ˜.K˜>KšœŸœ˜+KšŸœ˜K˜—K˜=˜%KšœŸœŸœ˜.—KšŸœŸœ Ÿœ˜;K˜DK˜Kšœ˜K˜—šกœŸœŸœŸœŸœŸœŸœ ŸœŸœŸœ˜ธ˜YKšœ'ŸœŸœ˜;—KšŸœ˜Kšœ˜K˜—šกœŸœŸœŸœŸœŸœŸœŸœ ŸœŸœ ŸœŸœ˜อKšœŸœŸœ˜ KšŸœŸœŸœ˜9šœŸœ˜"KšœŸœ7ŸœŸœ˜O—KšŸœŸœŸœ˜7Kšœ˜K˜—š กœŸœŸœŸœŸœŸœ˜SKšœŸœcŸœ ฯb œ+™ฒKšœ Ÿœ ˜Kšœ ŸœŸœ˜KšœŸœŸœ˜ KšœŸœ ˜KšœŸœ/˜8KšŸœŸœŸœŸœ˜,KšŸœŸœŸœ˜9šœŸœ˜&KšœŸœ(ŸœŸœ˜@—KšŸœŸœŸœ˜7KšŸœ Ÿœ˜1Kšœ˜K˜—š  œŸœŸœŸœŸœ˜0KšŸœŸœŸœ˜2Kšœ˜K˜—š  œŸœŸœŸœŸœ˜.KšŸœŸœ˜Kšœ˜K˜—š กœŸœ ŸœŸœ Ÿœ˜[Kšœ Ÿœ˜šŸœŸœ™%Kšœ™Kšœ-™-šŸœŸœ™Kšœ™Kšœ Ÿœ™K™—Kšœ™—K˜&KšŸœ˜Kšœ˜K˜—š   œŸœŸœŸœŸœ˜.Kš ŸœŸœŸœŸœŸœŸœ˜`KšŸœŸœ˜Kšœ˜K˜—š   œŸœŸœŸœŸœ˜0KšŸœŸœŸœ˜8KšŸœ˜ Kšœ˜K˜—š  œŸœŸœŸœŸœ˜.Kšœ Ÿœ˜Kšœ Ÿœ˜KšŸœŸœŸœŸœ˜(KšŸœ;Ÿœ˜IKšœ˜K˜—š ก œŸœŸœŸœŸœŸœ˜KKšœŸœŸœ˜KšœŸœŸœ˜KšœŸœŸœ˜ KšŸœŸœŸœ˜9šœŸœ˜šœŸœ ˜šŸœŸ˜KšœTŸœ˜ZKšœ3Ÿœ˜8KšŸœ˜—KšŸœ˜ ——šŸœ ˜KšŸœ ˜$—KšŸœŸœŸœ˜7KšŸœ˜ Kšœ˜K˜—šก œŸœŸœŸœŸœŸœŸœ˜OšŸœ&˜,KšœŸœ ˜,—Kšœ˜K˜—KšœŸœŸœŸœ˜4šœŸœŸœ˜#Kšœ˜KšœŸœ˜ Kšœ Ÿœ˜Kšœ Ÿœ˜K˜Kšœ ŸœŸœ˜Kšœ ŸœŸ˜Kšœ˜—K˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ&˜&K˜šœ%Ÿœ˜*KšœI˜IKšœ9˜9Kšœ#˜#Kšœ+˜+Kšœ"˜"KšœY˜YKšœgŸœŸœ˜tKšœS˜SKšœ9ŸœŸœ˜FKšœO˜OKšœe˜eKšœk˜kKšœ9˜9Kšœ9˜9Kšœl˜lKšœ=˜=Kšœb˜bKšœb˜bKšœI˜IKšœ:Ÿœ˜@Kšœ:˜:Kšœi˜iKšœ3˜3Kšœ2˜2Kšœ'˜'KšœH˜HKšœ@˜@Kšœ6˜6KšœJ˜JKšœ7˜7Kšœ7˜7Kšœ'˜'Kšœ/˜/Kšœ=ŸœŸœ˜JKšœ,˜,Kšœ:Ÿœ˜@Kšœ8˜8KšœBŸœŸœ˜OKšœ:˜:K˜K˜—K˜š กœŸœŸœŸœŸœ ˜9KšŸœ.Ÿœ˜?KšŸœ1Ÿœ˜GKšŸœŸœ˜Kšœ˜K˜—šกœŸœ ŸœŸœ ˜@KšœŸœŸœ ˜KšŸœ˜!Kšœ˜—K˜š  œŸœŸœŸœŸœ˜MKšœ ˜ Kšœ ŸœŸœŸœ ˜KšœŸœŸœ˜KšœŸœŸœ˜5šœ Ÿœ'˜7KšœŸœŸœŸœ˜+—KšŸœ˜KšŸœŸœŸœŸœ˜šŸœŸœ2˜IKšœ!ŸœŸœ ˜<—KšŸœŸœŸœŸœ˜šŸœ'Ÿœ ŸœŸ˜BKšŸœŸœŸœŸœ˜1KšŸœ˜—KšŸœŸœ˜Kšœ˜K˜—šกœŸœŸœŸœ˜6Kšœ˜Kšœ ŸœŸœ˜˜KšœŸœŸœ˜1—KšŸœ ŸœŸœ˜KšŸœŸœ ŸœŸœ˜EšŸœ2ŸœŸœŸ˜FšŸœŸœ˜'KšŸœ$Ÿœ˜,KšŸœ#Ÿœ˜+KšŸœŸœ˜—KšŸœ˜—K˜ Kšœ/Ÿœ˜7šŸœ2ŸœŸœŸ˜FšŸœ!Ÿœ˜.KšŸœ%Ÿœ˜2KšŸœŸœ˜—KšŸœ˜—KšŸœ˜Kšœ˜K˜—šกœŸœ Ÿœ˜=KšœŸœŸœ ˜KšŸœ˜Kšœ˜K˜—šกœŸœŸœ Ÿœ˜DKšŸœŸœŸœ˜?KšŸœ(˜.Kšœ˜K˜—šกœŸœŸœ˜GšŸœ2ŸœŸœŸ˜FKšŸœŸœŸœ ˜.KšŸœ˜—KšŸœ@Ÿœ ŸœŸœ˜cKšœ˜K˜—šกœŸœŸœŸœ˜šŸœŸ˜˜Kšœ˜Kšœ4˜4——šŸœŸœŸ˜&˜ K˜K˜Kšœ ˜ Kšœ:˜:——˜ K˜K˜Kšœ ˜ Kšœ8˜8—Kšœ˜K˜——Kšœ˜—…—ฒQฝ