-- 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.