-- Transport Mechanism Mail Server - client reading mail --

-- [Indigo]<Grapevine>MS>Readmail.mesa

-- Andrew Birrell  25-Oct-82 10:53:32 --

DIRECTORY
BodyDefs	USING[ maxRNameLength, Password, RName ],
LogDefs		USING[ WriteLogEntry],
MailboxDefs	USING[ Close, DeleteMessage, FlushAndClose,
		       InaccessibleArchive, MBXHandle,
                       NextMessage, Open, ReadTOC, SendBody, WriteTOC ],
NameInfoDefs	USING[ AuthenticateInfo, AuthenticateKey ],
PolicyDefs	USING [CheckOperation, EndOperation],
ProtocolDefs	USING[ Failed, Handle, mailServerOutputSocket,
		       maxRemarkLength, MSOperation,
		       ReceiveMSOperation, ReceivePassword,
		       ReceiveRemark,
		       ReceiveRName, Remark, SendAck, SendBoolean,
		       SendByte, SendCount, SendNow, SendRemark ],
PupStream	USING[ CreatePupByteStreamListener,
                       PupAddress, RejectThisRequest, SecondsToTocks ],
RestartDefs	USING[] --EXPORT only--,
String		USING [EquivalentSubStrings, SubStringDescriptor];

ReadMail: PROGRAM
   IMPORTS LogDefs, MailboxDefs, NameInfoDefs, PolicyDefs, ProtocolDefs,
           PupStream, String
   EXPORTS RestartDefs --PROGRAM-- =

BEGIN


EndsWith: PROC[s: STRING, b: STRING] RETURNS[ BOOLEAN ] =
   BEGIN
   pattern: String.SubStringDescriptor ← [b, 0, b.length];
   target: String.SubStringDescriptor ← [s,s.length-b.length,b.length];
   RETURN[ s.length >= b.length
           AND String.EquivalentSubStrings[@pattern,@target] ]
   END;

Receive: PROCEDURE[ str: ProtocolDefs.Handle, where: PupStream.PupAddress ] =
   BEGIN
   OPEN ProtocolDefs;

   client: BodyDefs.RName = [BodyDefs.maxRNameLength];
   state: { closed, open, inMessage } ← closed;
   mbx: MailboxDefs.MBXHandle;
   found: BOOLEAN ← FALSE;
   Open: PROC =
      BEGIN
      count: CARDINAL;
      [found, count, mbx] ← MailboxDefs.Open[client];
      IF found THEN state ← open;
      ProtocolDefs.SendCount[str,IF found THEN count ELSE 0];
      END;


   DO ENABLE ProtocolDefs.Failed, MailboxDefs.InaccessibleArchive => EXIT;
      op: ProtocolDefs.MSOperation;
      op ← ProtocolDefs.ReceiveMSOperation[str !
             ProtocolDefs.Failed =>
                  IF why = noData AND EndsWith[client, ".gv"]
                  THEN RETRY -- i.e. if caller is an R-Server --];
      SELECT op FROM
        openMBX =>
          BEGIN
          key: BodyDefs.Password;
          info: NameInfoDefs.AuthenticateInfo;
          ProtocolDefs.ReceiveRName[str,client];
          key ← ProtocolDefs.ReceivePassword[str, [0,0,0,0] ];
          IF state # closed THEN EXIT;
          info ← NameInfoDefs.AuthenticateKey[client, key];
          ProtocolDefs.SendByte[str, LOOPHOLE[info]];
          IF info = individual THEN Open[];
          END;
        nextMessage =>
          BEGIN
          msgExists, archived, deleted: BOOLEAN;
          IF state = inMessage THEN state ← open;
          -- pretend that not found means empty mailbox --
          IF found
          THEN IF state # open
               THEN EXIT
               ELSE BEGIN
                    [msgExists,archived,deleted] ←
                        MailboxDefs.NextMessage[mbx];
                    state ← IF deleted THEN open ELSE inMessage;
                    END
          ELSE msgExists ← archived ← deleted ← FALSE;
          ProtocolDefs.SendBoolean[str, msgExists];
          ProtocolDefs.SendBoolean[str, archived];
          ProtocolDefs.SendBoolean[str, deleted];
          END;
        readTOC =>
          BEGIN
          remark: ProtocolDefs.Remark = [ProtocolDefs.maxRemarkLength];
          IF state # inMessage THEN EXIT;
          MailboxDefs.ReadTOC[mbx, remark];
          ProtocolDefs.SendRemark[str, remark];
          END;
        readMessage =>
          BEGIN
          IF state # inMessage THEN EXIT;
          MailboxDefs.SendBody[mbx, str];
          END;
        writeTOC =>
          BEGIN
          remark: ProtocolDefs.Remark = [ProtocolDefs.maxRemarkLength];
          ProtocolDefs.ReceiveRemark[str, remark];
          IF state # inMessage THEN EXIT;
          MailboxDefs.WriteTOC[mbx, remark];
          ProtocolDefs.SendAck[str];
          END;
        deleteMessage =>
          BEGIN
          IF state # inMessage THEN EXIT;
          MailboxDefs.DeleteMessage[mbx];
          ProtocolDefs.SendAck[str];
          END;
        flushMBX =>
          BEGIN
          IF found
          THEN BEGIN
               IF state = closed THEN EXIT;
               MailboxDefs.FlushAndClose[mbx];
               state ← closed;
               END
          ELSE NULL;
          ProtocolDefs.SendAck[str];
          END;
      ENDCASE => EXIT;
      ProtocolDefs.SendNow[str];
   ENDLOOP;

   IF state # closed THEN MailboxDefs.Close[mbx];

   str.delete[str];

   PolicyDefs.EndOperation[readMail];

   END;


ReadMailFilter: PROCEDURE[PupStream.PupAddress] =
   BEGIN
   IF NOT PolicyDefs.CheckOperation[readMail]
   THEN BEGIN
        LogDefs.WriteLogEntry["Rejected ReadMail connection"L];
        ERROR PupStream.RejectThisRequest["Server full"L];
        END
   END;


[] ← PupStream.CreatePupByteStreamListener[
        ProtocolDefs.mailServerOutputSocket, Receive,
        PupStream.SecondsToTocks[120],
        ReadMailFilter ];


END.