-- Transport Mechanism - subroutines for mail and reg server protocols --

-- [Juniper]<DMS>MS>Protocol.mesa

-- Andrew Birrell  23-Jan-81 17:07:47 --

DIRECTORY
BodyDefs	USING[ ItemHeader, maxRNameLength, oldestTime, Password,
		       RName, RNameSize, Timestamp],
Inline		USING[ BITSHIFT, BITXOR ],
ProtocolDefs	USING[ FailureReason, Handle, MSOperation, ReceiveRName,
		       ReceiveString, ReturnCode, RSOperation, SendRName,
		       SendString ],
PupDefs		USING[ PupPackageMake, SecondsToTocks ],
PupStream	USING[ GetPupAddress, PupAddress, PupByteStreamCreate,
		       StreamClosing ],
PupTypes	USING[ Byte, PupAddress, PupSocketID ],
Stream		USING[ CompletionCode, GetBlock, Handle, PutBlock, SendNow,
		       TimeOut],
String		USING[ AppendChar, AppendLongNumber, AppendNumber ];

Protocol: MONITOR
   IMPORTS BodyDefs, Inline, ProtocolDefs, PupDefs, PupStream, Stream,
           String
   EXPORTS ProtocolDefs =

BEGIN

OPEN ProtocolDefs;


MakeKey: PUBLIC PROC[password: STRING] RETURNS[ key: BodyDefs.Password ] =
   BEGIN
   key ← ALL[0];
   FOR i: CARDINAL IN [0..password.length)
   DO j: [0..LENGTH[key]) = (i/bpw) MOD LENGTH[key];
      c: WORD = LOOPHOLE[ IF password[i] IN ['A..'Z]
                          THEN password[i] - 'A + 'a
                          ELSE password[i] ];
      key[j] ← Inline.BITXOR[ key[j],
                  Inline.BITSHIFT[c, IF (i MOD 2) = 0 THEN 9 ELSE 1]];
   ENDLOOP;
   END;


bpw: CARDINAL = 2;

RegServerEnquirySocket:  PUBLIC PupTypes.PupSocketID ← [0, 50B];
RegServerSpareSocket:    PUBLIC PupTypes.PupSocketID ← [0, 51B];
RegServerPollingSocket:  PUBLIC PupTypes.PupSocketID ← [0, 52B];
spareSocket:             PUBLIC PupTypes.PupSocketID ← [0, 53B];
mailServerPollingSocket: PUBLIC PupTypes.PupSocketID ← [0, 54B];
mailServerServerSocket:  PUBLIC PupTypes.PupSocketID ← [0, 55B];
mailServerInputSocket:   PUBLIC PupTypes.PupSocketID ← [0, 56B];
mailServerOutputSocket:  PUBLIC PupTypes.PupSocketID ← [0, 57B];

running: BOOLEAN ← FALSE;
TooLateForTestingMode: SIGNAL = CODE;

SetTestingMode: PUBLIC ENTRY PROCEDURE =
   BEGIN
   IF running THEN SIGNAL TooLateForTestingMode[];
   RegServerEnquirySocket.a ← 1;
   RegServerSpareSocket.a ← 1;
   RegServerPollingSocket.a ← 1;
   spareSocket.a ← 1;
   mailServerPollingSocket.a ← 1;
   mailServerServerSocket.a ← 1;
   mailServerInputSocket.a ← 1;
   mailServerOutputSocket.a ← 1;
   END;

Init: PUBLIC ENTRY PROCEDURE =
   { --initialize the exported variables;-- running ← TRUE };

myAddr: PupTypes.PupAddress;

IsLocal: PUBLIC PROCEDURE[addr: PupTypes.PupAddress] RETURNS[ BOOLEAN ] =
   BEGIN
   RETURN[ addr.net = myAddr.net AND addr.host = myAddr.host ]
   END;

Failed: PUBLIC ERROR[why: FailureReason] = CODE;

