#!/usr/local/bin/cbsd
#v12.0.4
CBSDMODULE="jail"
MYARG="mode"
MYOPTARG="chroot epw fromfile fullname group jname login pw secgroup shell uid"
MYDESC="Manage user and password in jail/chroot environment"
ADDHELP="
${H3_COLOR}Description${N0_COLOR}:

The script creates or modifies a user account in the specified environment.

${H3_COLOR}Options${N0_COLOR}:

  ${N2_COLOR}chroot=${N0_COLOR}   - use chroot instead of jail's jname=;
  ${N2_COLOR}epw=${N0_COLOR}      - \"hash\", encrypted password (instead of pw=);
  ${N2_COLOR}fromfile=${N0_COLOR} - read data from file;
  ${N2_COLOR}fullname=${N0_COLOR} - gecos;
  ${N2_COLOR}group=${N0_COLOR}    - primary group;
  ${N2_COLOR}jname=${N0_COLOR}    - target jail;
  ${N2_COLOR}login=${N0_COLOR}    - login name;
  ${N2_COLOR}mode=${N0_COLOR}     - mode, can be: 'remove', 'add', or 'show';
  ${N2_COLOR}pw=${N0_COLOR}       - \"string\", plaintext password (instead of epw=);
  ${N2_COLOR}secgroup=${N0_COLOR} - secondary group;
  ${N2_COLOR}shell=${N0_COLOR}    - system/default shell, e.g.: /bin/sh;
  ${N2_COLOR}uid=${N0_COLOR}      - set UID, e.g. uid=1001;

${H3_COLOR}Examples${N0_COLOR}:

 # cbsd adduser jname=jail1 login=test
 # cbsd adduser chroot=/usr/jails/jails-data/jail1-data login=root mode=add pw='test'

 # cbsd adduser chroot=/usr/jails/jails/xxx login=test mode=add fromfile=/tmp/pw
 , where content of '/tmp/pw' file:
--
user_add="test"
user_pw_test_crypt='$6$i1X1S3mJfQ/Qdaml$mgLe/nLMUxWk55oQkxIJhMwhUd1rRAQDMHlpw4z.c4zRk1lOL5MIzs1kxZvKeXC3OcFcGP2IkgQAxvcaiHD.B0'
user_gecos_test="FullName"
user_shell_test="/bin/sh"
--

${H3_COLOR}See also${N0_COLOR}:

"

. ${subrdir}/nc.subr

jname=
chroot=
. ${cbsdinit}

. ${system}

_MYDIR=$( ${DIRNAME_CMD} `${REALPATH_CMD} $0` )
SERVICE="adduser"

f_getvar()
{
	local __var_to_get="$1" __var_to_set="$2"
	[ "$__var_to_set" ] || local value
	eval [ \"\${$__var_to_get+set}\" ]
	local __retval=$?
	eval ${__var_to_set:-value}=\"\${$__var_to_get}\"
	[ "$__var_to_set" ] || { [ "$value" ] && echo "$value"; }
	return $__retval
}


check_datadir()
{
	local _check_file="${path}/etc/master.passwd ${path}/etc/passwd ${path}/etc/group"

	local _i
	local ret=0

	for _i in ${_check_file}; do
		[ ! -f "${_i}" ] && echo ${_i} && ret=1
	done

	return ${ret}
}

# not for multiple action
getpw()
{
	local _oldmodes=$( /bin/stty -g ) _try=0
	pw=""
	_try=0

	while [ ${_try} -ne 3 ]; do
		printf "${BOLD}Enter password:${N0_COLOR}"
		while [ -z "${npw}" ]; do
			/bin/stty -echo
			read npw
			/bin/stty ${_oldmodes}
		done

		printf "\n${BOLD}Re-enter password: ${N0_COLOR}"
		while [ -z "${tpw}" ]; do
			/bin/stty -echo
			read tpw
			/bin/stty ${_oldmodes}
		done

		[ "${tpw}" = "${npw}" ] && pw="${npw}" && break

		printf "\n${N1_COLOR}Wrong password${N0_COLOR}\n"
		unset npw tpw
		_try=$(( _try + 1 ))
	done

	/bin/stty ${_oldmodes}
	echo
}

# must be filled:
# mandatory: login, pw
# opt: uid, fullname, group, secgroup, shell
c_adduser()
{
	local _res= _ret=

	[ -z "${login}" ] && ${ECHO} "${N2_COLOR}login= ${N1_COLOR} is mandatory${N0_COLOR}" && return 1

	_res=$( ${PW_CMD} -V ${data}/etc user show ${login} 2>&1)
	_ret=$?
	case ${_ret} in
		0)
			${ECHO} "${N1_COLOR}User already exist: ${N0_COLOR}${_res}"
			# modify
			if [ -n "${shell}" ]; then
				${PW_CMD} -V ${data}/etc usermod ${login} -s /bin/sh
			fi
			if [ -n "${pw}" ]; then
				echo "${pw}" | ${PW_CMD} -V ${data}/etc usermod -n ${login} -h 0
			fi
			if [ -z "${pw}" -a -z "${fromfile}" ]; then
				[ -z "${epw}" ] && getpw
			fi
			[ -z "${pw}" -a -z "${epw}" ] && err 1 "${N2_COLOR}pw= ${N1_COLOR}or ${N2_COLOR}epw= ${N1_COLOR} is mandatory${N0_COLOR}"

			if [ -n "${pw}" ]; then
				/bin/sh <<EOF
echo "${pw}" | ${PW_CMD} -V ${data}/etc usermod -n ${login} -h 0
EOF
			elif [ -n "${epw}" ]; then
				/bin/sh <<EOF
echo '${epw}' | ${PW_CMD} -V ${data}/etc usermod -n ${login} -H 0
EOF
			fi

			return 1
			;;
		67)
			true
			;;
		*)
			${ECHO} "${N1_COLOR}Error: ${N0_COLOR}${_res}"
			return 1
			;;
	esac

	if [ -z "${pw}" -a -z "${fromfile}" ]; then
		[ -z "${epw}" ] && getpw
	fi

	[ -z "${pw}" -a -z "${epw}" ] && err 1 "${N2_COLOR}pw= ${N1_COLOR}or ${N2_COLOR}epw= ${N1_COLOR} is mandatory${N0_COLOR}"
	ADDON=""
	[ -n "$uid" ] && ADDON="${ADDON} -u ${uid}"
	[ -n "$fullname" ] && ADDON="${ADDON} -c \"${fullname}\""
	[ -n "$group" ] && ADDON="${ADDON} -g ${group}"
	[ -n "$secgroup" ] && ADDON="${ADDON} -G ${secgroup}"

	if [ -n "${shell}" ]; then
		ADDON="${ADDON} -s ${shell}"
	else
		ADDON="${ADDON} -s /bin/sh"
	fi

	/bin/sh <<EOF
