if [ ! "$_USERMGMT_USER_INPUT_SUBR" ]; then _USERMGMT_USER_INPUT_SUBR=1
#
# Copyright (c) 2012 Ron McDowell
# Copyright (c) 2012-2013 Devin Teske
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD: release/10.0.0/usr.sbin/bsdconfig/usermgmt/share/user_input.subr 256181 2013-10-09 08:12:26Z dteske $
#
############################################################ INCLUDES

BSDCFG_SHARE="/usr/share/bsdconfig"
. $BSDCFG_SHARE/common.subr || exit 1
f_dprintf "%s: loading includes..." usermgmt/user_input.subr
f_include $BSDCFG_SHARE/dialog.subr
f_include $BSDCFG_SHARE/strings.subr

BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt"
f_include_lang $BSDCFG_LIBE/include/messages.subr
f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr

############################################################ CONFIGURATION

#
# Default location of shells(5)
#
: ${ETC_SHELLS:=/etc/shells}

############################################################ FUNCTIONS

# f_get_member_groups $user
#
# Get a list of additional groups $user is a member of in group(5).
#
f_get_member_groups()
{
	echo $( pw groupshow -a | awk -F: "/[:,]$1(,|\$)/{print \$1}" )
}

# f_input_user $user
#
# Given $user name or id, create the environment variables pw_name, pw_uid,
# pw_gid, pw_class, pw_password_expire, pw_account_expire, pw_gecos,
# pw_home_dir, pw_shell, and pw_member_groups (and pw_password is reset to
# NULL).
#
f_input_user()
{
	local user="$1"
	eval $( pw usershow "$user" | awk -F: '
	{
		printf "pw_name='\'%s\''\n", $1
		printf "pw_password=\n"
		printf "pw_uid='\'%s\''\n", $3
		printf "pw_gid='\'%s\''\n", $4
		printf "pw_class='\'%s\''\n", $5
		printf "pw_password_expire='\'%s\''\n", $6
		printf "pw_account_expire='\'%s\''\n", $7
		printf "pw_gecos='\'%s\''\n", $8
		printf "pw_home_dir='\'%s\''\n", $9
		printf "pw_shell='\'%s\''\n", $10
	}' )
	pw_member_groups=$( f_get_member_groups "$user" )
}

# f_dialog_menu_user_list [$default]
#
# Allows the user to select a login from a list. Optionally, if present and
# non-NULL, initially highlight $default user.
#
f_dialog_menu_user_list()
{
	local prompt=
	local menu_list="
		'X $msg_exit' ''
	" # END-QUOTE
	local defaultitem="$1"
	local hline="$hline_alnum_punc_tab_enter"

	# Add users from passwd(5)
	menu_list="$menu_list $( pw usershow -a | awk -F: '
		!/^[[:space:]]*(#|$)/ {
			printf "'\'%s\'\ \'%s\''\n", $1, $8
		}'
	)"

	local height width rows
	eval f_dialog_menu_size height width rows \
	                        \"\$DIALOG_TITLE\"     \
	                        \"\$DIALOG_BACKTITLE\" \
	                        \"\$prompt\"           \
	                        \"\$hline\"            \
	                        $menu_list

	local menu_choice
	menu_choice=$( eval $DIALOG \
		--title \"\$DIALOG_TITLE\"         \
		--backtitle \"\$DIALOG_BACKTITLE\" \
		--hline \"\$hline\"                \
		--ok-label \"\$msg_ok\"            \
		--cancel-label \"\$msg_cancel\"    \
		--default-item \"\$defaultitem\"   \
		--menu \"\$prompt\"                \
		$height $width $rows               \
		$menu_list                         \
		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
	)
	local retval=$?
	f_dialog_menutag_store -s "$menu_choice"
	return $retval
}

