#!/bin/bash
#
# ::copy::
# ::maintainer::
#
CMD=$(basename $0)

set -u

. /lib/ku-base/echo.sh

usage()
{
	echo "
usage: $CMD [options] user(s) ...

options:
 -c|--create	create user if not exists (must exists in
 		kusa db, anyway)

 -f|--force	force update even if not needed (usefull to update
 		non posix fields, that are not checked at the moment)

 --reset-password  set password field if defined for the user(s)

 -a|--all	process all users authorized on this system
 -l|--list	list only (valid only with --all)

 -n|--dry-run	don't do anyting, only show actions
 -v|--verbose	be verbose
 -q|--quiet	be quiet
 -D		activate debug
 -Dn|--debug n	acrivate debug with level 'n'
" >&2
	exit 127
}

cleanup()
{
	:
}

_ERRORS=
error()
{
	echo -e "\nerror: $@\n" >&2
	_ERRORS="$_ERRORS\n$@"
}

sortgroups()
{
	local item=
	local out=
	local in=
	read in
	out=$(for item in $in
	do
		echo "$item" | sed -e 's/=.*//'
	done | sort -u)
	[ "$out" != "" ] && echo $out
}

pdebug()
{
	$DEBUG || return 0
	echocr
	echo -n "D> ">&2
	echo "$@" >&2
}


printentry()
{
	local dif=
	local fmt=" %1.1s %-12s %-30s %-30s\n"

	[ "$3" != "" -a "$2" != "$3" ] && dif="*"
	printf "$fmt" "$dif" "$1" "$2" "$3"
}
printblock()
{
	local cols=$(tput cols) || cols=80
	local cols=$(expr $cols - 5)
	echo "$@" | fold -s -w $cols | sed -e 's/^/     /'
}

