//RAKFCL   JOB (TSO),
//             'Install RAKFCL',
//             CLASS=A,
//             MSGCLASS=A,
//             MSGLEVEL=(1,1),
//             USER=IBMUSER,PASSWORD=SYS1
//*
//* THIS JCL AUTOMATICALLY GENERATED BY make_release.sh
//*
//* Member $RAKFCL in SYS2.EXEC explains how to use these commands
//*
//* To install to MVS/CE:
//*     In TSO: RX MVP INSTALL RAKFCL
//*     In bash: cat release.jcl|ncat -w1 -v localhost 3505
//*
//RFEPLIB   EXEC PGM=PDSLOAD
//STEPLIB  DD  DSN=SYSC.LINKLIB,DISP=SHR
//SYSPRINT DD  SYSOUT=*
//SYSUT2   DD  DSN=SYS2.EXEC,DISP=SHR
//SYSUT1   DD  DATA,DLM=@@
./ ADD NAME=ADDGROUP
/* RAKF ADD GROUP BREXX SCRIPT */
/* GET ARGUMENTS */
parse arg args
/* -------- Default -------- */
GROUPOWNER = userid()

/* --------  DONE  -------- */
if length(args) = 0 then do
  say "RAKF01I Insufficient arguments"
  say ""
  say "ADDGROUP GROUPNAME"
  say " [OWNER(USERID)] Default: " || userid()
  say "GROUPNAME IS REQUIRED"
  exit
end
parse upper var args groupname args
if pos("(",userid) > 0 then do
    say "RAKF01E First argument must be group name"
    exit
end
call check_length groupname 'group' 8
/* -------- Parse Arguments -------- */
do while (length(args) > 0)
   parse var args t .
      if pos("(",t) = 0 then do
         say 'RAKF01E Argument' t 'not recognized'
         exit
      end
      else do
        parse var args o "(" s ")" args
        current = o||"("||s||")"
      end
/* parse var args current args */
     parse var current option "(" selection ")"
     select
       when (upper(option) = 'OWNER') then do
         GROUPOWNER = selection
         call check_length GROUPOWNER 'groupowner' 7
       end
       otherwise do
         say 'RAKF01E Argument' current 'not recognized'
         exit
       end
     end
   
end

/* ----- Open RAKF User file ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
notfound = 1
do i=1 to sortin.0
  PARSE VAR sortin.i 1 ruser 10 GROUP 18 DFLT 19 PASSWORD_ADMIN_COMMENT

  if strip(ruser) = upper(GROUPOWNER) then do
    notfound = 0
    owner_pass = PASSWORD_ADMIN_COMMENT
  end
  if strip(ruser) = upper(GROUPOWNER) & strip(group) = groupname then do
    say "RAKF02E Group" groupname "already exists."
    say "        Use RX CONNECT '"||GROUPOWNER||" GROUP("||groupname||")'"
    exit
  end
  if strip(group) = groupname then do
    say "RAKF02E Group" groupname "already exists"
    exit
  end
end

if notfound then do
    say "RAKF02E Owner" GROUPOWNER "not found in user database."
    say "        User must already exist before assigning group to it."
    say "        Use RX ADDUSER " GROUPOWNER "to add the user."
    exit
end

rakf = LEFT(GROUPOWNER,8)||" "||LEFT(groupname,8)||" "||owner_pass

/* --------  DONE  -------- */

/* ----- Adding Group */
sortin.0 = sortin.0 + 1
x = sortin.0
sortin.x = rakf

call rxsort

ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKW RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
/* Done */
/* ----- Update RAKF ----- */
call console("s rakfuser")
/* ----- DONE        ----- */
exit

check_length:
  parse arg x y z
  if length(x) > z then do
    say 'RAKF02I' y 'must be' z 'characters or shorter'
    exit
  end
return

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF user database'
    exit
  end
return

./ ADD NAME=ADDSD
/* RAKF ADDSD: ADD DATASET PROFILE BREXX SCRIPT */
/* THIS IS ALSO THE ALIAS FOR ALTDSD */
/* GET ARGUMENTS */
parse arg args
/* -------- Default -------- */
DFLTUACC = 'NONE'
UACC = ''
DFLTCLASS = 'DATASET'
/* --------  DONE  -------- */
if length(args) = 0 then do
  say "RAKF01I Insufficient arguments"
  say ""
  say "ADDSD PROFILE-NAME"
  say " [UACC(READ)] Default: NONE"
  say "DATASET PROFILE IS REQUIRED"
  say "EXAMPLE: RX ADDSD 'SYSGEN.ISPF.* UACC(READ)'"
  say "EXAMPLE: RX ADDSD '(SYS3.**,SYS4.MACLIB) UACC(NONE)'"
  exit
end
parse upper var args rules args
if pos("UACC",rules) > 0 then do
    say "RAKF01E First argument must be dataset profile(s) e.g. 'SYS2.*'"
    exit
end

/* -------- Parse profiles -------- */
if pos("(", rules) > 0 then do
    if pos(")", rules) = 0 then do
        say "RAKF02E No spaces allows in profiles:" rules
        exit
    end
    parse var rules "(" rule ")"
    count = 0
    do while (length(rule) > 0)
        parse var rule r "," rule
        count = count + 1
        profiles.count = r
    end
    profiles.0 = count
end
else do
    profiles.0 = 1
    profiles.1 = rules 
end

do i=1 to profiles.0
    call check_length profiles.i DATASET 44
end


/* -------- Parse Arguments -------- */
if length(args) > 0 then do
    parse upper var args arguacc args
    if pos("UACC",arguacc) > 0 then do
        parse var arguacc . "(" UACC ")" .
        if UACC = '' then do
            say "RAKF03E Invalid UACC syntax:" arguacc
            exit
        end
    end
end

if length(args) > 0 then do
    say "RAKF05E Invalid argument:" args
    exit
end

if UACC = '' then UACC = DFLTUACC

/* ----- Open RAKF profile file ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(PROFILES)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"

/* ----- Does the rule already exist? ----- */
do i=1 to sortin.0
  parse var sortin.i class resource PUACC NOTUACC
  if class = 'DATASET' & length(NOTUACC) = 0 then do
    do j = 1 to profiles.0
        if profiles.j = resource & UACC = PUACC then do
            /* this means the rule exists with the same UACC */
            say 'RAKF04I RAKF Dataset profile' profiles.j "already exists"
            exit 
        end
    end
  end
end
/* -------- Generate profile line -------- */
/* Are we just changing the UACC? Yes we allow that, unlike other corps */
count = 0
do i=1 to sortin.0
    parse var sortin.i class resource PUACC NOTUACC
    if class = 'DATASET' & length(NOTUACC) = 0 then do
        do j = 1 to profiles.0
            if profiles.j = resource then do
                PROF = LEFT(DFLTCLASS,8) || LEFT(profiles.j,52)||LEFT(UACC,6)
                sortin.i = PROF
            end
            else do
                notfound = 1
                do x = 1 to newprof.0
                    if newprof.x = profiles.j then do
                        notfound = 0
                    end
                end
                if notfound then do
                    count = count + 1
                    newprof.count = profiles.j
                end
            end
        end
        newprof.0 = count
    end
end