# f_dialog_input_member_groups [$member_groups]
#
# Allows the user to edit group memberships for a given user. If the user does
# not cancel or press ESC, the $pw_member_groups variable will hold the newly-
# configured value upon return.
#
f_dialog_input_member_groups()
{
	local _member_groups="$1"
	local prompt="$msg_member_of_groups"
	local check_list= # Calculated below
	local hline="$hline_alnum_space_tab_enter"
	local group

	#
	# Generate the checklist menu
	#
	for group in $(
		pw groupshow -a | awk -F: '!/^[[:space:]]*(#|$)/{print $1}'
	); do
		# Format of a checklist menu entry is "tag item status"
		# (setting both tag and item to the group name below).
		if echo "$_member_groups" | grep -q "\<$group\>"; then
			check_list="$check_list $group $group on"
		else
			check_list="$check_list $group $group off"
		fi
	done

	#
	# Loop until the user provides taint-free/valid input
	#
	local height width rows
	while :; do
		eval f_dialog_checklist_size height width rows \
		        	\"\$DIALOG_TITLE\"     \
		        	\"\$DIALOG_BACKTITLE\" \
		        	\"\$prompt\"           \
		        	\"\$hline\"            \
		        	$check_list
		_member_groups=$( eval $DIALOG \
			--title \"\$DIALOG_TITLE\"         \
			--backtitle \"\$DIALOG_BACKTITLE\" \
			--separate-output                  \
			--hline \"\$hline\"                \
			--ok-label \"\$msg_ok\"            \
			--cancel-label \"\$msg_cancel\"    \
			--checklist \"\$prompt\"           \
			$height $width $rows               \
			$check_list                        \
			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
		) || return $?
			# Return if user either pressed ESC or chose Cancel/No
		f_dialog_data_sanitize _member_groups

		#
		# Validate each of the groups the user has entered
		#
		local all_groups_valid=1
		for group in $_member_groups; do
			if ! f_quietly pw groupshow -n "$group"; then
				f_show_msg "$msg_group_not_found" "$group"
				all_groups_valid=
				break
			fi
		done
		[ "$all_groups_valid" ] || continue

		pw_member_groups="$_member_groups"
		break
	done
	save_flag=1

	f_dprintf "pw_member_groups: [%s]->[%s]" \
	          "$cur_pw_member_groups" "$pw_member_groups"

	return $DIALOG_OK
}

# f_dialog_input_name [$name]
#
# Allows the user to enter a new username for a given user. If the user does
# not cancel or press ESC, the $pw_name variable will hold the newly-configured
# value upon return.
#
# If $cur_pw_name is defined, the user can enter that and by-pass error-
# checking (allowing the user to "revert" to an old value without, for example,
# being told that the username already exists).
#
f_dialog_input_name()
{
	#
	# Loop until the user provides taint-free/valid input
	#
	local _name="$1" _input="$1"
	while :; do

		# Return if user has either pressed ESC or chosen Cancel/No
		f_dialog_input _input "$msg_login" "$_input" \
		               "$hline_alnum_tab_enter" || return $?

		# Check for no-change
		[ "$_input" = "$_name" ] && return $DIALOG_OK

		# Check for reversion
		if [ "$_input" = "$cur_pw_name" ]; then
			pw_name="$cur_pw_name"
			return $DIALOG_OK
		fi

		# Check for NULL entry
		if [ ! "$_input" ]; then
			f_show_msg "$msg_login_is_empty"
			continue
		fi

		# Check for invalid entry
		if ! echo "$_input" | grep -q "^[[:alpha:]]"; then
			f_show_msg "$msg_login_must_start_with_letter"
			continue
		fi

		# Check for duplicate entry
		if f_quietly pw usershow -n "$_input"; then
			f_show_msg "$msg_login_already_used" "$_input"
			continue
		fi

		pw_name="$_input"
		break
	done
	save_flag=1

	f_dprintf "pw_name: [%s]->[%s]" "$cur_pw_name" "$pw_name"

	return $DIALOG_OK
}