get_user_infos()
{
	local pwd_entry=
	local ldap_entry=
	local role=
	local stat=0
	local item=

	# check for auth, and set $db_entry to current one
	# if searching for F_ALL_USERS this check was already performed,
	# but we need $db_entry loaded
	#
	is_authorized || {
		error "user '$user' is not authorized on this system"
		return 1
	}


	# get POSIX infos
	#
	pwd_entry=$(getent passwd "$user" 2>/dev/null)
	if [ "$pwd_entry" != "" ]
	then
		uid=$(echo "$pwd_entry" | cut -d':' -f3)
		gid=$(id -gn $user)
		gecos=$(echo "$pwd_entry" | cut -d':' -f5)
		home=$(echo "$pwd_entry" | cut -d':' -f6)
		shell=$(echo "$pwd_entry" | cut -d':' -f7)
		groups=$(id -Gn $user | sortgroups)
		user_exists=true
	else
		user_exists=false
	fi

	# additional infos from LDAP
	#
	ldap_entry=$(ku-ldapsearch uid=$user 2>/dev/null) || :
	ldap_uid=$(echo "$ldap_entry" | sed -n -e '/^uid: /s/uid: //p')
	[ "X$uid" != "X" ] && {
		ldap_expire=$(echo "$ldap_entry" | sed -n -e '/^shadowExpire: /s/shadowExpire: //p')
		ldap_shadowmax=$(echo "$ldap_entry" | sed -n -e '/^shadowMax: /s/shadowMax: //p')
		ldap_samba="false"
		echo "$ldap_entry" | grep -q '^sambaSID: ' && ldap_samba="true"
		[ "X$ldap_expire" != "X" ] && {
			# convert ldap number of days in YYYY-MM-GG date
			ldap_expire=$(echo "${ldap_expire}*86400" | bc)
			ldap_expire=$(date '+%Y-%m-%d' --date "@$ldap_expire")
		}
	}

	# kusa db infos
	#
	k_gid=$(kusa-conf $db_entry.gid 2>/dev/null)
	k_gecos=$(kusa-conf $db_entry.gecos 2>/dev/null)
	k_home=$(kusa-conf $db_entry.home 2>/dev/null)
	k_shell=$(kusa-conf $db_entry.shell 2>/dev/null)
	k_roles=$(kusa-conf $db_entry.roles 2>/dev/null)
	k_samba=$(kusa-conf $db_entry.samba 2>/dev/null)
	k_expire=$(kusa-conf $db_entry.expire 2>/dev/null)
	k_password=$(kusa-conf $db_entry.password 2>/dev/null)
	k_shadowmax=$(kusa-conf $db_entry.shadow_max 2>/dev/null)


	# homedir mungle
	#  - empty? defaults to /home/$user
	#  - has trailing slash? so $user will be added
	#  - plain string, used as is
	#
	# then final cleanup:
	#  - @SELF@ subst with $user
	#  - double // removed
	#
	case "$k_home" in
	  "")		k_home="/home/$user" ;;
	  */)		k_home="$k_home$user" ;;
	esac
	k_home=$(echo "$k_home" | sed -e "s/@SELF@/$user/g" -e 's#//#/#g')

	# is samba enabled?
	#
	case $k_samba in
	 [yY][eE][sS])	k_samba=true ;;
	 [yY]|1|true)	k_samba=true ;;
	 *)		k_samba=false ;;
	esac

	for role in $k_roles
	do
		item=$(kusa-conf role.$role.groups) || {
			error "user '$user': role '$role' not defined in kusa db"
			return 1
		}
		k_groups="$k_groups $item"
	done
	k_groups=$(echo "$k_groups" | sortgroups)

	$VERBOSE && {
		echo "USER: $user"
		printentry "uid" "$uid" "$k_uid"
		printentry "gid" "$gid" "$k_gid"
		printentry "gecos" "$gecos" "$k_gecos"
		printentry "home" "$home" "$k_home"
		printentry "shell" "$shell" "$k_shell"
		printentry "samba" "$ldap_samba" "$k_samba"
		printentry "expire" "$ldap_expire" "$k_expire"
		printentry "shadowmax" "$ldap_shadowmax" "$k_shadowmax"
		echo
		echo "   roles/groups:"
		for role in $k_roles
		do
			printf "     %-10s %s\n" "$role" "$(kusa-conf role.$role.groups | sortgroups)"
		done
		echo
		if [ "$k_groups" != "" -a "$groups" != "$k_groups" ]
		then
			echo " * groups:"
			printblock "$groups"
			echo "   required groups:"
			printblock "$k_groups"
		else
			echo "   groups:"
			printblock "$groups"
		fi
		needs_update && {
			$VERBOSE && echo -e "\n  USER '$user': UPDATE NEEDED\n"
		}
	}
	return $stat
}



needs_update()
{
	$F_FORCE && return 0
	$user_exists || return 0
	
	[ "$k_uid" != "" -a "$uid" != "$k_uid" ]			&& return 0
	[ "$k_gid" != "" -a "$gid" != "$k_gid" ]			&& return 0
	[ "$k_gecos" != "" -a "$gecos" != "$k_gecos" ]			&& return 0
	[ "$k_home" != "" -a "$home" != "$k_home" ]			&& return 0
	[ "$k_shell" != "" -a "$shell" != "$k_shell" ]			&& return 0
	[ "$k_groups" != "" -a "$groups" != "$k_groups" ]		&& return 0
	[ "$k_samba" != "" -a "$ldap_samba" != "$k_samba" ]		&& return 0
	[ "$k_expire" != "" -a "$ldap_expire" != "$k_expire" ]		&& return 0
	[ "$k_shadowmax" != "" -a "$ldap_shadowmax" != "$k_shadowmax" ]	&& return 0
	return 1
}

