-- Transport Mechanism Registration Server - Operations on entries in a registry.
-- [Indigo]<Grapevine>MS>RegServerImpl.mesa
-- Randy Gobbel, 19-May-81 18:51:53
-- J. Dion, September 8, 1979
-- Andrew Birrell, 27-Oct-82 15:46:02
DIRECTORY
BodyDefs USING [maxRNameLength, oldestTime, RName,
RNameSize, Timestamp],
HeapDefs USING [GetWriterOffset, HeapAbandonWrite, HeapEndWrite,
HeapEndRead, HeapStartRead, HeapStartWrite, HeapWriteData,
HeapWriteRName, ObjectNumber, ObjectOffset,
objectStart, ReaderHandle,
SetReaderOffset, SetWriterOffset,
WriterHandle],
LogDefs USING[ WriteLogEntry, WriteString ],
NameInfoDefs USING[ Close, Enumerate, GetMembers, MemberInfo ],
ObjectDirDefs USING [FreeObject, UseObject],
ProtocolDefs,
RegAccessDefs USING[ Abandon, Insert, Lookup, NameState ],
RegBTreeDefs USING[ EnumerateTree, RegistryObject ],
RegCacheDefs USING[ AddConnect, ReadConnect ],
RegServerDefs USING[ MailUpdate, Membership, MembershipAcl,
MembershipGrade, MembershipLevel ],
RegistryDefs USING[ AddName, CheckStampList, CompareTimestamps,
Comparison, Copy, EnumerateRList, FilterStampList,
MakeTimestamp, MergeLists, ReadConnect,
ReadPassword, ReadPrefix, RemoveName, Skip,
SkipIfEmpty, UpdateInfo, WriteConnect, WriteList,
WritePassword, WritePrefix],
String USING [AppendString, EquivalentString,
EquivalentSubStrings, LowerCase, SubStringDescriptor],
Time USING[ Current, Packed ];
RegServerImpl: MONITOR
IMPORTS BodyDefs, HeapDefs, LogDefs, NameInfoDefs,
ObjectDirDefs, ProtocolDefs, RegAccessDefs, RegBTreeDefs, RegCacheDefs,
RegistryDefs, RegServerDefs, String, Time
EXPORTS RegServerDefs =
BEGIN
OPEN ProtocolDefs;
-- This module supports complete database entries, but without the
-- concept of "Registry"
-- ================ Layout of entries ================ --
Position: TYPE = MACHINE DEPENDENT { -- components of an entry --
--dead-- prefixD(1), endD(2),
--individual-- prefixI(11),
password(12),
connect(13),
forward(14), --(15,16,17)--
sites(18), --(19,20,21)--
endI(22),
--group-- prefixG(31),
remark(32),
members(33), --(34,35,36)--
owners(37), --(38,39,40)--
friends(41), --(42,43,44)--
endG(45) };
Start: PROC[type: ProtocolDefs.RNameType] RETURNS[Position] = INLINE
BEGIN
RETURN[ SELECT type FROM
dead => prefixD,
individual => prefixI,
group => prefixG,
ENDCASE => ERROR ]
END;
End: PROC[type: ProtocolDefs.RNameType] RETURNS[Position] = INLINE
BEGIN
RETURN[ SELECT type FROM
dead => endD,
individual => endI,
group => endG,
ENDCASE => ERROR ]
END;
Copy: PROC[reader: HeapDefs.ReaderHandle,
writer: HeapDefs.WriterHandle,
from, to: Position] =
BEGIN
-- copies parts of an entry; "from" is position that has just been read
-- "to" indicates what will be available to read after the copying --
-- Copy[x,y, prefixG, endG] copies a group entry except the prefix --
DO from ← SUCC[from]; IF from = to THEN EXIT;
RegistryDefs.Copy[reader, writer];
ENDLOOP;
END;
Skip: PROC[reader: HeapDefs.ReaderHandle,
from, to: Position] =
BEGIN
-- moves the reader efficiently; "from" is current position --
-- "to" indicates what will be available to read after the skipping --
-- Skip[x, prefixI, y] will skip from initial position to read "y" --
DO IF from = to THEN EXIT;
RegistryDefs.Skip[reader]; from ← SUCC[from];
ENDLOOP;
END;
-- ================ Pseudo-names ================ --
EnumeratedName: PROCEDURE[name: BodyDefs.RName]
RETURNS[enumType: ProtocolDefs.RNameType ] =
BEGIN
-- checks for correct syntax, with special case if
-- no registry is given.
sn: BodyDefs.RName = [BodyDefs.maxRNameLength];
index: CARDINAL;
FOR index DECREASING IN [0..name.length)
DO IF name[index] = '. THEN EXIT;
REPEAT
FINISHED => index ← name.length
ENDLOOP;
String.AppendString[sn, name];
sn.length ← index; -- "index" is also used below --
IF sn.length > 0 AND sn[sn.length-1] = '↑ THEN sn.length ← sn.length-1;
enumType ← IF String.EquivalentString[sn, "Individuals"L]
THEN individual
ELSE IF String.EquivalentString[sn, "Groups"L]
THEN group
ELSE IF String.EquivalentString[sn, "Dead"L]
THEN dead
ELSE notFound;
END;
EnumeratedMembers: PUBLIC PROCEDURE [name: BodyDefs.RName,
type: ProtocolDefs.RNameType]
RETURNS[reader: HeapDefs.ReaderHandle] =
BEGIN
registry: CARDINAL;
writer: HeapDefs.WriterHandle = HeapDefs.HeapStartWrite[temp];
memberWords: CARDINAL ← 0;
members: CARDINAL ← 0;
Action: PROCEDURE[ found: BodyDefs.RName ] =
BEGIN
index: CARDINAL;
IF found.length >= name.length - registry
THEN FOR index IN (0..name.length-registry]
DO IF String.LowerCase[found[found.length-index]] #
--already l.c.-- name[name.length-index]
THEN EXIT;
REPEAT
FINISHED =>
BEGIN
HeapDefs.HeapWriteRName[writer, found]; members ← members+1;
memberWords ← memberWords + BodyDefs.RNameSize[found];
END
ENDLOOP;
END;
GetReader: PROCEDURE[ obj: HeapDefs.ObjectNumber ] =
{ reader ← HeapDefs.HeapStartRead[obj] };
WriteComponentLength: PROCEDURE[length: CARDINAL] =
{ HeapDefs.HeapWriteData[writer, [@length,SIZE[CARDINAL]] ] };
BEGIN -- parse name to find registry --
FOR registry DECREASING IN [0..name.length)
DO IF name[registry] = '. THEN EXIT;
name[registry] ← String.LowerCase[name[registry]];
REPEAT
FINISHED => registry ← name.length
ENDLOOP;
END;
BEGIN -- members
lengthPos: HeapDefs.ObjectOffset = HeapDefs.GetWriterOffset[writer];
memberStampPos: HeapDefs.ObjectOffset;
WriteComponentLength[0]--place holder--;
RegBTreeDefs.EnumerateTree[type, Action];
memberStampPos ← HeapDefs.GetWriterOffset[writer];
HeapDefs.SetWriterOffset[writer, lengthPos];
WriteComponentLength[memberWords];
END;
HeapDefs.HeapEndWrite[writer, GetReader];
END;
ImpliedOwners: PROC[entry: BodyDefs.RName,
stamp: POINTER TO BodyDefs.Timestamp,
nameObj: POINTER TO RegAccessDefs.NameState]
RETURNS[reader: HeapDefs.ReaderHandle, rc: ReturnCode] =
BEGIN
-- ReadMembers for names of the form "Owners-foo↑.pa" --
IF CheckForOwners[entry]
THEN BEGIN
IF nameObj.reader # NIL THEN HeapDefs.HeapEndRead[nameObj.reader];
[reader, rc] ← ReadOwners[entry, stamp];
IF rc.code = done AND RegistryDefs.SkipIfEmpty[reader]
THEN BEGIN -- owner list is empty, so use registry --
HeapDefs.HeapEndRead[reader];
[reader, rc] ← ReadRegistryFriends[entry, stamp];
END;
-- special case of Owners-foo.pa where foo.pa is individual: --
-- must adjust return code to avoid confusing client of Expand --
IF rc = [BadRName,individual] THEN rc ← [BadRName,notFound];
END
ELSE { rc ← RegAccessDefs.Abandon[entry, nameObj]; reader ← NIL };
END;
CheckForOwners: PROC[entry: BodyDefs.RName] RETURNS[ BOOLEAN ] =
BEGIN
owner: STRING = "Owner-"L;
owners: STRING = "Owners-"L;
begin: STRING;
IF BeginsWith[entry, (begin←owner)]
OR BeginsWith[entry, (begin←owners)]
THEN BEGIN
FOR i: CARDINAL IN [begin.length..entry.length)
DO entry[i-begin.length] ← entry[i] ENDLOOP;
entry.length ← entry.length - begin.length;
RETURN[TRUE]
END
ELSE RETURN[FALSE]
END;
-- ================ Access to entries ================ --
CheckRName: PUBLIC PROCEDURE [entry: BodyDefs.RName,
stamp: POINTER TO BodyDefs.Timestamp]
RETURNS [rc: ReturnCode] =
BEGIN
nameObj: RegAccessDefs.NameState ← RegAccessDefs.Lookup[entry, readNone];
SELECT TRUE FROM
nameObj.type = notFound AND nameObj.regState = yes =>
BEGIN
IF EnumeratedName[entry] # notFound
THEN { rc ← [done, group]; stamp↑ ← RegistryDefs.MakeTimestamp[] }
ELSE IF CheckForOwners[entry] -- updates "entry" if successful --
THEN BEGIN
rc ← CheckRName[entry, stamp];
IF rc.type = individual THEN rc ← [BadRName,notFound];
END
ELSE rc ← RegAccessDefs.Abandon[entry, @nameObj];
END;
nameObj.type = notFound OR nameObj.type = dead =>
rc ← RegAccessDefs.Abandon[entry, @nameObj];
ENDCASE =>
IF nameObj.stamp # stamp↑
THEN { stamp↑ ← nameObj.stamp; rc ← [done, nameObj.type] }
ELSE rc ← [noChange, nameObj.type];
END;
Read: PUBLIC PROCEDURE[entry: BodyDefs.RName]
RETURNS [reader: HeapDefs.ReaderHandle, rc: ReturnCode,
components, password: CARDINAL] =
BEGIN
nameObj: RegAccessDefs.NameState ← RegAccessDefs.Lookup[entry, readAny];
reader ← nameObj.reader;
rc ← [done, nameObj.type];
SELECT nameObj.type FROM
individual =>
BEGIN
components ← LOOPHOLE[Position[endI],CARDINAL]
- LOOPHOLE[Position[prefixI],CARDINAL];
password ← LOOPHOLE[Position[password],CARDINAL]
- LOOPHOLE[Position[prefixI],CARDINAL];
END;
group =>
BEGIN
components ← LOOPHOLE[Position[endG],CARDINAL]
- LOOPHOLE[Position[prefixG],CARDINAL];
password ← LAST[CARDINAL];
END;
dead =>
BEGIN
components ← LOOPHOLE[Position[endD],CARDINAL]
- LOOPHOLE[Position[prefixD],CARDINAL];
password ← LAST[CARDINAL];
END;
ENDCASE => rc ← RegAccessDefs.Abandon[entry, @nameObj];
END;
ReadPassword: PUBLIC PROCEDURE [entry: BodyDefs.RName]
RETURNS [pw:Password, rc: ReturnCode] =
BEGIN
nameObj: RegAccessDefs.NameState ←
RegAccessDefs.Lookup[entry, readIndividual];
IF nameObj.type # individual
THEN rc ← RegAccessDefs.Abandon[entry, @nameObj]
ELSE BEGIN
stamp: BodyDefs.Timestamp;
Skip[nameObj.reader, prefixI, password];
[pw, stamp] ← RegistryDefs.ReadPassword[nameObj.reader];
HeapDefs.HeapEndRead [nameObj.reader];
rc ← [done, nameObj.type];
END;
END;
ReadConnect: PUBLIC PROCEDURE [entry: BodyDefs.RName, connect: Connect]
RETURNS [rc: ReturnCode] =
BEGIN
nameObj: RegAccessDefs.NameState;
IF RegCacheDefs.ReadConnect[entry, connect]
THEN RETURN[ [done,individual] ];
nameObj ← RegAccessDefs.Lookup[entry, readIndividual];
IF nameObj.type # individual
THEN rc ← RegAccessDefs.Abandon[entry, @nameObj]
ELSE BEGIN
stamp: BodyDefs.Timestamp;
Skip[nameObj.reader, prefixI, connect];
stamp ← RegistryDefs.ReadConnect[nameObj.reader, connect];
HeapDefs.HeapEndRead [nameObj.reader];
RegCacheDefs.AddConnect[entry, nameObj.stamp, connect];
rc ← [done, nameObj.type];
END;
END;
ReadRemark: PUBLIC PROCEDURE [entry: BodyDefs.RName, remark: Remark]
RETURNS [rc: ReturnCode] =
BEGIN
nameObj: RegAccessDefs.NameState ←
RegAccessDefs.Lookup[entry, readGroup];
IF nameObj.type # group
THEN rc ← RegAccessDefs.Abandon[entry, @nameObj]
ELSE BEGIN
stamp: BodyDefs.Timestamp;
Skip[nameObj.reader, prefixG, remark];
stamp ← RegistryDefs.ReadConnect[nameObj.reader, remark];
HeapDefs.HeapEndRead [nameObj.reader];
rc ← [done, nameObj.type];
END;
END;
Expand: PUBLIC PROCEDURE [entry: BodyDefs.RName,
stamp: POINTER TO BodyDefs.Timestamp]
RETURNS [reader: HeapDefs.ReaderHandle, rc: ReturnCode] =
BEGIN
-- don't allow delivery to enumerated groups --
nameObj: RegAccessDefs.NameState ←
RegAccessDefs.Lookup[entry, readEither];
reader ← nameObj.reader;
SELECT nameObj.type FROM
individual =>
IF (rc ← PositionReader[@nameObj, forward, stamp]).code = done
THEN IF RegistryDefs.SkipIfEmpty[reader]
THEN Skip[reader, SUCC[forward], sites]
ELSE rc ← [done, group]
ELSE NULL;
group => rc ← PositionReader [@nameObj, members, stamp];
ENDCASE =>
[reader, rc] ← ImpliedOwners[entry, stamp, @nameObj];
END;
BeginsWith: PROC[s: STRING, b: STRING] RETURNS[ BOOLEAN ] =
BEGIN
pattern: String.SubStringDescriptor ← [b, 0, b.length];
target: String.SubStringDescriptor ← [s, 0, b.length];
RETURN[ s.length >= b.length
AND String.EquivalentSubStrings[@pattern,@target] ]
END;
EndsWith: PROC[s: STRING, b: STRING] RETURNS[ BOOLEAN ] =
BEGIN
pattern: String.SubStringDescriptor ← [b, 0, b.length];
target: String.SubStringDescriptor ← [s,s.length-b.length,b.length];
RETURN[ s.length >= b.length
AND String.EquivalentSubStrings[@pattern,@target] ]
END;
ReadSites: PUBLIC PROCEDURE [entry: BodyDefs.RName,
stamp: POINTER TO BodyDefs.Timestamp]
RETURNS [reader: HeapDefs.ReaderHandle, rc: ReturnCode] =
BEGIN
nameObj: RegAccessDefs.NameState ←
RegAccessDefs.Lookup[entry, readIndividual];
reader ← nameObj.reader;
IF nameObj.type = individual
THEN rc ← PositionReader[@nameObj, sites, stamp]
ELSE rc ← RegAccessDefs.Abandon[entry, @nameObj];
END;
ReadMembers: PUBLIC PROCEDURE [entry: BodyDefs.RName,
stamp: POINTER TO BodyDefs.Timestamp]
RETURNS [reader: HeapDefs.ReaderHandle, rc: ReturnCode] =
BEGIN
nameObj: RegAccessDefs.NameState ←
RegAccessDefs.Lookup[entry, readGroup];
reader ← nameObj.reader;
IF nameObj.type = group
THEN rc ← PositionReader[@nameObj, members, stamp]
ELSE IF nameObj.type # notFound OR nameObj.regState # yes
THEN rc ← RegAccessDefs.Abandon[entry, @nameObj]
ELSE BEGIN
enumType: ProtocolDefs.RNameType = EnumeratedName[entry];
IF enumType # notFound
THEN BEGIN
reader ← EnumeratedMembers[entry, enumType];
stamp↑ ← RegistryDefs.MakeTimestamp[];
rc ← [done,group];
END
ELSE [reader, rc] ← ImpliedOwners[entry, stamp, @nameObj];
END;
END;
ReadOwners: PUBLIC PROCEDURE [entry: BodyDefs.RName,
stamp: POINTER TO BodyDefs.Timestamp]
RETURNS [reader: HeapDefs.ReaderHandle, rc: ReturnCode] =
BEGIN
nameObj: RegAccessDefs.NameState ←
RegAccessDefs.Lookup[entry, readGroup];
reader ← nameObj.reader;
IF nameObj.type = group
THEN rc ← PositionReader[@nameObj, owners, stamp]
ELSE rc ← RegAccessDefs.Abandon[entry, @nameObj];
END;
ReadFriends: PUBLIC PROCEDURE [entry: BodyDefs.RName,
stamp: POINTER TO BodyDefs.Timestamp]
RETURNS [reader: HeapDefs.ReaderHandle, rc: ReturnCode] =
BEGIN
nameObj: RegAccessDefs.NameState ←
RegAccessDefs.Lookup[entry, readGroup];
reader ← nameObj.reader;
IF nameObj.type = group
THEN rc ← PositionReader[@nameObj, friends, stamp]
ELSE rc ← RegAccessDefs.Abandon[entry, @nameObj];
END;
ReadRegistry: PROC[entry: BodyDefs.RName,
stamp: POINTER TO BodyDefs.Timestamp,
work: PROC[BodyDefs.RName,POINTER TO BodyDefs.Timestamp]
RETURNS[HeapDefs.ReaderHandle,ReturnCode] ]
RETURNS[reader: HeapDefs.ReaderHandle, rc: ReturnCode] =
BEGIN
gvReg: STRING = ".gv"L;
regName: BodyDefs.RName = [BodyDefs.maxRNameLength];
index: CARDINAL;
sep: CARDINAL;
FOR index DECREASING IN [0..entry.length)
DO IF entry[index] = '. THEN BEGIN sep ← index+1; EXIT END; REPEAT
FINISHED => sep ← 0
ENDLOOP;
FOR index IN [sep..entry.length)
DO regName[index-sep] ← entry[index] ENDLOOP;
regName.length ← entry.length-sep;
IF regName.length + gvReg.length > regName.maxlength
THEN { reader ← NIL; rc ← [BadRName, notFound] }
ELSE BEGIN
String.AppendString[regName, gvReg];
[reader,rc] ← work[regName, stamp];
END;
END;
ReadRegistryMembers: PUBLIC PROC[entry: BodyDefs.RName,
stamp: POINTER TO BodyDefs.Timestamp]
RETURNS[reader: HeapDefs.ReaderHandle, rc: ReturnCode] =
{ [reader,rc] ← ReadRegistry[entry, stamp, ReadMembers] };
ReadRegistryOwners: PUBLIC PROC[entry: BodyDefs.RName,
stamp: POINTER TO BodyDefs.Timestamp]
RETURNS[reader: HeapDefs.ReaderHandle, rc: ReturnCode] =
{ [reader,rc] ← ReadRegistry[entry, stamp, ReadOwners] };
ReadRegistryFriends: PUBLIC PROC[entry: BodyDefs.RName,
stamp: POINTER TO BodyDefs.Timestamp]
RETURNS[reader: HeapDefs.ReaderHandle, rc: ReturnCode] =
{ [reader,rc] ← ReadRegistry[entry, stamp, ReadFriends] };
PositionReader: PROCEDURE [nameObj: POINTER TO RegAccessDefs.NameState,
sublist: Position,
oldStamp: POINTER TO BodyDefs.Timestamp]
RETURNS [rc: ReturnCode] =
BEGIN
IF oldStamp↑ = nameObj.stamp
THEN BEGIN
HeapDefs.HeapEndRead[nameObj.reader];
rc ← [noChange, nameObj.type];
END
ELSE BEGIN
oldStamp↑ ← nameObj.stamp;
Skip[nameObj.reader, Start[nameObj.type], sublist];
rc ← [done, nameObj.type]
END
END;
-- ================ Access control and membership ================ --
LowerCase: PROC[c: CHARACTER] RETURNS[CHARACTER] = INLINE
{ RETURN[ IF c IN ['A..'Z] THEN 'a + (c-'A) ELSE c ] };
StarPattern: PROC[p: STRING] RETURNS[BOOLEAN] = INLINE
{ RETURN[ p.length > 0 AND p[0] = '* ] };
StarMatch: PROC[pattern, name: STRING] RETURNS[BOOLEAN] =
BEGIN
-- Assumes pattern.length > 0 and pattern[0] = '* --
nPtr: CARDINAL ← name.length;
IF name.length < pattern.length THEN RETURN[FALSE];
FOR pPtr: CARDINAL DECREASING IN [1..pattern.length)
DO nPtr ← nPtr - 1;
IF LowerCase[name[nPtr]] # LowerCase[pattern[pPtr]]
THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE]
END;
HaveIDoneThis: SIGNAL[new: BodyDefs.RName]RETURNS[BOOLEAN] = CODE;
-- used to prevent recursive loops --
IsInList: PUBLIC PROCEDURE[entry: BodyDefs.RName, member: BodyDefs.RName,
level: RegServerDefs.MembershipLevel,
grade: RegServerDefs.MembershipGrade,
acl: RegServerDefs.MembershipAcl ]
RETURNS[ membership: RegServerDefs.Membership,
rc: ProtocolDefs.ReturnCode ] =
BEGIN
getList: PROCEDURE[BodyDefs.RName, POINTER TO BodyDefs.Timestamp]
RETURNS[HeapDefs.ReaderHandle,ReturnCode] =
SELECT acl FROM
members => IF grade = self THEN ReadMembers ELSE ReadRegistryMembers,
owners => IF grade = self THEN ReadOwners ELSE ReadRegistryOwners,
friends => IF grade = self THEN ReadFriends ELSE ReadRegistryFriends,
ENDCASE => ERROR;
[membership,rc] ← IsInListSub[entry, member,
IF level = closure THEN upArrow ELSE level,
getList !
HaveIDoneThis => RESUME[FALSE] ];
IF level = closure AND membership = no
THEN [membership,rc] ← IsInListSub[entry, member, level, getList !
HaveIDoneThis => RESUME[FALSE] ];
END;
IsInListSub: PROCEDURE[entry: BodyDefs.RName, member: BodyDefs.RName,
level: RegServerDefs.MembershipLevel,
getList: PROCEDURE[BodyDefs.RName,
POINTER TO BodyDefs.Timestamp]
RETURNS[HeapDefs.ReaderHandle,ReturnCode] ]
RETURNS[ membership: RegServerDefs.Membership,
rc: ProtocolDefs.ReturnCode ] =
BEGIN
reader: HeapDefs.ReaderHandle;
Work: PROC[name: BodyDefs.RName] RETURNS[done: BOOLEAN] =
BEGIN
Accept: PROC =
{ membership ← yes; done ← TRUE; rc ← [done,group] };
ExpandThis: PROC RETURNS[ BOOLEAN ] = INLINE
BEGIN
SELECT level FROM
direct => RETURN[FALSE];
closure => RETURN[TRUE];
upArrow =>
BEGIN
FOR i: CARDINAL DECREASING IN [0..name.length)
DO IF name[i] = '. THEN RETURN[i > 0 AND name[i-1] = '↑];
ENDLOOP;
RETURN[FALSE]
END;
ENDCASE => ERROR;
END;
done ← FALSE;
SELECT TRUE FROM
(IF StarPattern[name]
THEN StarMatch[name, member]
ELSE String.EquivalentString[name, member]) => Accept[];
ExpandThis[] =>
IF SIGNAL HaveIDoneThis[name]
THEN NULL -- recursive loop elimination --
ELSE BEGIN
ENABLE HaveIDoneThis =>
IF String.EquivalentString[new,name] THEN RESUME[TRUE];
subMember: RegServerDefs.Membership;
subRC: ProtocolDefs.ReturnCode;
[subMember, subRC] ←
IsInListSub[entry:name, member:member,
level:level,
getList: ReadMembers];
SELECT subRC.code FROM
done => { IF subMember = yes THEN Accept[] };
WrongServer =>
BEGIN -- ask remote R-Server -
subInfo: NameInfoDefs.MemberInfo =
NameInfoDefs.GetMembers[name];
WITH s: subInfo SELECT FROM
group =>
BEGIN
NameInfoDefs.Enumerate[s.members, Work];
NameInfoDefs.Close[s.members];
IF membership = yes THEN done ← TRUE;
END;
allDown => rc.code ← AllDown;
ENDCASE => NULL;
END;
AllDown => rc.code ← AllDown; -- stronger default RC --
ENDCASE => NULL;
END;
ENDCASE => NULL;
END;
SELECT TRUE FROM
-- IsMember["*", foo] or IsMember["*.pa", foo] --
getList = ReadMembers AND StarPattern[entry] =>
BEGIN
rc ← [done,group];
membership ← IF StarMatch[entry, member] THEN yes ELSE no;
END;
-- IsMember["individuals.reg", foo] --
getList = ReadMembers AND (EnumeratedName[entry] = individual) =>
BEGIN
silly: BodyDefs.Timestamp ← BodyDefs.oldestTime;
namePtr: CARDINAL ← entry.length;
membership ← no; rc ← [done,group];
FOR memberPtr: CARDINAL DECREASING IN [0 .. member.length)
DO namePtr ← namePtr-1;
IF namePtr = 0 THEN EXIT;
IF String.LowerCase[entry[namePtr]] #
String.LowerCase[member[memberPtr]] THEN EXIT;
IF entry[namePtr] = '.
THEN { membership ← IF CheckRName[member,@silly].type=individual
THEN yes ELSE no;
EXIT };
ENDLOOP;
END;
ENDCASE =>
BEGIN
oldTimePtr: BodyDefs.Timestamp ← BodyDefs.oldestTime; --ugh!--
[reader, rc] ← getList[entry, @oldTimePtr]; -- sets default RC --
IF rc.code = done
THEN BEGIN
membership ← no;
RegistryDefs.EnumerateRList[reader, Work];
HeapDefs.HeapEndRead[reader];
END
ELSE membership ← badList;
END;
END;
-- ================ Changing entries ================ --
CheckCreation: PROC[name: BodyDefs.RName,
nameObj: POINTER TO RegAccessDefs.NameState]
RETURNS[ ok: BOOLEAN ] =
BEGIN
IF EnumeratedName[name] # notFound
OR CheckForOwners[name]
OR StarPattern[name]
THEN nameObj.type ← group;
RETURN[ nameObj.type = notFound AND nameObj.regState = yes ]
END;
CreateIndividual: PUBLIC PROC[name: BodyDefs.RName,
password: Password]
RETURNS [rc: ReturnCode] =
BEGIN
nameObj: RegAccessDefs.NameState ← RegAccessDefs.Lookup[name, readAny];
-- must use "readAny", for update synchronization logic if entry was dead
DO IF CheckCreation[name, @nameObj]
THEN BEGIN
stamp: BodyDefs.Timestamp ← RegistryDefs.MakeTimestamp[];
IF RegistryDefs.CompareTimestamps[stamp, nameObj.stamp] # greater
THEN BEGIN
rc ← [outOfDate, nameObj.type];
IF nameObj.reader # NIL
THEN HeapDefs.HeapEndRead[nameObj.reader];
END
ELSE BEGIN
writer: HeapDefs.WriterHandle =
HeapDefs.HeapStartWrite[RSobject];
RegistryDefs.WritePrefix[writer, individual, @stamp, name];
RegistryDefs.WritePassword [writer, password, stamp];
RegistryDefs.WriteConnect[writer, ""L, stamp];
RegistryDefs.WriteList[writer, NIL, stamp];
RegistryDefs.WriteList[writer, NIL, stamp];
IF NOT FinishUpdate[name, individual, @stamp, writer,
@nameObj, NIL, CreateIndividual]
THEN LOOP;
rc ← [done, individual];
END;
END
ELSE rc ← RegAccessDefs.Abandon[name, @nameObj];
EXIT
ENDLOOP
END;
DeleteIndividual: PUBLIC PROCEDURE [name: BodyDefs.RName]
RETURNS [rc: ReturnCode] =
BEGIN
rc ← DeleteRName[name, individual];
END;
CreateGroup: PUBLIC PROCEDURE [name, caller: BodyDefs.RName]
RETURNS [rc: ReturnCode] =
BEGIN
nameObj: RegAccessDefs.NameState ← RegAccessDefs.Lookup[name, readAny];
-- must use "readAny", for update synchronization logic if entry was dead
DO IF CheckCreation[name, @nameObj]
THEN BEGIN
stamp: BodyDefs.Timestamp ← RegistryDefs.MakeTimestamp [];
IF RegistryDefs.CompareTimestamps[stamp, nameObj.stamp] # greater
THEN BEGIN
rc ← [outOfDate, nameObj.type];
IF nameObj.reader # NIL
THEN HeapDefs.HeapEndRead[nameObj.reader];
END
ELSE BEGIN
writer: HeapDefs.WriterHandle =
HeapDefs.HeapStartWrite [RSobject];
RegistryDefs.WritePrefix[writer, group, @stamp, name];
--TEMP-- RegistryDefs.WriteConnect[writer, ""L, stamp]; -- remark
RegistryDefs.WriteList[writer, NIL, stamp]; -- members
RegistryDefs.WriteList[writer, NIL, stamp]; -- owners
RegistryDefs.WriteList[writer, NIL, stamp]; -- friends
IF NOT FinishUpdate[name, group, @stamp, writer, @nameObj, NIL, CreateGroup]
THEN LOOP;
rc ← [done, group];
END;
END
ELSE rc ← RegAccessDefs.Abandon[name, @nameObj];
EXIT
ENDLOOP;
END;
DeleteGroup: PUBLIC PROCEDURE [name: BodyDefs.RName]
RETURNS [rc: ReturnCode] =
BEGIN
rc ← DeleteRName[name, group];
END;
DeleteRName: PROCEDURE[name: BodyDefs.RName, type: ProtocolDefs.RNameType]
RETURNS[rc: ReturnCode] =
BEGIN
nameObj: RegAccessDefs.NameState ← RegAccessDefs.Lookup[name, readAny];
-- must acquire reader on old object if it's an individual, to allow
-- internal mail to mail servers --
DO IF nameObj.type = type
THEN BEGIN
stamp: BodyDefs.Timestamp ← RegistryDefs.MakeTimestamp[];
IF RegistryDefs.CompareTimestamps[stamp, nameObj.stamp] # greater
THEN BEGIN
rc ← [outOfDate, nameObj.type];
IF nameObj.reader # NIL
THEN HeapDefs.HeapEndRead[nameObj.reader];
END
ELSE BEGIN
writer: HeapDefs.WriterHandle =
HeapDefs.HeapStartWrite [RSobject];
RegistryDefs.WritePrefix[writer, dead, @stamp, name];
IF NOT FinishUpdate[name, dead, @stamp, writer, @nameObj,
NIL,
IF type = individual THEN DeleteIndividual ELSE DeleteGroup]
THEN LOOP;
rc ← [done, dead];
END;
END
ELSE rc ← RegAccessDefs.Abandon[name, @nameObj];
EXIT
ENDLOOP;
END;
NewName: PUBLIC PROCEDURE[new, old: BodyDefs.RName]
RETURNS[rc: ProtocolDefs.ReturnCode] =
BEGIN
reader: HeapDefs.ReaderHandle;
components: CARDINAL;
[reader, rc, components,] ← Read[old];
IF rc.code = done
THEN BEGIN
nameObj: RegAccessDefs.NameState ←
RegAccessDefs.Lookup[new, readAny];
DO IF CheckCreation[new, @nameObj]
THEN BEGIN
stamp: BodyDefs.Timestamp ← RegistryDefs.MakeTimestamp [];
IF RegistryDefs.CompareTimestamps[stamp, nameObj.stamp] # greater
THEN BEGIN
rc ← [outOfDate, nameObj.type];
IF nameObj.reader # NIL
THEN HeapDefs.HeapEndRead[nameObj.reader];
END
ELSE BEGIN
writer: HeapDefs.WriterHandle =
HeapDefs.HeapStartWrite [RSobject];
RegistryDefs.WritePrefix[writer, rc.type, @stamp, new];
HeapDefs.SetReaderOffset[reader, HeapDefs.objectStart];
RegistryDefs.Skip[reader];
THROUGH (1..components]
DO RegistryDefs.Copy[reader, writer] ENDLOOP;
IF NOT FinishUpdate[new, rc.type, @stamp, writer,
@nameObj, NIL, NewName]
THEN BEGIN
HeapDefs.SetReaderOffset[reader,
HeapDefs.objectStart];
LOOP
END;
END;
END
ELSE rc ← RegAccessDefs.Abandon[new, @nameObj];
EXIT
ENDLOOP;
-- Only specify to support names both in same registry --
IF rc.code = WrongServer THEN rc.code ← BadProtocol;
END;
IF reader # NIL THEN HeapDefs.HeapEndRead[reader];
END;
ChangePassword: PUBLIC PROCEDURE [entry: BodyDefs.RName, pw: Password]
RETURNS [rc: ReturnCode] =
BEGIN
rc ← ChangeItem[entry, [password[pw]], ChangePassword ];
END;
ChangeConnect: PUBLIC PROCEDURE [entry: BodyDefs.RName,
connect: ProtocolDefs.Connect]
RETURNS [rc: ProtocolDefs.ReturnCode] =
BEGIN
rc ← ChangeItem[entry, [conn[connect]], ChangeConnect ];
END;
ChangeRemark: PUBLIC PROCEDURE [entry: BodyDefs.RName,
remark: ProtocolDefs.Remark]
RETURNS [rc: ProtocolDefs.ReturnCode] =
BEGIN
rc ← ChangeItem[entry, [rem[remark]], ChangeRemark ];
END;
ChangeItem: PROCEDURE[ entry: BodyDefs.RName,
item: RECORD[ SELECT t:* FROM
password => [pw: Password],
conn => [connect: Connect],
rem => [remark: Remark],
ENDCASE ],
op: ProtocolDefs.RSOperation]
RETURNS[ rc: ReturnCode ] =
BEGIN
nameObj: RegAccessDefs.NameState ← RegAccessDefs.Lookup[entry, readAny];
DO IF nameObj.type = (SELECT item.t FROM
password, conn => individual, rem => group, ENDCASE => ERROR)
THEN BEGIN
stamp: BodyDefs.Timestamp ← RegistryDefs.MakeTimestamp[];
oldpw: Password;
oldConnect: Connect = [ProtocolDefs.maxConnectLength];
oldRemark: Remark = [ProtocolDefs.maxRemarkLength];
oldstamp: BodyDefs.Timestamp;
writer : HeapDefs.WriterHandle =
HeapDefs.HeapStartWrite [RSobject];
start: Position = Start[nameObj.type];
[,] ← RegistryDefs.ReadPrefix[nameObj.reader, entry]--correct case--;
RegistryDefs.WritePrefix[writer, nameObj.type, @stamp, entry];
Copy[nameObj.reader, writer, start,
SELECT item.t FROM
password => password, conn => connect,
rem => remark ENDCASE => ERROR];
WITH item SELECT FROM
password =>
[oldpw, oldstamp] ← RegistryDefs.ReadPassword [nameObj.reader];
conn =>
oldstamp ← RegistryDefs.ReadConnect[nameObj.reader, oldConnect];
rem =>
--TEMP-- oldstamp ← RegistryDefs.ReadConnect[nameObj.reader, oldRemark];
ENDCASE => ERROR;
SELECT RegistryDefs.CompareTimestamps [stamp, oldstamp] FROM
less => BEGIN
HeapDefs.HeapAbandonWrite[writer];
HeapDefs.HeapEndRead[nameObj.reader];
rc ← [outOfDate, nameObj.type];
END;
equal => BEGIN
HeapDefs.HeapAbandonWrite[writer];
HeapDefs.HeapEndRead[nameObj.reader];
rc ← [noChange, nameObj.type];
END;
greater=>
BEGIN
WITH item SELECT FROM
password =>
BEGIN
RegistryDefs.WritePassword [writer, pw, stamp];
Copy[nameObj.reader, writer, password, endI];
END;
conn =>
BEGIN
RegistryDefs.WriteConnect[writer, connect, stamp];
Copy[nameObj.reader, writer, connect, endI];
END;
rem =>
BEGIN
--TEMP-- RegistryDefs.WriteConnect[writer, remark, stamp];
Copy[nameObj.reader, writer, remark, endG];
END;
ENDCASE => ERROR;
IF NOT FinishUpdate[entry, nameObj.type, @stamp, writer,
@nameObj,
NIL,
op ]
THEN LOOP;
rc ← [done, nameObj.type];
END;
ENDCASE => ERROR;
END
ELSE rc ← RegAccessDefs.Abandon[entry, @nameObj];
EXIT
ENDLOOP;
END;
AddMailbox: PUBLIC PROCEDURE [name, entry: BodyDefs.RName]
RETURNS [rc: ReturnCode] =
BEGIN
rc ← UpdateSublist[name, entry, AddMailBox];
END;
DeleteMailbox: PUBLIC PROCEDURE [name, entry: BodyDefs.RName]
RETURNS [rc: ReturnCode] =
BEGIN
rc ← UpdateSublist[name, entry, DeleteMailBox];
END;
AddForward: PUBLIC PROCEDURE [name, entry: BodyDefs.RName]
RETURNS [rc: ReturnCode] =
BEGIN
rc ← UpdateSublist[name, entry, AddForward];
END;
DeleteForward: PUBLIC PROCEDURE [name, entry: BodyDefs.RName]
RETURNS [rc: ReturnCode] =
BEGIN
rc ← UpdateSublist[name, entry, DeleteForward];
END;
AddMember: PUBLIC PROCEDURE [name, entry: BodyDefs.RName]
RETURNS [rc: ReturnCode] =
BEGIN
[rc] ← UpdateSublist[name, entry, AddMember]
END;
DeleteMember: PUBLIC PROCEDURE [name, entry: BodyDefs.RName]
RETURNS [rc: ReturnCode] =
BEGIN
[rc] ← UpdateSublist[name, entry, DeleteMember]
END;
AddOwner: PUBLIC PROCEDURE [name, entry: BodyDefs.RName]
RETURNS [rc: ReturnCode] =
BEGIN
rc ← UpdateSublist[name, entry, AddOwner]
END;
DeleteOwner: PUBLIC PROCEDURE [name, entry: BodyDefs.RName]
RETURNS [rc: ReturnCode] =
BEGIN
rc ← UpdateSublist[name, entry, DeleteOwner]
END;
AddFriend: PUBLIC PROCEDURE [name, entry: BodyDefs.RName]
RETURNS [rc: ReturnCode] =
BEGIN
rc ← UpdateSublist[name, entry, AddFriend]
END;
DeleteFriend: PUBLIC PROCEDURE [name, entry: BodyDefs.RName]
RETURNS [rc: ReturnCode] =
BEGIN
rc ← UpdateSublist[name, entry, DeleteFriend]
END;
AddListOfMembers: PUBLIC PROCEDURE[name: BodyDefs.RName,
reader: HeapDefs.ReaderHandle]
RETURNS[ rc: ReturnCode ] =
BEGIN
stamp: BodyDefs.Timestamp ← RegistryDefs.MakeTimestamp[];
rc ← UpdateSingleSublist[name, reader, @stamp, group, members];
END;
UpdateSublist: PUBLIC PROCEDURE [name, element: BodyDefs.RName,
op: ProtocolDefs.RSOperation,
prevStamp: BodyDefs.Timestamp ← BodyDefs.oldestTime]
RETURNS [rc: ReturnCode] =
BEGIN
propagate: BOOLEAN = prevStamp = BodyDefs.oldestTime;
expectedType: ProtocolDefs.RNameType = SELECT op FROM
AddMailBox, DeleteMailBox, AddForward, DeleteForward => individual,
AddMember, DeleteMember, AddOwner, DeleteOwner, AddFriend, DeleteFriend => group,
ENDCASE => ERROR;
nameObj: RegAccessDefs.NameState ←
RegAccessDefs.Lookup[name, SELECT expectedType FROM
individual => readIndividual,
group => readGroup,
ENDCASE => ERROR];
DO IF nameObj.type = expectedType
THEN BEGIN
writer: HeapDefs.WriterHandle = HeapDefs.HeapStartWrite[RSobject];
elementStamp: BodyDefs.Timestamp ←
IF propagate
THEN RegistryDefs.MakeTimestamp[]
ELSE prevStamp;
globalStamp: BodyDefs.Timestamp ←
IF RegistryDefs.CompareTimestamps[elementStamp, nameObj.stamp]
= greater
THEN elementStamp
ELSE RegistryDefs.MakeTimestamp[];
info: RegistryDefs.UpdateInfo;
component: Position = SELECT op FROM
AddMailBox, DeleteMailBox => sites,
AddForward, DeleteForward => forward,
AddMember, DeleteMember => members,
AddOwner, DeleteOwner => owners,
AddFriend, DeleteFriend => friends,
ENDCASE => ERROR;
add: BOOLEAN = SELECT op FROM
AddMailBox, AddForward, AddMember, AddOwner, AddFriend => TRUE,
ENDCASE => FALSE;
[,] ← RegistryDefs.ReadPrefix[nameObj.reader, name]--correct case--;
RegistryDefs.WritePrefix[writer, nameObj.type,
@globalStamp, name];
Copy[nameObj.reader, writer, Start[nameObj.type], component];
info ← IF add
THEN RegistryDefs.AddName[nameObj.reader, element,
@elementStamp, writer]
ELSE RegistryDefs.RemoveName[nameObj.reader, element,
@elementStamp, writer];
IF info = done OR (NOT propagate AND info = noChange)
THEN BEGIN
Copy[nameObj.reader, writer,
SUCC[SUCC[SUCC[component]]], End[nameObj.type] ];
IF NOT (IF propagate
THEN FinishUpdate[name, expectedType,
@globalStamp,
writer,
@nameObj,
element, op]
ELSE RegAccessDefs.Insert[name, expectedType,
@globalStamp,
writer,
@nameObj]
)
THEN LOOP;
rc ← [done, nameObj.type]
END
ELSE BEGIN
HeapDefs.HeapEndRead [nameObj.reader];
rc ← [SELECT info FROM
outOfDate => outOfDate,
noChange => noChange,
ENDCASE => ERROR, nameObj.type];
HeapDefs.HeapAbandonWrite[writer];
END;
IF NOT propagate
THEN LogMerge[name, prevStamp, info = outOfDate,
info # noChange, TRUE];
END
ELSE rc ← RegAccessDefs.Abandon[name, @nameObj];
EXIT
ENDLOOP;
END;
UpdateSingleSublist: PROCEDURE[name: BodyDefs.RName,
update: HeapDefs.ReaderHandle,
stamp: POINTER TO BodyDefs.Timestamp,
expectedType: RNameType,
component: Position]
RETURNS [rc: ReturnCode] =
BEGIN
nameObj: RegAccessDefs.NameState ←
RegAccessDefs.Lookup[name, SELECT expectedType FROM
individual => readIndividual,
group => readGroup,
ENDCASE => ERROR];
DO IF nameObj.type = expectedType
THEN BEGIN
writer: HeapDefs.WriterHandle = HeapDefs.HeapStartWrite[RSobject];
updateNewer, readerNewer: BOOLEAN;
[,] ← RegistryDefs.ReadPrefix[nameObj.reader, name]--correct case--;
RegistryDefs.WritePrefix[writer, nameObj.type, stamp, name];
Copy[nameObj.reader, writer, Start[nameObj.type], component];
[updateNewer, readerNewer] ←
RegistryDefs.MergeLists [update, nameObj.reader, writer];
Copy[nameObj.reader, writer,
SUCC[SUCC[SUCC[component]]], End[nameObj.type] ];
IF updateNewer
THEN BEGIN
IF NOT FinishUpdate[name, expectedType, stamp, writer,
@nameObj, NIL, AddListOfMembers]
THEN LOOP;
rc ← [done, nameObj.type]
END
ELSE BEGIN
HeapDefs.HeapEndRead [nameObj.reader];
IF readerNewer
THEN rc ← [outOfDate, nameObj.type]
ELSE rc ← [noChange, nameObj.type];
HeapDefs.HeapAbandonWrite[writer];
END;
END
ELSE rc ← RegAccessDefs.Abandon[name, @nameObj];
EXIT
ENDLOOP;
HeapDefs.HeapEndRead[update];
END;
dontOptimizeMembers: BOOLEAN ← TRUE;
FinishUpdate: PROCEDURE [name: BodyDefs.RName,
type: ProtocolDefs.RNameType,
stamp: POINTER TO BodyDefs.Timestamp,
writer: HeapDefs.WriterHandle,
info: POINTER TO RegAccessDefs.NameState,
element: BodyDefs.RName,
op: ProtocolDefs.RSOperation]
RETURNS[done: BOOLEAN] =
BEGIN
-- write an RSmail object to ensure others are told about the update --
-- then end the writer, committing us to the update --
-- then tell others about the update --
-- then throw away the RSmail object --
-- returns done=FALSE iff Insert does so --
rsMailObject: HeapDefs.ObjectNumber = RecordDelivery[name];
ResetForMailboxSites[info];
IF (done ← RegAccessDefs.Insert[name, type, stamp, writer, info])
THEN RegServerDefs.MailUpdate[name, stamp↑, element, op, rsMailObject]
ELSE ObjectDirDefs.FreeObject[rsMailObject];
END;
RecordDelivery: PROCEDURE [name: BodyDefs.RName]
RETURNS [object: HeapDefs.ObjectNumber] =
BEGIN
writer: HeapDefs.WriterHandle = HeapDefs.HeapStartWrite [RSmail];
now: Time.Packed ← Time.Current[];
GetObjectNumber: PROCEDURE [number: HeapDefs.ObjectNumber] =
{ object ← number; ObjectDirDefs.UseObject[number] };
HeapDefs.HeapWriteRName[writer, name];
HeapDefs.HeapWriteData[writer, [@now,SIZE[Time.Packed]] ];
HeapDefs.HeapEndWrite [writer, GetObjectNumber];
END;
-- ================ Merge update from other server ================ --
Update: PUBLIC PROCEDURE [object: HeapDefs.ObjectNumber] =
BEGIN
oldNameObj: RegAccessDefs.NameState;
newReader: HeapDefs.ReaderHandle = HeapDefs.HeapStartRead[object];
newRName: BodyDefs.RName = [BodyDefs.maxRNameLength];
newType: ProtocolDefs.RNameType;
newStamp: BodyDefs.Timestamp;
mineIsNewer: BOOLEAN ← FALSE;
hisIsNewer: BOOLEAN ← FALSE;
[newType, newStamp] ← RegistryDefs.ReadPrefix[newReader, newRName];
oldNameObj ← RegAccessDefs.Lookup[newRName, readAny];
-- state of newReader here must be same as after the "tryAgain" exit --
DO BEGIN
stampCompare: RegistryDefs.Comparison =
RegistryDefs.CompareTimestamps[newStamp,oldNameObj.stamp];
bestStamp: BodyDefs.Timestamp ←
IF stampCompare = greater
THEN newStamp
ELSE -- Our stamp was newer (or equal?) --
-- Stamps on our disk must be monotonic, for restart sequence --
RegistryDefs.MakeTimestamp[];
writer: HeapDefs.WriterHandle = HeapDefs.HeapStartWrite[RSobject];
-- newReader has had the prefix read from it;
-- oldRegObj.reader has had nothing read from it;
-- writer has had nothing written to it;
SELECT TRUE FROM -- the order of the arms of the SELECT matters! --
( oldNameObj.type = dead AND (newType # dead OR stampCompare = less) ),
-- and: ignore update for registry we don't know about.
-- NB: this is correct only in absence of addition of registries!
( oldNameObj.type = notFound AND oldNameObj.regState # yes ) =>
BEGIN
-- our object is better --
IF oldNameObj.reader # NIL
THEN HeapDefs.HeapEndRead[oldNameObj.reader];
HeapDefs.HeapAbandonWrite[writer];
mineIsNewer ← TRUE;
EXIT --no update needed--
END;
(oldNameObj.type = notFound
OR newType = dead--and newStamp is newer or old is not dead--) =>
BEGIN
-- new entry, or his object is "dead" --
RegistryDefs.WritePrefix[writer, newType, @bestStamp, newRName];
Copy[newReader, writer, Start[newType], End[newType]];
hisIsNewer ← TRUE;
END
ENDCASE =>
BEGIN
n1, n2: BOOLEAN;
RegistryDefs.WritePrefix[writer, newType, @bestStamp, newRName];
SELECT newType FROM
dead => ERROR;
group =>
[n1,n2] ← UpdateGroup[oldNameObj.reader, newReader, writer];
individual =>
[n1,n2] ← UpdateIndividual[oldNameObj.reader, newReader, writer];
ENDCASE => ERROR;
IF n1 THEN mineIsNewer ← TRUE;
IF n2 THEN hisIsNewer ← TRUE;
END;
ResetForMailboxSites[@oldNameObj];
IF RegAccessDefs.Insert[newRName, newType, @bestStamp, writer,
@oldNameObj]
THEN EXIT
ELSE BEGIN
HeapDefs.SetReaderOffset[newReader, HeapDefs.objectStart];
[newType, newStamp] ← RegistryDefs.ReadPrefix[newReader, newRName];
END;
END
ENDLOOP;
HeapDefs.HeapEndRead[newReader];
LogMerge[newRName, newStamp, mineIsNewer, hisIsNewer, FALSE];
END;
LogMerge: PROC[name: BodyDefs.RName, stamp: BodyDefs.Timestamp,
mineIsNewer, hisIsNewer, brief: BOOLEAN] =
BEGIN
log: STRING = [140];
String.AppendString[log, IF brief THEN "Brief merge "L ELSE "Merge "L];
String.AppendString[log, name];
String.AppendString[log, ", stamp="L];
ProtocolDefs.AppendTimestamp[log, stamp];
IF NOT hisIsNewer
THEN String.AppendString[log, ", no change"L];
IF mineIsNewer
THEN String.AppendString[log, ", anti-entropy"L];
LogDefs.WriteLogEntry[log];
IF brief THEN LogDefs.WriteString["Brief "L];
LogDefs.WriteString["Merge "L]; LogDefs.WriteString[name];
END;
UpdateGroup: PROCEDURE [oldReader, newReader: HeapDefs.ReaderHandle,
writer: HeapDefs.WriterHandle]
RETURNS[ n1, n2: BOOLEAN ] =
BEGIN
-- returns n1=TRUE iff oldReader has info newer than newReader;
-- returns n2=TRUE iff newReader has info newer than oldReader;
-- Note that both or neither may be true.
ListsInGroup: CARDINAL = 3;
n1 ← n2 ← FALSE;
Skip[oldReader, prefixG, remark];
BEGIN
oldStamp, newStamp: BodyDefs.Timestamp;
oldRem: Remark = [maxRemarkLength];
newRem: Remark = [maxRemarkLength];
oldStamp ← RegistryDefs.ReadConnect[oldReader, oldRem];
newStamp ← RegistryDefs.ReadConnect[newReader, newRem];
SELECT RegistryDefs.CompareTimestamps[oldStamp, newStamp] FROM
greater => { n1 ← TRUE;
RegistryDefs.WriteConnect[writer, oldRem, oldStamp] };
equal => RegistryDefs.WriteConnect[writer, newRem, newStamp];
less => { n2 ← TRUE;
RegistryDefs.WriteConnect[writer, newRem, newStamp] };
ENDCASE => ERROR;
END;
THROUGH [1 .. ListsInGroup]
DO newer1, newer2: BOOLEAN;
[newer1, newer2] ←
RegistryDefs.MergeLists[oldReader, newReader, writer];
IF newer1 THEN n1 ← TRUE; IF newer2 THEN n2 ← TRUE;
ENDLOOP;
END;
UpdateIndividual: PROCEDURE [oldReader, newReader: HeapDefs.ReaderHandle,
writer: HeapDefs.WriterHandle]
RETURNS[ n1, n2: BOOLEAN ] =
BEGIN
-- returns n1=TRUE iff oldReader has info newer than newReader;
-- returns n2=TRUE iff newReader has info newer than oldReader;
-- Note that both or neither may be true.
ListsInIndividual: CARDINAL = 2;
n1 ← n2 ← FALSE;
Skip[oldReader, prefixI, SUCC[prefixI]];
BEGIN
oldStamp, newStamp: BodyDefs.Timestamp;
oldpw, newpw: Password;
[oldpw, oldStamp] ← RegistryDefs.ReadPassword [oldReader];
[newpw, newStamp] ← RegistryDefs.ReadPassword [newReader];
SELECT RegistryDefs.CompareTimestamps[oldStamp, newStamp] FROM
greater => { n1 ← TRUE;
RegistryDefs.WritePassword[writer, oldpw, oldStamp] };
equal => RegistryDefs.WritePassword[writer, newpw, newStamp];
less => { n2 ← TRUE;
RegistryDefs.WritePassword[writer, newpw, newStamp] };
ENDCASE => ERROR;
END;
BEGIN
oldStamp, newStamp: BodyDefs.Timestamp;
oldConn: Connect = [maxConnectLength];
newConn: Connect = [maxConnectLength];
oldStamp ← RegistryDefs.ReadConnect[oldReader, oldConn];
newStamp ← RegistryDefs.ReadConnect[newReader, newConn];
SELECT RegistryDefs.CompareTimestamps[oldStamp, newStamp] FROM
greater => { n1 ← TRUE;
RegistryDefs.WriteConnect[writer, oldConn, oldStamp] };
equal => RegistryDefs.WriteConnect[writer, newConn, newStamp];
less => { n2 ← TRUE;
RegistryDefs.WriteConnect[writer, newConn, newStamp] };
ENDCASE => ERROR;
END;
THROUGH [1 .. ListsInIndividual]
DO newer1, newer2: BOOLEAN;
[newer1, newer2] ←
RegistryDefs.MergeLists [oldReader, newReader, writer];
IF newer1 THEN n1 ← TRUE; IF newer2 THEN n2 ← TRUE;
ENDLOOP;
END;
CantReadOldEntry: ERROR = CODE;
ResetForMailboxSites: PROC[info: POINTER TO RegAccessDefs.NameState] =
BEGIN
-- re-position reader to mailbox sites, for generating
-- internal mail to appropriate MS's --
IF info.type = individual -- old entry was individual --
THEN BEGIN
IF info.reader = NIL THEN ERROR CantReadOldEntry[];
HeapDefs.SetReaderOffset[info.reader, HeapDefs.objectStart];
Skip[info.reader, Start[individual], sites];
END;
END;
-- ================ RegPurger utilities ================ --
ConsiderPurging: PUBLIC PROC[regObj: RegBTreeDefs.RegistryObject,
oldest: BodyDefs.Timestamp]
RETURNS[ yes: BOOLEAN ] =
BEGIN
SELECT regObj.type FROM
dead =>
yes ← RegistryDefs.CompareTimestamps[regObj.stamp, oldest] = less;
individual =>
BEGIN
Skip[regObj.reader, prefixI, SUCC[SUCC[SUCC[forward]]]];
yes ← RegistryDefs.CheckStampList[regObj.reader, oldest];
IF yes THEN RETURN;
Skip[regObj.reader, SUCC[SUCC[SUCC[SUCC[forward]]]],
SUCC[SUCC[SUCC[sites]]]];
yes ← RegistryDefs.CheckStampList[regObj.reader, oldest];
END;
group =>
BEGIN
Skip[regObj.reader, prefixG, SUCC[SUCC[SUCC[members]]]];
yes ← RegistryDefs.CheckStampList[regObj.reader, oldest];
IF yes THEN RETURN;
Skip[regObj.reader, SUCC[SUCC[SUCC[SUCC[members]]]],
SUCC[SUCC[SUCC[owners]]]];
yes ← RegistryDefs.CheckStampList[regObj.reader, oldest];
IF yes THEN RETURN;
Skip[regObj.reader, SUCC[SUCC[SUCC[SUCC[owners]]]],
SUCC[SUCC[SUCC[friends]]]];
yes ← RegistryDefs.CheckStampList[regObj.reader, oldest];
END;
ENDCASE => ERROR;
END;
ReallyPurge: PUBLIC PROC[name: BodyDefs.RName,
regObj: RegBTreeDefs.RegistryObject,
oldest: BodyDefs.Timestamp]
RETURNS[yes: BOOLEAN, writer: HeapDefs.WriterHandle] =
BEGIN
SELECT regObj.type FROM
dead =>
BEGIN
yes ← RegistryDefs.CompareTimestamps[regObj.stamp, oldest] = less;
writer←NIL
END;
individual =>
BEGIN
yes ← TRUE; writer ← HeapDefs.HeapStartWrite[RSobject];
RegistryDefs.Copy[regObj.reader, writer]; --prefix--
Copy[regObj.reader, writer, prefixI, SUCC[SUCC[forward]]];
RegistryDefs.FilterStampList[regObj.reader, oldest, writer];
Copy[regObj.reader, writer, SUCC[SUCC[SUCC[forward]]],
SUCC[SUCC[sites]]];
RegistryDefs.FilterStampList[regObj.reader, oldest, writer];
END;
group =>
BEGIN
yes ← TRUE; writer ← HeapDefs.HeapStartWrite[RSobject];
RegistryDefs.Copy[regObj.reader, writer]; --prefix--
Copy[regObj.reader, writer, prefixG, SUCC[SUCC[members]]];
RegistryDefs.FilterStampList[regObj.reader, oldest, writer];
Copy[regObj.reader, writer, SUCC[SUCC[SUCC[members]]],
SUCC[SUCC[owners]]];
RegistryDefs.FilterStampList[regObj.reader, oldest, writer];
Copy[regObj.reader, writer, SUCC[SUCC[SUCC[owners]]],
SUCC[SUCC[friends]]];
RegistryDefs.FilterStampList[regObj.reader, oldest, writer];
END;
ENDCASE => ERROR;
IF regObj.reader # NIL THEN HeapDefs.HeapEndRead[regObj.reader];
END;
END.