-- Copyright (C) 1981, 1983, 1984, 1985  by Xerox Corporation. All rights reserved. 
-- ReceiveMail.mesa, Transport Mechanism: Accepting mail from client

-- HGM, 18-Sep-85  3:19:25
-- Mark Johnson    24-Nov-81 16:51:13
-- Andrew Birrell  30-Mar-81 13:40:41
-- Brenda Hankins  24-Feb-83  9:16:16

DIRECTORY
  BodyDefs USING [maxRNameLength, Password, RName],
  LogDefs USING [ShowRejection],
  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],
  String USING [AppendNumber, AppendString];

ReceiveMail: PROGRAM
  IMPORTS LogDefs, PolicyDefs, ProtocolDefs, PupStream, SendDefs, Stream, String
  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;
    blockAddr: LONG POINTER = @blockData;
    block: Stream.Block ← [blockAddr, 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.ShowRejection["ClientInput", from];  -- No L
      ERROR PupStream.RejectThisRequest["Server full"L]
      END;
    END;

  [] ← PupStream.CreatePupByteStreamListener[
    ProtocolDefs.mailServerInputSocket, ClientReceiver, PupStream.SecondsToTocks[
    60], ClientFilter];

  END.