is_authorized()
{
	local myname=`uname -n`
	local myname_s=`uname -n | sed -e 's/\..*//'`
	local sys=
	local systems=
	local result=

	result=$(kusa-conf --list user. | grep "\.$user$") || {
		error "user '$user' not defined in kusa db"
		return 1
	}
	[ $(echo "$result" | wc -l) -gt 1 ] && {
		error "user '$user' has multiple definitions:"
		echo "$result" >&2
		return 1
	}

	# no uid? not a valid user, maybe a template entry
	k_uid=$(kusa-conf $result.uid 2>/dev/null)

	[ "$k_uid" == "" ] && {
		$F_ALL_USERS || {	# be quiet if searching all users
			error "user '$user' doesn't have a valid uid in kusa db" >&2
		}
		pdebug "is_authorized($user): NO (no uid)"
		return 1
	}

	systems=$(kusa-conf $result.systems 2>/dev/null) || {
		$F_ALL_USERS || {	# be quiet if searching all users
			error "user '$user' is not assigned to any system" >&2
		}
		pdebug "is_authorized($user): NO (no systems)"
		return 1
	}

	for sys in $systems
	do
		case $sys in
		  ALL)	pdebug "is_authorized($user): yes (ALL)"
			db_entry=$result
		  	return 0
			;;
		  .*)	in_domain $sys && {
		  		pdebug "is_authorized($user): yes ($myname in_domain $sys)"
				db_entry=$result
				return 0
			}
			;;
		  *)	[ $sys == $myname -o $sys == $myname_s ] && {
		  		pdebug "is_authorized($user): yes ($sys == $myname or $myname_s)"
				db_entry=$result
				return 0
			}
			;;
		esac
	done
	pdebug "is_authorized($user): NO (fallback)"
	return 1
}

in_domain()
{
	local domain=$1
	local here=$(uname -n)
	while :
	do
		here=$(echo $here | sed -e 's/^.[^.]*//')	# strip firse element
		[ "$here" == "" ] && break
		[ $domain == $here ] && return 0
	done
	return 1
}



# (MAIN)

trap 'echo -e "\n*INTR*\n"; cleanup; exit 255' 1 2 3
trap 'echo -e "\nunknown error $? at $LINENO\n"; cleanup' ERR
trap 'cleanup' EXIT

VERBOSE=true
DEBUG=false
DEBUGLEVEL=0
F_EXEC=true
F_CREATE=false
F_FORCE=false
F_ALL_USERS=false
F_LIST=false
F_RESET_PASSWORD=false

while [ $# != 0 ]
do
  case $1 in
    -v|--verbose)	VERBOSE=true ;;
    -q|--quiet)		VERBOSE=false ;;
    -n|--dry-run)	F_EXEC=false ;;
    -c|--create)	F_CREATE=true ;;
    -f|--force)		F_FORCE=true ;;
    -a|--all)		F_ALL_USERS=true ;;
    -l|--list)		F_LIST=true ;;
    --reset-password)	F_RESET_PASSWORD=true ;;
    -D)			DEBUG=true ;;
    -D[0-9])
	DEBUG=true
    	DEBUGLEVEL=$(echo "X$1" | sed -e 's/^-D//')
	;;
    --)			break ;;
    -*|"")		usage ;;
    *)			break ;;
  esac
  shift
done

