-- Copyright (C) 1981, 1984, 1985  by Xerox Corporation. All rights reserved. 
-- ReadForward.mesa, Transport Mechanism Mail Server - algorithm to process forward queue --

-- HGM: 14-Sep-85 20:11:53
-- Randy Gobbel		19-May-81 11:57:02 --
-- Andrew Birrell	29-Dec-81 14:53:38 --
-- Hankins	30-Jul-84 14:22:52	Klamath update (BufferDefs)

DIRECTORY
  BodyDefs USING [RName],
  Buffer,
  Heap USING [systemZone],
  HeapDefs USING [
    ObjectNumber, HeapReadData, HeapStartRead, ReaderHandle, HeapEndRead,
    SendObj],
  LocalNameDefs USING [ReadMSName],
  LogDefs USING [WriteLogEntry],
  PolicyDefs USING [EndOperation, WaitOperation],
  Process USING [Detach],
  ProtocolDefs USING [
    AppendTimestamp, CreateStream, DestroyStream, Failed, Handle, Password,
    ReceiveAck, SendRName, SendTimestamp],
  RestartDefs USING [],
  ServerDefs USING [
    ServerAddr, ServerHandle, ServerNotUp, ServerUp, DownServer, NoSuchServer],
  SLDefs USING [
    GetCount, SLHeader, SLStartRead, SLReadHandle, SLEndRead, SLTransfer],
  String USING [AppendLongDecimal, AppendString],
  System USING [GetGreenwichMeanTime, GreenwichMeanTime];

ReadForward: PROGRAM
  IMPORTS
    Heap, HeapDefs, LocalNameDefs, LogDefs, PolicyDefs, Process, ProtocolDefs,
    ServerDefs, SLDefs, String, System
  EXPORTS RestartDefs =

  BEGIN

  NoRecipients: ERROR = CODE;  --not caught; should not occur--

  ForwardMain: PROCEDURE =
    BEGIN
    -- multiple instantiations of this procedure are allowed --
    DO
      BEGIN
      str: ProtocolDefs.Handle ← NIL;
      SLobj: HeapDefs.ReaderHandle;
      SLhandle: SLDefs.SLReadHandle;
      bodyObj: HeapDefs.ObjectNumber;
      slHeader: SLDefs.SLHeader;
      outcome: {ok, down} ← ok;
      startTime: System.GreenwichMeanTime;
      seconds: LONG CARDINAL;

      [SLhandle, bodyObj, SLobj] ← SLDefs.SLStartRead[forward];
      PolicyDefs.WaitOperation[readForward];
      BEGIN
      -- read SL header --
      ended: BOOLEAN;
      used: CARDINAL;
      [ended, used] ← HeapDefs.HeapReadData[
        SLobj, [@slHeader, SIZE[SLDefs.SLHeader]]];
      IF ended THEN ERROR NoRecipients[];
      END;
      IF NOT ServerDefs.ServerUp[slHeader.server] THEN {
        HeapDefs.HeapEndRead[SLobj]; outcome ← down}
      ELSE
        BEGIN
       startTime  ← System.GetGreenwichMeanTime[];
       str ← ProtocolDefs.CreateStream[
          ServerDefs.ServerAddr[
          slHeader.server !
          ServerDefs.ServerNotUp, ServerDefs.NoSuchServer => GOTO noAddr] !
          ProtocolDefs.Failed => GOTO noStr];
        BEGIN
        ENABLE
          ProtocolDefs.Failed => {HeapDefs.HeapEndRead[SLobj]; GOTO wentDown};
        myName: BodyDefs.RName;
        myKey: ProtocolDefs.Password;
        [name: myName, key: myKey, password:] ← LocalNameDefs.ReadMSName[];
        ProtocolDefs.SendRName[str, myName];
        ProtocolDefs.SendTimestamp[str, slHeader.created];
        END;
        BEGIN
        ENABLE ProtocolDefs.Failed => GOTO wentDown;
        HeapDefs.SendObj[SLobj, str];
        HeapDefs.SendObj[HeapDefs.HeapStartRead[bodyObj], str];
        ProtocolDefs.ReceiveAck[str];
        END;

        ProtocolDefs.DestroyStream[str];
        seconds ← System.GetGreenwichMeanTime[]-startTime;

        EXITS
          noAddr => {HeapDefs.HeapEndRead[SLobj]; outcome ← down};
          noStr =>
            BEGIN
            HeapDefs.HeapEndRead[SLobj];
            ServerDefs.DownServer[slHeader.server];
            outcome ← down;
            END;
          wentDown =>
            BEGIN
            ProtocolDefs.DestroyStream[str];
            ServerDefs.DownServer[slHeader.server];
            outcome ← down;
            END;
        END;

      LogForwarded[slHeader, outcome # ok, seconds];

      SELECT outcome FROM
        ok => SLDefs.SLEndRead[SLhandle];
        down => SLDefs.SLTransfer[SLhandle, input];
        ENDCASE => ERROR;
      END;
      PolicyDefs.EndOperation[readForward];
      ENDLOOP;
    END;

  LogForwarded: PROCEDURE [slHeader: SLDefs.SLHeader, failed: BOOLEAN, seconds: LONG CARDINAL] =
    BEGIN
    log: LONG STRING ← Heap.systemZone.NEW[StringBody [200]];
    String.AppendString[log, "Forwarded  "L];
    ProtocolDefs.AppendTimestamp[log, slHeader.created];
    String.AppendString[log, " to "L];
    WITH slHeader.server.name SELECT FROM
      rName => String.AppendString[log, value];
      ENDCASE;
    IF failed THEN String.AppendString[log, ": failed"L];
    IF seconds > 30 THEN {
      String.AppendString[log, ", "L];
      String.AppendLongDecimal[log, seconds];
      String.AppendString[log, " seconds"L]; };
    LogDefs.WriteLogEntry[log];
    Heap.systemZone.FREE[@log];
    END;

  ReadForward1: PUBLIC PROC =
    BEGIN
    END;

  ReadForward2: PUBLIC PROC =
    BEGIN
    -- on restart, must transfer everything to input, since ServerHandles
    -- are no longer valid --
    THROUGH [0..SLDefs.GetCount[forward]) DO
      handle: SLDefs.SLReadHandle;
      body: HeapDefs.ObjectNumber;
      SL: HeapDefs.ReaderHandle;
      [handle, body, SL] ← SLDefs.SLStartRead[forward];
      HeapDefs.HeapEndRead[SL];
      SLDefs.SLTransfer[handle, input];
      ENDLOOP;
    Process.Detach[FORK ForwardMain[]];
    END;


  END.