/* ----- Adding profiles */
start = sortin.0
do i = 1 to newprof.0
    start = start + 1
    PROF = LEFT(DFLTCLASS,8) || LEFT(newprof.i,52)||LEFT(UACC,6)
    sortin.start = PROF
end

sortin.0 = sortin.0 + newprof.0

call rxsort

/* Add changes to PROFILES */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(PROFILES)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKW RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
/* Done */
/* ----- Update RAKF ----- */
call console("s rakfprof")
/* ----- DONE  ----- */
exit

check_length:
  parse arg x y z
  if length(x) > z then do
    say 'RAKF02I' y 'must be' z 'characters or shorter'
    exit
  end
return

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF profile database'
    exit
  end
return

./ ADD NAME=ADDUSER
/* RAKF ADD USER BREXX SCRIPT */
/* GET ARGUMENTS */
parse arg args
/* -------- Default -------- */
DFLTGRP = 'USER'
OPER=0
SPECIAL=0
NAME=''
PASSWORD='PASSWORD'
USERID=''
/* --------  DONE  -------- */
if length(args) = 0 then do
  say "RAKF01I Insufficient arguments"
  say ""
  say "ADDUSER USERID"
  say " [PASSWORD(PASSWORD)] Default: PASSWORD"
  say " [DFLTGRP(USER)]      Default: USER"
  say " [OPERATIONS|OPER]    Default: NO OPERATIONS"
  say " [SPECIAL]            Default: NO SPECIAL"
  say " [NAME()]             Default: Blank"
  say "USERID IS REQUIRED"
  exit
end
parse upper var args userid args
if pos("(",userid) > 0 then do
    say "RAKF01I First argument must be userid"
    exit
end
call check_length userid 'USERID' 7
/* -------- Parse Arguments -------- */
do while (length(args) > 0)
   parse var args t .
      if pos("(",t) = 0 then
        parse var args current args
      else do
        parse var args o "(" s ")" args
        current = o||"("||s||")"
      end
/* parse var args current args */
   if pos("(",current) = 0 then do
     select
       when upper(current) = 'OPER' then
         OPER = 1
       when upper(current) = 'OPERATIONS' then
         OPER = 1
       when upper(current) = 'SPECIAL' then
         SPECIAL = 1
       otherwise do
         say 'RAKF01I Argument' current 'not recognized'
         exit
       end
     end
   end  
   else do
     parse var current option "(" selection ")"
     select
       when (upper(option) = 'PASSWORD') then do
         password = selection
         call check_length PASSWORD 'PASSWORD' 8
       end
       when (upper(option) = 'NAME') then
         NAME = selection
       when (upper(option) = 'COMMENT') then
         NAME = selection
       when (upper(option) = 'DFLTGRP') then do
         DFLTGRP = selection
         call check_length DFLTGRP 'DFLTGRP' 8
       end
       otherwise do
         say 'RAKF01I Argument' current 'not recognized'
         exit
       end
     end
   end
end

/* ----- Open RAKF User file ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
do i=1 to sortin.0
  parse var sortin.i current_user .
  if current_user = upper(USERID) then do
    say 'RAKF03I RAKF user' USERID 'already exists'
    exit
  end
end
/* --------  DONE  -------- */
/* -------- Generate user line -------- */
rakf = LEFT(USERID,8)||" "||LEFT(DFLTGRP,8)
if special then do
  rakf_admin = LEFT(USERID,8)||" RAKFADM "
  if c2d(left(DFLTGRP,1)) < c2d("R") then do
    rakf_admin = rakf_admin||" "||LEFT(PASSWORD,8)||" "
    rakf = rakf||"*"||LEFT(PASSWORD,8)||" "
  end
  else do
    rakf_admin = rakf_admin||"*"||LEFT(PASSWORD,8)||" "
    rakf = rakf||" "||LEFT(PASSWORD,8)||" "
  end
end
else
  rakf = rakf||" "||LEFT(PASSWORD,8)||" "
if oper then do
  rakf = rakf||"Y"
  if special then
    rakf_admin = rakf_admin||"Y"
end
else do
  rakf = rakf||"N"
  if special then
    rakf_admin = rakf_admin||"N"
end

if length(name) > 0 then do
  rakf = rakf||"  "||LEFT(name,19)
  if special then
    rakf_admin = rakf_admin||"  "||LEFT(name,19)
end
/* ----- Adding user */
sortin.0 = sortin.0 + 1
x = sortin.0
sortin.x = rakf
if special then do
  sortin.0 = x + 1
  x = x + 1
  sortin.x = rakf_admin
end
call rxsort
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKW RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
/* Done */
/* ----- Update RAKF ----- */
call console("s rakfuser")
/* ----- DONE        ----- */
exit

check_length:
  parse arg x y z
  if length(x) > z then do
    say 'RAKF02I' y 'must be' z 'characters or shorter'
    exit
  end
return

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF user database'
    exit
  end
return

./ ADD NAME=ALTUSER
/* RAKF ALTER USER BREXX SCRIPT */
/* GET ARGUMENTS */
parse arg args
/* -------- Default -------- */
OPER=0
NOOPER=0
SPECIAL=0
NAME=''
NEWPASS=''
USERID=''
NOSPECIAL=0
notalreadyspecial = 1
/* --------  DONE  -------- */
if length(args) = 0 then do
  say "RAKF01I Insufficient arguments"
  say ""
  say "ALTUSER USERID"
  say " [PASSWORD(PASSWORD)]"
  say " [OPERATIONS|OPER]"
  say " [NOOPERATIONS|NOOPER]"
  say " [SPECIAL]"
  say " [NOSPECIAL]"
  say " [NAME()|COMMENT()]"
  say ""
  say "USERID IS REQUIRED"
  exit
end
parse upper var args userid args
if pos("(",userid) > 0 then do
    say "RAKF01I First argument must be userid"
    exit
end

if length(args) <= 0 THEN EXIT
call check_length userid 'USERID' 7

/* -------- Parse Arguments -------- */
do while (length(args) > 0)
   parse var args t .
   if pos("(",t) = 0 then
     parse var args current args
   else do
     parse var args o "(" s ")" args
     current = o||"("||s||")"
   end
/* parse var args current args */
   if pos("(",current) = 0 then do
     select
       when upper(current) = 'OPER' then
         OPER = 1
       when upper(current) = 'OPERATIONS' then
         OPER = 1
       when upper(current) = 'SPECIAL' then
         SPECIAL = 1
       when upper(current) = 'NOOPER' then
         NOOPER = 1
       when upper(current) = 'NOOPERATIONS' then
         NOOPER = 1
       when upper(current) = 'NOSPECIAL' then
         NOSPECIAL = 1
       otherwise do
         say 'RAKF01I Argument' current 'not recognized'
         exit
       end
     end
   else do
     parse var current option "(" selection ")"
     select
       when (upper(option) = 'PASSWORD') then do
         NEWPASS = selection
         call check_length NEWPASS 'PASSWORD' 8
       end
       when (upper(option) = 'NAME') then
         NAME = selection
       when (upper(option) = 'COMMENT') then
         NAME = selection
       otherwise do
         say 'RAKF01I Argument' current 'not recognized'
         exit
       end
     end
   end
end