# f_dialog_input_password
#
# Prompt the user to enter a password (twice).
#
f_dialog_input_password()
{
	local prompt1="$msg_password"
	local prompt2="$msg_reenter_password"
	local hline="$hline_alnum_punc_tab_enter"

	local height1 width1
	f_dialog_inputbox_size height1 width1 \
	                       "$DIALOG_TITLE"     \
	                       "$DIALOG_BACKTITLE" \
	                       "$prompt1"          \
	                       ""                  \
	                       "$hline"
	local height2 width2
	f_dialog_inputbox_size height2 width2 \
	                       "$DIALOG_TITLE"     \
	                       "$DIALOG_BACKTITLE" \
	                       "$prompt2"          \
	                       ""                  \
	                       "$hline"

	#
	# Loop until the user provides taint-free/valid input
	#
	local _password1 _password2
	while :; do
		_password1=$( $DIALOG \
			--title "$DIALOG_TITLE"         \
			--backtitle "$DIALOG_BACKTITLE" \
			--hline "$hline"                \
			--ok-label "$msg_ok"            \
			--cancel-label "$msg_cancel"    \
			--insecure                      \
			--passwordbox "$prompt1"        \
			$height1 $width1                \
			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
		) || return $?
			# Return if user either pressed ESC or chose Cancel/No
		debug= f_dialog_line_sanitize _password1

		_password2=$( $DIALOG \
			--title "$DIALOG_TITLE"         \
			--backtitle "$DIALOG_BACKTITLE" \
			--hline "$hline"                \
			--ok-label "$msg_ok"            \
			--cancel-label "$msg_cancel"    \
			--insecure                      \
			--passwordbox "$prompt2"        \
			$height2 $width2                \
			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
		) || return $?
			# Return if user either pressed ESC or chose Cancel/No
		debug= f_dialog_line_sanitize _password2

		# Check for password mismatch
		if [ "$_password1" != "$_password2" ]; then
			f_show_msg "$msg_passwords_do_not_match"
			continue
		fi

		# Check for NULL entry
		if [ ! "$_password1" ]; then
			f_dialog_yesno "$msg_disable_password_auth_for_account"
			local retval=$?
			if [ $retval -eq $DIALOG_ESC ]; then
				return $retval
			elif [ $retval -eq $DIALOG_OK ]; then
				pw_password_disable=1
			else
				continue # back to password prompt
			fi
		else
			pw_password_disable=
		fi

		pw_password="$_password1"
		break
	done
	save_flag=1

	f_dprintf "pw_password: [%s]->[%s]" "$cur_pw_password" "$pw_password"

	return $DIALOG_OK
}

# f_dialog_input_gecos [$gecos]
#
# Allow the user to enter new GECOS information for a given user. This
# information is commonly used to store the ``Full Name'' of the user. If the
# user does not cancel or press ESC, the $pw_gecos variable will hold the
# newly-configured value upon return.
#
f_dialog_input_gecos()
{
	local _input="$1"

	# Return if user has either pressed ESC or chosen Cancel/No
	f_dialog_input _input "$msg_full_name" "$_input" \
	               "$hline_alnum_punc_tab_enter" || return $?

	pw_gecos="$_input"
	save_flag=1

	f_dprintf "pw_gecos: [%s]->[%s]" "$cur_pw_gecos" "$pw_gecos"

	return $DIALOG_OK
}

# f_dialog_input_uid [$uid]
#
# Allow the user to enter a new UID for a given user. If the user does not
# cancel or press ESC, the $pw_uid variable will hold the newly-configured
# value upon return.
#
f_dialog_input_uid()
{
	local _input="$1"

	# Return if user has either pressed ESC or chosen Cancel/No
	f_dialog_input _input "$msg_user_id_leave_empty_for_default" \
	               "$_input" "$hline_num_tab_enter" || return $?

	pw_uid="$_input"
	save_flag=1

	f_dprintf "pw_uid: [%s]->[%s]" "$cur_pw_uid" "$pw_uid"

	return $DIALOG_OK
}