if $F_ALL_USERS
then
	[ $# != 0 ] && usage

	all_users=$(kusa-conf --list user. | sed -e 's/.*\.//' | sort -u)
	selected_users=
	cnt_users=$(echo $all_users | wc -w)
	cnt_selected=0

	for user in $(kusa-conf --list user. | sed -e 's/.*\.//')
	do
		echocr "  total_in_db=$cnt_users, selected=$cnt_selected, checking $user ..."
		is_authorized && {
			selected_users="$selected_users $user"
			cnt_selected=$(expr $cnt_selected + 1)
		}
	done
	echocr

	[ "$selected_users" == "" ] && {
		echo "no users authorized on this systems" >&2
		exit 1
	}
	$F_LIST && {
		echo $selected_users
		exit 0
	}
	set $selected_users
else
	$F_LIST && usage
	[ $# == 0 ] && usage
fi


for user
do
	# POSIX account infos
	uid=
	gid=
	gecos=
	home=
	shell=
	groups=

	# LDAP infos
	#
	ldap_uid=
	ldap_samba=
	ldap_expire=
	ldap_shadowmax=

	# extended kusa infos
	k_uid=
	k_gid=
	k_gecos=
	k_home=
	k_shell=
	k_groups=
	k_roles=
	k_samba=
	k_expire=
	k_password=
	k_shadowmax=

	user_exists=
	db_entry=

	get_user_infos || continue

	needs_update && {
		$F_EXEC && {
			[ -r /etc/smbldap-tools/smbldap.conf ] || {
				echo -e "\nerror: you need to be root, or have admin privileges," >&2
				echo -e "to apply changes with this program, maybe you can use" >&2
				echo -e "'sudo' or use '--dry-run' option\n" >&2
				exit 1
			}
		}
		k_gid=${k_gid:-$gid}
		k_gecos=${k_gecos:-$gecos}
		k_home=${k_home:-$home}
		k_shell=${k_shell:-$shell}
		k_groups=${k_groups:-$groups}
		k_expire=${k_expire:-$ldap_expire}
		k_shadowmax=${k_shadowmax:-$ldap_shadowmax}
		k_samba=${k_samba:-$ldap_samba}

		k_groups=$(echo "$k_groups" | tr ' ' ',')

		[ "$k_expire" != "" ] && {
			if $k_samba
			then
				k_expire="--shadowExpire $k_expire --sambaExpire $k_expire"
			else
				k_expire="--shadowExpire $k_expire"
			fi
		}

		$user_exists || {
			if $F_CREATE
			then
				cmd="smbldap-useradd --non-unique -n -m"
				$k_samba && cmd="$cmd -a" || :
				$VERBOSE && echo -e "executing\n    $cmd -u '$k_uid' -g '$k_gid' -d '$k_home' $user\n" || :
				$F_EXEC  && {
					out=$($cmd -u "$k_uid" -g "$k_gid" -d "$k_home" $user 2>&1) || {
						error "(smbldap-useradd $user) $out"
						continue
					}
				}
			else
				error "skipping non existent user '$user' (use --create to force creation)"
				continue
			fi
		}

		$VERBOSE && echo "executing:
    smbldap-usermod -o
	--uid '$k_uid' --gid '$k_gid'
	--gecos '$k_gecos'
	--homedir '$k_home'
	--shell '$k_shell'
	--group '$k_groups'
	--shadowMax '$k_shadowmax'
	$k_expire
	$user
"
		$F_EXEC && {
		    out=$(smbldap-usermod -o \
			--uid "$k_uid" --gid "$k_gid" \
			--gecos "$k_gecos" \
			--homedir "$k_home" \
			--shell "$k_shell" \
			--group "$k_groups" \
			--shadowMax "$k_shadowmax" \
			$k_expire \
			$user 2>&1) || {
				if [ "X$ldap_uid" == "X" ]
				then
					error "(smbldap-usermod $user) $out\n	(FIXME user defined locally but not in LDAP)"
				else
					error "(smbldap-usermod $user) $out"
				fi
				continue
			}
		}
	}

	$F_RESET_PASSWORD && {
		[ "$k_password" != "" ] && {
			$VERBOSE && echo "changing password for user $user (using smbldap-passwd)"
			$F_EXEC && {
				set -o pipefail		# returns last pipe command status
				echo -e "$k_password\n$k_password" | smbldap-passwd $user || exit $?
			}
		}
	}
done

[ "X$_ERRORS" != "X" ] && {
	echo -e "\nERRORS SUMMARY:\n$_ERRORS"
	exit 1
}
echo -e "\nno errors\n"
exit 0