IF NOSPECIAL THEN SPECIAL = 0
IF NOOPER THEN OPER=0
/* ----- Open RAKF User file ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM userdb. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"

do i=1 to userdb.0
  parse var userdb.i current_user .
  if current_user = upper(USERID) then do
     leave
  end
end

if i > userdb.0 then do
  say "RAKF02I Userid" USERID "does not exist"
  exit
end

newuserdb.0 = 0
j = 0
olduser.0 = 0
k = 0
do i=1 to userdb.0
  parse var userdb.i current_user .
  if current_user \= upper(USERID) then do
    j = j + 1
    newuserdb.j = userdb.i
  end
  else do
    k = k + 1
    olduser.k = userdb.i
  end
end
newuserdb.0 = j
olduser.0 = k
/* --------  DONE  -------- */
/* -------- Make needed changes ----- */
changeuser.0 = 0
c = 0
do i=1 to olduser.0
  GROUP = strip(substr(olduser.i,10,8))
  PASSWORD = strip(substr(olduser.i,19,8))
  COMMENT = strip(substr(olduser.i,31,20))
  OPS = strip(substr(olduser.i,28,1))

  IF GROUP = 'RAKFADM' & NOSPECIAL THEN ITERATE
  IF GROUP = 'RAKFADM' THEN notalreadyspecial = 0
  IF LENGTH(NEWPASS) > 0 THEN PASSWORD = NEWPASS
  IF LENGTH(NAME) > 0 THEN COMMENT = NAME
  IF OPER THEN OPS = 'Y'
  IF NOOPER THEN OPS = 'N'

  rakf = LEFT(USERID,8)||" "||LEFT(GROUP,8)||"*"||LEFT(PASSWORD,8)||,
         " "||OPS||"  "||LEFT(COMMENT,20)
  c = c + 1
  changeuser.c = rakf
end

changeuser.0 = c

if SPECIAL & notalreadyspecial then do
  c = c + 1
  rakf = LEFT(USERID,8)||" RAKFADM *"||LEFT(PASSWORD,8)||,
         " "||OPS||"  "||LEFT(COMMENT,20)
  changeuser.c = rakf
  changeuser.0 = c
end

sortin.0=0
j=0
do i=1 to newuserdb.0
 j = j + 1
 sortin.j = newuserdb.i
end
do i=1 to changeuser.0
 j = j + 1
 sortin.j = changeuser.i
end
sortin.0=j

call rxsort
/* ----- DONE ----- */
/* ----- Update the RAKF database ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKW RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
/* ----- DONE ----- */
/* ----- Update RAKF ----- */
call console("s rakfuser")
/* ----- DONE        ----- */
exit

check_length:
  parse arg x y z
  if length(x) > z then do
    say 'RAKF02I' y 'must be' z 'characters or shorter'
    exit
  end
return

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF user database'
    exit
  end
return
./ ADD NAME=CONNECT
/* RAKF CONNECT USER TO GROUP BREXX SCRIPT */
/* GET ARGUMENTS */
parse arg args
/* --------  DONE  -------- */
if length(args) = 0 then do
  say "RAKF01I Insufficient arguments"
  say ""
  say "CONNECT USERNAME GROUP(GROUPNAME)"
  say "USERNAME AND GROUP IS REQUIRED"
  exit
end
parse upper var args USERNAME args
if pos("(",userid) > 0 then do
    say "RAKF01E First argument must be user name"
    exit
end
call check_length USERNAME 'username' 7
/* -------- Parse Arguments -------- */
do while (length(args) > 0)
   parse var args t .
      if pos("(",t) = 0 then do
         say 'RAKF01E Argument' t 'not recognized'
         exit
      end
      else do
        parse var args o "(" s ")" args
        current = o||"("||s||")"
      end
     parse var current option "(" selection ")"
     select
       when (upper(option) = 'GROUP') then do
         GROUPNAME = selection
         call check_length GROUPNAME 'GROUP' 8
       end
       otherwise do
         say 'RAKF01E Argument' current 'not recognized'
         exit
       end
     end
end

/* ----- Open RAKF User file ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"

notfound_user = 1
notfound_group = 1
do i=1 to sortin.0
  PARSE VAR sortin.i 1 ruser 10 GROUP 18 DFLT 19 PASSWORD_ADMIN_COMMENT

  if strip(group) = GROUPNAME then do
    notfound_group = 0
  end

  if strip(ruser) = upper(USERNAME) then do
    notfound_user = 0
    owner_pass = PASSWORD_ADMIN_COMMENT
  end
  if strip(ruser) = upper(USERNAME) & strip(group) = GROUPNAME then do
    say "RAKF02E Group" groupname "already exists for" USERNAME
    exit
  end
end

if notfound_user then do
    say "RAKF02E User" USERNAME "not found in user database."
    say "        User must already exist before assigning group to it."
    say "        Use RX ADDUSER " USERNAME "to add the user."
    exit
end

if notfound_group then do
    say "RAKF02E Group" GROUPNAME "not found in user database."
    say "        Group must already exist before connecting a user to it."
    say "        Use RX ADDGROUP '"||GROUPNAME||"'to add the group."
    exit
end

rakf = LEFT(USERNAME,8)||" "||LEFT(GROUPNAME,8)||" "||owner_pass

/* --------  DONE  -------- */

/* ----- Adding Group */
sortin.0 = sortin.0 + 1
x = sortin.0
sortin.x = rakf

call rxsort

ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKW RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
/* Done */
/* ----- Update RAKF ----- */
call console("s rakfuser")
/* ----- DONE        ----- */
exit

check_length:
  parse arg x y z
  if length(x) > z then do
    say 'RAKF02I' y 'must be' z 'characters or shorter'
    exit
  end
return

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF user database'
    exit
  end
return

./ ADD NAME=DELDSD
/* RAKF DELDSD: DELETE DATASET PROFILE BREXX SCRIPT */
/* THIS IS ALSO THE ALIAS FOR ALTDSD */
/* GET ARGUMENTS */
parse arg profile .

if length(profile) = 0 then do
  say 'RAKF01I Missing dataset profile to remove'
  exit
end

ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(PROFILES)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM classes. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"


newprofile.0 = 0
c = 0
found = 0
DO i=1 to classes.0
 parse var classes.i FACILITY prof .
 if upper(prof) = upper(profile) then do
    found = 1
    iterate
 end
 c = c + 1
 newprofile.c = classes.i
end
newprofile.0 = c

if found = 0 then do
    say "RAKF01I Dataset Profile" upper(profile) "not found"
    exit
