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