-- Transport Mechanism Mail Server - algorithm to process forward queue --

-- [ReadForward]<Grapevine>MS>ReadForward.mesa

-- Randy Gobbel		19-May-81 11:57:02 --
-- Andrew Birrell	29-Dec-81 14:53:38 --

DIRECTORY
BodyDefs         USING[ RName ],
BufferDefs,
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[ AppendString ];

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

BEGIN

NoRecipients:  ERROR = CODE; --not caught; should not occur--
ForeignServer: 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;

      [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;
--MTP--IF slHeader.server.type = foreign THEN ERROR ForeignServer[];
      IF NOT ServerDefs.ServerUp[slHeader.server]
      THEN { HeapDefs.HeapEndRead[SLobj]; outcome ← down }
      ELSE BEGIN
            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];

            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;

      BEGIN
         log: STRING = [128];
         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 outcome # ok THEN String.AppendString[log, ": failed"L];
         LogDefs.WriteLogEntry[log];
      END;

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

ForwardRestart: PROCEDURE =
   BEGIN
   -- on restart, must transfer everything to input, since ServerHandles
   -- are no longer valid --
   THROUGH [1..SLDefs.GetCount[forward]]
   DO BEGIN
      handle: SLDefs.SLReadHandle;
      body:   HeapDefs.ObjectNumber;
      SL:     HeapDefs.ReaderHandle;
      [handle, body, SL] ← SLDefs.SLStartRead[forward];
      HeapDefs.HeapEndRead[SL];
      SLDefs.SLTransfer[handle, input];
      END;
   ENDLOOP;
   END;

ForwardRestart[];
Process.Detach[ FORK ForwardMain[] ];

END.