CreateStream: PUBLIC PROCEDURE[addr: PupTypes.PupAddress,
                 secs: CARDINAL ← 120 ]
              RETURNS[ ProtocolDefs.Handle ] =
   BEGIN
   ENABLE PupStream.StreamClosing => ERROR Failed[communicationError];
   RETURN[ PupStream.PupByteStreamCreate[addr,
              PupDefs.SecondsToTocks[secs] ] ]
   END;

DestroyStream: PUBLIC PROCEDURE[str: ProtocolDefs.Handle] =
   BEGIN
   str.delete[str];
   END;

SendNow: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle ] =
   BEGIN
   ENABLE
      BEGIN
      PupStream.StreamClosing => ERROR Failed[communicationError];
      Stream.TimeOut => ERROR Failed[noData];
      END;
   Stream.SendNow[str];
   END;

Byte: TYPE = [0..256);

SendByte: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle, byte: Byte ] =
   BEGIN
   word: PACKED ARRAY [0..bpw) OF Byte;
   word[0] ← byte;
   SendBytes[str, @word, 1];
   END;

ReceiveByte: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle ]
                      RETURNS[ byte: Byte ] =
   BEGIN
   word: PACKED ARRAY [0..bpw) OF Byte;
   ReceiveBytes[str, @word, 1];
   byte ← word[0];
   END;

SendCount: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle, count: CARDINAL ] =
   { SendBytes[str, @count, bpw*SIZE[CARDINAL]] };

ReceiveCount: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle ]
              RETURNS[ count: CARDINAL ] =
   { ReceiveBytes[str, @count, bpw*SIZE[CARDINAL]] };

SendItemHeader: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle,
                                  header: BodyDefs.ItemHeader] =
   { SendBytes[str, @header, bpw*SIZE[BodyDefs.ItemHeader]] };

ReceiveItemHeader: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle ]
                            RETURNS[ header: BodyDefs.ItemHeader ] =
   { ReceiveBytes[str, @header, bpw*SIZE[BodyDefs.ItemHeader]] };

SendTimestamp: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle,
                                  stamp: BodyDefs.Timestamp] =
   { SendBytes[str, @stamp, bpw*SIZE[BodyDefs.Timestamp]] };

ReceiveTimestamp: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle ]
                            RETURNS[ stamp: BodyDefs.Timestamp ] =
   { ReceiveBytes[str, @stamp, bpw*SIZE[BodyDefs.Timestamp]] };

AppendTimestamp: PUBLIC PROCEDURE[s: STRING, stamp: BodyDefs.Timestamp] =
   BEGIN
   String.AppendNumber[s, stamp.net, 8];
   String.AppendChar[s, '#];
   String.AppendNumber[s, stamp.host, 8];
   String.AppendChar[s, '@];
   String.AppendLongNumber[s, stamp.time, 10];
   END;

SendPassword: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle,
                                key, pw: BodyDefs.Password] =
   { SendBytes[str, @pw, bpw*SIZE[BodyDefs.Password]] };

ReceivePassword: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle,
                                   key: BodyDefs.Password ]
                            RETURNS[ pw: BodyDefs.Password ] =
   { ReceiveBytes[str, @pw, bpw*SIZE[BodyDefs.Password]] };

SendRC: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle,
                          rc: ProtocolDefs.ReturnCode] =
   { SendBytes[str, @rc, bpw*SIZE[ProtocolDefs.ReturnCode]] };

ReceiveRC: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle ]
                    RETURNS[ rc: ProtocolDefs.ReturnCode ] =
   { ReceiveBytes[str, @rc, bpw*SIZE[ProtocolDefs.ReturnCode]] };

SendMSOperation: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle,
                                   op: ProtocolDefs.MSOperation] =
   { SendBytes[str, @op, bpw*SIZE[ProtocolDefs.MSOperation]] };

ReceiveMSOperation: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle ]
                             RETURNS[ op: ProtocolDefs.MSOperation ] =
   { ReceiveBytes[str, @op, bpw*SIZE[ProtocolDefs.MSOperation]] };