# f_dialog_input_gid [$gid]
#
# Allow the user to enter a new primary GID for a given user. If the user does
# not cancel or press ESC, the $pw_gid variable will hold the newly-configured
# value upon return.
#
f_dialog_input_gid()
{
	local _input="$1"

	# Return if user has either pressed ESC or chosen Cancel/No
	f_dialog_input _input "$msg_group_id_leave_empty_for_default" \
	               "$_input" "$hline_num_tab_enter" || return $?

	pw_gid="$_input"
	save_flag=1

	f_dprintf "pw_gid: [%s]->[%s]" "$cur_pw_gid" "$pw_gid"

	return $DIALOG_OK
}

# f_dialog_input_class [$class]
#
# Allow the user to enter a new login class for a given user. If the user does
# not cancel or press ESC, the $pw_class variable will hold the newly-
# configured value upon return.
#
f_dialog_input_class()
{
	local _input="$1"

	# Return if user has either pressed ESC or chosen Cancel/No
	f_dialog_input _input "$msg_login_class" "$_input" \
	               "$hline_alnum_tab_enter" || return $?

	pw_class="$_input"
	save_flag=1

	f_dprintf "pw_class: [%s]->[%s]" "$cur_pw_class" "$pw_class"

	return $DIALOG_OK
}

