#!/bin/bash

# Copyright Jean-Philippe Guillemin <jp.guillemin@free.fr>. This program is free software;
# you can redistribute it and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 of the License,
# or (at your option) any later version. Please take a look at http://www.gnu.org/copyleft/gpl.htm
# Version 1.7 changes by Teran McKinney (sega01).

# Userconfig can:
# - list existing users/groups and their properties
# - create/delete users and groups
# - modify any user properties


# version='1.0'  # - 30/10/2005
# version='1.1'  # - 01/11/2005 - change some buttons labels
# version='1.2'  # - 04/11/2005 - fixed "Never" expiry problem
# version='1.3'  # - 30/11/2005 - bugfix space in realname
# version='1.4'  # - 30/11/2005 - improved password dialog window
# version='1.5'	 # - 31/03/2006 - bugfix expiry problem
# version='1.6'	 # - 21/04/2007 - password box
# version='1.7'	 # - 07/06/2007 - text changes, default to never expire
# version='1.8'	 # - 15/04/2008 - dynamic available user groups detection 
version='1.9'    # - 06/08/2009 - fixed chpasswd to not forget rest of
# over 8 char long password 


# Take a look at "Xdialog" and use it instead of "dialog" in case X is running
if [[ "$DISPLAY" && "$(which Xdialog 2>&1 | grep -v "which: no")" ]]; then
	dialog="Xdialog --wrap --left --icon userconfig "
else
	dialog="dialog"
fi

# Translations only work with utf8 locales
if [ ! `echo $LANG | grep -i utf` ]; then
	LANG=en_US
fi

# Gettext internationalization
export TEXTDOMAIN="userconfig"
export TEXTDOMAINDIR="/usr/share/locale"
. gettext.sh

# Path needs to be extended in case you're only half a root:)
export PATH="${PATH}:/usr/sbin:/sbin"

if [[ "$DISPLAY" && "$(which xterm 2>&1 | grep -v "which: no")" ]]; then
	terminal="xterm -bg black -fg red -geometry 48x12 -e"
else
	terminal=""
fi


skeleton='/etc/skel'
defaultshell='/bin/bash'
defaultgroup='users'
pwfile='/etc/passwd'
grpfile='/etc/group'
shadowfile='/etc/shadow'

for isgroupinzen in audio video lp cdrom fuse floppy plugdev netdev scanner taratata ; do
	grep -q "^${isgroupinzen}:" $grpfile && useraddmembership="${isgroupinzen},${useraddmembership}"
done
useraddmembership="$( echo ${useraddmembership} | sed -e 's/^\(.*\),$/\1/' )"


###############################################
# What do you want to do ?

promptaction() {
mode=$(${dialog} \
--stdout \
--cancel-label "$(eval_gettext 'Exit')" \
--title "User management" \
--menu \
"\n $(eval_gettext 'Welcome to') $(basename $0), $(eval_gettext 'please choose an option:')\n\n" \
22 75 11 \
   "$(eval_gettext 'List users')" "$(eval_gettext 'List existing users and edit their properties')" \
   "$(eval_gettext 'New user')" "$(eval_gettext 'Create a new user')" \
   "$(eval_gettext 'Delete users')" "$(eval_gettext 'Delete one or more users')" \
   "$(eval_gettext 'Change password')" "$(eval_gettext 'Change the password of a user')" \
   "$(eval_gettext 'List groups')" "$(eval_gettext 'List groups and their members')" \
   "$(eval_gettext 'Modify group')" "$(eval_gettext 'Change the name of a group')" \
   "$(eval_gettext 'New group')" "$(eval_gettext 'Create a new group')" \
   "$(eval_gettext 'Delete groups')" "$(eval_gettext 'Delete one or more groups')" )

if [ ! "$mode" ]; then
  mode="none"
fi

}

###############################################
# list existing users

showusers() {
liste=$(cat $pwfile \
| sed -e 's/^\(.*\):x:\(.*\):.*:.*:.*:.*$/\1\ \2\ /')
user=$(${dialog} --stdout --title "$(eval_gettext 'User list')" --cancel-label "$(eval_gettext 'Back')" --menu \
"\n $(eval_gettext 'Users of the system:')" 20 75 11 \
${liste})
}

