-- Transport Mechanism - R-Name of this registration or mail server --

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

-- Randy Gobbel		19-May-81 22:32:55 --
-- Andrew Birrell	17-Mar-81 15:06:46 --

DIRECTORY
Ascii,
BodyDefs	USING[ maxRNameLength, RName ],
HeapDefs	USING[ HeapEndRead, HeapEndWrite, HeapStartRead,
		       HeapStartWrite, HeapReadData, HeapReadRName,
		       HeapWriteData, HeapWriteRName,
		       HeapWriteString, ReaderHandle,
		       WriterHandle ],
IODefs,
LocalNameDefs --EXPORT only--,
LocateDefs	USING[ FindLocalServer, FindRegServer, FoundServerInfo ],
NameInfoDefs	USING[ Authenticate, GetConnect, NameType ],
ObjectDirDefs	USING[ Enumerate, FreeObject, noObject, ObjectNumber,
		       UseObject ],
ProtocolDefs,
PupDefs		USING[ AppendPupAddress, GetPupAddress, PupAddress, PupNameTrouble, PupSocketID ],
Storage	USING[ String ],
String	USING[ AppendString, EquivalentString, WordsForString];

LocalName: MONITOR[ initHeap: BOOLEAN ]
           LOCKS info USING info: POINTER TO NameInfo
   IMPORTS HeapDefs, IODefs, LocateDefs, NameInfoDefs,
           ObjectDirDefs, ProtocolDefs, PupDefs, Storage, String
   EXPORTS LocalNameDefs =
BEGIN

OPEN IODefs;

NameInfo: TYPE = MONITORED RECORD[ name: BodyDefs.RName,
                                   password: STRING,
                                   key: ProtocolDefs.Password,
                                   known: BOOLEAN ];

MSInfo: NameInfo ← [ name: NIL,
                     password: NIL,
                     key:,
                     known: FALSE ];

RSInfo: NameInfo ← [ name: NIL,
                     password: NIL,
                     key:,
                     known: FALSE ];

ReadMSName: PUBLIC PROCEDURE RETURNS[ name: BodyDefs.RName,
                                      password: STRING,
                                      key: ProtocolDefs.Password ] =
   { [name, password, key] ← ReadName[@MSInfo] };

ReadRSName: PUBLIC PROCEDURE RETURNS[ name: BodyDefs.RName,
                                      password: STRING,
                                      key: ProtocolDefs.Password ] =
   { [name, password, key] ← ReadName[@RSInfo] };

ReadName: PROC[info: POINTER TO NameInfo]
       RETURNS[name: BodyDefs.RName,
               password: STRING,
               key: ProtocolDefs.Password ] =
   BEGIN
   IF PossiblyFindName[info]
   THEN CheckConnect[info]--this call must be outside the monitor--;
   RETURN[info.name, info.password, info.key]
   END;

PossiblyFindName: ENTRY PROC[info: POINTER TO NameInfo]
                     RETURNS[first: BOOLEAN] = INLINE
   { first ← NOT info.known; IF first THEN FindName[info] };

BadHeapNameObject: SIGNAL = CODE;