end

ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(PROFILES)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKW RAKF (STEM newprofile. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
exit

/* ----- Update RAKF ----- */
call console("s rakfprof")
/* ----- DONE        ----- */

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF profile database'
    exit
  end
return
./ ADD NAME=DELGROUP
/* DELETE A RAKF GROUP */
/* Open the RAKF db */
parse arg group .

if length(group) = 0 then do
  say "RAKF01I Insufficient arguments"
  say ""
  say "DELGROUP GROUPNAME"
  say "GROUPNAME IS REQUIRED"
  exit
end

ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM userdb. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"

newuserdb.0 = 0
c = 0
found = 0
DO i=1 to userdb.0
 PARSE VAR userdb.i 1 rUSER 10 rGROUP 18 .
 if strip(rGROUP) = upper(group) then do
    /* Make sure this isn't the only group */
    user_count = 0
    do j = 1 to userdb.0
      parse var userdb.j user .
      if strip(rUSER) = strip(user) then do
        user_count = user_count + 1
      end 
    end

    if user_count = 1 then do
        say "RAKF02E Cannot remove group "group" from user" rUSER
        say "        This user only exists in this one group."
        say "        Either DELUSER or CONNECT to another group"
        say "        before deleting this group"
        exit
    end
    found = 1
    iterate
 end
 c = c + 1
 newuserdb.c = userdb.i
end
newuserdb.0 = c

if found = 0 then do
    say "RAKF01I Group" upper(group) "not found"
    exit
end


ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKW RAKF (STEM newuserdb. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
exit

/* ----- Update RAKF ----- */
call console("s rakfuser")
/* ----- DONE        ----- */

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF user database'
    exit
  end
return
./ ADD NAME=DELUSER
/* DELETE A RAKF USER */
/* Open the RAKF db */
parse arg userid .

if length(userid) = 0 then do
  say 'RAKF01I Missing userid'
  exit
end

ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM userdb. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
newuserdb.0 = 0
c = 0
found = 0
DO i=1 to userdb.0
 parse var userdb.i user .
 if upper(user) = upper(userid) then do
    found = 1
    iterate
 end
 c = c + 1
 newuserdb.c = userdb.i
end
newuserdb.0 = c

if found = 0 then do
    say "RAKF01I Userid" upper(userid) "not found"
    exit
end

ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKW RAKF (STEM newuserdb. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
exit

/* ----- Update RAKF ----- */
call console("s rakfuser")
/* ----- DONE        ----- */

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF user database'
    exit
  end
return
./ ADD NAME=LISTDSD
/* RAKF LISTDSD: LIST DATASET PROFILE BREXX SCRIPT */
/* THIS IS ALSO THE ALIAS FOR ALTDSD */
/* GET ARGUMENTS */
parse arg args
/* -------- Default -------- */
DFLTUACC = 'NONE'
UACC = ''
FACILITY = 'DATASET'
/* --------  DONE  -------- */
/* -------- Parse Arguments -------- */
do while (length(args) > 0)
   parse var args t .
      if pos("(",t) = 0 then do
         say 'RAKF01E Argument' t 'not recognized'
         exit
      end
      else do
        parse var args o "(" s ")" args
        current = o||"("||s||")"
      end
     parse var current option "(" selection ")"
     select
       when (upper(option) = 'DATASET') then do
         DATASET = selection
       end
       otherwise do
         say 'RAKF01E Argument' current 'not recognized'
         exit
       end
     end
end


/* ----- Open RAKF profile file ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(PROFILES)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
call rxsort

/* ----- Show the rules ----- */
do i=1 to sortin.0
  parse var sortin.i 1 class 9 resource 53 group 61 access
  if strip(class) = 'DATASET' & length(strip(group)) = 0 then do
    call print_access
  end
end

exit

print_access:
  say "INFORMATION FOR DATASET" strip(resource)
  groups_count = 0
  do j=1 to sortin.0
      parse var sortin.j 1 gclass 9 gresource 53 ggroup 61 gaccess
      if (strip(gclass) = 'DATASET') & (gresource = resource) &,
      (length(strip(ggroup)) > 0) then do
          say "  GROUP:" left(ggroup,8) "ACCESS: " gaccess
      end
  end
  say "  UNIVERSAL ACCESS:" access
  say ""
return

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF profile database'
    exit
  end
return
./ ADD NAME=LISTGRP
/* RAKF LIST GROUP(S) BREXX SCRIPT */
/* GET ARGUMENTS */
parse arg groupname .
/* Open the RAKF db */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM userdb. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
groups.0 = 0
gc = 0

call get_groups

if length(groupname) = 0 then do
    do i = 1 to groups.0   
        say "INFORMATION FOR GROUP" groups.i
        call print_users groups.i
    end
end
else do
    group_exists = 0
    do i = 1 to groups.0 
        if groups.i = groupname then group_exists = 1
    end
    if group_exists then do
      say "INFORMATION FOR GROUP" groupname
      call print_users groupname
    end
    else do
        say "RAKF02E Group" groupname "does not exist"
        exit
    end
end
EXIT

get_groups:
  do i = 1 to userdb.0
    PARSE VAR userdb.i 1 . 10 gGROUP 18 .
    /* is this group already in our stem? */
    notfound = 1
    do j = 1 to groups.0 
        if strip(groups.j) = strip(gGROUP) then do
            notfound = 0
        end
    end
    if notfound then do
        groups.0 = groups.0 + 1
        c = groups.0
        groups.c = strip(gGROUP)
    end
  end
return

print_users:
    parse arg group
    say " USER(S)="
    do x = 1 to userdb.0
        PARSE VAR userdb.x 1 user 10 pGROUP 18 .
        if strip(pGROUP) = strip(group) then do
            say " " user
        end
    end
    say ""
return

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF user database'
    exit
  end
return

./ ADD NAME=LISTUSER
/* RAKF LIST USER BREXX SCRIPT */
/* GET ARGUMENTS */
parse arg userid .
/* Default to current user */
if length(userid) = 0 then userid = userid()

/* Open the RAKF db */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM userdb. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"

/* If this is a single user */
if userid \= "*" then do
 oper=0
 rakfadm = 0
 line1 = ""
 line1 = line1 || "USER="||LEFT(upper(userid),8)|| " "
 grp = "GROUPS="
 cmnts = "COMMENTS="
 DO i=1 to userdb.0
  USER = strip(substr(userdb.i,1,8))
  GROUP = strip(substr(userdb.i,10,8))
  COMMENT = strip(substr(userdb.i,31,20))
  OPS = strip(substr(userdb.i,28,1))
  if upper(user) = upper(userid) then do
    if group = 'RAKFADM' then rakfadm = 1
    grp = grp|| GROUP||" "
    if OPS="Y" then oper=1
    cmnts=COMMENT
  end
 end
  attr = "ATTRIBUTES="
  if OPER THEN attr = attr||"OPERATIONS "
  if rakfadm then attr = attr||"SPECIAL"
  say line1
  say grp
  say attr
  say "COMMENTS="||cmnts
  say ""
end
else do
  current_user = ""
  grp = ''
  attr= ''
  cmnts=''
  OPER=0
  rakfadm=0
  do i=1 to userdb.0
    USER = strip(substr(userdb.i,1,8))
    GROUP = strip(substr(userdb.i,10,8))
    COMMENT = strip(substr(userdb.i,31,20))
    OPS = strip(substr(userdb.i,28,1))
    if length(current_user) = 0 then current_user = USER
    if current_user \= user then do
     say 'USER='||current_user
     say "GROUPS="||grp
     if OPER THEN attr = attr||"OPERATIONS "
     if rakfadm then attr = attr||"SPECIAL"
     say "ATTRIBUTES="||attr
     say "COMMENTS="||cmnts
     current_user = user
     grp = ''
     attr= ''
     cmnts=''
     OPER=0
     rakfadm=0
     say ''
    end
    grp=grp||GROUP||" "
    cmnts=COMMENT
    if group = 'RAKFADM' then rakfadm = 1
    if OPS="Y" then oper=1
  end
  /* catch the bottom user */
  say 'USER= '||user
  say "GROUPS= "||grp
  if OPER THEN attr = attr||"OPERATIONS "
  if rakfadm then attr = attr||"SPECIAL"
  say "ATTRIBUTES= "||attr
  say "COMMENTS= "||cmnts
  say ''
end
EXIT

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF user database'
    exit
  end
return

./ ADD NAME=PERMIT
/* RAKF PERMIT BREXX SCRIPT */
/* GET ARGUMENTS */
parse arg args
/* -------- Default -------- */
DFLTUACC = 'NONE'
UACC = ''
CLASS = 'DATASET'
DFLTACCESS = 'READ'
IDS.0 = 0
DELETE = 0
ACCESS = 0
ACCESSR = "NONE READ UPDATE ALTER"
/* --------  DONE  -------- */
if length(args) = 0 then do
  say "RAKF01I Insufficient arguments"
  say ""
  say "PERMIT PROFILE-NAME"
  say " ID(USERNAME) Supports multiple users/groups"
  say " [CLASS(FACILITY)] Default: DATASET"
  say " [ACCESS(UPDATE)] Default: READ"
  say "DATASET PROFILE IS REQUIRED"
  say "EXAMPLE: RX PERMIT 'SYSGEN.ISPF.* ID(PHIL,ADMIN) ACCESS(ALTER)'"
  say "EXAMPLE: RX PERMIT 'ISPF.* ID(PHIL)'"
  say "EXAMPLE: RX PERMIT 'DIAG8CMD CLASS(FACILITY)"||,
      " ID(ADMIN,STCGROUP) ACCESS(READ)'"
  exit
end

parse upper var args profile args
if pos("(",userid) > 0 then do
    say "RAKF01I First argument must be profile-name"
    exit
end

/* -------- Parse Arguments -------- */
do while (length(args) > 0)
   parse var args t .
      if pos("(",t) = 0 then
        parse var args current args
      else do
        parse var args o "(" s ")" args
        current = o||"("||s||")"
      end
   if pos("(",current) = 0 then do    
    select
      when current = 'DELETE' then do
          DELETE = 1
      end
      otherwise do 
        say "RAKF01E Argument not understood:" current
        exit 
      end
    end
   end
   else do
     parse var current option "(" selection ")"
     select
       when (upper(option) = 'CLASS') then CLASS = selection
       when (upper(option) = 'ID') then do
         do while length(selection) > 0
           parse var selection id "," selection
           ids.0 = ids.0 + 1
           c = ids.0
           ids.c = id
         end
       end
       when (upper(option) = 'ACCESS') then do
        ACCESS = selection
        if wordpos(ACCESS, ACCESSR) = 0 then do
            say "RAKF03E Access right" ACCESS "not recognized"
            say "        Must be one of: NONE, READ, UPDATE, ALTER"
            exit
        end
       end
       otherwise do
         say 'RAKF01I Argument' current 'not recognized'
         exit
       end
     end
   end
end

if ACCESS \= 0 & DELETE then do
    say "RAKF02E Arguments ACCESS and DELETE cannot be specified"
    say "        on same command"
    exit
end

if ids.0 = 0 then do
  say 'RAFK02E ID() argument is required'
  exit
end

/* ----- Done parsing options ----- */

if ACCESS = 0 then ACCESS = DFLTACCESS
if DELETE then ACCESS = 'DELETE'


/* ----- Open RAKF profile file ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(PROFILES)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"


/* ----- Check to make sure the class and other stuff is good */
notfound_class = 1
notfound_resource = 1
changed = ''
do i = 1 to sortin.0
  parse var sortin.i 1 rclass 9 rresource 53 rgroup 61 raccess
  /* if the class doesnt exist we need to exit */
  if strip(rclass) = class then do
    notfound_class = 0
    if strip(rresource) = profile then do
      notfound_resource = 0
      do c = 1 to ids.0
        if strip(rgroup) = ids.c then do
        /* If the exact rule already exists error out */
          if strip(raccess) = access then do
            say "RAKF04E Access right already exists"
            say "          Class:" class
            say "        Profile:" profile
            say "             ID:" ids.c
            say "         Access:" access
            exit
          end
          else do
            /* We change the rule */
            rule = LEFT(class,8) || LEFT(profile,44)||LEFT(ids.c,8)||access
            sortin.i = rule
            changed = changed || " " ids.c
          end
        end
      end
    end
  end
end

if notfound_class then do
  say 'RAKF03E Class' CLASS 'does not exist use RDEFINE to add a new class'
  exit
end

if notfound_resource then do
  say 'RAKF03E Resource' profile 'does not exist in the' class 'class'
  say '        use RDEFINE to add it'
  exit
end

/* ----- Deleting or Adding profiles */

if DELETE then do
  newprofile.0 = 0
  c = 0
  do i = 1 to sortin.0
    parse var sortin.i 1 rclass 9 rresource 53 rgroup 61 raccess
    if (strip(rclass) = class) & (strip(rresource) = profile) then do
      id = 0
      do j = 1 to ids.0
        if strip(rgroup) = ids.j then id = 1
      end
      if id then ITERATE
    end
    c = c + 1
    newprofile.c = sortin.i
  end
  newprofile.0 = c
  do i = 1 to c
    sortin.i = newprofile.i
  end
  sortin.0 = newprofile.0

end
else do
  start = sortin.0
  do i = 1 to ids.0
    /* If this a new rule, not a rule change then make the new rule */
    if wordpos(ids.i,changed) = 0 then do
      start = start + 1
      PROF = LEFT(class,8) || LEFT(profile,44)||LEFT(ids.i,8)||access
      sortin.start = PROF
    end
  end
  sortin.0 = start
end


call rxsort

/* Add changes to PROFILES */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(PROFILES)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKW RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
/* Done */
/* ----- Update RAKF ----- */
call console("s rakfprof")
/* ----- DONE  ----- */
exit

check_length:
  parse arg x y z
  if length(x) > z then do
    say 'RAKF02I' y 'must be' z 'characters or shorter'
    exit
  end
return

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF profile database'
    exit
  end
return

./ ADD NAME=RDEFINE
/* RAKF RDEFINE: ADD CLASS AND PROFILE BREXX SCRIPT */
/* THIS IS ALSO THE ALIAS FOR RALTER */
/* GET ARGUMENTS */
parse arg args
/* -------- Default -------- */
DFLTUACC = 'NONE'
UACC = ''
ACCESS = "NONE READ UPDATE ALTER"
/* --------  DONE  -------- */

if length(args) = 0 then do
    say "RAKF01I Insufficient arguments"
    call usage
end


parse upper var args class args
if pos("UACC",rules) > 0 then do
    say "RAKF01E First argument must be a class"
    call usage
    exit
end

parse upper var args profile args
if pos("UACC",rules) > 0 then do
    say "RAKF01E Second argument must be a profile name"
    call usage
    exit
end

if length(profile) = 0 then do
    say "RAKF01I Insufficient arguments"
    call usage
end

/* -------- Parse Arguments -------- */
if length(args) > 0 then do
    parse var args arguacc args
    if pos("UACC",arguacc) > 0 then do
        parse var arguacc . "(" UACC ")" .
        if UACC = '' then do
            say "RAKF03E Invalid UACC syntax:" arguacc
            exit
        end
        if wordpos(UACC,ACCESS) = 0 then do
            say "RAKF03E Invalid UACC access type. Must be one of"
            say "        NONE READ UPDATE ALTER"
            say "        Type provided:" UACC
            exit
        end
    end
    else do
     say "RAFK05E Invalid argument" arguacc
     exit
    end
end

if length(args) > 0 then do
    say "RAKF05E Invalid argument:" args
    exit
end

if UACC = '' then UACC = DFLTUACC

/* ----- Open RAKF profile file ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(PROFILES)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"

/* ----- Does the rule already exist? ----- */
unchanged = 1
do i=1 to sortin.0
  parse var sortin.i 1 rclass 9 rprofile 53 rgroup 61 rUACC
  if (strip(rclass) = class) &,
     (length(strip(rgroup)) = 0) &,
     (strip(rprofile) = profile) then do
    if (strip(rUACC) = UACC) then do
        say 'RAKF04E RAKF' class 'profile' profile "already exists"
        exit 
    end
    else do
/* Are we just changing the UACC? Yes we allow that, unlike other corps */
        sortin.i = LEFT(class,8) || LEFT(profile,52)||LEFT(UACC,6)
        unchanged = 0
    end
  end
end
/* -------- Generate profile line -------- */


/* ----- Adding profiles */
if unchanged then do 
    c = sortin.0 + 1
    sortin.c = LEFT(class,8) || LEFT(profile,52)||LEFT(UACC,6)  
    sortin.0 = c
end

call rxsort

/* Add changes to PROFILES */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(PROFILES)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKW RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
/* Done */
/* ----- Update RAKF ----- */
call console("s rakfprof")
/* ----- DONE  ----- */
exit

check_length:
  parse arg x y z
  if length(x) > z then do
    say 'RAKF02I' y 'must be' z 'characters or shorter'
    exit
  end
return

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF profile database'
    exit
  end
return

usage:
  say ""
  say "RDEFINE CLASS PROFILE-NAME"
  say " [UACC(READ)] Default: NONE"
  say "CLASS AND PROFILE IS REQUIRED"
  say "EXAMPLE: RX RDEFINE 'FACILITY SVC244'"
  say "EXAMPLE: RX RDEFINE 'FACILITY NONQAUTH UACC(READ)'"
  exit
return
./ ADD NAME=RDELETE
/* RAKF RDELETE: DELETE A RAKF PROFILE BREXX SCRIPT */
/* GET ARGUMENTS */
parse arg args


if length(args) = 0 then do
    say "RAKF01I Insufficient arguments"
    call usage
end

parse upper var args class profile args

if length(profile) = 0 then do
    say "RAKF01I Insufficient arguments"
    call usage
end

if length(args) > 0 then do
    say "RAKF05E Invalid argument:" args
    exit
end

/* ----- Open RAKF profile file ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(PROFILES)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"

newprofile.0 = 0
c = 0
notfound = 1
do i = 1 to sortin.0
  parse var sortin.i 1 rclass 9 rprofile 53 rgroup 61 raccess
  if (strip(rclass) = class) & (strip(rprofile) = profile) then do
    notfound = 0
    ITERATE
  end
  c = c + 1
  newprofile.c = sortin.i
end

if notfound then do
  say 'RAKF04E Class' class 'and profile' profile 'not found'
  exit
end

newprofile.0 = c

do i = 1 to c
  sortin.i = newprofile.i
end

sortin.0 = newprofile.0

call rxsort

/* Add changes to PROFILES */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(PROFILES)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKW RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
/* Done */
/* ----- Update RAKF ----- */
call console("s rakfprof")
/* ----- DONE  ----- */
exit

check_length:
  parse arg x y z
  if length(x) > z then do
    say 'RAKF02I' y 'must be' z 'characters or shorter'
    exit
  end
return

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF profile database'
    exit
  end
return

usage:
  say ""
  say "RDELETE CLASS PROFILE-NAME"
  say "CLASS AND PROFILE IS REQUIRED"
  say "EXAMPLE: RX RDELETE 'FACILITY SVC244'"
  exit
return
./ ADD NAME=REMOVE
/* RAKF REMOVE USER FROM GROUP BREXX SCRIPT */
/* GET ARGUMENTS */
parse arg args
/* --------  DONE  -------- */
if length(args) = 0 then do
  say "RAKF01I Insufficient arguments"
  say ""
  say "REMOVE USERNAME GROUP(GROUPNAME)"
  say "USERNAME AND GROUP IS REQUIRED"
  exit
end
parse upper var args USERNAME args
if pos("(",userid) > 0 then do
    say "RAKF01E First argument must be user name"
    exit
end
call check_length USERNAME 'username' 7

if length(args) = 0 then do
  say "RAKF01I Insufficient arguments"
  say ""
  say "REMOVE USERNAME GROUP(GROUPNAME)"
  say "USERNAME AND GROUP IS REQUIRED"
  exit
end

/* -------- Parse Arguments -------- */
do while (length(args) > 0)
   parse var args t .
      if pos("(",t) = 0 then do
         say 'RAKF01E Argument' t 'not recognized'
         exit
      end
      else do
        parse var args o "(" s ")" args
        current = o||"("||s||")"
      end
     parse var current option "(" selection ")"
     select
       when (upper(option) = 'GROUP') then do
         GROUPNAME = selection
         call check_length GROUPNAME 'GROUP' 8
       end
       otherwise do
         say 'RAKF01E Argument' current 'not recognized'
         exit
       end
     end
end


/* ----- Open RAKF User file ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"

notfound_user = 1
notfound_group = 1
newprofile.0 = 0
c = 0
do i=1 to sortin.0
  PARSE VAR sortin.i 1 ruser 10 GROUP 18 .

  if strip(ruser) = upper(USERNAME) then do
    notfound_user = 0
  end
  if strip(ruser) = upper(USERNAME) & strip(group) = GROUPNAME then do
    notfound_group = 0
    ITERATE
  end
  c = c + 1
  newprofile.c = sortin.i
end

if notfound_user then do
    say "RAKF02E User" USERNAME "not found in user database."
    exit
end

if notfound_group then do
    say "RAKF02E User" USERNAME "not connected to" GROUPNAME
    exit
end

newprofile.0 = c

do i = 1 to c
  sortin.i = newprofile.i
end

sortin.0 = newprofile.0

call rxsort

ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(USERS)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKW RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
/* Done */
/* ----- Update RAKF ----- */
call console("s rakfuser")
/* ----- DONE        ----- */
exit

check_length:
  parse arg x y z
  if length(x) > z then do
    say 'RAKF02I' y 'must be' z 'characters or shorter'
    exit
  end
return

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF user database'
    exit
  end
return

./ ADD NAME=RLIST
/* RAKF RLIST: LIST RAKF RESOURCES BREXX SCRIPT */
/* GET ARGUMENTS */
parse arg args

all = 0

if length(args) = 0 then do
    all = 1
end

parse upper var args class profile args

if length(args) > 0 then do
    say "RAKF05E Invalid argument:" args
    exit
end

/* ----- Open RAKF profile file ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(PROFILES)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM sortin. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"

newprofile.0 = 0
c = 0
notfound_class = 1
notfound_profile = 1
do i = 1 to sortin.0
  parse var sortin.i 1 rclass 9 rprofile 53 rgroup 61 raccess
  if length(strip(rgroup)) = 0 then do
    if all then do 
      call print_profile rclass rprofile raccess
      notfound_class = 0
      notfound_profile = 0
    end
    if (strip(rclass) = class) then do 
      notfound_class = 0
      if length(profile) = 0  then do
        notfound_profile = 0
        call print_profile rclass rprofile raccess
      end
      if strip(rprofile) = profile then do
        call print_profile class profile raccess
        notfound_profile = 0
      end
    end
  end
end

if notfound_class then do
  say 'RAKF04E Class' class 'not found'
  exit
end

if notfound_profile then do
  say 'RAKF04E profile' profile 'in class' class 'not found'
  exit
end


/* ----- DONE  ----- */
exit

check_length:
  parse arg x y z
  if length(x) > z then do
    say 'RAKF02I' y 'must be' z 'characters or shorter'
    exit
  end
return

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF profile database'
    exit
  end
return

print_profile:
    parse arg prt_class prt_profile puacc
    say "CLASS=  " prt_class
    say "PROFILE=" prt_profile
    say "UACC=   " puacc
    groups.0 = 0
    count = 0
    do c = 1 to sortin.0
        parse var sortin.c 1 pclass 9 pprofile 53 pgroup 61 paccess
        if strip(pclass) = strip(prt_class) &,
           strip(pprofile) = strip(prt_profile) &,
           length(strip(pgroup)) > 0 then do
            count = count + 1
            groups.count = left(pgroup,8) paccess
        end
    end
    groups.0 = count
    if groups.0 > 0 then do
      say "" LEFT("GROUP(S)=",9) "ACCESS"
      do x = 1 to groups.0
        say " " groups.x
      end
    end
    say ""
return

usage:
  say ""
  say "RLIST CLASS PROFILE-NAME"
  say "EXAMPLE: RX RLIST 'FACILITY'"
  say "EXAMPLE: RX RLIST 'FACILITY SVC244'"
  exit
return
./ ADD NAME=RVARY
/* RAKF RDELETE: DELETE A RAKF PROFILE BREXX SCRIPT */
/* GET ARGUMENTS */
parse arg args

all = 0

if length(args) > 0 then do
    say "RAKF05E Invalid argument:" args
    call usage
end


/* ----- Open RAKF profile file ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.PROCLIB(RAKF)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM RAKFJCL. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"   

say 'RAKF DATABASE STATUS:'
say "DATABASE VOLUME DATASET(MEMBER)"
say "-------- ------ ---------------"
do i = 1 to RAKFJCL.0
    parse var RAKFJCL.i "//" JCL DD "DSN=" DSN "(" MEM ")" .
    if JCL = RAKFPROF then do 
        x = LISTDSI("'"||DSN||"'")
        SAY "PROFILES" SYSVOLUME DSN||"("||MEM||")"
    end
    if JCL = RAKFUSER then do
        say "USERS   " SYSVOLUME DSN||"("||MEM||")"
    end
end
say "RVARY COMMAND HAS FINISHED PROCESSING."
/* ----- DONE  ----- */
exit

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF profile database'
    exit
  end
return

usage:
  say ""
  say "RVARY Takes no arguments"
  say "EXAMPLE: RX RVARY"
  exit
return
./ ADD NAME=SETROPTS
/* RAKF SETROPTS BREXX SCRIPT */
/* Replicates some SETROPTS functionality */
/* GET ARGUMENTS */
parse arg args
/* -------- Default -------- */
DEFAULT = 'LIST'
CLASSES = ''
/* --------  DONE  -------- */
if length(args) = 0 then do
  say "RAKF01I Insufficient arguments"
  say ""
  say "SETROPTS LIST"
  exit
end

if args \= "LIST" then do
    say 'RAKF01I Argument' args 'not recognized'
    exit
end

/* ----- Open RAKF PROFILE file ----- */
ADDRESS TSO "ALLOC FI(RAKF) DA('SYS1.SECURE.CNTL(PROFILES)') SHR REUSE"
call check_rc rc
ADDRESS TSO "EXECIO * DISKR RAKF (STEM profiles. OPEN FINIS"
call check_rc rc
ADDRESS TSO "FREE FI(RAKF)"
do i=1 to profiles.0
    class = strip(left(profiles.i,8))
    if pos(class, classes) = 0 then do
        classes = classes || " " || class
    end
end

say "ACTIVE CLASSES = " || classes

exit

check_rc:
  parse arg rcode
  if rcode > 0 then do
    say 'RAKF04I Unable to open RAKF user database'
    exit
  end
return
./ ADD NAME=$RAKFCL
# RAKF Command Library

**All these scripts require BREXX higher than V2R4M0**

This repo contains a set of REXX scripts for MVS3.8J which allows for
command line manipulation of the RAKF files. The syntax is explain for
each command below.

# Install

If you're using MVS/CE you can install with `RX MVP INSTALL RAKFCL`

If you wish to install manually do the following:

1) Run the script `bash make_release.sh`
2) Submit the resulting job stream to MVS: 
`cat release.jcl|ncat -w1 -v localhost 3505`

# RAKF Commands and usage


## ADDGROUP

Add a new group to RAKF

**Syntax**:

`RX ADDGROUP 'GROUPNAME" [OWNER(USERID)]`

Any argument in `[]` is optional. Arguments with parentheses `()` expect
the value to be in the parentheses. The groupname must be the first 
argument, the rest can be in any order.

**Defaults**:

| ARGUMENT        | DEFAULT            |
|-----------------|--------------------|
| OWNER           | USERID of the user that submitted the command |

**Examples**:

To add the group 'SYSTEM' and assign the owner to IBMUSER: 
`RX ADDGROUP 'SYSTEM OWNER(IBMUSER)`

To add the group 'TESTIN' and assign the owner to your currently logged
in user: `RX ADDGROUP 'TESTING'`

## ADDSD

Add a new dataset profile to RAKF

**Syntax**:

`RX ADDSD 'PROFILE-NAME [UACC(READ)]'`

Any argument in `[]` is optional. Arguments with parentheses `()` expect
 the value to be in the parentheses. The dataset profile must be the
 first argument, the rest can be in any order.

Mutliple profile names can be given, just surround the profile-name with
`()` and seperate them with a comma `,`


**Defaults**:

| ARGUMENT        | DEFAULT            |
|-----------------|--------------------|
| UACC            | NONE               |

**Examples**:


Add the dataset profile `SYSGEN.ISPF.*` with a UACC of READ: 
`RX ADDSD 'SYSGEN.ISPF.* UACC(READ)'`

Add two dataset profiles, `SYS3.**` and `SYS4.MACLIB` with a UACC of
NONE: `RX ADDSD '(SYS3.**,SYS4.MACLIB)'`

## ADDUSER

Add a new user to RAKF

**Syntax**:

```
RX ADDUSER 'USERID [PASSWORD(PASSWORD)] [DFLTGRP(USER)] 
   [OPERATIONS|OPER] [SPECIAL] [NAME()|COMMENT()]'
```

Any argument in `[]` is optional. Arguments with parentheses `()` expect
the value to be in the parentheses. The user id must be the first 
argument, the rest can be in any order.

**Defaults**:

| ARGUMENT        | DEFAULT            |
|-----------------|--------------------|
| PASSWORD        | PASSWORD           |
| DFLTGRP         | USER               |
| OPERATIONS/OPER | NOOPERATION/NOOPER |
| SPECIAL         | NOSPECIAL          |
| NAME/COMMENT    | `NONE`             |

**Examples**:

To add a user with the userid `MARKS` with a password of `E4#J3KIL`:

```
RX ADDUSER 'MARKS PASSWORD(E4#J3KIL)'
```

Defaults - `DFLTGRP(USER) NOSPECIAL NOOPER`

To add the user DA5ID as a RAKF admin user with privileged access and 
add their name in the comment field:

```
RX ADDUSER 'DA5ID OPER SPECIAL NAME(DAVID COPPERFIELD)'
```

Defaults - `DFLTGRP(USER) PASSWORD(PASSWORD) `


To add a service account for FTPD in the group FTPD with a password of
"CHANGEME":

```
RX ADDUSER 'FTPD DFLTGRP(FTPD) PASSWORD(CHANGEME) 
COMMENT(FTP SERVICE ACCOUNT)'
```

Defaults - `NOSPECIAL NOOPER`

## ALTUSER

Alter an existing RAKF user

**Syntax**:

```
RX ALTUSER 'USERID [PASSWORD(PASSWORD)] 
   [OPERATIONS|OPER|NOPERATIONS|NOOPER] [SPECIAL|NOSPECIAL] 
   [NAME()|COMMENT()]'
```

Any argument in `[]` is optional but at least one should be used. 
Arguments with parentheses `()` expect the value to be in the 
parentheses. The user id must be the first argument, the rest can be in
any order.

**Defaults**:

None

**Examples**:

To alter a user with the userid `MARKS` and change their password to 
`HYD3`:

```
RX ALTUSER 'MARKS PASSWORD(HYD3)'
```


To ALTER the user DA5ID and remove their RAKF admin status and 
privileged access:

```
RX ADDUSER 'DA5ID NOOPER NOSPECIAL'
```

To add alter the service account for FTPD giving it a stronger password
and changing the comment:

```
RX ADDUSER 'FTPD PASSWORD(SECRET21) COMMENT(051521 Changed PW)'
```

## CONNECT

Connect a user to a group

**Syntax**:

`RX CONNECT USERNAME GROUP(GROUPNAME)`

**Defaults**:

None

**Examples**:

Connect the user `PHIL` to the group `RAKFADM`: 
`RX CONNECT 'PHIL GROUP(RAKFADM)'`

## DELDSD

Delete a dataset profile

**Syntax**:

`RX DELDSD PROFILE-NAME`

**Defaults**:

None

**Examples**:

Delete the RAKF dataset profile `SYS4.MACLIB`: 
`RX DELDSD 'SYS4.MACLIB'`

## DELGROUP

Delete a RAKF group

**Syntax**:

`RX DELGROUP GROUPNAME`

**Defaults**:

None

**Examples**:

1) Delete the RAKF group `SYSTEM`: `RX DELGROUP 'SYSTEM'`