###############################################
# list existing groups

showgroups() {
liste=$(cat $grpfile | \
	sed -e 's/^\(.*\):.*:\(.*\):.*$/\1 \2 /')
group=$($dialog --stdout --title "$(eval_gettext 'Group list')" --cancel-label "$(eval_gettext 'Back')" --menu \
"\n $(eval_gettext 'Groups of the system:')" 20 75 11 \
${liste})
}

###############################################
# Get user parameters from system

getsysteminfo() {
  uid="$(grep -e "^$user:.*$" $pwfile | cut -d: -f 3 )"
  gid="$(grep -e "^$user:.*$" $pwfile | cut -d: -f 4 )"
  homedir="$(grep -e "^$user:.*$" $pwfile | cut -d: -f 6 )"
  shell="$(grep -e "^$user:.*$" $pwfile | cut -d: -f 7 )"
  group="$(groups "$user" | sed -e 's/^.*: \(.*\)$/\1/' | cut -d \  -f 1 )"
  membership="$(groups "$user" | sed -e 's/^.*: \(.*\)$/\1/' | cut -d \  -f 2- )"
  expirydays="$(grep -e "^$user:.*$" $shadowfile | cut -d: -f 8 )"
  if [ "$expirydays" ]; then
    expiry="$(date -u --date "Jan 1, 1970 + $expirydays days" +%Y-%m-%d)" # usermod date format
  else
    expiry="never"
  fi
  fingerinfo="$(grep -e "^$user:.*$" $pwfile | cut -d: -f 5 )"
  realname="$(echo $fingerinfo | cut -d , -f 1 )"
  office="$(echo $fingerinfo | cut -d , -f 2 )"
  workphone="$(echo $fingerinfo | cut -d , -f 3 )"
  homephone="$(echo $fingerinfo | cut -d , -f 4 )"

}

###############################################
# Show user info
showuserinfo() {
parameter=$($dialog --stdout --title "$(eval_gettext 'User information')" --ok-label "$(eval_gettext 'Modify')" --cancel-label \
"$(eval_gettext 'Done')" \
--menu "\n $(eval_gettext 'Detailed information for user') $user:" 20 60 11 \
"$(eval_gettext 'Real Name')" "$realname" \
"$(eval_gettext 'User Name')" "$user ($uid)" \
"$(eval_gettext 'Main Group')" "$group ($gid)" \
"$(eval_gettext 'Home')" "$homedir" \
"$(eval_gettext 'Shell')" "$shell" \
"$(eval_gettext 'Membership')" "$membership" \
"$(eval_gettext 'Expiry')" "$expiry" \
"$(eval_gettext 'Office')" "$office" \
"$(eval_gettext 'Work phone')" "$workphone" \
"$(eval_gettext 'Home phone')" "$homephone" )
}

###############################################
# Show user info
showgroupmembers() {

gid="$(grep -e "^$group:.*$" $grpfile | cut -d: -f 3)"
members="$(grep -e "^$group:.*$" $grpfile | cut -d: -f 4 | tr "," " ")"
for user in $(grep -e "^.*:x:.*:$gid:.*$" $pwfile \
  | sed -e 's/^\(.*\):x:.*:.*:.*:.*:.*$/\1/' \
  | tr "\n" " ") ; do
  members="$(echo $members | sed -e "s/^\(.*\)$user\(.*\)$/\1\2/")"
  members="$members $user"
done

if [ "$members" ]; then
liste=""
for user in $members
do
   uid="$(grep -e "^$user:.*$" $pwfile | cut -d: -f 3 )"
   liste="$liste
$user $uid"
done

user=$($dialog --stdout --title "$(eval_gettext 'Group members')" --cancel-label "$(eval_gettext 'Back')" --menu \
"\n $(eval_gettext 'Members of group') $group:" 20 75 11 \
${liste})

else
  $dialog --title "Group members" --msgbox \
"\nNo user in group $group" 12 75
fi

}