# f_dialog_input_expire_password [$seconds]
#
# Allow the user to enter a date/time (in number-of-seconds since the `epoch')
# for when a given user's password must be changed. If the user does not cancel
# or press ESC, the $pw_password_expire variable will hold the newly-
# configured value upon return.
#
f_dialog_input_expire_password()
{
	local prompt="$msg_password_expires_on"
	local menu_list="
		'1' '$msg_password_does_not_expire'
		'2' '$msg_edit_date_time_with_a_calendar'
		'3' '$msg_enter_number_of_days_into_the_future'
		'4' '$msg_enter_value_manually'
	" # END-QUOTE
	local hline="$hline_num_arrows_tab_enter"
	local retval _input="$1"

	local mheight mwidth mrows
	eval f_dialog_menu_size mheight mwidth mrows \
	                        \"\$DIALOG_TITLE\"     \
	                        \"\$DIALOG_BACKTITLE\" \
	                        \"\$prompt\"           \
	                        \"\$hline\"            \
	                        $menu_list
	local cheight cwidth
	f_dialog_calendar_size cheight cwidth \
	                       "$DIALOG_TITLE"     \
	                       "$DIALOG_BACKTITLE" \
	                       "$prompt"           \
	                       "$hline"
	local theight twidth
	f_dialog_timebox_size theight twidth \
	                      "$DIALOG_TITLE"     \
	                      "$DIALOG_BACKTITLE" \
	                      "$prompt"           \
	                      "$hline"

	#
	# Loop until the user provides taint-free/cancellation-free input
	#
	local date_type defaultitem=
	while :; do
		date_type=$( eval $DIALOG \
			--title \"\$DIALOG_TITLE\"         \
			--backtitle \"\$DIALOG_BACKTITLE\" \
			--hline \"\$hline\"                \
			--default-item \"\$defaultitem\"   \
			--ok-label \"\$msg_ok\"            \
			--cancel-label \"\$msg_cancel\"    \
			--menu \"\$prompt\"                \
			$mheight $mwidth $mrows            \
			$menu_list                         \
			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
		)
		retval=$?
		f_dialog_data_sanitize date_type
		defaultitem="$date_type"
		f_dprintf "retval=%u date_type=[%s]" $retval "$date_type"

		# Return if user has either pressed ESC or chosen Cancel/No
		[ $retval -eq $DIALOG_OK ] || return $retval

		case "$date_type" in
		1) # Password does not expire
			_input=""
			break ;;

		2) # Edit date/time with a calendar
			local _input_date _input_time ret_date ret_time

			local secs="$_input"
			{ f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs=
			_input_date=$( date -j -f "%s" -- "$secs" \
			               		"+%d %m %Y" 2> /dev/null )
			ret_date=$( eval $DIALOG \
				--title \"\$DIALOG_TITLE\"          \
				--backtitle \"\$DIALOG_BACKTITLE\"  \
				--hline \"\$hline\"                 \
				--ok-label \"\$msg_ok\"             \
				--cancel-label \"\$msg_cancel\"     \
				--calendar \"\$prompt\"             \
				$cheight $cwidth                    \
				$_input_date                        \
				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
			)
			retval=$?
			f_dialog_data_sanitize ret_date
			f_dprintf "retval=%u ret_date=[%s]" $retval "$ret_date"

			# Return to menu if either ESC or Cancel/No
			[ $retval -eq $DIALOG_OK ] || continue

			_input_time=
			[ "$secs" ] && _input_time=$( date -j \
				-f %s -- "$_input" "+%H %M %S" 2> /dev/null )
			ret_time=$( eval $DIALOG \
				--title \"\$DIALOG_TITLE\"         \
				--backtitle \"\$DIALOG_BACKTITLE\" \
				--hline \"\$hline\"                \
				--ok-label \"\$msg_ok\"            \
				--cancel-label \"\$msg_cancel\"    \
				--timebox \"\$prompt\"             \
				$theight $twidth                   \
				$_input_time                       \
				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
			)
			retval=$?
			f_dialog_data_sanitize ret_time
			f_dprintf "retval=%u ret_time=[%s]" $retval "$ret_time"

			# Return to menu if either ESC or Cancel/No
			[ $retval -eq $DIALOG_OK ] || continue

			_input=$( date \
			          	-j -f "%d/%m/%Y %T" \
			          	-- "$ret_date $ret_time" \
			          	+%s 2> /dev/null )
			f_dprintf "_input=[%s]" "$_input"
			break ;;

		3) # Enter number of days into the future
			local ret_days seconds="$( date +%s )"

			f_isinteger "$_input" || _input=0
			[ $_input -gt 0 -a $_input -gt $seconds ] &&
				ret_days=$(( ( $_input - $seconds ) / 86400 ))
			f_isinteger "$ret_days" &&
				ret_days=$(( $ret_days + 1 ))

			# Return to menu if either ESC or Cancel/No
			f_dialog_input ret_days \
				"$msg_password_expires_in_how_many_days" \
				"$ret_days" "$hline" || continue

			# Taint-check the user's input
			if ! f_isinteger "$ret_days"; then
				f_show_msg "$msg_invalid_number_of_days"
				continue
			fi

			f_dprintf "ret_days=[%s]" "$ret_days"
			case "$ret_days" in
			[-+]*) _input=$( date -v${ret_days}d +%s ) ;;
			    0) _input=$( date +%s ) ;;
			    *) _input=$( date -v+${ret_days}d +%s ) ;;
			esac
			f_dprintf "_input=[%s]" "$_input"
			break ;;

		4) # Enter value manually
			local msg ret_secs
			msg=$( printf "$msg_number_of_seconds_since_epoch" \
			              "$( date -r 1 "+%c %Z" )" )

			# Return to menu if either ESC or Cancel/No
			f_dialog_input ret_secs \
			               "$msg" "$_input" "$hline" || continue

			_input="$ret_secs"

			# Taint-check the user's input
			if ! f_isinteger "${_input:-0}"; then
				f_show_msg "$msg_invalid_number_of_seconds"
				continue
			fi

			f_dprintf "_input=[%s]" "$_input"
			break ;;

		esac

	done # Loop forever

	pw_password_expire="$_input"
	save_flag=1

	f_dprintf "pw_password_expire: [%s]->[%s]" \
	          "$cur_pw_password_expire" "$pw_password_expire"

	return $DIALOG_OK
}