SendRSOperation: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle,
                                   op: ProtocolDefs.RSOperation] =
   { SendBytes[str, @op, bpw*SIZE[ProtocolDefs.RSOperation]] };

ReceiveRSOperation: PUBLIC PROCEDURE[ str: ProtocolDefs.Handle ]
                             RETURNS[ op: ProtocolDefs.RSOperation ] =
   { ReceiveBytes[str, @op, bpw*SIZE[ProtocolDefs.RSOperation]] };

SendBytes: PUBLIC PROC[str: ProtocolDefs.Handle,
                buffer: POINTER, count: CARDINAL] =
   BEGIN
   ENABLE PupStream.StreamClosing => ERROR Failed[communicationError];
   Stream.PutBlock[str,[buffer,0,count], FALSE ];
   END;

ReceiveBytes: PUBLIC PROC[str: ProtocolDefs.Handle,
                   buffer: POINTER, count: CARDINAL] =
   BEGIN
   ENABLE
      BEGIN
      PupStream.StreamClosing => ERROR Failed[communicationError];
      Stream.TimeOut => ERROR Failed[noData];
      END;
   why: Stream.CompletionCode;
   [,why,] ← Stream.GetBlock[str, [buffer,0,count] ];
   IF why # normal THEN ERROR Failed[protocolError];
   END;

SendString: PUBLIC PROC[ str: ProtocolDefs.Handle,
                         string: STRING] =
   BEGIN
   ENABLE PupStream.StreamClosing => ERROR Failed[communicationError];
   Stream.PutBlock[str,
     [string,0,bpw*SIZE[StringBody[string.length]]],
      FALSE ];
   END;

ReceiveString: PUBLIC PROC[ str: ProtocolDefs.Handle,
                            string: STRING] =
   BEGIN
   ENABLE
      BEGIN
      PupStream.StreamClosing => ERROR Failed[communicationError];
      Stream.TimeOut => ERROR Failed[noData];
      END;
   used:   CARDINAL ← 0;
   why:    Stream.CompletionCode;
   temp:   STRING = [0];
   [used,why,] ←
      Stream.GetBlock[str,[temp,0,SIZE[StringBody[0]]*bpw]];
   IF why # normal OR temp.length > string.maxlength
   THEN ERROR Failed[protocolError];
   string.length ← temp.length;
   [,why,]← Stream.GetBlock[str,[string,used,
                     bpw*SIZE[StringBody[string.length]]]];
   IF why # normal THEN ERROR Failed[protocolError];
   END;

Enquire: PUBLIC PROCEDURE[str: ProtocolDefs.Handle,
                          op: ProtocolDefs.RSOperation,
                          name: BodyDefs.RName,
                          oldStamp: BodyDefs.Timestamp
                             ← BodyDefs.oldestTime ]
         RETURNS[ rc: ProtocolDefs.ReturnCode, stamp: BodyDefs.Timestamp ] =
   BEGIN
   SendRSOperation[str, op];
   SendRName[str, name];
   IF op IN [Expand .. CheckStamp]
   THEN SendTimestamp[str, oldStamp];
   SendNow[str];
   rc ← ReceiveRC[str];
   IF rc.code = done
   AND op IN [Expand .. CheckStamp]
   THEN stamp ← ReceiveTimestamp[str]
   ELSE stamp ← oldStamp;
   END;

ReceiveRList: PUBLIC PROCEDURE[str: ProtocolDefs.Handle,
                               work: PROCEDURE[BodyDefs.RName] ] =
   BEGIN
   length: CARDINAL ← ReceiveCount[str];
   WHILE length > 0
   DO name: BodyDefs.RName = [BodyDefs.maxRNameLength];
      ReceiveRName[str, name];
      length ← length - BodyDefs.RNameSize[name];
      work[name];
   ENDLOOP;
   END;


PupDefs.PupPackageMake[];
PupStream.GetPupAddress[@myAddr, "ME"L];

END.