-- Transport Mechanism Mail Server - Create mail from user --
-- [Juniper]<DMS>MS>CreateMail.mesa
-- Randy Gobbel 19-May-81 21:21:39 --
-- Andrew Birrell 1-Apr-81 14:54:01 --
DIRECTORY
BodyDefs USING[ ItemHeader, ItemLength, ItemType, maxRNameLength,
Password, RName, RNameSize, Timestamp ],
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 ],
Process USING[ InitializeMonitor ],
ProtocolDefs USING[ AppendTimestamp, MakeKey ],
PupDefs USING[ PupAddress, PupNameLookup, PupPackageMake ],
SendDefs USING[ ExpandInfo, StartSendInfo ],
SiteCacheDefs USING[ FindMBXSite, RecipientInfo, ValidateRName ],
SLDefs USING[ SLHeader, SLWrite ],
Storage USING[ Free, Node ],
String USING[ AppendDecimal, AppendLongDecimal, AppendString ],
Time USING[ Current ];
CreateMail: MONITOR LOCKS handle USING handle: Handle
IMPORTS BodyDefs, HeapDefs, LogDefs, NameInfoDefs, PupDefs,
Process, ProtocolDefs, SiteCacheDefs, SLDefs, String,
Storage, Time
EXPORTS SendDefs =
BEGIN
RNameFromString: PROCEDURE[ s: STRING] RETURNS[BOOLEAN] =
BEGIN
RETURN[IF s.length > BodyDefs.maxRNameLength THEN FALSE ELSE TRUE]
END;
LogStart: PROCEDURE[handle: Handle, sender: STRING] =
BEGIN
log: STRING = [128];
String.AppendString[log, "Created "L];
ProtocolDefs.AppendTimestamp[log, handle.stamp];
String.AppendString[log, ": sender "L];
String.AppendString[log, sender];
LogDefs.WriteLogEntry[log];
END;
LogCommit: PROCEDURE[handle: Handle] =
BEGIN
log: STRING = [96];
String.AppendString[log, "Received "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];
LogDefs.WriteLogEntry[log];
LogDefs.WriteChar[IF handle.express THEN 'E ELSE 'C];
END;
LogAbort: PROCEDURE[handle: Handle] =
BEGIN
log: STRING = [128];
String.AppendString[log, "Abandoned "L];
ProtocolDefs.AppendTimestamp[log, handle.stamp];
LogDefs.WriteLogEntry[log];
END;
Handle: PUBLIC TYPE = 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,
express: 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 ← Storage.Node[SIZE[HandleObject]];
handle.state ← idle;
Process.InitializeMonitor[@(handle.LOCK)];
END;
Destroy: PUBLIC PROCEDURE[ handle: Handle ] =
BEGIN
Abort[handle];
handle.state ← dead;
Storage.Free[handle];
END;
validationOn: BOOLEAN ← TRUE; -- debugging facility--
validateSender: BOOLEAN ← TRUE; -- debugging facility--
StartSend: PUBLIC PROCEDURE[ handle: Handle,
senderPwd: 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.express ← 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];
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;
IF handle.validate AND validationOn
AND( NOT RNameFromString[recipient]
OR NOT SiteCacheDefs.ValidateRName[recipient] )
THEN 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
ELSE 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--;
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: 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.