###############################################
# Set the user name or create the user if new name
setusername() {
  current="$user"
  user=$($dialog --stdout --title "$(eval_gettext 'User name')" --inputbox "$(eval_gettext 'Enter the user name for user') ${user}:" 11 75 $user)
  [ ! "$user" ] && user="$current"
  if [ "$current" ]; then
    usermod -l $user $current 2>/dev/null
  else
    message="ok"
    if [ "$( echo "$user" | grep " " )" ]; then
      message="\"$user\" $(eval_gettext 'contains illegal characters (space)')"                 
    elif [ "$(grep -e "^$user:.*$" $pwfile )" ]; then
      message="\"$user\" $(eval_gettext 'already exists')"              
    elif [ ! "$user" ]; then
      message="$(eval_gettext 'User name is empty')"
    elif [ "$( echo $user | grep "^[0-9]" )" ]; then
      message="$(eval_gettext 'User names cannot begin with a number, please choose another')"
    elif [ ! "$user" = "$(echo $user | tr A-Z a-z)" ]; then
      message="\"$user\" $(eval_gettext 'contains illegal characters (uppercase), please choose another')"                
	  elif [ ! -z "$( echo $user | grep '\.' )" ]; then
      message="\"$user\" $(eval_gettext 'contains illegal characters (period/dot); please choose another')"
    fi
    if [ "$message" = "ok" ]; then
      for membershipgroup in $(echo $useraddmembership | tr "," " ") ; do
        groupadd $membershipgroup 2>/dev/null
      done
      useradd -s $defaultshell -g $defaultgroup -m -k $skeleton -G $useraddmembership "$user" 2>/dev/null
      # echo "useradd -s $defaultshell -g $defaultgroup -m -k $skeleton -G $useraddmembership"
    fi
  fi
  current=""
}

###############################################
# Set the main group name
setgroup() {
  current="$group"
  group=$($dialog --stdout --title "Groupname" --cancel-label "Skip" \
		  --inputbox \
		  "Enter the group for user $user:" 11 75 $group)
  [ "$group" ] && usermod -g $group $user 2>/dev/null
  current=""
}

###############################################
# Set the home dir
sethomedir() {
  current="$homedir"
  homedir=$($dialog --stdout --title "$(eval_gettext 'Home directory')" \
		  --cancel-label "$(eval_gettext 'Skip')" \
		  --inputbox "$(eval_gettext 'Enter the home directory for user') \
		  $user:" 11 75 $homedir)
  if [ "$homedir" ]; then
    usermod -d $homedir $user 2>/dev/null
    if [ -e $homedir ]; then
      chown -R $user:$group $homedir 2>/dev/null
    else
      cp -rf $skeleton $homedir 2>/dev/null
      chown -R $user:$group $homedir 2>/dev/null
    fi
  fi
  current=""
}

###############################################
# Set the shell
setshell() {
  current="$shell"
  shell=$($dialog --stdout --title "$(eval_gettext 'Shell')" \
		  --cancel-label "Skip" --inputbox \
		  "$(eval_gettext 'Enter the shell interpreter for user') $user:" 11 75 \
		  $shell)
  if [ "$shell" != "$current" ]; then
    usermod -s $shell $user 2>/dev/null
  fi
  current=""
}

###############################################
# Set additional group membership
setmembership() {
  current="$membership"
  usermodmembership="$(echo $membership | tr " " "," )"
  membership=$($dialog --stdout --title "$(eval_gettext 'Additional groups membership')" \
		  --cancel-label "Skip" --inputbox "$(eval_gettext 'Enter a list of additional groups for user') $user:$group:" 11 75 \
		  $usermodmembership)
  if [ "$membership" != "$current"  ]; then
    usermodmembership="$(echo $membership | tr " " "," )"
    usermod -G $usermodmembership $user 2>/dev/null
  fi
  current=""
}

###############################################
# Set expiry date
setexpiry() {
  # date must be in format DD MM YYYY for dialog
  if [ "$expirydays" ]; then
    dialogdate="$(date -u --date "Jan 1, 1970 00:00:00 + $expirydays days" +%d\ %m\ %Y 2>/dev/null)"
  else
    dialogdate="$(date -u +%d\ %m\ %Y 2>/dev/null)"
  fi

  dialogdate=$($dialog --stdout --cancel-label "$(eval_gettext 'Never')" --defaultno --calendar "$(eval_gettext 'Set account expiry date') " 0 75 $dialogdate)

  # converting dialog outputs DD/MM/YYYY into YYYY-MM-DD for usermod
  expiry="$(echo "$dialogdate" | sed -e "s/^\([0-9]*\)\/\([0-9]*\)\/\([0-9]*\)/\3-\2-\1/")" # usermod date format
  usermod -e "" $user 2>/dev/null
  if [ "$expiry" ]; then
    usermod -e "$expiry" $user 2>/dev/null
  fi

}