## DELUSER

Delete RAKF user

**Syntax**:

`RX DELUSER 'USERID'`

**Defaults**:

None

**Examples**:

1) Delete the user `DA5ID`: `RX DELUSER 'DA5ID'`

## LISTDSD

List RAKF dataset profiles

**Syntax**:

`RX LISTDSD`

**Defaults**:

None

**Examples**:

1) List all RAKF dataset profiles: `LISTDSD`

## LISTGRP

List RAKF group(s) and users connected to groups

**Syntax**:

`RX LISTGRP [GROUPNAME]`

**Defaults**:

If no groupname is provided all groups will be listed

**Examples**:

1) List all RAKF group members: `LISTGRP`
2) List members of the group USERS: `LISTGRP USERS`

## LISTUSER

List RAKF user information

**Syntax**:

`RX LISTUSER '[USERID|*]`

**Defaults**:

Current user id

**Examples**:

List current user attributes:

`RX LISTUSER`

Output:

```
USER=DA5ID    GROUPS=ADMIN RAKFADM
ATTRIBUTES=OPERATIONS SPECIAL
COMMENTS=
```

List a different user attributes:

`RX LISTUSER 'HMVS01'`

Output:

```
USER=HMVS01   GROUPS=ADMIN RAKFADM
ATTRIBUTES=OPERATIONS SPECIAL
COMMENTS=
```

