DIRECTORY ConvertUnsafe USING [ToRope], GVBasics USING [Connect, GVString, maxGVStringLength, maxRNameLength, oldestTime, Password, Remark, RName, Timestamp], GVLocate USING [FindRegServer, FoundServerInfo], GVNames USING [ AuthenticateInfo, ConnectInfo, ExpandInfo, GetEntryInfo, GetEntryList, ListType, MembershipGrade, MemberInfo, MembershipLevel, Membership, NameType, Outcome, RemarkInfo, ReporterProc, RListHandle, SetServerInfo, StampInfo], GVProtocol USING [Close, CreateStream, Enquire, Failed, GetSocket, Handle, IsLocal, ReceiveBoolean, ReceiveByte, ReceiveConnect, ReceiveCount, ReceivePassword, ReceiveRC, ReceiveRemark, ReceiveRList, ReceiveRName, ReceiveTimestamp, ReturnCode, RNameType, RSOperation, SendByte, SendNow, SendPassword, SendRName, SendRSOperation], PupDefs USING [GetPupAddress, NameLookupErrorCode, PupAddress, PupAddressToRope, PupNameTrouble], Rope USING [Equal, Find, Length, ROPE, Substr]; GVNamesImpl: CEDAR MONITOR IMPORTS ConvertUnsafe, GVLocate, GVProtocol, PupDefs, Rope EXPORTS GVNames = BEGIN Expand: PUBLIC PROC[name: GVBasics.RName, oldStamp: GVBasics.Timestamp _ GVBasics.oldestTime, reporter: GVNames.ReporterProc _ NIL] RETURNS[ GVNames.ExpandInfo ] = BEGIN info: GVNames.NameType; list: GVNames.RListHandle; count: INT; stamp: GVBasics.Timestamp; [info, stamp, list, count] _ GetCompound[name, oldStamp, Expand, reporter]; SELECT info FROM noChange => RETURN[ [noChange[]] ]; group => RETURN[ [group[list, stamp, count]] ]; individual => RETURN[ [individual[list, stamp, count]] ]; notFound => RETURN[ [notFound[]] ]; protocolError => RETURN[ [protocolError[]] ]; wrongServer => RETURN[ [wrongServer[]] ]; allDown => RETURN[ [allDown[]] ]; ENDCASE => RETURN[ [protocolError[]] ]; END; GetList: PUBLIC PROC[name: GVBasics.RName, oldStamp: GVBasics.Timestamp, list: GVNames.ListType, reporter: GVNames.ReporterProc _ NIL] RETURNS[ GVNames.MemberInfo ] = BEGIN info: GVNames.NameType; members: GVNames.RListHandle; count: INT; stamp: GVBasics.Timestamp; [info, stamp, members, count] _ GetCompound[name, oldStamp, SELECT list FROM members => ReadMembers, owners => ReadOwners, friends => ReadFriends, ENDCASE => ERROR, reporter]; SELECT info FROM noChange => RETURN[ [noChange[]] ]; group => RETURN[ [group[members, stamp, count]] ]; individual => RETURN[ [individual[]] ]; notFound => RETURN[ [notFound[]] ]; protocolError => RETURN[ [protocolError[]] ]; wrongServer => RETURN[ [wrongServer[]] ]; allDown => RETURN[ [allDown[]] ]; ENDCASE => RETURN[ [protocolError[]] ]; END; ReceiveRList: PROC[str: GVProtocol.Handle] RETURNS[list: GVNames.RListHandle, count: INT] = BEGIN tail: GVNames.RListHandle _ NIL; ReceiveRListWork: PROC[name: GVBasics.RName] = BEGIN new: GVNames.RListHandle = CONS[first: name, rest: NIL]; IF tail = NIL THEN list _ new ELSE tail.rest _ new; tail _ new; count _ count+1; END; list _ NIL; count _ 0; GVProtocol.ReceiveRList[str, ReceiveRListWork]; END; ReceiveStampList: PROC[str: GVProtocol.Handle] RETURNS[list: LIST OF GVBasics.Timestamp] = BEGIN words: CARDINAL _ GVProtocol.ReceiveCount[str]; tail: LIST OF GVBasics.Timestamp _ NIL; list _ NIL; WHILE words > 0 DO stamp: GVBasics.Timestamp = GVProtocol.ReceiveTimestamp[str]; words _ words - SIZE[GVBasics.Timestamp]; IF tail = NIL THEN { list _ CONS[stamp,NIL]; tail _ list } ELSE { tail.rest _ CONS[stamp,NIL]; tail _ tail.rest }; ENDLOOP; END; GetCompound: PROC[name: GVBasics.RName, stamp: GVBasics.Timestamp, op: GVProtocol.RSOperation, reporter: GVNames.ReporterProc _ NIL] RETURNS[info: GVNames.NameType, newStamp: GVBasics.Timestamp, list: GVNames.RListHandle, count: INT] = BEGIN GetCompoundWork: PROC[str: GVProtocol.Handle] RETURNS[rc: GVProtocol.ReturnCode] = BEGIN [rc, newStamp] _ GVProtocol.Enquire[str, op, name, stamp]; END; list _ NIL; count _ 0; info _ Enquire[name, GetCompoundWork, reporter]; IF ( info=individual AND ( op=Expand OR op=ReadMailboxes)) OR info=group THEN [list, count] _ ReceiveRList[str ! GVProtocol.Failed => GOTO notQuite]; ReleaseStream[]; EXITS notQuite => { CloseStream[]; ReleaseStream[]; info _ allDown; } END; GetEntry: PUBLIC PROC[name: GVBasics.RName, reporter: GVNames.ReporterProc _ NIL] RETURNS[ rc: GVNames.NameType[group..allDown], info: REF GVNames.GetEntryInfo] = BEGIN [rc, info] _ AuthenticatedGetEntry[name: name, reporter: reporter]; END; AuthenticatedGetEntry: PUBLIC PROC[user: GVBasics.RName _ NIL, password: GVBasics.Password _ [,,,], name: GVBasics.RName, reporter: GVNames.ReporterProc _ NIL] RETURNS[ rc: GVNames.NameType[group..allDown], info: REF GVNames.GetEntryInfo] = BEGIN rcOK: BOOL _ FALSE; GetEntryWork: PROC[str: GVProtocol.Handle] RETURNS[rc: GVProtocol.ReturnCode] = BEGIN rc _ PresentCredentials[user, password]; IF rc = [done, individual] THEN [rc, ] _ GVProtocol.Enquire[str, ReadEntry, name]; rcOK _ rc.code = done; END; info _ NEW[GVNames.GetEntryInfo _ [,,notFound[]]]; rc _ Enquire[name, GetEntryWork, reporter]; IF rcOK THEN BEGIN ENABLE GVProtocol.Failed => GOTO notQuite; ReceiveFunnyString: PROC RETURNS[ GVBasics.GVString ] = TRUSTED BEGIN s: STRING = [GVBasics.maxGVStringLength]; s.length _ GVProtocol.ReceiveCount[str]; FOR i: CARDINAL IN [0..s.length) DO s[i] _ LOOPHOLE[GVProtocol.ReceiveByte[str]] ENDLOOP; IF s.length MOD 2 # 0 THEN [] _ GVProtocol.ReceiveByte[str]; RETURN[ ConvertUnsafe.ToRope[s] ]; END; ReceiveList: PROC RETURNS[ list: GVNames.GetEntryList ] = BEGIN list.current _ ReceiveRList[str].list; list.currentStamps _ ReceiveStampList[str]; list.deleted _ ReceiveRList[str].list; list.deletedStamps _ ReceiveStampList[str]; END; type: GVProtocol.RNameType; stamp: GVBasics.Timestamp; [] _ GVProtocol.ReceiveCount[str]; -- number of components [] _ GVProtocol.ReceiveCount[str]; -- component length stamp _ GVProtocol.ReceiveTimestamp[str]; type _ LOOPHOLE[GVProtocol.ReceiveCount[str]]; info _ NEW[GVNames.GetEntryInfo _ SELECT type FROM group => [,,group[,,,]], individual => [,,individual[,,,,,]], notFound => [,,notFound[]], dead => [,,dead[]], ENDCASE => ERROR]; info.name _ GVProtocol.ReceiveRName[str]; info.stamp _ stamp; TRUSTED{ WITH i: info SELECT FROM group => BEGIN [] _ GVProtocol.ReceiveCount[str]; -- component length i.remarkStamp _ GVProtocol.ReceiveTimestamp[str]; i.remark _ ReceiveFunnyString[]; i.members _ ReceiveList[]; i.owners _ ReceiveList[]; i.friends _ ReceiveList[]; END; individual => BEGIN [] _ GVProtocol.ReceiveCount[str]; -- component length i.passwordStamp _ GVProtocol.ReceiveTimestamp[str]; i.password _ GVProtocol.ReceivePassword[str]; [] _ GVProtocol.ReceiveCount[str]; -- component length i.connectStamp _ GVProtocol.ReceiveTimestamp[str]; i.connect _ ReceiveFunnyString[]; i.forward _ ReceiveList[]; i.sites _ ReceiveList[]; END; ENDCASE => NULL }; END; ReleaseStream[]; EXITS notQuite => { CloseStream[]; ReleaseStream[]; info _ NEW[GVNames.GetEntryInfo _ [,,notFound[]]] }; END; GetConnect: PUBLIC PROC[name: GVBasics.RName, reporter: GVNames.ReporterProc _ NIL] RETURNS[ info: GVNames.ConnectInfo, connect: GVBasics.Connect _ NIL ] = BEGIN ConnectWork: PROC[str: GVProtocol.Handle] RETURNS[rc: GVProtocol.ReturnCode] = BEGIN [rc,] _ GVProtocol.Enquire[str, ReadConnect, name]; END; info _ Enquire[name, ConnectWork, reporter]; IF info = individual THEN connect _ GVProtocol.ReceiveConnect[str ! GVProtocol.Failed => GOTO notQuite ]; ReleaseStream[]; SELECT info FROM IN GVNames.ConnectInfo => NULL; ENDCASE => info _ protocolError; EXITS notQuite => { CloseStream[]; ReleaseStream[]; info _ allDown } END; GetRemark: PUBLIC PROC[name: GVBasics.RName, reporter: GVNames.ReporterProc _ NIL] RETURNS[ info: GVNames.RemarkInfo, remark: GVBasics.Remark _ NIL ] = BEGIN RemarkWork: PROC[str: GVProtocol.Handle] RETURNS[rc: GVProtocol.ReturnCode] = BEGIN [rc,] _ GVProtocol.Enquire[str, ReadRemark, name]; END; info _ Enquire[name, RemarkWork, reporter]; IF info = group THEN remark _ GVProtocol.ReceiveRemark[str ! GVProtocol.Failed => GOTO notQuite ]; ReleaseStream[]; SELECT info FROM IN GVNames.RemarkInfo => NULL; ENDCASE => info _ protocolError; EXITS notQuite => { CloseStream[]; ReleaseStream[]; info _ allDown } END; CheckStamp: PUBLIC PROC[name: GVBasics.RName, oldStamp: GVBasics.Timestamp _ GVBasics.oldestTime, reporter: GVNames.ReporterProc _ NIL] RETURNS[ GVNames.StampInfo ] = BEGIN info: GVNames.NameType; CheckStampWork: PROC[str: GVProtocol.Handle] RETURNS[rc: GVProtocol.ReturnCode] = BEGIN [rc,] _ GVProtocol.Enquire[str, CheckStamp, name, oldStamp]; END; info _ Enquire[name, CheckStampWork, reporter]; ReleaseStream[]; SELECT info FROM IN GVNames.StampInfo => RETURN[info]; ENDCASE => RETURN[protocolError]; END; AuthenticateKey: PUBLIC PROC[name: GVBasics.RName, key: GVBasics.Password, reporter: GVNames.ReporterProc _ NIL] RETURNS[GVNames.AuthenticateInfo ] = BEGIN AuthWork: PROC[str: GVProtocol.Handle] RETURNS[rc: GVProtocol.ReturnCode] = BEGIN GVProtocol.SendRSOperation[str, Authenticate]; GVProtocol.SendRName[str, name]; GVProtocol.SendPassword[str: str, pw: key]; GVProtocol.SendNow[str]; rc _ GVProtocol.ReceiveRC[str]; END; info: GVNames.NameType = Enquire[name, AuthWork, reporter]; ReleaseStream[]; SELECT info FROM IN GVNames.AuthenticateInfo => RETURN[info]; ENDCASE => RETURN[protocolError]; END; IsInList: PUBLIC PROC[name, member: GVBasics.RName, level: GVNames.MembershipLevel, grade: GVNames.MembershipGrade, acl: GVNames.ListType, reporter: GVNames.ReporterProc _ NIL] RETURNS[res: GVNames.Membership] = BEGIN IsInListWork: PROC[str: GVProtocol.Handle] RETURNS[rc: GVProtocol.ReturnCode] = BEGIN GVProtocol.SendRSOperation[str, IsInList]; GVProtocol.SendRName[str, name]; GVProtocol.SendRName[str, member]; GVProtocol.SendByte[str, LOOPHOLE[grade]]; GVProtocol.SendByte[str, LOOPHOLE[acl]]; GVProtocol.SendByte[str, LOOPHOLE[level]]; GVProtocol.SendNow[str]; rc _ GVProtocol.ReceiveRC[str]; END; info: GVNames.NameType = Enquire[name, IsInListWork, reporter]; SELECT info FROM group => res _ IF GVProtocol.ReceiveBoolean[str ! GVProtocol.Failed => GOTO notQuite] THEN yes ELSE no; individual, notFound => res _ notGroup; ENDCASE => res _ allDown; -- includes various protocol errors ReleaseStream[]; EXITS notQuite => { CloseStream[]; ReleaseStream[]; res _ allDown; } END; Update: PUBLIC PROC[ user: GVBasics.RName, password: GVBasics.Password, op: GVProtocol.RSOperation, target: GVBasics.RName, value: GVBasics.GVString _ NIL, newPwd: GVBasics.Password _ NULL, list: GVNames.RListHandle _ NIL, reporter: GVNames.ReporterProc _ NIL] RETURNS[info: GVNames.Outcome] = BEGIN UpdateWork: PROC[str: GVProtocol.Handle] RETURNS[rc: GVProtocol.ReturnCode] = BEGIN rc _ PresentCredentials[user, password]; IF rc = [done, individual] THEN BEGIN GVProtocol.SendRSOperation[str, op]; GVProtocol.SendRName[str, target]; SELECT op FROM CreateGroup, DeleteIndividual, DeleteGroup, AddSelf, DeleteSelf => NULL; ChangePassword, CreateIndividual => GVProtocol.SendPassword[str, newPwd]; ENDCASE => GVProtocol.SendRName[str, value]; GVProtocol.SendNow[str]; rc _ GVProtocol.ReceiveRC[str]; END; END; IF user.Length[] > GVBasics.maxRNameLength THEN RETURN[badPwd]; IF value.Length[] > GVBasics.maxGVStringLength THEN value _ Rope.Substr[value, 0, GVBasics.maxGVStringLength]; info _ Enquire[target, UpdateWork, reporter]; ReleaseStream[]; END; PresentCredentials: PROC [user: GVBasics.RName, password: GVBasics.Password] RETURNS [rc: GVProtocol.ReturnCode] = BEGIN IF user=NIL OR (Rope.Equal[user, authUser, FALSE] AND password=authPassword) THEN rc _ [done, individual] ELSE BEGIN GVProtocol.SendRSOperation[str, IdentifyCaller]; GVProtocol.SendRName[str, user]; GVProtocol.SendPassword[str, password]; GVProtocol.SendNow[str]; rc _ GVProtocol.ReceiveRC[str]; SELECT rc FROM [done, individual] => {authUser _ user; authPassword _ password}; [BadRName, group] => rc _ [BadRName,notFound]; ENDCASE; END; END; released: CONDITION; free: BOOLEAN _ TRUE; str: GVProtocol.Handle _ NIL; -- cached R-Server stream cacheAddr: PupDefs.PupAddress; -- address of cached stream addrHint: BOOLEAN _ FALSE; -- whether to use cached address as a hint authUser: GVBasics.RName _ NIL; -- authenticated credentials for Grapevine access authPassword: GVBasics.Password; ClaimStream: ENTRY PROC = { UNTIL free DO WAIT released ENDLOOP; free _ FALSE; }; ReleaseStream: ENTRY PROC = { free _ TRUE; NOTIFY released }; CleanUp: PUBLIC --NameInfoSpecialDefs-- PROC = BEGIN ClaimStream[]; IF NOT GVProtocol.IsLocal[cacheAddr] THEN CloseStream[]; ReleaseStream[]; END; SetServer: PUBLIC PROC[name: Rope.ROPE] RETURNS[ GVNames.SetServerInfo ] = BEGIN pupName: Rope.ROPE; addr: PupDefs.PupAddress; trouble: PupDefs.NameLookupErrorCode; IF name.Find["."] >= 0 THEN BEGIN info: GVNames.ConnectInfo; [info, pupName] _ GetConnect[name]; SELECT info FROM individual => NULL; allDown => RETURN[allDown]; ENDCASE => RETURN[badName]; END ELSE pupName _ name; BEGIN addr _ PupDefs.GetPupAddress[GVProtocol.GetSocket[RSEnquiry], pupName ! PupDefs.PupNameTrouble => { trouble _ code; GOTO lookupFailed } ]; EXITS lookupFailed => RETURN[SELECT trouble FROM noRoute => noRoute, noResponse => allDown, errorFromServer => badName, ENDCASE => ERROR] END; ClaimStream[]; cacheAddr _ addr; addrHint _ TRUE; ReleaseStream[]; RETURN[ok] END; Enquire: PROC[name: GVBasics.RName, EnquiryWork: PROC[GVProtocol.Handle] RETURNS[GVProtocol.ReturnCode], reporter: GVNames.ReporterProc _ NIL ] RETURNS[info: GVNames.Outcome] = BEGIN Create: PROC = BEGIN IF reporter # NIL THEN reporter[PupDefs.PupAddressToRope[cacheAddr]]; str _ GVProtocol.CreateStream[cacheAddr, RSEnquiry]; END; rc: GVProtocol.ReturnCode; oldBad: BOOLEAN _ FALSE; IF name.Length[] > GVBasics.maxRNameLength THEN RETURN[notFound]; ClaimStream[]; BEGIN AcceptGV: PROC[addr: PupDefs.PupAddress]RETURNS[BOOLEAN] = BEGIN IF str # NIL THEN ERROR; cacheAddr _ addr; Create[! GVProtocol.Failed => GOTO failed ]; RETURN[TRUE]; EXITS failed => { CloseStream[]; RETURN[FALSE] } END; IF addrHint THEN -- cached address hint set by client, so try it { addrHint _ FALSE; CloseStream[]; [] _ AcceptGV[cacheAddr] }; IF str # NIL -- try cached stream first THEN BEGIN rc _ EnquiryWork[str ! GVProtocol.Failed => GOTO streamGone]; EXITS streamGone => CloseStream[]; END; IF str = NIL THEN BEGIN [] _ GVLocate.FindRegServer["x.GV", AcceptGV]; IF str = NIL THEN { info _ allDown; RETURN }; rc _ EnquiryWork[str ! GVProtocol.Failed => GOTO notThere]; END; IF rc.code = WrongServer THEN { oldBad _ TRUE; IF reporter # NIL THEN reporter["wrong server"] } ELSE oldBad _ FALSE; EXITS notThere => { CloseStream[]; oldBad _ TRUE; }; END; IF oldBad THEN BEGIN -- need to find the correct R-Server Accept: PROC[addr: PupDefs.PupAddress]RETURNS[BOOLEAN] = BEGIN ClaimStream[]; IF str # NIL AND cacheAddr # addr THEN CloseStream[]; IF str = NIL THEN BEGIN cacheAddr _ addr; Create[! GVProtocol.Failed => GOTO failed]; END; RETURN[TRUE]; EXITS failed => { CloseStream[]; ReleaseStream[]; RETURN[FALSE] } END; foundInfo: GVLocate.FoundServerInfo; ReleaseStream[] -- for FindRegServer --; TRUSTED {foundInfo _ GVLocate.FindRegServer[name, Accept]}; WITH foundInfo SELECT FROM notFound => { info _ notFound; ClaimStream[]; RETURN }; allDown => { info _ allDown; ClaimStream[]; RETURN }; found => BEGIN rc _ EnquiryWork[str ! GVProtocol.Failed => GOTO down ]; EXITS down => { CloseStream[]; info _ allDown; RETURN } END; ENDCASE => ERROR; END; info _ SELECT rc.code FROM noChange => noChange, done, BadRName => SELECT rc.type FROM individual => individual, group => group, ENDCASE => notFound -- includes dead --, BadOperation, BadProtocol => protocolError, WrongServer => wrongServer, AllDown => allDown, BadPassword => badPwd, outOfDate => outOfDate, NotAllowed => notAllowed, ENDCASE => allDown; END; CloseStream: PROC = { IF str # NIL THEN { GVProtocol.Close[str]; str _ NIL; authUser _ NIL } }; END.  GVNamesImpl.mesa - communication with registration server Copyright c 1985 by Xerox Corporation. All rights reserved. Andrew Birrell August 25, 1983 5:09 pm Levin, September 22, 1983 11:57 am Taft, November 28, 1983 5:22 pm target.Length[] is checked inside Enquire There is a cache of one stream and one address. Access to this is under mutual exclusion, enforced by calls on ClaimStream and ReleaseStream optimize for R-Server/M-Server internal stream "SetServer" allows a client (typically Maintain) to provide a server name as a hint. slightly subtle optimisation: If we don't have a stream, or the stream we have times out, we'll need to get one to an R-Server for GV. Getting one early might save us from having to call FindRegServer later Don't use cached address, so that we return to a near server Note: The following call of FindRegServer doesn't call us back recursively! stream was claimed inside "Accept" Κ– "cedar" style˜codešœ9™9Kšœ Οmœ1™˜@Kšœžœ˜)Kšžœž˜ Kšžœ žœžœ˜,Kšžœžœžœ˜7—Kšžœ˜Kšžœ˜K˜—š Ÿ œžœožœžœYžœ˜λKšž˜šŸœžœ˜-Kšžœ˜$Kšž˜˜,K˜ —Kšžœ˜—Kšœžœ˜ K˜ K˜0Kšžœžœ žœ˜:Kšžœ ˜ Kšžœ9žœ ˜LK˜Kšžœ@˜EKšžœ˜K˜K˜K˜—š Ÿœžœžœ8žœžœ.žœ˜’Kšž˜KšœC˜CKšžœ˜—K˜K˜šŸœžœžœžœ^žœžœ.žœ˜πKšž˜Kšœžœžœ˜šŸ œžœ˜*Kšžœ˜$Kšž˜K˜(Kšžœžœ3˜RK˜Kšžœ˜—Kšœžœ(˜2K˜+Kšžœ˜šžœž˜ Kšžœžœ ˜*šŸœžœžœž˜?Kšž˜Kšœžœ ˜)K˜(Kš žœžœžœžœžœžœ˜YKšžœ žœžœ"˜žœ ˜RK˜šžœž˜Kšžœžœ˜—Kšžœ˜ Kšžœ?˜DKšžœ˜K˜K˜K˜—šŸ œžœžœ˜-K˜3Kšœ!žœ˜%Kšžœ˜Kšž˜K˜šŸœžœ˜,Kšžœ˜$Kšž˜K˜—Kšžœžœ ˜'šžœž˜ Kšœ,žœ ˜=Kšžœ˜"Kšžœ˜—Kšžœž˜ šžœž˜ Kšœ<™