-- Copyright (C) 1981, 1983, 1984, 1985 by Xerox Corporation. All rights reserved. -- CreateMail.mesa, HGM, 10-Dec-85 23:31:35 -- Transport Mechanism Mail Server - Create mail from user -- -- Randy Gobbel 19-May-81 21:21:39 -- -- Andrew Birrell 1-Apr-81 14:54:01 -- -- Mike Schroeder 25-Jan-83 16:18:46 -- DIRECTORY BodyDefs USING [ ItemHeader, ItemLength, ItemType, maxRNameLength, Password, RName, RNameSize, Timestamp], Heap USING [systemZone], HeapDefs USING [ GetWriterOffset, HeapAbandonWrite, HeapEndRead, HeapEndWrite, HeapReadData, HeapReadRName, HeapStartRead, HeapStartWrite, HeapWriteData, HeapWriteRName, ObjectNumber, ObjectOffset, ReaderHandle, ReadRList, SetWriterOffset, WriterHandle], LogDefs USING [WriteChar, WriteLogEntry], NameInfoDefs USING [Close, Enumerate, IsMemberDirect, IsOwnerDirect], ProtocolDefs USING [AppendTimestamp, MakeKey], PupDefs USING [PupAddress, PupNameLookup, PupPackageMake], SendDefs USING [ExpandInfo, StartSendInfo], SiteCacheDefs USING [FindMBXSite, RecipientInfo, ValidateRName], SLDefs USING [SLHeader, SLWrite], String USING [AppendDecimal, AppendLongDecimal, AppendString], Time USING [Current]; CreateMail: MONITOR LOCKS handle USING handle: Handle IMPORTS BodyDefs, Heap, HeapDefs, LogDefs, NameInfoDefs, PupDefs, ProtocolDefs, SiteCacheDefs, SLDefs, String, Time EXPORTS SendDefs = BEGIN RNameFromString: PROCEDURE [s: LONG STRING] RETURNS [BOOLEAN] = BEGIN RETURN[IF s.length > BodyDefs.maxRNameLength THEN FALSE ELSE TRUE] END; LogStart: PROCEDURE [handle: Handle, sender: LONG STRING] = BEGIN log: LONG STRING ← Heap.systemZone.NEW[StringBody[128]]; String.AppendString[log, "Created "L]; ProtocolDefs.AppendTimestamp[log, handle.stamp]; String.AppendString[log, ": sender "L]; String.AppendString[log, sender]; LogDefs.WriteLogEntry[log]; Heap.systemZone.FREE[@log]; END; LogCommit: PROCEDURE [handle: Handle] = BEGIN log: LONG STRING ← Heap.systemZone.NEW[StringBody[128]]; String.AppendString[log, "Client input: "L]; ProtocolDefs.AppendTimestamp[log, handle.stamp]; String.AppendString[log, ", "L]; String.AppendDecimal[log, handle.validRecipients]; String.AppendString[log, " recipients, "L]; String.AppendLongDecimal[log, HeapDefs.GetWriterOffset[handle.body]]; String.AppendString[log, " words"L]; IF handle.express THEN String.AppendString[log, ", express"L]; IF handle.moderated THEN String.AppendString[log, ", moderated"L]; LogDefs.WriteLogEntry[log]; Heap.systemZone.FREE[@log]; LogDefs.WriteChar[IF handle.express THEN 'E ELSE 'C]; END; LogAbort: PROCEDURE [handle: Handle] = BEGIN log: LONG STRING ← Heap.systemZone.NEW[StringBody[128]]; String.AppendString[log, "Abandoned "L]; ProtocolDefs.AppendTimestamp[log, handle.stamp]; LogDefs.WriteLogEntry[log]; Heap.systemZone.FREE[@log]; END; Handle: PUBLIC TYPE = LONG POINTER TO HandleObject; HandleObject: TYPE = MONITORED RECORD [ body, SL, bad: HeapDefs.WriterHandle, validate: BOOLEAN, state: {idle, starting, inItem, dead}, header: BodyDefs.ItemHeader, itemStart: HeapDefs.ObjectOffset, stamp: BodyDefs.Timestamp, recipientCount: CARDINAL, validRecipients: CARDINAL, sender: BodyDefs.RName, express, moderated: BOOLEAN]; WrongCallSequence: ERROR = CODE; -- Defined only to satisfy the Defs. It is not signalled -- SendFailed: PUBLIC ERROR [notDelivered: BOOLEAN] = CODE; Create: PUBLIC PROCEDURE RETURNS [handle: Handle] = BEGIN handle ← Heap.systemZone.NEW[HandleObject]; handle.state ← idle; handle.sender ← Heap.systemZone.NEW[StringBody[BodyDefs.maxRNameLength]]; END; Destroy: PUBLIC PROCEDURE [handle: Handle] = BEGIN Abort[handle]; handle.state ← dead; Heap.systemZone.FREE[@handle]; END; validationOn: BOOLEAN ← TRUE; -- debugging facility-- validateSender: BOOLEAN ← TRUE; -- debugging facility-- StartSend: PUBLIC PROCEDURE [ handle: Handle, senderPwd: LONG STRING, sender: BodyDefs.RName, returnTo: BodyDefs.RName ← NIL, validate: BOOLEAN] RETURNS [info: SendDefs.StartSendInfo] = BEGIN info ← SendFromClient[ handle, 0, 0, ProtocolDefs.MakeKey[senderPwd], sender, IF returnTo = NIL THEN sender ELSE returnTo, validate]; END; SendFromClient: PUBLIC ENTRY PROCEDURE [ handle: Handle, fromNet: [0..256), fromHost: [0..256), senderKey: BodyDefs.Password, --ignored-- sender, returnTo: BodyDefs.RName, validate: BOOLEAN] RETURNS [info: SendDefs.StartSendInfo] = BEGIN header: BodyDefs.ItemHeader; WriteHeader: PROCEDURE = BEGIN HeapDefs.HeapWriteData[handle.body, [@header, SIZE[BodyDefs.ItemHeader]]]; END; IF handle.state # idle THEN ERROR WrongCallSequence[]; handle.state ← starting; handle.SL ← HeapDefs.HeapStartWrite[SLinput]; handle.body ← HeapDefs.HeapStartWrite[body]; handle.bad ← NIL; handle.validate ← validate; handle.sender.length ← 0; handle.express ← FALSE; handle.moderated ← FALSE; info ← ok; IF fromHost = 0 THEN BEGIN fromNet ← myName.net; fromHost ← myName.host; END; -- postmark -- BEGIN stamp: BodyDefs.Timestamp; header.type ← PostMark; header.length ← 2 * SIZE[BodyDefs.Timestamp]; WriteHeader[]; stamp.net ← fromNet; stamp.host ← fromHost; stamp.time ← Time.Current[]; handle.stamp ← stamp; HeapDefs.HeapWriteData[handle.body, [@stamp, SIZE[BodyDefs.Timestamp]]]; END; -- sender -- BEGIN header.type ← Sender; IF NOT RNameFromString[sender] OR (validateSender AND NOT SiteCacheDefs.ValidateRName[sender]) THEN BEGIN header.length ← 0; WriteHeader[]; info ← badSender; END ELSE BEGIN header.length ← BodyDefs.RNameSize[sender] * 2; WriteHeader[]; HeapDefs.HeapWriteRName[handle.body, sender]; IF handle.sender.length <= handle.sender.maxlength THEN String.AppendString[handle.sender, sender]; END; END; -- return-to -- BEGIN header.type ← ReturnTo; IF NOT RNameFromString[returnTo] OR (validateSender AND NOT SiteCacheDefs.ValidateRName[returnTo]) THEN BEGIN header.length ← 0; WriteHeader[]; IF info = ok THEN info ← badReturnTo; END ELSE BEGIN header.length ← BodyDefs.RNameSize[returnTo] * 2; WriteHeader[]; HeapDefs.HeapWriteRName[handle.body, returnTo]; END; END; -- recipients -- BEGIN handle.recipientCount ← handle.validRecipients ← 0; handle.header.type ← Recipients; handle.header.length ← 0; handle.itemStart ← HeapDefs.GetWriterOffset[handle.body]; header.type ← Recipients; header.length ← 0; -- fix up later -- WriteHeader[]; END; -- Write SL header -- BEGIN -- SL header -- slHeader: SLDefs.SLHeader; slHeader.server ← NIL; slHeader.created ← handle.stamp; slHeader.received.host ← 0; slHeader.received.net ← 0; slHeader.received.time ← handle.stamp.time; HeapDefs.HeapWriteData[handle.SL, [@slHeader, SIZE[SLDefs.SLHeader]]]; END; LogStart[handle, sender]; IF info # ok THEN InnerAbort[handle]; END --StartSend-- ; expressRecipients: CARDINAL ← 50; AddRecipient: PUBLIC ENTRY PROCEDURE [ handle: Handle, recipient: BodyDefs.RName] = BEGIN IF handle.state # starting THEN ERROR WrongCallSequence[]; handle.recipientCount ← handle.recipientCount + 1; SELECT TRUE FROM handle.validate AND validationOn AND (NOT RNameFromString[recipient] OR NOT SiteCacheDefs.ValidateRName[recipient]) => BEGIN genuine: CARDINAL = recipient.length; recipient.length ← MIN[recipient.length, BodyDefs.maxRNameLength]; IF handle.bad = NIL THEN handle.bad ← HeapDefs.HeapStartWrite[temp]; HeapDefs.HeapWriteData[ handle.bad, [@(handle.recipientCount), SIZE[CARDINAL]]]; HeapDefs.HeapWriteRName[handle.bad, recipient]; recipient.length ← genuine; END; NeedsModerating[handle, recipient] => BEGIN moderator: BodyDefs.RName = [20 + BodyDefs.maxRNameLength]; String.AppendString[moderator, "Owner-"]; String.AppendString[moderator, recipient]; moderator.length ← MIN[moderator.length, BodyDefs.maxRNameLength]; handle.validRecipients ← handle.validRecipients + 1; HeapDefs.HeapWriteRName[handle.SL, moderator]; END; ENDCASE => BEGIN genuine: CARDINAL = recipient.length; recipient.length ← MIN[recipient.length, BodyDefs.maxRNameLength]; handle.validRecipients ← handle.validRecipients + 1; IF handle.validRecipients > expressRecipients THEN handle.express ← TRUE; -- Writing recipient names into message body disabled until Laurel 6. -- handle.header.length ← -- handle.header.length + 2*BodyDefs.RNameSize[recipient]; -- HeapDefs.HeapWriteRName[handle.body, recipient]; HeapDefs.HeapWriteRName[handle.SL, recipient]; recipient.length ← genuine; CheckExpressMail[handle, recipient]; END; END --AddRecipient-- ; NeedsModerating: INTERNAL PROC [handle: Handle, recipient: BodyDefs.RName] RETURNS [BOOL] = BEGIN FOR i: CARDINAL DECREASING IN [0..recipient.length) DO IF recipient[i] = '. THEN BEGIN IF i = 0 OR recipient[i - 1] # '↑ THEN RETURN[FALSE]; IF NameInfoDefs.IsMemberDirect["Moderated↑.ms"L, recipient] # yes THEN RETURN[FALSE]; IF NameInfoDefs.IsOwnerDirect[recipient, handle.sender] = yes THEN RETURN[FALSE]; handle.moderated ← TRUE; RETURN[TRUE]; END; ENDLOOP; RETURN[FALSE]; -- Humm. No dot?? END; CheckExpressMail: INTERNAL PROC [handle: Handle, recipient: BodyDefs.RName] = BEGIN IF NOT handle.express THEN FOR i: CARDINAL DECREASING IN [0..recipient.length) DO IF recipient[i] = '. THEN BEGIN IF i > 0 AND recipient[i - 1] = '↑ THEN IF NameInfoDefs.IsMemberDirect["ExpressMail↑.ms"L, recipient] = yes THEN handle.express ← TRUE; EXIT END; ENDLOOP; END; BadBadList: ERROR = CODE; CheckValidity: PUBLIC ENTRY PROC [ handle: Handle, notify: PROCEDURE [CARDINAL, BodyDefs.RName]] RETURNS [ok: CARDINAL] = BEGIN rName: BodyDefs.RName = [BodyDefs.maxRNameLength]; number: CARDINAL; reader: HeapDefs.ReaderHandle; ended: BOOLEAN; used: CARDINAL; GetReader: PROCEDURE [obj: HeapDefs.ObjectNumber] = BEGIN reader ← HeapDefs.HeapStartRead[obj] END; IF handle.state # starting THEN ERROR WrongCallSequence[]; handle.state ← inItem; IF handle.bad # NIL THEN BEGIN HeapDefs.HeapEndWrite[handle.bad, GetReader]; handle.bad ← NIL; [ended, ] ← HeapDefs.HeapReadData[reader, [@number, 0]]; UNTIL ended DO BEGIN ENABLE UNWIND => HeapDefs.HeapEndRead[reader]; [ended, used] ← HeapDefs.HeapReadData[reader, [@number, SIZE[CARDINAL]]]; IF ended OR used # SIZE[CARDINAL] THEN ERROR BadBadList[]; ended ← HeapDefs.HeapReadRName[reader, rName]; IF notify # NIL THEN notify[number, rName]; END; ENDLOOP; HeapDefs.HeapEndRead[reader]; END; RETURN[handle.validRecipients] END; EndItem: INTERNAL PROCEDURE [handle: Handle] = BEGIN IF handle.state = starting THEN BEGIN IF handle.bad # NIL THEN HeapDefs.HeapAbandonWrite[handle.bad]; handle.bad ← NIL; handle.state ← inItem; END; IF handle.state # inItem THEN ERROR WrongCallSequence[]; BEGIN save: HeapDefs.ObjectOffset = HeapDefs.GetWriterOffset[handle.body]; HeapDefs.SetWriterOffset[handle.body, handle.itemStart]; HeapDefs.HeapWriteData[ handle.body, [@(handle.header), SIZE[BodyDefs.ItemHeader]]]; HeapDefs.SetWriterOffset[handle.body, save]; handle.itemStart ← save; END; END; StartItem: PUBLIC ENTRY PROC [handle: Handle, type: BodyDefs.ItemType] = BEGIN header: BodyDefs.ItemHeader; EndItem[handle]; handle.state ← inItem; handle.header.type ← type; handle.header.length ← 0; header.type ← type; header.length ← 0; --fixed up later-- HeapDefs.HeapWriteData[handle.body, [@header, SIZE[BodyDefs.ItemHeader]]]; END --StartItem-- ; AddToItem: PUBLIC ENTRY PROC [ handle: Handle, buffer: LONG DESCRIPTOR FOR PACKED ARRAY OF CHARACTER] = BEGIN IF handle.state # inItem THEN ERROR WrongCallSequence[]; IF handle.header.length MOD 2 # 0 THEN ERROR WrongCallSequence[]; HeapDefs.HeapWriteData[handle.body, [BASE[buffer], (1 + LENGTH[buffer]) / 2]]; handle.header.length ← handle.header.length + LENGTH[buffer]; END --AddToItem-- ; Send: PUBLIC ENTRY PROCEDURE [handle: Handle] = BEGIN EndItem[handle]; handle.state ← idle; -- End -- BEGIN header: BodyDefs.ItemHeader; header.type ← LastItem; header.length ← 0; HeapDefs.HeapWriteData[handle.body, [@header, SIZE[BodyDefs.ItemHeader]]]; END; LogCommit[handle]; -- commit -- BEGIN Action: PROCEDURE [obj: HeapDefs.ObjectNumber] = BEGIN SLDefs.SLWrite[ body: obj, SL: handle.SL, queue: IF handle.express THEN express ELSE input]; END; HeapDefs.HeapEndWrite[handle.body, Action]; END; END; InnerAbort: INTERNAL PROCEDURE [handle: Handle] = BEGIN IF handle.state IN [starting..inItem] THEN BEGIN IF handle.bad # NIL THEN HeapDefs.HeapAbandonWrite[handle.bad]; HeapDefs.HeapAbandonWrite[handle.body]; HeapDefs.HeapAbandonWrite[handle.SL]; LogAbort[handle]; END; handle.state ← idle; END; Abort: PUBLIC ENTRY PROCEDURE [handle: Handle] = BEGIN InnerAbort[handle]; END; ExpandFailed: PUBLIC ERROR = CODE; -- not raised -- Expand: PUBLIC PROC [name: BodyDefs.RName, work: PROC [BodyDefs.RName]] RETURNS [info: SendDefs.ExpandInfo] = BEGIN nameInfo: SiteCacheDefs.RecipientInfo = SiteCacheDefs.FindMBXSite[name]; MyWork: PROC [n: BodyDefs.RName] RETURNS [done: BOOLEAN] = { work[n]; RETURN[FALSE]}; WITH i: nameInfo SELECT FROM allDown => info ← allDown; notFound => info ← notFound; local, found => info ← individual; dl => BEGIN ENABLE UNWIND => NameInfoDefs.Close[i.members]; NameInfoDefs.Enumerate[i.members, MyWork]; NameInfoDefs.Close[i.members]; info ← ok; END; foreign => BEGIN ENABLE UNWIND => HeapDefs.HeapEndRead[i.members]; HeapDefs.ReadRList[i.members, MyWork]; HeapDefs.HeapEndRead[i.members]; info ← ok; END; ENDCASE => ERROR; END; myName: PupDefs.PupAddress; [] ← PupDefs.PupPackageMake[]; PupDefs.PupNameLookup[@myName, "ME"L]; END.