# f_dialog_input_expire_account [$seconds]
#
# Allow the user to enter a date/time (in number-of-seconds since the `epoch')
# for when a given user's account should become expired. If the user does not
# cancel or press ESC, the $pw_account_expire variable will hold the newly-
# configured value upon return.
#
f_dialog_input_expire_account()
{
	local prompt="$msg_account_expires_on"
	local menu_list="
		'1' '$msg_account_does_not_expire'
		'2' '$msg_edit_date_time_with_a_calendar'
		'3' '$msg_enter_number_of_days_into_the_future'
		'4' '$msg_enter_value_manually'
	" # END-QUOTE
	local hline="$hline_num_arrows_tab_enter"
	local retval _input="$1"

	local mheight mwidth mrows 
	eval f_dialog_menu_size mheight mwidth mrows \
	                        \"\$DIALOG_TITLE\"     \
	                        \"\$DIALOG_BACKTITLE\" \
	                        \"\$prompt\"           \
	                        \"\$hline\"            \
	                        $menu_list
	local cheight cwidth
	f_dialog_calendar_size cheight cwidth \
	                       "$DIALOG_TITLE"     \
	                       "$DIALOG_BACKTITLE" \
	                       "$prompt"           \
	                       "$hline"
	local theight twidth
	f_dialog_timebox_size theight twidth \
	                      "$DIALOG_TITLE"     \
	                      "$DIALOG_BACKTITLE" \
	                      "$prompt"           \
	                      "$hline"

	#
	# Loop until the user provides taint-free/cancellation-free input
	#
	local date_type defaultitem=
	while :; do
		date_type=$( eval $DIALOG \
			--title \"\$DIALOG_TITLE\"         \
			--backtitle \"\$DIALOG_BACKTITLE\" \
			--hline \"\$hline\"                \
			--default-item \"\$defaultitem\"   \
			--ok-label \"\$msg_ok\"            \
			--cancel-label \"\$msg_cancel\"    \
			--menu \"\$prompt\"                \
			$mheight $mwidth $mrows            \
			$menu_list                         \
			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
		)
		retval=$?
		f_dialog_data_sanitize date_type
		defaultitem="$date_type"
		f_dprintf "retval=%u date_type=[%s]" $retval "$date_type"

		# Return if user has either pressed ESC or chosen Cancel/No
		[ $retval -eq $DIALOG_OK ] || return $retval

		case "$date_type" in
		1) # Account does not expire
			_input=""
			break ;;

		2) # Edit date/time with a calendar
			local _input_date _input_time ret_date ret_time

			local secs="$_input"
			{ f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs=
			_input_date=$( date -j -f "%s" -- "$secs" \
			               		"+%d %m %Y" 2> /dev/null )
			ret_date=$( eval $DIALOG \
				--title \"\$DIALOG_TITLE\"          \
				--backtitle \"\$DIALOG_BACKTITLE\"  \
				--hline \"\$hline\"                 \
				--ok-label \"\$msg_ok\"             \
				--cancel-label \"\$msg_cancel\"     \
				--calendar \"\$prompt\"             \
				$cheight $cwidth                    \
				$_input_date                        \
				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
			)
			retval=$?
			f_dialog_data_sanitize ret_date
			f_dprintf "retval=%u ret_date=[%s]" $retval "$ret_date"

			# Return to menu if either ESC or Cancel/No
			[ $retval -eq $DIALOG_OK ] || continue

			_input_time=
			[ "$secs" ] && _input_time=$( date -j \
				-f %s -- "$_input" "+%H %M %S" 2> /dev/null )
			ret_time=$( eval $DIALOG \
				--title \"\$DIALOG_TITLE\"         \
				--backtitle \"\$DIALOG_BACKTITLE\" \
				--hline \"\$hline\"                \
				--ok-label \"\$msg_ok\"            \
				--cancel-label \"\$msg_cancel\"    \
				--timebox \"\$prompt\"             \
				$theight $twidth                   \
				$_input_time                       \
				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
			)
			retval=$?
			f_dialog_data_sanitize ret_time
			f_dprintf "retval=%u ret_time=[%s]" $retval "$ret_time"

			# Return to menu if either ESC or Cancel/No
			[ $retval -eq $DIALOG_OK ] || continue

			_input=$( date \
			          	-j -f "%d/%m/%Y %T" \
			          	-- "$ret_date $ret_time" \
			          	+%s 2> /dev/null )
			f_dprintf "_input=[%s]" "$_input"
			break ;;

		3) # Enter number of days into the future
			local ret_days seconds="$( date +%s )"

			f_isinteger "$_input" || _input=0
			[ $_input -gt 0 -a $_input -gt $seconds ] &&
				ret_days=$(( ( $_input - $seconds ) / 86400 ))
			f_isinteger "$ret_days" &&
				ret_days=$(( $ret_days + 1 ))

			# Return to menu if either ESC or Cancel/No
			f_dialog_input ret_days \
				"$msg_account_expires_in_how_many_days" \
				"$ret_days" "$hline" || continue

			# Taint-check the user's input
			if ! f_isinteger "$ret_days"; then
				f_show_msg "$msg_invalid_number_of_days"
				continue
			fi

			f_dprintf "ret_days=[%s]" "$ret_days"
			case "$ret_days" in
			[-+]*) _input=$( date -v${ret_days}d +%s ) ;;
			    0) _input=$( date +%s ) ;;
			    *) _input=$( date -v+${ret_days}d +%s ) ;;
			esac
			f_dprintf "_input=[%s]" "$_input"
			break ;;

		4) # Enter value manually
			local msg ret_secs
			msg=$( printf "$msg_number_of_seconds_since_epoch" \
			              "$( date -r 1 "+%c %Z" )" )

			# Return to menu if either ESC or Cancel/No
			f_dialog_input ret_secs "$msg" \
			               "$_input" "$hline" || continue

			_input="$ret_secs"

			# Taint-check the user's input
			if ! f_isinteger "${_input:-0}"; then
				f_show_msg "$msg_invalid_number_of_seconds"
				continue
			fi

			f_dprintf "_input=[%s]" "$_input"
			break ;;

		esac

	done # Loop forever

	pw_account_expire="$_input"
	save_flag=1

	f_dprintf "pw_account_expire: [%s]->[%s]" \
	          "$cur_pw_account_expire" "$pw_account_expire"

	return $DIALOG_OK
}