FindName: INTERNAL PROC[info: POINTER TO NameInfo] =
   BEGIN
   which: {mail, reg} = IF info=@MSInfo THEN mail ELSE reg;
   changed: BOOLEAN ← FALSE;
   oldObj: ObjectDirDefs.ObjectNumber ← ObjectDirDefs.noObject;
   text: STRING = IF which = reg THEN "R-Server"L ELSE "M-Server"L;
   FindObject: PROCEDURE[obj: ObjectDirDefs.ObjectNumber] RETURNS[BOOLEAN] =
      BEGIN
      reader: HeapDefs.ReaderHandle = HeapDefs.HeapStartRead[obj];
      Read: PROCEDURE[to: POINTER, amount: CARDINAL] =
         BEGIN
         used: CARDINAL;
         [,used] ← HeapDefs.HeapReadData[reader, [to, amount]];
         IF used # amount
         THEN BEGIN SIGNAL BadHeapNameObject[]; info.known ← FALSE END;
         END;
      temp: STRING = [0];
      info.known ← TRUE;
      [] ← HeapDefs.HeapReadRName[reader, info.name];
      Read[ temp, String.WordsForString[0] ];
      IF temp.length > info.password.maxlength
      THEN BEGIN SIGNAL BadHeapNameObject[]; info.known ← FALSE END;
      info.password.length ← temp.length;
      Read[ @(info.password.text),
            String.WordsForString[info.password.length] -
            String.WordsForString[0] ];
      Read[ @(info.key), SIZE[ProtocolDefs.Password] ];
      HeapDefs.HeapEndRead[reader];
      IF info.known
      THEN { ObjectDirDefs.UseObject[oldObj←obj];
             RETURN[TRUE] -- terminate -- }
      ELSE RETURN[FALSE] --keep going--
      END;
   AskOthers: INTERNAL PROCEDURE =
      BEGIN
        info.name.length ← 0;
        WriteLine["Consulting registration servers ... "L];
        SELECT LocateDefs.FindLocalServer[IF which = reg THEN "GV.GV"L
                                     ELSE "MailDrop.MS"L, info.name] FROM
          allDown => WriteLine["all down"L];
          notFound => WriteLine["no local name found"L];
          found => info.known ← TRUE;
        ENDCASE => ERROR;
        IF NOT info.known
        THEN BEGIN
             ConfirmSystem[];
             String.AppendString[info.name, "Anon.GV"L];
             info.known ← TRUE;
             END;
        WriteString["My "L]; WriteString[text];
        WriteString[" name appears to be "L];
        WriteLine[info.name];
        WriteString["If you are certain the name is correct, type my password: "L];
        info.key ← ReadPassword[info.password];
        WriteLine[""L];
        changed ← TRUE;
      END;
   info.name ← Storage.String[BodyDefs.maxRNameLength];
   info.password ← Storage.String[32];
   [] ← ObjectDirDefs.Enumerate[IF which = reg THEN RSname ELSE MSname,
                                FindObject];
   IF NOT info.known
   THEN BEGIN
        WriteString["No "]; WriteString[text];
        WriteLine[" name is recorded in the heap"L];
        IF NOT initHeap THEN ConfirmSystem[]
        END
   ELSE BEGIN
        WriteString[text];
        WriteString[" name recorded in the heap is "L];
        WriteLine[info.name];
        END;
   DO UNTIL info.known DO AskOthers[]; ENDLOOP;
      SELECT NameInfoDefs.Authenticate[info.name, info.password] FROM
        individual => EXIT;
        notFound, group =>
          BEGIN
          WriteString[text]; WriteLine[" name is not valid"L];
          ConfirmSystem[];
          info.known ← FALSE;
          END;
        allDown =>
          BEGIN
          WriteString["No authentication server available"L];
          ConfirmSystem[];
          EXIT
          END;
        badPwd =>
          BEGIN
          WriteString["My password is incorrect; please re-type it: "L];
          info.key ← ReadPassword[info.password];
          WriteLine[""L];
          changed ← TRUE;
          END;
      ENDCASE => ERROR;
   ENDLOOP;
   IF changed
   THEN BEGIN -- create the heap object --
        writer: HeapDefs.WriterHandle =
              HeapDefs.HeapStartWrite[IF which = reg THEN RSname ELSE MSname];
        Keep: PROCEDURE[obj: ObjectDirDefs.ObjectNumber] =
           BEGIN ObjectDirDefs.UseObject[obj]; END;
        HeapDefs.HeapWriteRName[writer, info.name];
        HeapDefs.HeapWriteString[writer, info.password];
        HeapDefs.HeapWriteData[writer,
              [@(info.key),SIZE[ProtocolDefs.Password]] ];
        HeapDefs.HeapEndWrite[writer, Keep];
        IF oldObj # ObjectDirDefs.noObject
        THEN ObjectDirDefs.FreeObject[oldObj];
        END;
   END;

system: BOOLEAN ← FALSE;

ConfirmSystem: PROCEDURE =
   BEGIN
   -- In principle, we should keep only a one-way function of the
   -- system password in this source (e.g. [0,0,0,0] encrypted with the
   -- system password), and then check for equality with what the user
   -- types.  In practice, the system password is only there to prevent
   -- idiots messing up the system in some error cases (when the idiot
   -- has physical access to the computer), so proper security isn't
   -- worth the trouble.
   sysPwd: STRING = "Botrytis"L;
   hisAttempt: STRING = [32];
   IF system
   THEN BEGIN
        WriteString["Continue? [confirm] "L];
        UNTIL SELECT IODefs.ReadChar[] FROM
                Ascii.CR, 'y, 'Y => TRUE,
              ENDCASE => FALSE
        DO NULL ENDLOOP;
        WriteLine["yes"L];
        END
   ELSE DO WriteString["Type the system password to continue: "L];
           [] ← ReadPassword[hisAttempt];
           IF String.EquivalentString[hisAttempt, sysPwd]
           THEN { system ← TRUE; WriteLine[" ... ok"L]; EXIT }
           ELSE WriteLine[" ... incorrect"L];
        ENDLOOP;
   END;