###############################################

# Password dialog
passbox(){
	if [[ "$DISPLAY" && "$(which Xdialog 2>&1 | grep -v "which: no")" ]]; then
		DIALOG='Xdialog --stdout --wrap --ignore-eof --no-cancel --password --icon password --inputbox'
	else
		DIALOG='dialog --stdout --insecure --fixed-font --no-cancel --smooth --passwordbox'
	fi
	pw="$(${DIALOG} "${1}" 12 80)"
	echo -n "$pw"
}


# Password checking
changepw(){

maxcount=3
minlength=5

count=1 ; pw='_'
while [ "${pw}" != "${password}" ]; do
	password="" ; pw='_'
	if [ $count -gt $maxcount ]; then
		echo -n ""
		break
	fi

	message="Enter password for user ${1} (#${count}):"
	[ $count -ge 2 ] && message="$(eval_gettext 'Passwords do not match, enter password for user') ${1} (#${count}):"
	while [ "$(echo -n "${pw}" | wc --chars)" -lt ${minlength} ]; do
		pw="$(passbox "${message}")"
		message="$(eval_gettext 'Password is too short') (${minlength} chars minimum), $(eval_gettext 'please try another one:')"		
	done
	password="$pw" ; pw='_'
	message="$(eval_gettext 'Enter password again for user') ${1} (#${count}):"
	pw="$(passbox "${message}")"
	
	count=$((count+1))

done

echo -n $password
}

# Change password
setpw() {
  
	if [[ "$DISPLAY" && "$(which Xdialog 2>&1 | grep -v "which: no")" ]]; then
		DIALOG="Xdialog --wrap --center --icon password"
	else
		DIALOG="dialog"
	fi
	
	[ "$user" ] && pass="$(changepw ${user})"
	if [ ! "$pass" ] ; then
		${dialog} --msgbox "$(eval_gettext 'Failed to set password for user') \"${user}\"" 8 75
	else
		# echo "${user}:${pass}"
		[ "$user" ] && echo -e "${pass}\n${pass}\n" | passwd ${user} 1>/dev/null 2>&1
	fi
}

###############################################
# Set finger info
setrealname() {
  current="$realname"
  realname=$($dialog --stdout --title "$(eval_gettext 'Real name')" --no-cancel \
  	--inputbox "$(eval_gettext 'Enter the real name of user') $user:" 11 75 "$realname")
  if [ "$realname" != "$current" ]; then
    chfn -f "$realname" $user 2>/dev/null
  fi
  current=""
}

setoffice() {
  current="$office"
  office=$($dialog --stdout --title "$(eval_gettext 'Office information')" --no-cancel \
	--inputbox "$(eval_gettext 'Enter office information for user') \
		  $user:" 11 75 "$office")
  if [ "$office" != "$current" ]; then
    chfn -r "$office" $user 2>/dev/null
  fi
  current=""
}

setworkphone() {
  current="$workphone"
  workphone=$($dialog --stdout --title "$(eval_gettext 'Work phone')" --no-cancel \
	  --inputbox "$(eval_gettext 'Enter work phone number for user') $user:" \
	  11 75 "$workphone")
  if [ "$workphone" != "$current" ]; then
    chfn -w "$workphone" $user 2>/dev/null
  fi
  current=""
}

sethomephone() {
  current="$homephone"
  homephone=$($dialog --stdout --title "$(eval_gettext 'Home phone')" --no-cancel \
	  --inputbox "$(eval_gettext 'Enter home phone number for user') $user:" \
	  11 75 "$homephone")
  if [ "$homephone" != "$current" ]; then
    chfn -h "$homephone" $user 2>/dev/null
  fi
  current=""
}