# f_dialog_input_home_dir [$home_dir]
#
# Allow the user to enter a new home directory for a given user. If the user
# does not cancel or press ESC, the $pw_home_dir variable will hold the newly-
# configured value upon return.
#
f_dialog_input_home_dir()
{
	local _input="$1"

	# Return if user has either pressed ESC or chosen Cancel/No
	f_dialog_input _input "$msg_home_directory" "$_input" \
	               "$hline_alnum_punc_tab_enter" || return $?

	pw_home_dir="$_input"
	save_flag=1

	f_dprintf "pw_home_dir: [%s]->[%s]" "$cur_pw_home_dir" "$pw_home_dir"

	return $DIALOG_OK
}

# f_dialog_input_home_create
#
# Prompt the user to confirm creation of a given user's home directory. If the
# user does not cancel (by choosing "No") or press ESC, the $pw_home_create
# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return
# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible.
#
f_dialog_input_home_create()
{
	local retval

	f_dialog_yesno "$msg_create_home_directory"
	retval=$?

	if [ $retval -eq $DIALOG_OK ]; then
		pw_home_create="$msg_yes"
	else
		pw_home_create="$msg_no"
	fi
	save_flag=1

	f_dprintf "pw_home_create: [%s]->[%s]" \
	          "$cur_pw_home_create" "$pw_home_create"

	[ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC
}

# f_dialog_input_group_delete
#
# Prompt the user to confirm deletion of a given user's primary group. If the
# user does not cancel (by choosing "No") or press ESC, the $pw_group_delete
# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return
# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible.
#
f_dialog_input_group_delete()
{
	local retval

	if f_isinteger "$pw_gid"; then
		if [ $pw_gid -lt 1000 ]; then
			f_dialog_noyes "$msg_delete_primary_group"
		else
			f_dialog_yesno "$msg_delete_primary_group"
		fi
	elif [ "$pw_gid" ]; then
		local gid=0
		gid=$( pw groupshow "$pw_gid" | awk -F: '{print $3}' )
		if f_isinteger "$gid" && [ $gid -lt 1000 ]; then
			f_dialog_noyes "$msg_delete_primary_group"
		else
			f_dialog_yesno "$msg_delete_primary_group"
		fi
	else
		f_dialog_yesno "$msg_delete_primary_group"
	fi
	retval=$?

	if [ $retval -eq $DIALOG_OK ]; then
		pw_group_delete="$msg_yes"
	else
		pw_group_delete="$msg_no"
	fi
	save_flag=1

	f_dprintf "pw_group_delete: [%s]->[%s]" \
	          "$cur_pw_group_delete" "$pw_group_delete"

	[ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC
}

# f_dialog_input_home_delete
#
# Prompt the user to confirm deletion of a given user's home directory. If the
# user does not cancel (by choosing "No") or press ESC, the $pw_home_delete
# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return
# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible.
#
f_dialog_input_home_delete()
{
	local retval

	f_dialog_yesno "$msg_delete_home_directory"
	retval=$?

	if [ $retval -eq $DIALOG_OK ]; then
		pw_home_delete="$msg_yes"
	else
		pw_home_delete="$msg_no"
	fi
	save_flag=1

	f_dprintf "pw_home_delete: [%s]->[%s]" \
	          "$cur_pw_home_delete" "$pw_home_delete"

	[ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC
}

# f_dialog_input_dotfiles_create
#
# Prompt the user to confirm population of a given user's home directory with
# sample dotfiles. If the user does not cancel (by choosing "No") or press ESC,
# the $pw_dotfiles_create variable will hold $msg_yes upon return, otherwise
# $msg_no. Use these return variables ($msg_yes and $msg_no) for comparison to
# be i18n-compatible.
#
f_dialog_input_dotfiles_create()
{
	local retval

	f_dialog_yesno "$msg_create_dotfiles"
	retval=$?

	if [ $retval -eq $DIALOG_OK ]; then
		pw_dotfiles_create="$msg_yes"
	else
		pw_dotfiles_create="$msg_no"
	fi
	save_flag=1

	f_dprintf "pw_dotfiles_create: [%s]->[%s]" \
	          "$cur_pw_dotfiles_create" "$pw_dotfiles_create"

	[ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC
}

# f_dialog_input_shell [$shell]
#
# Allow the user to select a new login shell for a given user. If the user does
# not cancel or press ESC, the $pw_home_dir variable will hold the newly-
# configured value upon return.
#
#
f_dialog_input_shell()
{
	local _input="$1"
	local prompt="$msg_select_login_shell"
	local radio_list= # Calculated below
	local hline="$hline_arrows_space_tab_enter"

	#
	# Generate the radiolist of shells
	#
	local shell
	for shell in $( awk '!/^[[:space:]]*(#|$)/{print}' "$ETC_SHELLS" ); do
		# Format of a radiolist menu entry is "tag item status"
		if [ "$shell" = "$_input" ]; then
			radio_list="$radio_list '$shell' '' 'on'"
		else
			radio_list="$radio_list '$shell' '' 'off'"
		fi
	done

	local height width rows
	eval f_dialog_radiolist_size height width rows \
	                             \"\$DIALOG_TITLE\"     \
	                             \"\$DIALOG_BACKTITLE\" \
	                             \"\$prompt\"           \
	                             \"\$hline\"            \
	                             $radio_list

	_input=$( eval $DIALOG \
		--title \"\$DIALOG_TITLE\"         \
		--backtitle \"\$DIALOG_BACKTITLE\" \
		--hline \"\$hline\"                \
		--ok-label \"\$msg_ok\"            \
		--cancel-label \"\$msg_cancel\"    \
		--radiolist \"\$prompt\"           \
		$height $width $rows               \
		$radio_list                        \
		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
	) || return $?
		# Return if user either pressed ESC or chose Cancel/No
	f_dialog_data_sanitize _input

	pw_shell="$_input"
	save_flag=1

	f_dprintf "pw_shell: [%s]->[%s]" "$cur_pw_shell" "$pw_shell"

	return $DIALOG_OK
}

############################################################ MAIN

f_dprintf "%s: Successfully loaded." usermgmt/user_input.subr

fi # ! $_USERMGMT_USER_INPUT_SUBR