List all users:

`RX LISTUSER '*'`

Output:

```
USER=HMVS01
GROUPS=ADMIN RAKFADM
ATTRIBUTES=OPERATIONS SPECIAL
COMMENTS=

USER=HMVS02
GROUPS=USER
ATTRIBUTES=
COMMENTS=

USER=IBMUSER
GROUPS=ADMIN RAKFADM
ATTRIBUTES=OPERATIONS SPECIAL
COMMENTS=
```

## PERMIT

Gives groups access to resources (classes and profiles)

**Syntax**:

```
RX PERMIT PROFILE-NAME ID(GROUP[,GROUP2,...,n]) [CLASS(DATASET)] 
   [ACCESS(READ)]
```

**Defaults**:

| ARGUMENT        | DEFAULT            |
|-----------------|--------------------|
| CLASS           | DATASET            |
| ACCESS          | READ               |

**Examples**:

Permit the groups `ADMIN` and `USERS` to have read access to the 
BRXAUTH profile in the FACILITY class: 
`RX PERMIT 'BRXAUTH ID(ADMIN,USERS) CLASS(FACILITY) ACCESS(READ)`

Give the group `USERS` alter access to the dataset `SYS4.MACLIB`: 
`RX PERMIT 'SYS4.MACLIB ID(USERS) ACCESS(ALTER)'`