ReadPassword: PROCEDURE[pwd: STRING] RETURNS[key: ProtocolDefs.Password] =
   BEGIN
   pwd.length ← 0;
   DO BEGIN
      c: CHARACTER = IODefs.ReadChar[];
      SELECT c FROM
        Ascii.SP, Ascii.TAB, Ascii.CR, Ascii.ESC => EXIT;
        Ascii.BS, Ascii.ControlA =>
          IF pwd.length > 0
          THEN BEGIN
               pwd.length ← pwd.length - 1;
               WriteChar[Ascii.BS];
               END;
      ENDCASE =>
        IF pwd.length < pwd.maxlength
        THEN BEGIN
             pwd[pwd.length] ← c; pwd.length ← pwd.length + 1;
             WriteChar['*];
             END;
      END;
   ENDLOOP;
   RETURN[ ProtocolDefs.MakeKey[pwd] ]
   END;

FindConnect: PROCEDURE[name: BodyDefs.RName,
                       addr: POINTER TO PupDefs.PupAddress]
               RETURNS[ BOOLEAN ] =
   BEGIN
   connect: ProtocolDefs.Connect = [ProtocolDefs.maxConnectLength];
   info: NameInfoDefs.NameType = NameInfoDefs.GetConnect[name, connect];
   SELECT info FROM
     individual =>
       PupDefs.GetPupAddress[addr, connect ! PupDefs.PupNameTrouble =>
                             GOTO couldnt ];
   ENDCASE => GOTO couldnt;
   RETURN[TRUE];
   EXITS couldnt => RETURN[FALSE];
   END;

SetConnectLocal: PROCEDURE[name: BodyDefs.RName,
                    key: ProtocolDefs.Password,
                    socket: PupDefs.PupSocketID]
                 RETURNS[ BOOLEAN ] =
   BEGIN
   info: LocateDefs.FoundServerInfo;
   str: ProtocolDefs.Handle ← NIL;
   Action: PROCEDURE[serverAddr: PupDefs.PupAddress] RETURNS[ BOOLEAN ] =
      BEGIN
      ENABLE ProtocolDefs.Failed => GOTO no;
      serverAddr.socket ← ProtocolDefs.RegServerEnquirySocket;
      str ← ProtocolDefs.CreateStream[serverAddr];
      RETURN[TRUE];
      EXITS no => RETURN[FALSE]
      END;
   info ← LocateDefs.FindRegServer[name, Action];
   IF str = NIL THEN GOTO couldnt;
   BEGIN
      ENABLE ProtocolDefs.Failed => GOTO badAnswer;
      rc: ProtocolDefs.ReturnCode;
      myAddr: PupDefs.PupAddress;
      connect: ProtocolDefs.Connect = [ProtocolDefs.maxConnectLength];
      PupDefs.GetPupAddress[ @myAddr, "ME"L ];
      myAddr.socket ← socket;
      PupDefs.AppendPupAddress[ connect, myAddr ];
      ProtocolDefs.SendRSOperation[str, IdentifyCaller];
      ProtocolDefs.SendRName[str, name];
      ProtocolDefs.SendPassword[str:str, pw:key, key:[0,0,0,0] ];
      ProtocolDefs.SendNow[str];
      rc ← ProtocolDefs.ReceiveRC[str];
      IF rc.code # done THEN GOTO badAnswer;
      ProtocolDefs.SendRSOperation[str, ChangeConnect];
      ProtocolDefs.SendRName[str, name];
      ProtocolDefs.SendConnect[str, connect];
      ProtocolDefs.SendNow[str];
      rc ← ProtocolDefs.ReceiveRC[str];
      IF rc.code # done THEN GOTO badAnswer;
      EXITS badAnswer =>
         BEGIN ProtocolDefs.DestroyStream[str]; GOTO couldnt END;
   END;
   ProtocolDefs.DestroyStream[str];
   RETURN[TRUE];
   EXITS couldnt => RETURN[FALSE];
   END;

CheckConnect: PROC[info: POINTER TO NameInfo] =
   BEGIN
   which: {mail, reg} = IF info=@MSInfo THEN mail ELSE reg;
   name: STRING = ReadName[info].name;
   key: ProtocolDefs.Password = ReadName[info].key;
   foundAddr: PupDefs.PupAddress;
   IF NOT FindConnect[name, @foundAddr]
   OR NOT ProtocolDefs.IsLocal[foundAddr]
   OR ( foundAddr.socket # (IF which = reg
                        THEN ProtocolDefs.RegServerPollingSocket
                        ELSE ProtocolDefs.mailServerPollingSocket) )
   THEN BEGIN
        WriteString["Re-setting my "L];
        WriteString[IF which=mail THEN "M-Server"L ELSE "R-Server"L];
        WriteString[" connect-site ... "L];
        IF SetConnectLocal[name, key,
                   IF which = reg
                   THEN ProtocolDefs.RegServerPollingSocket
                   ELSE ProtocolDefs.mailServerPollingSocket]
        THEN WriteLine["done"L]
        ELSE BEGIN
             WriteLine["couldn't"L];
             ConfirmSystem[];
             END;
        END;
   END;


END.