${PW_CMD} -V ${data}/etc useradd ${login} ${ADDON} -m
EOF
	_ret=$?

	if [ -n "${pw}" ]; then
		/bin/sh <<EOF
echo "${pw}" | ${PW_CMD} -V ${data}/etc usermod -n ${login} -h 0
EOF
	elif [ -n "${epw}" ]; then
		/bin/sh <<EOF
echo '${epw}' | ${PW_CMD} -V ${data}/etc usermod -n ${login} -H 0
EOF
	fi

	return ${_ret}
}

# must be filled:
# mandatory: login
remove()
{
	local _ret _res

	_res=$( ${PW_CMD} -V ${data}/etc user del ${login} )
	_ret=$?
	if [ ${_ret} -eq 0 ]; then
		${ECHO} "${N1_COLOR}user removed: ${N2_COLOR}${login}${N0_COLOR}"
	else
		echo "${_res}"
	fi
	return ${_ret}
}

# -n "name of the tools" - show <name> in Info string, e.g: -n jexec, -n "pkg install" ...
# -o uniq_name_of_the_task (one world)
adduser_multi_init()
{
	local _jname

	while getopts "c:n:o:" opt; do
		case "${opt}" in
			c) cmd="${OPTARG}" ;;
			n) _multiple_consumer_name="${OPTARG}" ;;
			o) task_owner="${OPTARG}" ;;
		esac
		shift $(($OPTIND - 1))
	done

	[ -z "${task_owner}" ] && err 1 "${N1_COLOR}multiple_processing_spawn: empty -o multiple_task_owner${N0_COLOR}"

	. ${subrdir}/multiple.subr
	${ECHO} "${N1_COLOR}Hint: Press ${N2_COLOR}'Ctrl+t'${N1_COLOR} to see last logfile line for active task${N0_COLOR}" 1>&2
	task_id=
	task_id_cur=
	task_owner="${task_owner}"
	# spawn command for all jail
	for _jname in ${jail_list}; do
		task_id_cur=$( task mode=new logfile=${tmpdir}/${task_owner}.${_jname}.log.$$ client_id=${_jname} autoflush=0 owner=${task_owner} /usr/bin/env NOCOLOR=1 /usr/local/bin/cbsd adduser jname=${_jname} ${cmd} 2>/dev/null )
		sleep 0.1               # dont brute taskdb
		if ! is_number "${task_id_cur}"; then
			task_id="${task_id} ${task_id_cur}"
		fi
	done

	multiple_task_id_all=$( echo ${task_id} | ${TR_CMD} " " "," )
	sleep 0.5
	multiple_processing_spawn -o ${task_owner} -n "${_multiple_consumer_name}"
}

