THE GRAPEVINE INTERFACE Appendix: The Cedar Grapevine Interfaces This appendix contains those interfaces of the Cedar Grapevine client interface which were referred to earlier in this document. <> <> <> DIRECTORY Rope USING [ROPE]; GVBasics: CEDAR DEFINITIONS = BEGIN <> <> GVString: TYPE = Rope.ROPE; maxGVStringLength: CARDINAL = 64; <> RName: TYPE = GVString; <> maxRNameLength: CARDINAL = maxGVStringLength; Connect: TYPE = GVString; <<"Connect" is the representation of a connect-site for an individual (typically a server). It is a string which is either an NLS name or a PUP address.>> maxConnectLength: CARDINAL = maxGVStringLength; Remark: TYPE = GVString; <<"Remark" is the representation of a remark associated with a group, or of a TOC entry in a mailbox. It is a human readable string.>> maxRemarkLength: CARDINAL = maxGVStringLength; Password: TYPE = ARRAY[0..3] OF CARDINAL; <<"Password" is the representation of an individual's encryption key. It is intended to be used with the DES encryption algorithm. Note that this is not the same as DESFace.Key (today), not even if you use LOOPHOLE.>> MakeKey: PROC[Rope.ROPE] RETURNS[ Password ]; <> Timestamp: TYPE = MACHINE DEPENDENT RECORD[ <> net: [0..256), -- the PUP net number -- host: [0..256), -- the PUP host number -- time: PackedTime]; PackedTime: TYPE = LONG CARDINAL; <> oldestTime: Timestamp = [net:0, host:0, time:0]; RopeFromTimestamp: PROC[Timestamp] RETURNS[Rope.ROPE]; <> <> ItemHeader: TYPE = MACHINE DEPENDENT RECORD[ type: ItemType, length: ItemLength ]; <> Item: TYPE = REF ItemHeader; ItemLength: TYPE = INT; <> ItemType: TYPE = MACHINE DEPENDENT { <> PostMark(10B), <> Sender(20B), <> ReturnTo(30B), <> Recipients(40B), <> <> Text(1010B), <> Capability(1020B), <> Audio(1030B), <> <> updateItem(2000B), <> reMail(2100B), <> <> LastItem(LAST[CARDINAL]) <> }; END. <<>> <> <> <> <<>> DIRECTORY GVBasics USING[ Connect, GVString, oldestTime, Password, MakeKey, Remark, RName, Timestamp ], Rope USING[ ROPE ]; GVNames: CEDAR DEFINITIONS IMPORTS GVBasics = BEGIN Outcome: TYPE = { -- possible outcomes of RS operations -- noChange, -- updates and timestamped enquiries -- group, individual, notFound, -- any -- protocolError, wrongServer, allDown, -- any -- badPwd, -- authentication and updates -- outOfDate, notAllowed -- updates -- }; NameType: TYPE = Outcome[noChange..badPwd]; -- outcomes for enquiries -- RListHandle: TYPE = LIST OF GVBasics.RName; ExpandInfo: TYPE = RECORD[ SELECT type: NameType[noChange..allDown] FROM noChange => NULL, group => [ members: RListHandle, stamp: GVBasics.Timestamp, count: INT ], individual => [ sites: RListHandle, stamp: GVBasics.Timestamp, count: INT ], notFound => NULL, protocolError => NULL, wrongServer => NULL, allDown => NULL, ENDCASE ]; Expand: PROC[name: GVBasics.RName, oldStamp: GVBasics.Timestamp _ GVBasics.oldestTime, reporter: ReporterProc _ NIL] RETURNS[ ExpandInfo ]; <<"Expand" returns mailbox site names for individuals, membership list for groups. If the old stamp is still current, returns "noChange". Will not return "noChange" if the old stamp is defaulted.>> MemberInfo: TYPE = RECORD[ SELECT type: NameType[noChange..allDown] FROM noChange => NULL, group => [ members: RListHandle, stamp: GVBasics.Timestamp, count: INT ], individual => NULL, notFound => NULL, protocolError => NULL, wrongServer => NULL, allDown => NULL, ENDCASE ]; GetMembers: PROC[name: GVBasics.RName, oldStamp: GVBasics.Timestamp _ GVBasics.oldestTime] RETURNS[ MemberInfo ] = INLINE { RETURN[ GetList[name, oldStamp, members] ] }; <<"GetMembers" returns the membership list for a group. If the old stamp is still current, returns "noChange". Will not return "noChange" if the old stamp is defaulted.>> GetOwners: PROC[name: GVBasics.RName, oldStamp: GVBasics.Timestamp _ GVBasics.oldestTime] RETURNS[ MemberInfo ] = INLINE { RETURN[ GetList[name, oldStamp, owners] ] }; GetFriends: PROC[name: GVBasics.RName, oldStamp: GVBasics.Timestamp _ GVBasics.oldestTime] RETURNS[ MemberInfo ] = INLINE { RETURN[ GetList[name, oldStamp, friends] ] }; GetList: PROC[name: GVBasics.RName, oldStamp: GVBasics.Timestamp, list: ListType, reporter: ReporterProc _ NIL] RETURNS[MemberInfo]; StampInfo: TYPE = NameType[noChange..allDown]; CheckStamp: PROC[name: GVBasics.RName, oldStamp: GVBasics.Timestamp _ GVBasics.oldestTime, reporter: ReporterProc _ NIL] RETURNS[ StampInfo ]; <<"CheckStamp" performs basic name validation, also telling the caller the name type. If the old stamp is still current, returns "noChange". Will not return "noChange" if the old stamp is defaulted.>> ConnectInfo: TYPE = NameType[group..allDown]; GetConnect: PROC[name: GVBasics.RName, reporter: ReporterProc _ NIL] RETURNS[ info: ConnectInfo, connect: GVBasics.Connect ]; <<"GetConnect" returns the connect-site for an individual. The connect-site is either an NLS name or a net-address.>> RemarkInfo: TYPE = NameType[group..allDown]; GetRemark: PROC[name: GVBasics.RName, reporter: ReporterProc _ NIL] RETURNS[ info: RemarkInfo, remark: GVBasics.Remark ]; <<"GetRemark" returns the remark for a group. The remark is a human readable string..>> GetEntryInfo: TYPE = RECORD[ name: GVBasics.RName, stamp: GVBasics.Timestamp, body: SELECT type: * FROM group => [ remark: GVBasics.Remark, remarkStamp: GVBasics.Timestamp, members: GetEntryList, owners: GetEntryList, friends: GetEntryList], individual => [ password: GVBasics.Password, passwordStamp: GVBasics.Timestamp, connect: GVBasics.Connect, connectStamp: GVBasics.Timestamp, forward: GetEntryList, sites: GetEntryList], notFound => NULL, dead => NULL, ENDCASE ]; GetEntryList: TYPE = RECORD[ current: RListHandle, currentStamps: LIST OF GVBasics.Timestamp, deleted: RListHandle, deletedStamps: LIST OF GVBasics.Timestamp]; GetEntry: PRIVATE PROC[name: GVBasics.RName, reporter: ReporterProc _ NIL] RETURNS[rc: NameType[group..allDown], info: REF GetEntryInfo]; <<"GetEntry" is mainly for use by Maintain. It returns the entire contents of a database entry. Consult a wizard before using GetEntry>> AuthenticateInfo: TYPE = NameType[group..badPwd]; Authenticate: PROC[name: GVBasics.RName, password: Rope.ROPE] RETURNS[ AuthenticateInfo ] = INLINE { RETURN[ AuthenticateKey[name, GVBasics.MakeKey[password]] ] }; <<"Authenticate" checks a user name and password.>> AuthenticateKey: PROC[name: GVBasics.RName, key: GVBasics.Password, reporter: ReporterProc _ NIL] RETURNS[ AuthenticateInfo ]; <> Membership: TYPE = { yes, no, notGroup, allDown }; IsMemberDirect: PROC[name: GVBasics.RName, member: GVBasics.RName] RETURNS[ Membership ] = INLINE { RETURN[IsInList[name, member, direct, self, members]] }; IsOwnerDirect: PROC[name: GVBasics.RName, owner: GVBasics.RName] RETURNS[ Membership ] = INLINE { RETURN[IsInList[name, owner, direct, self, owners]] }; IsFriendDirect: PROC[name: GVBasics.RName, friend: GVBasics.RName] RETURNS[ Membership ] = INLINE { RETURN[IsInList[name, friend, direct, self, friends]] }; IsMemberClosure: PROC[name: GVBasics.RName, member: GVBasics.RName] RETURNS[ Membership ] = INLINE { RETURN[IsInList[name, member, closure, self, members]] }; IsOwnerClosure: PROC[name: GVBasics.RName, owner: GVBasics.RName] RETURNS[ Membership ] = INLINE { RETURN[IsInList[name, owner, closure, self, owners]] }; IsFriendClosure:PROC[name: GVBasics.RName, friend: GVBasics.RName] RETURNS[ Membership ] = INLINE { RETURN[IsInList[name, friend, closure, self, friends]] }; IsMemberUpArrow:PROC[name: GVBasics.RName, member: GVBasics.RName] RETURNS[ Membership ] = INLINE { RETURN[IsInList[name, member, upArrow, self, members]] }; IsOwnerUpArrow: PROC[name: GVBasics.RName, owner: GVBasics.RName] RETURNS[ Membership ] = INLINE { RETURN[IsInList[name, owner, upArrow, self, owners]] }; IsFriendUpArrow:PROC[name: GVBasics.RName, friend: GVBasics.RName] RETURNS[ Membership ] = INLINE { RETURN[IsInList[name, friend, upArrow, self, friends]] }; MembershipLevel: TYPE = MACHINE DEPENDENT { direct(0), closure(1), upArrow(2) }; MembershipGrade: TYPE = MACHINE DEPENDENT { self(0), registry(1) }; ListType: TYPE = MACHINE DEPENDENT { members(0), owners(1), friends(2) }; IsInList: PROC[name: GVBasics.RName, member: GVBasics.RName, level: MembershipLevel, grade: MembershipGrade, acl: ListType, reporter: ReporterProc _ NIL] RETURNS[Membership]; <> <> CreateIndividual: PROC[user: GVBasics.RName, password: GVBasics.Password, individual: GVBasics.RName, newPwd: GVBasics.Password ] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, CreateIndividual, individual, NIL, newPwd] ] }; DeleteIndividual: PROC[user: GVBasics.RName, password: GVBasics.Password, individual: GVBasics.RName] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, DeleteIndividual, individual] ] }; CreateGroup: PROC[user: GVBasics.RName, password: GVBasics.Password, group: GVBasics.RName] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, CreateGroup, group] ] }; DeleteGroup: PROC[user: GVBasics.RName, password: GVBasics.Password, group: GVBasics.RName] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, DeleteGroup, group] ] }; <> SetPassword: PROC[user: GVBasics.RName, password: GVBasics.Password, individual: GVBasics.RName, newPwd: GVBasics.Password] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, ChangePassword, individual, NIL, newPwd] ] }; SetConnect: PROC[user: GVBasics.RName, password: GVBasics.Password, individual: GVBasics.RName, connect: GVBasics.Connect] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, ChangeConnect, individual, connect] ] }; SetRemark: PROC[user: GVBasics.RName, password: GVBasics.Password, group: GVBasics.RName, remark: GVBasics.Remark] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, ChangeRemark, group, remark] ] }; <> AddMailbox: PROC[user: GVBasics.RName, password: GVBasics.Password, individual: GVBasics.RName, site: GVBasics.RName] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, AddMailBox, individual, site] ] }; AddForward: PROC[user: GVBasics.RName, password: GVBasics.Password, individual: GVBasics.RName, dest: GVBasics.RName] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, AddForward, individual, dest] ] }; AddMember: PROC[user: GVBasics.RName, password: GVBasics.Password, group: GVBasics.RName, member: GVBasics.RName] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, AddMember, group, member] ] }; AddOwner: PROC[user: GVBasics.RName, password: GVBasics.Password, group: GVBasics.RName, owner: GVBasics.RName] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, AddOwner, group, owner] ] }; AddFriend: PROC[user: GVBasics.RName, password: GVBasics.Password, group: GVBasics.RName, friend: GVBasics.RName] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, AddFriend, group, friend] ] }; <> RemoveMailbox: PROC[user: GVBasics.RName, password: GVBasics.Password, individual: GVBasics.RName, site: GVBasics.RName] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, DeleteMailBox, individual, site] ] }; RemoveForward: PROC[user: GVBasics.RName, password: GVBasics.Password, individual: GVBasics.RName, dest: GVBasics.RName] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, DeleteForward, individual, dest] ] }; RemoveMember: PROC[user: GVBasics.RName, password: GVBasics.Password, group: GVBasics.RName, member: GVBasics.RName] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, DeleteMember, group, member] ] }; RemoveOwner: PROC[user: GVBasics.RName, password: GVBasics.Password, group: GVBasics.RName, owner: GVBasics.RName] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, DeleteOwner, group, owner] ] }; RemoveFriend: PROC[user: GVBasics.RName, password: GVBasics.Password, group: GVBasics.RName, friend: GVBasics.RName] RETURNS[Outcome] = INLINE { RETURN[ Update[user, password, DeleteFriend, group, friend] ] }; Update: PROC[ user: GVBasics.RName, password: GVBasics.Password, op: RSOperation, target: GVBasics.RName, value: GVBasics.GVString _ NIL, newPwd: GVBasics.Password _ NULL, list: RListHandle _ NIL, reporter: ReporterProc _ NIL] RETURNS[Outcome]; RSOperation: TYPE = MACHINE DEPENDENT { NoOp(0), <> Expand(1), ReadMembers(2), ReadOwners(3), ReadFriends(4), ReadEntry(5), CheckStamp(6), ReadConnect(7), ReadRemark(8), Authenticate(9), <> CreateRegistry(10), -- not implemented DeleteRegistry(11), -- not implemented CreateIndividual(12), DeleteIndividual(13), CreateGroup(14), DeleteGroup(15), ChangePassword(16), ChangeConnect(17), ChangeRemark(18), AddMember(19), AddMailBox(20), AddForward(21), AddOwner(22), AddFriend(23), DeleteMember(24), DeleteMailBox(25), DeleteForward(26), DeleteOwner(27), DeleteFriend(28), AddSelf(29), DeleteSelf(30), AddListOfMembers(31), NewName(32), IdentifyCaller(33),--internal to GVNamesImpl IsMemberDirect(40), -- archaic IsOwnerDirect(41), -- archaic IsFriendDirect(42), -- archaic IsMemberClosure(43), -- archaic IsOwnerClosure(44), -- archaic IsFriendClosure(45), -- archaic IsInList(46), <<60..69 reserved for more enquiries>> ReadMailboxes(60), -- not implemented (255) }; ReporterProc: TYPE = PROC[Rope.ROPE]; <<"ReporterProc" is primarily to allow Maintain to report progress to its user. Consult a wizard before using this facility.>> SetServerInfo: TYPE = { badName, allDown, noRoute, ok }; SetServer: PROC[Rope.ROPE] RETURNS[ SetServerInfo ]; <<"SetServer" provides a server hint, primarily used by Maintain. Consult a wizard before using this facility.>> END. <<>> <> <> <> DIRECTORY GVBasics USING[ RName ], PupDefs USING[ PupAddress ]; GVLocate: CEDAR DEFINITIONS = BEGIN FoundState: TYPE = { allDown, notFound, found }; FoundServerInfo: TYPE = RECORD[ SELECT t: FoundState FROM allDown => NULL, notFound => NULL, found => [where: PupDefs.PupAddress], ENDCASE ]; FindNearestServer: PROC[list: GVBasics.RName, accept: PROC[PupDefs.PupAddress]RETURNS[BOOLEAN] ] RETURNS[ FoundServerInfo ]; FindLocalServer: PROC[listl: GVBasics.RName ] RETURNS[ FoundState, GVBasics.RName ]; FindRegServer: PROC[ who: GVBasics.RName, accept: PROC[PupDefs.PupAddress]RETURNS[BOOLEAN] ] RETURNS[ FoundServerInfo ]; AcceptFirst: PROC[PupDefs.PupAddress]RETURNS[BOOLEAN]; <> END. <<>> <> <> <> DIRECTORY GVBasics USING[ ItemHeader, RName, Timestamp ], IO USING[ STREAM ], Rope USING[ ROPE ]; GVRetrieve: CEDAR DEFINITIONS = BEGIN ROPE: TYPE = Rope.ROPE; <> Handle: TYPE = REF HandleObject; <> HandleObject: TYPE; Create: PROC[pollingInterval: CARDINAL, reportChanges: PROCEDURE[MBXState] _ NIL] RETURNS[Handle]; <> Close: PROC[Handle]; <> <> NewUser: PROC[ handle: Handle, user: GVBasics.RName, password: ROPE]; <> MBXState: TYPE = { unknown, badName, badPwd, cantAuth, userOK, allDown, someEmpty, allEmpty, notEmpty }; <> MailboxState: PROC[ handle: Handle] RETURNS[ state: MBXState]; <> WaitForMail: PROC[ handle: Handle ]; <> <> <> <> <> <> <> <> <<"ReadTOC" may be used to read any TOC entry for the message (giving length=0 if there is no TOC entry), then "StartMessage" may be called to read the guaranteed properties of the message; these are not available for MTP servers; these may not be called after you have called "NextItem" for this message.>> <<"NextItem" may be called to access in sequence the items which are the contents of the message body. Note that the ItemHeader contains the item type and length in bytes. The first item will be the guaranteed recipient list. The message body is followed by an item of type "LastItem". Within an item, use an IO.STREAM may be used to access the data of the item. EndOf[] will be true at the end of the item. Calling NextItem closes any IO.STREAM on a current item (even if the entrie item has not been read yet) and moves on to the next item.>> <> <> <> ServerType: TYPE = { MTP, GV }; ServerState: TYPE = { unknown, empty, notEmpty }; <<"unknown" means the server didn't reply to mail check packets; in this case, for efficiency, you should NOT call "NextMessage". There is no point in calling NextMessage if the ServerState is "empty".>> NextServer: PROC[ handle: Handle ] RETURNS[ noMore: BOOLEAN, state: ServerState, type: ServerType ]; <> ServerName: PROC[ handle: Handle] RETURNS [serverName: GVBasics.RName]; <> FailureReason: TYPE = { communicationFailure, -- server or network down -- noSuchServer, -- server name incorrect -- connectionRejected, -- server full, mbx busy, etc -- badCredentials, -- name/pwd rejected -- unknownFailure -- protocol violation or unknown MTP error: likely to be permanent -- }; Failed: ERROR[why: FailureReason, text: ROPE]; <> NextMessage: PROC[handle: Handle] RETURNS[msgExists, archived, deleted: BOOLEAN]; StartMessage: PROC[handle: Handle ] RETURNS[postmark: GVBasics.Timestamp, sender: GVBasics.RName, returnTo: GVBasics.RName]; NextItem: PROC[handle: Handle] RETURNS[GVBasics.ItemHeader]; <> GetItem: PROC[handle: Handle] RETURNS[IO.STREAM]; <> Accept: PROC[handle: Handle]; <> <<"ReadTOC", "WriteTOC" and "DeleteMessage" are useful mainly for Lily.>> ReadTOC: PROC[handle: Handle] RETURNS[ ROPE ]; WriteTOC: PROC[handle: Handle, entry:ROPE ]; DeleteMessage: PROC[handle: Handle] ; END. <<>> <> <> <> DIRECTORY Rope USING[ ROPE ], GVBasics USING[ ItemType, Password, RName ]; GVSend: CEDAR DEFINITIONS = BEGIN ROPE: TYPE = Rope.ROPE; <> Handle: TYPE = REF Object; Object: TYPE; Create: PROCEDURE RETURNS[handle: Handle]; <> <> <> <> <> <> <> SendFailed: ERROR [ why: Rope.ROPE, notDelivered: BOOLEAN ] ; <<"StartSend" raises no signals that may be caught by the client. The ERROR "SendFailed" may be raised by any of AddRecipient, CheckValidity, StartItem, AddToItem, or Send if some communication failure occurs. If it is raised, the client should generally catch it, go back to the start of the message, and re-call "StartSend". "StartSend" will then attempt to find a better mail server to talk to. Only when "StartSend" returns "allDown" is it not possible to send the message. The client may want to inform the user if thisre-try mechanism has been invoked.>> StartSendInfo: TYPE = MACHINE DEPENDENT { ok, badPwd, badSender, badReturnTo, allDown }; StartSend: PROC[ handle: Handle, senderPwd: ROPE, sender: GVBasics.RName, returnTo: GVBasics.RName _ NIL, validate: BOOLEAN ] RETURNS[ StartSendInfo ]; <> SendFromClient: PROC[ handle: Handle, fromNet: [0..256) _ 0, fromHost: [0..256) _ 0, senderKey: GVBasics.Password, sender: GVBasics.RName, returnTo: GVBasics.RName, validate: BOOLEAN ] RETURNS[ StartSendInfo ]; <> AddRecipient: PROC[ handle: Handle, recipient: GVBasics.RName ]; <> CheckValidity: PROC[ handle: Handle, notify: PROCEDURE[INT,GVBasics.RName] ] RETURNS[ ok: INT ]; <> StartItem: PROC[ handle: Handle, type: GVBasics.ItemType ]; <> StartText: PROC[ handle: Handle ] = INLINE{ StartItem[handle,Text] }; AddToItem: PROC[ handle: Handle, buffer: ROPE ]; <> Send: PROC[ handle: Handle ]; <> Abort: PROC[ handle: Handle ]; <> ExpandInfo: TYPE = MACHINE DEPENDENT { ok, notFound, individual, allDown} ; ExpandFailed: ERROR[why: Rope.ROPE]; Expand: PROC[name: GVBasics.RName, work: PROC[GVBasics.RName]] RETURNS[ ExpandInfo ]; <> END.