## RDEFINE

Define a new CLASS and a resource within that class

**Syntax**:

`RX RDEFINE 'CLASS PROFILE-NAME [UACC(NONE)]'`

**Defaults**:


| ARGUMENT        | DEFAULT            |
|-----------------|--------------------|
| UACC            | NONE               |

**Examples**:

Define the resource `SVC244` in the FACILITY class, UACC of NONE: 
`RX RDEFINE 'FACILITY SVC244'`

Define the resource `NONQAUTH` in the FACILITY class with a UACC of 
READ: `RX RDEFINE 'FACILITY NONQAUTH UACC(READ)'`

## RDELETE

Delete a resource from RAKF

**Syntax**:

`RX RDELETE 'CLASS PROFILE-NAME'`

**Defaults**:

None

**Examples**:

Delete the resource `SVC244` in the FACILITY class:
 `RX RDELETE 'FACILITY SVC244'`

## REMOVE

Remove a user from a RAKF group

**Syntax**:

`RX REMOVE 'USERNAME GROUP(GROUPNAME)'`

**Defaults**:

None

**Examples**:

Remove the user `PHIL` from the group `USERS`:
`RX REMOVE PHIL GROUP(USERS)`

## RLIST

List RAKF resources

**Syntax**:

`RX RLIST [CLASS PROFILE-NAME]`

**Defaults**:

If no class and no profile name is given all resources will be printed.

**Examples**:

List all RAKF profiles: `RX RLIST`
List the details for the profiles `SVC244` in the FACILITY class: 
`RX RLIST 'FACILITY SVC244'`


## RVARY

Lists the current status of the RAKF "database"

**Syntax**:

`RX RVARY`

**Defaults**:

None

**Examples**:

1) List the current status/location of the RAKF databases: `RX RVARY`

## SETROPTS

Lists the current status of the RAKF "database"

**Syntax**:

`RX SETROPTS LIST`

**Defaults**:

None

**Examples**:

1) List the current active classes: `RX SETROPTS 'LIST'`
@@
