-- GrapevineUser: communication with registration server -- [Indigo]<Grapevine>MS>NameInfo.mesa -- Andrew Birrell August 30, 1982 12:00 pm DIRECTORY BodyDefs USING [Connect, oldestTime, Password, Remark, RName, Timestamp], LocateDefs USING [FindRegServer, FoundServerInfo], NameInfoDefs USING [ AuthenticateInfo, ConnectInfo, ExpandInfo, ListType, MembershipGrade, MemberInfo, MembershipLevel, Membership, NameType, Outcome, RemarkInfo, RListHandle, StampInfo], NameInfoPrivate USING[ ], NameInfoSpecialDefs USING [], NameUpdateDefs USING [], ProtocolDefs USING [ CreateStream, DestroyStream, Enquire, Failed, Handle, IsLocal, MakeKey, ReceiveBoolean, ReceiveConnect, ReceiveRC, ReceiveRemark, RegServerEnquirySocket, ReturnCode, RSOperation, SendByte, SendNow, SendPassword, SendRName, SendRSOperation], PupDefs USING [AppendPupAddress, PupAddress], RListDefs USING [Close, Enumerate, Receive, RListHandle]; NameInfo: MONITOR IMPORTS LocateDefs, ProtocolDefs, PupDefs, RListDefs EXPORTS NameInfoDefs, NameInfoPrivate, NameInfoSpecialDefs, NameUpdateDefs = BEGIN -- Re-export RListDefs into NameInfoDefs -- RListHandle: PUBLIC TYPE = RECORD[l:RListDefs.RListHandle]; Enumerate: PUBLIC PROC[list: RListHandle, work: PROC[BodyDefs.RName]RETURNS[done: BOOLEAN] ] = { RListDefs.Enumerate[list.l, work] }; Close: PUBLIC PROC[list: RListHandle] = { RListDefs.Close[list.l] }; Expand: PUBLIC PROC[name: BodyDefs.RName, oldStamp: BodyDefs.Timestamp ← BodyDefs.oldestTime] RETURNS[ NameInfoDefs.ExpandInfo ] = BEGIN info: NameInfoDefs.NameType; list: RListHandle; stamp: BodyDefs.Timestamp; Receive: PROC[str: ProtocolDefs.Handle] = { list ← [l: RListDefs.Receive[str]] }; [info, stamp] ← GetCompound[name, oldStamp, Expand, Receive]; SELECT info FROM noChange => RETURN[ [noChange[]] ]; group => RETURN[ [group[list, stamp]] ]; individual => RETURN[ [individual[list, stamp]] ]; notFound => RETURN[ [notFound[]] ]; protocolError => RETURN[ [protocolError[]] ]; wrongServer => RETURN[ [wrongServer[]] ]; allDown => RETURN[ [allDown[]] ]; ENDCASE => RETURN[ [protocolError[]] ]; END; GetMembers: PUBLIC PROC[name: BodyDefs.RName, oldStamp: BodyDefs.Timestamp ← BodyDefs.oldestTime] RETURNS[ NameInfoDefs.MemberInfo ] = { RETURN[ GetGroupList[name, oldStamp, ReadMembers] ] }; GetOwners: PUBLIC PROC[name: BodyDefs.RName, oldStamp: BodyDefs.Timestamp ← BodyDefs.oldestTime] RETURNS[ NameInfoDefs.MemberInfo ] = { RETURN[ GetGroupList[name, oldStamp, ReadOwners] ] }; GetFriends: PUBLIC PROC[name: BodyDefs.RName, oldStamp: BodyDefs.Timestamp ← BodyDefs.oldestTime] RETURNS[ NameInfoDefs.MemberInfo ] = { RETURN[ GetGroupList[name, oldStamp, ReadFriends] ] }; GetGroupList: PUBLIC PROC[name: BodyDefs.RName, oldStamp: BodyDefs.Timestamp, op: ProtocolDefs.RSOperation] RETURNS[ NameInfoDefs.MemberInfo ] = BEGIN info: NameInfoDefs.NameType; list: RListHandle; stamp: BodyDefs.Timestamp; Receive: PROC[str: ProtocolDefs.Handle] = { list ← [l: RListDefs.Receive[str]] }; [info, stamp] ← GetCompound[name, oldStamp, op, Receive]; SELECT info FROM noChange => RETURN[ [noChange[]] ]; group => RETURN[ [group[list, stamp]] ]; individual => RETURN[ [individual[]] ]; notFound => RETURN[ [notFound[]] ]; protocolError => RETURN[ [protocolError[]] ]; wrongServer => RETURN[ [wrongServer[]] ]; allDown => RETURN[ [allDown[]] ]; ENDCASE => RETURN[ [protocolError[]] ]; END; GetCompound: PUBLIC PROC[name: BodyDefs.RName, stamp: BodyDefs.Timestamp, op: ProtocolDefs.RSOperation, receive: PROC[ProtocolDefs.Handle], reporter: PROC[STRING] ← NIL] RETURNS[info: NameInfoDefs.NameType, newStamp: BodyDefs.Timestamp] = BEGIN rcCopy: ProtocolDefs.ReturnCode; GetListWork: PROC[str: ProtocolDefs.Handle] RETURNS[rc: ProtocolDefs.ReturnCode] = BEGIN [rc, newStamp] ← ProtocolDefs.Enquire[str, op, name, stamp]; rcCopy ← rc; END; info ← Enquire[name, GetListWork, reporter]; IF ( info=individual AND ( op=Expand OR op=ReadEntry OR op=ReadMailboxes)) OR info=group OR ( op=ReadEntry AND rcCopy = [done, dead] ) THEN receive[str ! ProtocolDefs.Failed => GOTO notQuite]; ReleaseStream[]; EXITS notQuite => { CloseStream[]; ReleaseStream[]; info ← allDown; } END; GetConnect: PUBLIC PROC[name: BodyDefs.RName, connect: BodyDefs.Connect] RETURNS[ NameInfoDefs.ConnectInfo ] = BEGIN ConnectWork: PROC[str: ProtocolDefs.Handle] RETURNS[rc: ProtocolDefs.ReturnCode] = BEGIN [rc,] ← ProtocolDefs.Enquire[str, ReadConnect, name]; END; info: NameInfoDefs.NameType ← Enquire[name, ConnectWork]; IF info = individual THEN ProtocolDefs.ReceiveConnect[str, connect ! ProtocolDefs.Failed => GOTO notQuite ]; ReleaseStream[]; SELECT info FROM IN NameInfoDefs.ConnectInfo => RETURN[info]; ENDCASE => RETURN[protocolError]; EXITS notQuite => { CloseStream[]; ReleaseStream[]; RETURN[allDown] } END; GetRemark: PUBLIC PROC[name: BodyDefs.RName, remark: BodyDefs.Remark] RETURNS[ NameInfoDefs.RemarkInfo ] = BEGIN RemarkWork: PROC[str: ProtocolDefs.Handle] RETURNS[rc: ProtocolDefs.ReturnCode] = BEGIN [rc,] ← ProtocolDefs.Enquire[str, ReadRemark, name]; END; info: NameInfoDefs.NameType = Enquire[name, RemarkWork]; IF info = group THEN ProtocolDefs.ReceiveRemark[str, remark ! ProtocolDefs.Failed => GOTO notQuite ]; ReleaseStream[]; SELECT info FROM IN NameInfoDefs.RemarkInfo => RETURN[info]; ENDCASE => RETURN[protocolError]; EXITS notQuite => { CloseStream[]; ReleaseStream[]; RETURN[allDown] } END; CheckStamp: PUBLIC PROC[name: BodyDefs.RName, oldStamp: BodyDefs.Timestamp ← BodyDefs.oldestTime] RETURNS[ NameInfoDefs.StampInfo ] = BEGIN info: NameInfoDefs.NameType; CheckStampWork: PROC[str: ProtocolDefs.Handle] RETURNS[rc: ProtocolDefs.ReturnCode] = BEGIN [rc,] ← ProtocolDefs.Enquire[str, CheckStamp, name, oldStamp]; END; info ← Enquire[name, CheckStampWork]; ReleaseStream[]; SELECT info FROM IN NameInfoDefs.StampInfo => RETURN[info]; ENDCASE => RETURN[protocolError]; END; Authenticate: PUBLIC PROC[name: BodyDefs.RName, password: STRING] RETURNS[NameInfoDefs.AuthenticateInfo ] = { RETURN[ AuthenticateKey[name, ProtocolDefs.MakeKey[password]] ] }; AuthenticateKey: PUBLIC PROC[name: BodyDefs.RName, key: BodyDefs.Password] RETURNS[NameInfoDefs.AuthenticateInfo ] = BEGIN AuthWork: PROC[str: ProtocolDefs.Handle] RETURNS[rc: ProtocolDefs.ReturnCode] = BEGIN ProtocolDefs.SendRSOperation[str, Authenticate]; ProtocolDefs.SendRName[str, name]; ProtocolDefs.SendPassword[str: str, key: [0,0,0,0], pw: key]; ProtocolDefs.SendNow[str]; rc ← ProtocolDefs.ReceiveRC[str]; END; info: NameInfoDefs.NameType = Enquire[name, AuthWork]; ReleaseStream[]; SELECT info FROM IN NameInfoDefs.AuthenticateInfo => RETURN[info]; ENDCASE => RETURN[protocolError]; END; IsInList: PUBLIC PROC[name, member: BodyDefs.RName, level: NameInfoDefs.MembershipLevel, grade: NameInfoDefs.MembershipGrade, acl: NameInfoDefs.ListType] RETURNS[res: NameInfoDefs.Membership] = BEGIN IsInListWork: PROC[str: ProtocolDefs.Handle] RETURNS[rc: ProtocolDefs.ReturnCode] = BEGIN ProtocolDefs.SendRSOperation[str, IsInList]; ProtocolDefs.SendRName[str, name]; ProtocolDefs.SendRName[str, member]; ProtocolDefs.SendByte[str, LOOPHOLE[grade]]; ProtocolDefs.SendByte[str, LOOPHOLE[acl]]; ProtocolDefs.SendByte[str, LOOPHOLE[level]]; ProtocolDefs.SendNow[str]; rc ← ProtocolDefs.ReceiveRC[str]; END; info: NameInfoDefs.NameType = Enquire[name, IsInListWork]; SELECT info FROM group => res ← IF ProtocolDefs.ReceiveBoolean[str ! ProtocolDefs.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: BodyDefs.RName, password: BodyDefs.Password, op: ProtocolDefs.RSOperation, target: BodyDefs.RName, value: STRING ← NIL, newPwd: BodyDefs.Password ← [,,,], reporter: PROC[STRING] ← NIL] RETURNS[info: NameInfoDefs.Outcome] = BEGIN UpdateWork: PROC[str: ProtocolDefs.Handle] RETURNS[rc: ProtocolDefs.ReturnCode] = BEGIN ProtocolDefs.SendRSOperation[str, IdentifyCaller]; ProtocolDefs.SendRName[str, user]; ProtocolDefs.SendPassword[str, [0,0,0,0], password]; ProtocolDefs.SendNow[str]; rc ← ProtocolDefs.ReceiveRC[str]; IF rc = [BadRName, group] THEN rc ← [BadRName,notFound]; IF rc = [done, individual] THEN BEGIN ProtocolDefs.SendRSOperation[str, op]; ProtocolDefs.SendRName[str, target]; SELECT op FROM CreateGroup, DeleteIndividual, DeleteGroup, AddSelf, DeleteSelf => NULL; ChangePassword, CreateIndividual => ProtocolDefs.SendPassword[str, [0,0,0,0], newPwd]; ENDCASE => ProtocolDefs.SendRName[str, value]; ProtocolDefs.SendNow[str]; rc ← ProtocolDefs.ReceiveRC[str]; END; END; info ← Enquire[target, UpdateWork, reporter]; ReleaseStream[]; END; -- There is a cache of one stream and one address. -- -- Access to this is under mutual exclusion, enforced by calls -- on ClaimStream and ReleaseStream -- released: CONDITION; free: BOOLEAN ← TRUE; str: ProtocolDefs.Handle ← NIL; -- cached R-Server stream -- cacheAddr: PupDefs.PupAddress; -- address of cached stream -- addrHint: BOOLEAN ← FALSE; -- whether to use cached address as a hint -- ClaimStream: ENTRY PROC = { UNTIL free DO WAIT released ENDLOOP; free ← FALSE; }; ReleaseStream: ENTRY PROC = { free ← TRUE; NOTIFY released }; CleanUp: PUBLIC --NameInfoSpecialDefs-- PROC = BEGIN ClaimStream[]; -- optimize for R-Server/M-Server internal stream -- IF NOT ProtocolDefs.IsLocal[cacheAddr] THEN CloseStream[]; ReleaseStream[]; END; -- "SetServer" allows a client (typically Maintain) to provide -- a server address as a hint. SetServer: PUBLIC PROC[addr: PupDefs.PupAddress] = BEGIN ClaimStream[]; cacheAddr ← addr; addrHint ← TRUE; ReleaseStream[]; END; Enquire: PROC[name: BodyDefs.RName, EnquiryWork: PROC[ProtocolDefs.Handle] RETURNS[ProtocolDefs.ReturnCode], reporter: PROC[STRING] ← NIL ] RETURNS[info: NameInfoDefs.Outcome] = BEGIN -- 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 -- Create: PROC = BEGIN cacheAddr.socket ← ProtocolDefs.RegServerEnquirySocket; IF reporter # NIL THEN BEGIN s: STRING = [64]; PupDefs.AppendPupAddress[s, cacheAddr]; reporter[s]; END; str ← ProtocolDefs.CreateStream[cacheAddr]; END; rc: ProtocolDefs.ReturnCode; oldBad: BOOLEAN ← FALSE; ClaimStream[]; BEGIN AcceptGV: PROC[addr: PupDefs.PupAddress]RETURNS[BOOLEAN] = BEGIN IF str # NIL THEN ERROR; cacheAddr ← addr; Create[! ProtocolDefs.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 ! ProtocolDefs.Failed => GOTO streamGone]; EXITS streamGone => CloseStream[]; END; IF str = NIL THEN BEGIN -- 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! [] ← LocateDefs.FindRegServer["x.GV"L, AcceptGV]; IF str = NIL THEN { info ← allDown; RETURN }; rc ← EnquiryWork[str ! ProtocolDefs.Failed => GOTO notThere]; END; IF rc.code = WrongServer THEN oldBad ← TRUE 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[]; addr.socket ← ProtocolDefs.RegServerEnquirySocket; IF str # NIL AND cacheAddr # addr THEN CloseStream[]; IF str = NIL THEN BEGIN cacheAddr ← addr; Create[! ProtocolDefs.Failed => GOTO failed]; END; RETURN[TRUE]; EXITS failed => { CloseStream[]; ReleaseStream[]; RETURN[FALSE] } END; foundInfo: LocateDefs.FoundServerInfo; ReleaseStream[] -- for FindRegServer --; foundInfo ← LocateDefs.FindRegServer[name, Accept]; WITH foundInfo SELECT FROM notFound => { info ← notFound; ClaimStream[]; RETURN }; allDown => { info ← allDown; ClaimStream[]; RETURN }; found => BEGIN -- stream was claimed inside "Accept" -- rc ← EnquiryWork[str ! ProtocolDefs.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 { ProtocolDefs.DestroyStream[str]; str ← NIL } }; END.