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

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

-- Mark Johnson    12-Nov-81 10:02:40 --
-- 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];
    stringAddr: LONG POINTER ← string;
    Stream.PutBlock[
      str, [LOOPHOLE[stringAddr], 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];
    tempAddr: LONG POINTER ← temp;
    stringAddr: LONG POINTER ← string;
    [used, why, ] ← Stream.GetBlock[
      str, [LOOPHOLE[tempAddr], 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, [LOOPHOLE[stringAddr], 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.