-- Copyright (C) 1981, 1982, 1983, 1984 by Xerox Corporation. All rights reserved. -- Transport Mechanism Registration Server - Internal Mail. -- [Ibis]<Grapevine>Pilot>RegMail.mesa -- HGM, 10-Dec-84 23:57:13 -- Randy Gobbel, 19-May-81 12:26:40 -- Andrew Birrell, 18-Nov-82 10:33:12 -- Mike Schroeder 8-Feb-83 9:43:22 -- Hankins 14-Aug-84 16:30:13 (Klamath update - remove STOPs) DIRECTORY BodyDefs USING [ ItemHeader, ItemLength, ItemType, maxRNameLength, oldestTime, Password, RName, RNameSize, Timestamp], EnquiryDefs USING [], HeapDefs USING [ HeapEndWrite, HeapEndRead, HeapReadData, HeapStartWrite, HeapWriteData, ObjectNumber, ReaderHandle, WriterHandle], LocalNameDefs USING [ReadRSName], LogDefs USING [WriteLogEntry], ObjectDirDefs USING [FreeObject, noObject], PolicyDefs USING [CheckOperation, EndOperation, Wait, WaitOperation], Process USING [Detach, DisableTimeout], ProtocolDefs USING [AppendTimestamp, ReturnCode, RSOperation], RegBTreeDefs USING [Lookup, LookupReason, RegistryObject], RegServerDefs USING [IsMember, ReadRegistryMembers, Update, UpdateSublist], RegistryDefs USING [EnumerateRList], RetrieveDefs USING [ AccessProcs, Failed, Handle, Create, MailboxState, NewUser, NextServer, ServerName, ServerState, WaitForMail], SendDefs USING [ Abort, AddRecipient, AddToItem, Create, Destroy, Handle, StartItem, StartSend, Send], String USING [AppendString, EquivalentString], VMDefs USING [AllocatePage, Page, pageSize, Release]; RegMail: MONITOR IMPORTS BodyDefs, HeapDefs, LocalNameDefs, LogDefs, ObjectDirDefs, PolicyDefs, Process, ProtocolDefs, RegBTreeDefs, RegistryDefs, RegServerDefs, RetrieveDefs, SendDefs, String, VMDefs EXPORTS EnquiryDefs --LoginRSMail-- , RegServerDefs = BEGIN OPEN ProtocolDefs; -- the update mail is generated in a separate process to allow return to -- client sooner, and to avoid deadlock if we generate an update while -- determining our R-Server name. updateEntry: BodyDefs.RName = [BodyDefs.maxRNameLength]; updateStamp: BodyDefs.Timestamp; updateElement: BodyDefs.RName = [BodyDefs.maxRNameLength]; updateOp: ProtocolDefs.RSOperation; updateRSMailObj: HeapDefs.ObjectNumber; updatePending: BOOLEAN ← FALSE; updateCond: CONDITION; updateEnabled: BOOLEAN ← FALSE; -- set TRUE early in restart -- NoRegistryForName: ERROR = CODE; FailedToSendUpdate: ERROR = CODE; MailUpdate: PUBLIC ENTRY PROCEDURE [ entry: BodyDefs.RName, stamp: BodyDefs.Timestamp, element: BodyDefs.RName, op: ProtocolDefs.RSOperation, rsMailObj: HeapDefs.ObjectNumber] = BEGIN IF updateEnabled THEN BEGIN WHILE updatePending DO WAIT updateCond ENDLOOP; updateEntry.length ← 0; String.AppendString[updateEntry, entry]; updateStamp ← stamp; updateElement.length ← 0; IF element # NIL THEN String.AppendString[updateElement, element]; updateOp ← op; updateRSMailObj ← rsMailObj; updatePending ← TRUE; NOTIFY updateCond; END ELSE IF rsMailObj # ObjectDirDefs.noObject THEN ObjectDirDefs.FreeObject[rsMailObj]; END; SendRSMail: ENTRY PROC = BEGIN DO UNTIL updatePending DO WAIT updateCond ENDLOOP; SendUpdate[updateEntry, updateStamp, updateElement, updateOp]; IF updateRSMailObj # ObjectDirDefs.noObject THEN ObjectDirDefs.FreeObject[updateRSMailObj]; updatePending ← FALSE; NOTIFY updateCond; ENDLOOP; END; briefUpdate: BodyDefs.ItemType = LOOPHOLE[2001B]; briefUpdateAllowed: BOOLEAN ← TRUE; SendUpdate: INTERNAL PROC [ entry: BodyDefs.RName, stamp: BodyDefs.Timestamp, element: BodyDefs.RName, op: ProtocolDefs.RSOperation] = BEGIN -- mail the object to other registration servers for entry's registry. regobj: RegBTreeDefs.RegistryObject = RegBTreeDefs.Lookup[entry, readAny]; myName: BodyDefs.RName; myPassword: STRING; [myName, myPassword, ] ← LocalNameDefs.ReadRSName[]; IF regobj.type # notFound THEN BEGIN message: SendDefs.Handle = SendDefs.Create[]; itsRegReader: HeapDefs.ReaderHandle = LookupRegistry[entry]; IF SendDefs.StartSend[ handle: message, senderPwd: myPassword, sender: myName, returnTo: "DeadLetter.MS"L, validate: FALSE] # ok THEN ERROR FailedToSendUpdate[]; IF CopyRList[message, myName, itsRegReader] > 0 THEN BEGIN humanHint: STRING = "RS internal mail for R-Name "L; SELECT op FROM AddMember, DeleteMember => IF briefUpdateAllowed THEN BriefUpdate[message, entry, stamp, element, op] ELSE FullUpdate[message, regobj.reader]; ENDCASE => FullUpdate[message, regobj.reader]; HeapDefs.HeapEndRead[regobj.reader]; -- in case it gets to DeadLetter! -- SendDefs.StartItem[message, Text]; SendDefs.AddToItem[ message, DESCRIPTOR[@(humanHint.text), humanHint.length]]; SendDefs.AddToItem[message, DESCRIPTOR[@(entry.text), entry.length]]; SendDefs.Send[message]; END ELSE BEGIN --no recipients-- SendDefs.Abort[message]; HeapDefs.HeapEndRead[regobj.reader]; END; SendDefs.Destroy[message]; END; END; LookupRegistry: PROCEDURE [name: BodyDefs.RName] RETURNS [reader: HeapDefs.ReaderHandle] = BEGIN oldTimePtr: BodyDefs.Timestamp ← BodyDefs.oldestTime; --ugh!-- rc: ProtocolDefs.ReturnCode; [reader, rc] ← RegServerDefs.ReadRegistryMembers[name, @oldTimePtr]; IF rc.code # done THEN ERROR NoRegistryForName[]; END; CopyRList: PROCEDURE [ message: SendDefs.Handle, me: BodyDefs.RName, reader: HeapDefs.ReaderHandle] RETURNS [recipients: CARDINAL] = BEGIN Work: PROC [name: BodyDefs.RName] RETURNS [done: BOOLEAN] = BEGIN done ← FALSE; IF NOT String.EquivalentString[me, name] THEN BEGIN SendDefs.AddRecipient[message, name]; recipients ← recipients + 1; END; END; recipients ← 0; RegistryDefs.EnumerateRList[reader, Work]; HeapDefs.HeapEndRead[reader]; END; FullUpdate: PROCEDURE [ message: SendDefs.Handle, reader: HeapDefs.ReaderHandle] = BEGIN buffer: VMDefs.Page = VMDefs.AllocatePage[]; BEGIN ENABLE UNWIND => VMDefs.Release[buffer]; ended: BOOLEAN; used: CARDINAL; SendDefs.StartItem[message, updateItem]; DO [ended, used] ← HeapDefs.HeapReadData[reader, [buffer, VMDefs.pageSize]]; SendDefs.AddToItem[ message, DESCRIPTOR[LOOPHOLE[buffer, POINTER], 2 * used]]; IF ended THEN EXIT; -- why isn't the error SendDefs.SendFailed recognized in this module? ENDLOOP; END; VMDefs.Release[buffer]; END; BriefUpdate: PROCEDURE [ message: SendDefs.Handle, entry: BodyDefs.RName, stamp: BodyDefs.Timestamp, element: BodyDefs.RName, op: ProtocolDefs.RSOperation] = BEGIN SendDefs.StartItem[message, briefUpdate]; SendDefs.AddToItem[ message, DESCRIPTOR[ LOOPHOLE[@op, POINTER], 2 * SIZE[ProtocolDefs.RSOperation]]]; SendDefs.AddToItem[ message, DESCRIPTOR[ LOOPHOLE[entry, POINTER], 2 * BodyDefs.RNameSize[entry]]]; SendDefs.AddToItem[ message, DESCRIPTOR[ LOOPHOLE[element, POINTER], 2 * BodyDefs.RNameSize[element]]]; SendDefs.AddToItem[ message, DESCRIPTOR[ LOOPHOLE[@stamp, POINTER], 2 * SIZE[BodyDefs.Timestamp]]]; END; -- RS Mail reading -- handle: RetrieveDefs.Handle = RetrieveDefs.Create[pollingInterval: 30]; readerActive: BOOLEAN ← FALSE; readerInactive: CONDITION ← [timeout: 0]; LoginRSMail: PUBLIC ENTRY PROC = BEGIN myName: BodyDefs.RName; myPassword: STRING; WHILE readerActive DO WAIT readerInactive ENDLOOP; [myName, myPassword, ] ← LocalNameDefs.ReadRSName[]; RetrieveDefs.NewUser[handle: handle, user: myName, password: myPassword]; DO sName: BodyDefs.RName = [BodyDefs.maxRNameLength]; noMore: BOOLEAN; [noMore, , ] ← RetrieveDefs.NextServer[handle]; IF noMore THEN EXIT; LogInboxSite["RS inbox-site: "L]; ENDLOOP; END; LogInboxSite: INTERNAL PROC [s: STRING] = BEGIN log: STRING = [64]; server: STRING = [64]; RetrieveDefs.ServerName[handle, server]; String.AppendString[log, s]; server.length ← MIN[server.length, log.maxlength - log.length]; String.AppendString[log, server]; LogDefs.WriteLogEntry[log]; END; LockReader: ENTRY PROC = INLINE {readerActive ← TRUE}; UnlockReader: ENTRY PROC = INLINE {readerActive ← FALSE; NOTIFY readerInactive}; MyNameIsBad: ERROR = CODE; MyPwdIsBad: ERROR = CODE; ReadMail: PUBLIC PROCEDURE = BEGIN -- Reads outstanding mail before returning -- LoginRSMail[]; SELECT RetrieveDefs.MailboxState[handle] FROM badName => ERROR MyNameIsBad[]; badPwd => ERROR MyPwdIsBad[]; notEmpty => IF PolicyDefs.CheckOperation[RSReadMail] THEN { RegMailReader[]; PolicyDefs.EndOperation[RSReadMail]}; ENDCASE => NULL; Process.Detach[FORK RegMailWatcher[]]; END; RegMailWatcher: PROC = BEGIN DO RetrieveDefs.WaitForMail[handle]; PolicyDefs.WaitOperation[RSReadMail]; RegMailReader[]; PolicyDefs.EndOperation[RSReadMail]; PolicyDefs.Wait[mins: 3]; ENDLOOP; END; RegMailReader: PROC = BEGIN LockReader[]; DO noMore: BOOLEAN; state: RetrieveDefs.ServerState; gv: RetrieveDefs.AccessProcs; [noMore, state, gv] ← RetrieveDefs.NextServer[handle]; IF noMore THEN EXIT; IF state # notEmpty THEN LOOP; BEGIN ENABLE RetrieveDefs.Failed => CONTINUE; pleaseFlush: BOOLEAN ← TRUE; DO sender: BodyDefs.RName = [BodyDefs.maxRNameLength]; time: BodyDefs.Timestamp; msgExists, archived, deleted: BOOLEAN; pleaseDelete: BOOLEAN ← TRUE; [msgExists, archived, deleted] ← gv.nextMessage[handle]; IF deleted THEN LOOP; IF NOT msgExists THEN EXIT; gv.startMessage[handle: handle, sender: sender, postmark: @time]; IF CheckAndLog[sender, time] THEN DO header: BodyDefs.ItemHeader = gv.nextItem[handle]; SELECT header.type FROM LastItem => EXIT; updateItem => {AcceptUpdate[handle, @gv]; EXIT}; briefUpdate => BEGIN IF NOT AcceptBriefUpdate[handle, header.length, @gv] -- keep only names in a local registry which weren't found THEN pleaseDelete ← FALSE; EXIT END; ENDCASE => NULL; ENDLOOP --each item-- ; IF pleaseDelete THEN gv.deleteMessage[handle] ELSE pleaseFlush ← FALSE; ENDLOOP --each message-- ; IF pleaseFlush THEN gv.accept[handle]; END; ENDLOOP --each server-- ; UnlockReader[]; END; CheckAndLog: PROC [sender: BodyDefs.RName, time: BodyDefs.Timestamp] RETURNS [good: BOOLEAN] = BEGIN log: STRING = [128]; good ← TRUE; String.AppendString[log, "RS reading "L]; ProtocolDefs.AppendTimestamp[log, time]; String.AppendString[log, " from "L]; String.AppendString[log, sender]; IF RegServerDefs.IsMember["*.gv"L, sender, direct].membership # yes THEN { String.AppendString[log, ": bad sender"L]; good ← FALSE}; LogDefs.WriteLogEntry[log]; END; GetMsgItem: PROCEDURE [ handle: RetrieveDefs.Handle, gv: POINTER TO RetrieveDefs.AccessProcs] RETURNS [writer: HeapDefs.WriterHandle] = BEGIN buffer: VMDefs.Page = VMDefs.AllocatePage[]; BEGIN ENABLE UNWIND => VMDefs.Release[buffer]; length: CARDINAL; writer ← HeapDefs.HeapStartWrite[temp]; WHILE (length ← gv.nextBlock[handle, DESCRIPTOR[buffer, 2 * VMDefs.pageSize]]) > 0 DO HeapDefs.HeapWriteData[writer, [buffer, (1 + length) / 2]]; ENDLOOP; END; VMDefs.Release[buffer]; END; MangledMessage: ERROR = CODE; AcceptUpdate: PROCEDURE [ handle: RetrieveDefs.Handle, gv: POINTER TO RetrieveDefs.AccessProcs] = BEGIN updateWriter: HeapDefs.WriterHandle = GetMsgItem[handle, gv]; AcceptRestOfMessage[handle, gv]; HeapDefs.HeapEndWrite[updateWriter, RegServerDefs.Update]; END; --AcceptUpdate-- AcceptBriefUpdate: PROCEDURE [ handle: RetrieveDefs.Handle, itemLength: BodyDefs.ItemLength, gv: POINTER TO RetrieveDefs.AccessProcs] RETURNS [ok: BOOLEAN] = BEGIN strHeader: CARDINAL = 2 * SIZE[StringBody [0]]; op: ProtocolDefs.RSOperation; entry: BodyDefs.RName = [BodyDefs.maxRNameLength]; element: BodyDefs.RName = [BodyDefs.maxRNameLength]; stamp: BodyDefs.Timestamp; rc: ProtocolDefs.ReturnCode; regLocal: BOOLEAN; length: CARDINAL; length ← gv.nextBlock[ handle, DESCRIPTOR[ LOOPHOLE[@op, POINTER], 2 * SIZE[ProtocolDefs.RSOperation]]]; itemLength ← itemLength - length; length ← gv.nextBlock[ handle, DESCRIPTOR[LOOPHOLE[entry, POINTER], strHeader]]; itemLength ← itemLength - length; length ← gv.nextBlock[ handle, DESCRIPTOR[ LOOPHOLE[@entry.text, POINTER], 2 * BodyDefs.RNameSize[entry] - strHeader]]; itemLength ← itemLength - length; length ← gv.nextBlock[ handle, DESCRIPTOR[LOOPHOLE[element, POINTER], strHeader]]; itemLength ← itemLength - length; length ← gv.nextBlock[ handle, DESCRIPTOR[ LOOPHOLE[@element.text, POINTER], 2 * BodyDefs.RNameSize[element] - strHeader]]; itemLength ← itemLength - length; length ← gv.nextBlock[ handle, DESCRIPTOR[ LOOPHOLE[@stamp, POINTER], 2 * SIZE[BodyDefs.Timestamp]]]; itemLength ← itemLength - length; IF itemLength # 0 OR length # 2 * SIZE[BodyDefs.Timestamp] THEN ERROR MangledMessage[]; AcceptRestOfMessage[handle, gv]; [rc, regLocal] ← RegServerDefs.UpdateSublist[entry, element, op, stamp]; RETURN[rc.type # notFound AND regLocal] END; AcceptRestOfMessage: PROCEDURE [ handle: RetrieveDefs.Handle, gv: POINTER TO RetrieveDefs.AccessProcs] = BEGIN DO header: BodyDefs.ItemHeader = gv.nextItem[handle]; SELECT header.type FROM LastItem => EXIT; ENDCASE => NULL; ENDLOOP; END; --AcceptRestOfMessage-- RegMailInit: PUBLIC PROCEDURE = { Process.DisableTimeout[@updateCond]; Process.Detach[FORK SendRSMail[]]}; RegMailEnableUpdates: PUBLIC ENTRY PROCEDURE = {updateEnabled ← TRUE}; END.