-- Transport Mechanism: Accepting mail from client
-- [Juniper]<Grapevine>MS>ReceiveMail.mesa
-- Andrew Birrell 30-Mar-81 13:40:41
-- M. D. Schroeder 7-Feb-83 13:16:31
DIRECTORY
BodyDefs USING[ maxRNameLength, Password, RName ],
LogDefs USING[ WriteLogEntry ],
PolicyDefs USING[ CheckOperation, EndOperation ],
ProtocolDefs,
PupDefs USING[ PupAddress ],
PupStream USING[ CreatePupByteStreamListener, RejectThisRequest,
SecondsToTocks, StreamClosing ],
RestartDefs USING[ ],
SendDefs USING[ AddRecipient, AddToItem, CheckValidity, Create,
Destroy, Expand, ExpandInfo, Handle, Send,
SendFromClient, StartItem, StartSendInfo ],
Stream USING[ Block, CompletionCode, GetBlock, TimeOut ];
ReceiveMail: PROGRAM
IMPORTS LogDefs, PolicyDefs, ProtocolDefs, PupStream, SendDefs, Stream
EXPORTS RestartDefs --PROGRAM-- =
BEGIN
ClientReceiver: PROC[str: ProtocolDefs.Handle, from: PupDefs.PupAddress] =
BEGIN
state: {idle, started, noItem, inItem} ← idle;
handle: SendDefs.Handle = SendDefs.Create[];
-- "block" is used for re-blocking incoming bytes of an item --
-- at top of loop, block.stopIndexPlusOne is buffered bytes in block --
-- at top of loop, block.startIndex is undefined --
blockLength: CARDINAL = 128;
blockData: PACKED ARRAY [0..blockLength) OF CHARACTER;
block: Stream.Block ← [@blockData, 0, 0];
ForceBlock: PROC =
BEGIN
block.startIndex ← 0;
SendDefs.AddToItem[handle,
DESCRIPTOR[@blockData, block.stopIndexPlusOne] ];
block.stopIndexPlusOne ← 0;
END;
validate: BOOLEAN;
op: ProtocolDefs.MSOperation;
DO BEGIN
ENABLE ProtocolDefs.Failed, PupStream.StreamClosing,
Stream.TimeOut => EXIT;
SELECT (op ← ProtocolDefs.ReceiveMSOperation[str]) FROM
startSend =>
BEGIN
sender: BodyDefs.RName = [BodyDefs.maxRNameLength];
returnTo: BodyDefs.RName = [BodyDefs.maxRNameLength];
key: BodyDefs.Password;
info: SendDefs.StartSendInfo;
IF state # idle THEN GOTO badState;
ProtocolDefs.ReceiveRName[str, sender];
key ← ProtocolDefs.ReceivePassword[str, [0,0,0,0]];
ProtocolDefs.ReceiveRName[str, returnTo];
validate ← ProtocolDefs.ReceiveBoolean[str];
info ← SendDefs.SendFromClient[handle: handle,
fromNet: from.net, fromHost: from.host,
senderKey: key,
sender: sender,
returnTo: returnTo,
validate: validate ];
ProtocolDefs.SendByte[str, LOOPHOLE[info]];
ProtocolDefs.SendNow[str];
IF info = ok THEN state ← started;
END;
addRecipient =>
BEGIN
recipient: BodyDefs.RName = [BodyDefs.maxRNameLength];
IF state # started THEN GOTO badState;
ProtocolDefs.ReceiveRName[str, recipient];
SendDefs.AddRecipient[handle, recipient];
END;
checkValidity =>
BEGIN
MyNotify: PROC[n: CARDINAL, bad: BodyDefs.RName] =
BEGIN
ProtocolDefs.SendCount[str, n];
ProtocolDefs.SendRName[str, bad];
END;
ok: CARDINAL;
IF state # started THEN GOTO badState;
ok ← SendDefs.CheckValidity[handle, MyNotify];
ProtocolDefs.SendCount[str, 0];
ProtocolDefs.SendCount[str, ok];
ProtocolDefs.SendNow[str];
state ← noItem;
END;
startItem =>
BEGIN
IF state = started THEN state ← noItem;
IF state = inItem THEN { ForceBlock[]; state ← noItem };
IF state # noItem THEN GOTO badState;
SendDefs.StartItem[handle, LOOPHOLE[ProtocolDefs.ReceiveCount[str]]];
state ← inItem;
END;
addToItem =>
BEGIN
length: CARDINAL;
IF state # inItem THEN GOTO badState;
length ← ProtocolDefs.ReceiveCount[str];
WHILE length > 0
DO used: CARDINAL;
why: Stream.CompletionCode;
block.startIndex ← block.stopIndexPlusOne;
block.stopIndexPlusOne ←
IF blockLength - block.startIndex < length
THEN blockLength ELSE block.startIndex+length;
[used,why,] ← Stream.GetBlock[str, block];
IF why # normal THEN GOTO protocolError;
length ← length - used;
block.stopIndexPlusOne ← block.startIndex + used;
IF block.stopIndexPlusOne >= blockLength
THEN BEGIN
IF block.stopIndexPlusOne > blockLength THEN ERROR;
ForceBlock[];
END;
ENDLOOP;
END;
send =>
BEGIN
IF state = started THEN state ← noItem;
IF state = inItem THEN { ForceBlock[]; state ← noItem };
IF state # noItem THEN GOTO badState;
SendDefs.Send[handle];
ProtocolDefs.SendAck[str];
ProtocolDefs.SendNow[str];
state ← idle;
END;
expand =>
BEGIN
name: BodyDefs.RName = [BodyDefs.maxRNameLength];
info: SendDefs.ExpandInfo;
ExpandWork: PROC[n: BodyDefs.RName] =
BEGIN
ProtocolDefs.SendBoolean[str, TRUE];
ProtocolDefs.SendRName[str, n];
END;
ProtocolDefs.ReceiveRName[str, name];
info ← SendDefs.Expand[name, ExpandWork];--shouldn't signal--
ProtocolDefs.SendBoolean[str, FALSE]; -- end --
ProtocolDefs.SendByte[str, LOOPHOLE[info]];
ProtocolDefs.SendNow[str];
END;
ENDCASE => GOTO protocolError;
EXITS
protocolError => EXIT;
badState => EXIT;
END;
ENDLOOP;
PolicyDefs.EndOperation[clientInput];
SendDefs.Destroy[handle];
ProtocolDefs.DestroyStream[str];
END;
ClientFilter: PROC[from: PupDefs.PupAddress] =
BEGIN
IF NOT PolicyDefs.CheckOperation[clientInput]
THEN BEGIN
LogDefs.WriteLogEntry["Rejected ClientInput connection"L];
ERROR PupStream.RejectThisRequest["Server full"L]
END;
END;
[] ← PupStream.CreatePupByteStreamListener[
ProtocolDefs.mailServerInputSocket, ClientReceiver,
PupStream.SecondsToTocks[60],
ClientFilter ];
END.