!***********************************************************************
!*
!*                 Command for changing user passwords 
!*
!*      Copyright (C)   R.D. Eager   University of Kent   MCMLXXXV
!*
!***********************************************************************
!
!
!***********************************************************************
!*
!*          Constants
!*
!***********************************************************************
!
constantinteger  minpass = 6;           ! Min length of a password
constantinteger  maxpass = 11;          ! Max length of a password
constantlonginteger  int mask = x'0002000A0002000A'
                                        ! INT: 'A','C','Q','a','c','q'
constantstring (1) snl = "
"
!
!
!***********************************************************************
!*
!*          Subsystem references
!*
!***********************************************************************
!
systemroutinespec  console(integer  ep,integername  start,len)
systemstring (255)mapspec  controlline(integername  flag)
systemstringfunctionspec  itos(integer  n)
systemroutinespec  journaloff
systemintegerfunctionspec  parmap
externalroutinespec  prompt(string (255) s)
systemroutinespec  psysmes(integer  root,mess)
systemroutinespec  reroutecontingency(integer  ep,class,longinteger  c 
                                      mask,routine  ontrap,
                                      integername  flag)
systemroutinespec  setfname(string (63) s)
externalroutinespec  setmode(string (255) s)
systemroutinespec  setpar(string (255) s)
externalroutinespec  set return code(integer  i)
systemstringfunctionspec  spar(integer  n)
systemroutinespec  signal(integer  ep,p1,p2,integername  flag)
systemroutinespec  uctranslate(integer  ad,len)
externalintegerfunctionspec  uinfi(integer  entry)
externalstringfunctionspec  uinfs(integer  entry)
!
!
!***********************************************************************
!*
!*          Director references
!*
!***********************************************************************
!
externalintegerfunctionspec  dsetpassword(string (6)user,integer  fsys,
                                          which,string (63) old,new)
!
!
!***********************************************************************
!*
!*          Forward references
!*
!***********************************************************************
!
routinespec  finalise security
!
!
!***********************************************************************
!*
!*          Service routines
!*
!***********************************************************************
!
routine  ontrap(integer  class,subclass)
integer  flag
!
flag = 0
finalise security
signal(3,class,subclass,flag);          ! Get the Subsystem to do the rest
end ;   ! of ontrap
!
!-----------------------------------------------------------------------
!
routine  initialise security(integername  flag)
! Intercept   single-character   INT: messages,  so  that  the  security
! precautions may be unset on exit.  
!
reroutecontingency(3,65,int mask,ontrap,flag)
if  flag = 0 then  start 
   setmode("-E");                       ! Turn off input echo
   journaloff;                          ! Turn off any journal
finish 
end ;   ! of initialise security
!
!-----------------------------------------------------------------------
!
routine  finalise security
integer  flag,ad
!
setmode("E");                           ! Turn on input echo
newline
ad = 0
console(14,ad,ad);                      ! Turn on recalling again
reroutecontingency(0,0,0,ontrap,flag);  ! Cancel contingency rerouting
end ;   ! of finalise security
!
!-----------------------------------------------------------------------
!
routine  read password(string (63) mes,stringname  p,integername  flag)
stringname  line
!
printstring(mes); newline
prompt(tostring(nl)."Password: ")
line == control line(flag)
newline
if  flag # 0 then  start 
   setfname("")
   flag = 202;                          ! Invalid parameter
   return 
finish 
!
p = line
uctranslate(addr(p)+1,length(p))
flag = 0
end ;   ! of read password
!
!
!***********************************************************************
!*
!*          P A S S W O R D
!*
!***********************************************************************
!
externalroutine  password(string (255) parms)
integer  flag,i,c,fore,back
string (maxpass) pass1,pass2,oldpass
stringname  line
!
! Decode and validate any parameter, after disallowing use in  OBEY  and
! batch.  
!
if  uinfi(2) # 1 then  start 
   printstring("Password may only be changed from an interactive terminal")
   newline
   flag = 173;                          ! No access permission
   -> err3
finish 
!
setpar(parms)
if  parmap > 1 then  start 
   flag = 263;                          ! Wrong number of parameters
   -> err2
finish 
!
parms = spar(1)
uctranslate(addr(parms)+1,length(parms))
line == parms
fore = 0
back = 0
cycle 
   for  i = 1,1,length(line) cycle 
      c = charno(line,i)
      if  c = 'F' then  fore = -1 else  c 
      if  c = 'B' then  back = -1 else  start 
         printstring("Parameter not recognised.".snl)
         fore = 0
         back = 0
         exit 
      finish 
   repeat 
   exit  if  fore + back # 0;           ! A valid letter was typed
   printstring("Which password do you want to change?".snl)
   printstring("(F for foreground, B for background, FB for both)".snl)
   prompt("F or B or FB: ")
   line == controlline(flag)
   if  flag # 0 then  start 
      line == parms
      line = ""
   finish 
   uctranslate(addr(line)+1,length(line))
repeat 
!
! Turn off input echo, and disable  the  session  journal,  for  obvious
! security reasons.  
!
initialise security(flag)
-> err if  flag # 0
!
! Read in the current foreground password (needed by DSETPASSWORD)
!
printstring("Passwords will not be echoed.".snl)
read password("Type in your current FOREGROUND password.",oldpass,flag)
-> err if  flag # 0
!
! Read the new password(s) twice, and check that they were the same both
! times. It is quite likely that the user will not be sure what he typed
! (since  input echo is off), so this helps to ensure that he knows what
! he has set the password(s) to.
!
cycle 
   if  fore # 0 then  printstring(snl."Change FORE") else  c 
   if  back # 0 then  printstring(snl."Change BACK") else  c 
   exit 
   printstring("GROUND password -".snl)
   !
   read password("Type in your new password -",pass1,flag)
   -> err if  flag # 0
   !
   if  length(pass1) < minpass then  start 
      printstring("For security reasons, your password must be at least ".itos(minpass)." characters long".snl)
      printstring("Please choose another one".snl)
      continue 
   finish 
   !
   if  length(pass1) > maxpass then  start 
      printstring("Passwords may not be more than ".itos(maxpass)." characters long".snl)
      printstring("Please choose another one".snl)
      continue 
   finish 
   !
   read password("Please type new password again to confirm -",pass2,flag)
   -> err if  flag # 0
   if  pass1 # pass2 then  start 
      printstring("You gave two different forms for your new password.".snl)
      printstring("The old password has not been changed.".snl)
      exit 
   finish 
   !
   flag = dsetpassword(uinfs(1),uinfi(1),¬fore,oldpass,pass1)
   if  fore # 0 then  fore = 0 else  c 
   if  back # 0 then  back = 0
   if  flag # 0 then  start 
      back = 0
      if  flag = 8 then  start 
         printstring("You did not give your current foreground")
         printstring(" password correctly.".snl)
         printstring("The password has not been changed.".snl)
         flag = 173;                    ! No access permission
         -> err4
      finish  else  start 
         flag = flag + 500
         -> err
      finish 
   finish 
   oldpass = pass1
   printstring("Password changed.".snl)
repeat 
!
err:
finalise security
if  flag = 1000 then  start 
   printstring("The password you typed was too long.".snl)
   printstring("Passwords may not be more than ".itos(maxpass)." characters long.".snl)
   -> err4
finish 
!
err2:
if  flag # 0 then  psysmes(63,flag)
!
err3:
set return code(flag)
return 
!
err4:
finalise security
-> err3
end ;   ! of password
endoffile