###############################################
# Set the group name or create the group if new name
setgroupname() {
  current="$group"
  group=$($dialog --stdout --title "$(eval_gettext 'Group')" --inputbox "$(eval_gettext 'Enter the name for group') \$group:" 11 75 $group)
  if [ "$current" ]; then
    [ "$group" ] && groupmod -n $group $current 2>/dev/null
  else
    [ "$group" ] && groupadd $group 2>/dev/null
  fi
  current=""
}


###############################################
# Modify a user

modifyuser() {
	if [ "$user" ]; then
	  while [ "1" = "1" ] ; do
	  parameter=""
	  getsysteminfo
	  showuserinfo
	  if [ "$parameter" ]; then
		case $parameter in
		$(eval_gettext 'User Name'))
		  setusername
		  getsysteminfo
		  ;;
		$(eval_gettext 'Main Group'))
		  setgroup
		  getsysteminfo
		  ;;
		$(eval_gettext 'Home'))
		  sethomedir
		  getsysteminfo
		  ;;
		$(eval_gettext 'Membership'))
		  setmembership
		  getsysteminfo
		  ;;
		$(eval_gettext 'Shell'))
		  setshell
		  getsysteminfo
		  ;;
		$(eval_gettext 'Expiry'))
		  setexpiry
		  getsysteminfo
		  ;;
		$(eval_gettext 'Real Name'))
		  setrealname
		  getsysteminfo
		  ;;
		$(eval_gettext 'Office'))
		  setoffice
		  getsysteminfo
		  ;;
		$(eval_gettext 'Work phone'))
		  setworkphone
		  getsysteminfo
		  ;;
		$(eval_gettext 'Home phone'))
		  sethomephone
		  getsysteminfo
		  ;;
		esac
	  else
		break
	  fi
	done
	fi
}



###############################################
# main()
while [ "1" = "1" ] ; do
  promptaction
            case $mode in
            $(eval_gettext 'List users'))
              while [ "1" = "1" ] ; do
                user=""
                showusers
                if [ "$user" ]; then
                  modifyuser
                else
                  break
                fi
              done
              ;;
            $(eval_gettext 'New user'))
            user=""
              setusername
              if [ "$message" = "ok" ]; then
                getsysteminfo
                setgroup
                sethomedir
                setmembership
                setshell
                setexpiry
                setrealname
                setoffice
                setworkphone
                sethomephone
                setpw
                getsysteminfo
                modifyuser
              else
                $dialog --title "Warning" --msgbox "\n $message \n" 8 50
              fi
              ;;
            $(eval_gettext 'Delete users'))
              while [ "1" = "1" ] ; do
              user=""
                showusers
                if [ "$user" ]; then
                  if [ "$user" == "root" ]; then
                    $dialog --title "Warning" --msgbox "\n $(eval_gettext 'No way to delete') $user\n" 8 50
                  else
                    userdel -f $user 2>/dev/null
                  fi
                else
                  break
                fi
              done
              ;;
            $(eval_gettext 'Change password'))
              user=""
              showusers
              if [ "$user" ]; then
                setpw
              fi
              ;;
            $(eval_gettext 'List groups'))
              while [ "1" = "1" ] ; do
              group=""
              showgroups
                if [ "$group" ]; then
                  while [ "1" = "1" ] ; do
                    user=""
                    showgroupmembers
                    if [ "$user" ]; then
                      getsysteminfo
                      showuserinfo
                    else
                      break
                    fi
                  done
                else
                  break
                fi
                done
              ;;
            $(eval_gettext 'Modify group'))
            while [ "1" = "1" ] ; do
            group=""
              showgroups
              if [ "$group" ]; then
                setgroupname
              else
                break
              fi
            done
              ;;
            $(eval_gettext 'New group'))
              group=""
              setgroupname
              showgroups
              ;;
            $(eval_gettext 'Delete groups'))
              while [ "1" = "1" ] ; do
              group=""
                showgroups
                if [ "$group" ]; then
                  if [ "$group" == "root" ]; then
                    $dialog --title "Warning" --msgbox --auto-placement "\n $(eval_gettext 'No way to delete') $group \n" 8 75 &
                  else
                    groupdel $group 2>/dev/null
                  fi
                else
                  break
                fi
               done
              ;;
            none)
              exit
          esac

done

# end