# MAIN
emulator="jail" # for jname_is_multiple
jail_list=
jname_is_multiple

# MAIN for multiple jails
if [ -n "${jail_list}" ]; then
	# multiple
	if [ -n "${jail_list}" ]; then
		JLIST="${jail_list}"
	fi

	task_owner="adduser_multiple_query"
	_args=

	# trim for jname= in "$*"
	for i in $*; do
		prefix=
		prefix=$( substr --pos=0 --len=6 --str="${i}" )
		[ "${prefix}" = "jname=" ] && continue
		if [ -n "${_args}" ]; then
			_args="${_args} ${i}"
		else
			_args="${i}"
		fi
	done

	case "${mode}" in
		add)
			[ -z "${pw}" -a -z "${epw}" ] && err 1 "${N2_COLOR}pw= ${N1_COLOR}or ${N2_COLOR}epw=${N1_COLOR} is mandatory for multiple operation${N0_COLOR}"
			task_owner="adduser_multiple_add"
			adduser_multi_init -c "${_args}" -o ${task_owner} -n "adduser add"
			;;
		remove)
			[ -z "${login}" ] && err 1 "${N2_COLOR}login= ${N1_COLOR} is mandatory${N0_COLOR}"
			task_owner="adduser_multiple_remove"
			adduser_multi_init -c "${_args}" -o ${task_owner} -n "adduser remove"
			;;
		show)
			[ -z "${login}" ] && err 1 "${N2_COLOR}login= ${N1_COLOR} is mandatory${N0_COLOR}"
			task_owner="adduser_multiple_show"
			adduser_multi_init -c "${_args}" -o ${task_owner} -n "adduser show"
			;;
	esac

	err 0 "${N1_COLOR}Multiple adduser: ${N2_COLOR}done${N0_COLOR}"
fi

if [ -z "${jname}" ]; then
	if [ -n "${chroot}" ]; then
		data="${chroot}"
	else
		data="/"
	fi
	if ! check_datadir; then
		err 1 "${N1_COLOR}Bad data dir: ${N2_COLOR}${data}${N0_COLOR}"
	fi
else
	# push original jname to temporary variable for extract after fromfile action
	origjname="${jname}"
	. ${subrdir}/rcconf.subr

	[ $? -eq 1 ] &&  err 1 "${N1_COLOR}No such jail: ${N2_COLOR}${jname}${N0_COLOR}"
	path="${data}"

	if ! check_datadir; then
		err 1 "${N1_COLOR}Bad data dir: ${N2_COLOR}${data}${N0_COLOR}"
	fi
fi


if [ -r "${fromfile}" ]; then
	. ${fromfile}

	if [ -n "${origjname}" ]; then
		# extract after fromfile original jname if exist
		jname="${origjname}"
	fi

	if [ -n "${jname}" ]; then
		. ${subrdir}/rcconf.subr
		path="${data}"
	else
		[ -z "${chroot}" ] && err 1 "${N1_COLOR}${CBSD_APP} fromfile: no such 'jname' or 'chroot'${N0_COLOR}"
		path="${chroot}"
	fi

	[ -z "${user_add}" ] && err 1 "${N1_COLOR}${CBSD_APP} fromfile: no such vars: ${N2_COLOR}user_add=${N0_COLOR}"

	for i in ${user_add}; do
		unset login epw pw fullname secgroup group shell res err
		login="${i}"

		f_getvar user_pw_${i} pw
		f_getvar user_pw_${i}_crypt epw
		f_getvar user_gecos_${i} fullname
		f_getvar user_uid_${i} uid
		f_getvar user_gid_${i} gid
		f_getvar user_home_${i} home
		f_getvar user_shell_${i} shell
		f_getvar user_member_groups_${i} secgroup

		case "${mode}" in
			add)
				c_adduser
				ret=$?
				;;
			remove)
				res=$( remove )
				ret=$?
				;;
			show)
				res=$( show )
				ret=$?
				;;
		esac
		[ ${ret} -ne 0 ] && ${ECHO} "${res}" && continue
	done
	exit 0
fi

case "${mode}" in
	remove)
		res=$( ${PW_CMD} -V ${data}/etc user show ${login} 2>&1 )
		err=$?
		case ${err} in
			0)
				;;
			*)
				err 1 "${N1_COLOR}Error: ${N0_COLOR}${res}"
				;;
		esac
		res=$( remove )
		err=$?
		;;
	add)
		c_adduser
		err=$?
		;;
	show)
		[ -z "${login}" ] && err 1 "${N2_COLOR}login= ${N1_COLOR}is mandatory${N0_COLOR}"
		res=$( ${PW_CMD} -V ${data}/etc user show ${login} )
		err=$?
		;;
esac

err ${err} "${res}"
