xref: /freebsd/usr.sbin/bsdconfig/usermgmt/share/user_input.subr (revision c6ec7d31830ab1c80edae95ad5e4b9dba10c47ac)
1if [ ! "$_USERMGMT_USER_INPUT_SUBR" ]; then _USERMGMT_USER_INPUT_SUBR=1
2#
3# Copyright (c) 2012 Ron McDowell
4# Copyright (c) 2012 Devin Teske
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27#
28# $FreeBSD$
29#
30############################################################ INCLUDES
31
32BSDCFG_SHARE="/usr/share/bsdconfig"
33. $BSDCFG_SHARE/common.subr || exit 1
34f_include $BSDCFG_SHARE/dialog.subr
35f_include $BSDCFG_SHARE/strings.subr
36
37BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt"
38f_include_lang $BSDCFG_LIBE/include/messages.subr
39f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr
40
41############################################################ CONFIGURATION
42
43#
44# Default location of shells(5)
45#
46: ${ETC_SHELLS:=/etc/shells}
47
48############################################################ FUNCTIONS
49
50# f_get_member_groups $user
51#
52# Get a list of additional groups $user is a member of in group(5).
53#
54f_get_member_groups()
55{
56	echo $( pw groupshow -a | awk -F: "/[:,]$1(,|\$)/{print \$1}" )
57}
58
59# f_input_user $user
60#
61# Given $user name or id, create the environment variables pw_name, pw_uid,
62# pw_gid, pw_class, pw_password_expire, pw_account_expire, pw_gecos,
63# pw_home_dir, pw_shell, and pw_member_groups (and pw_password is reset to
64# NULL).
65#
66f_input_user()
67{
68	local user="$1"
69	eval $( pw usershow "$user" | awk -F: '
70	{
71		printf "pw_name='\'%s\''\n", $1
72		printf "pw_password=\n"
73		printf "pw_uid='\'%s\''\n", $3
74		printf "pw_gid='\'%s\''\n", $4
75		printf "pw_class='\'%s\''\n", $5
76		printf "pw_password_expire='\'%s\''\n", $6
77		printf "pw_account_expire='\'%s\''\n", $7
78		printf "pw_gecos='\'%s\''\n", $8
79		printf "pw_home_dir='\'%s\''\n", $9
80		printf "pw_shell='\'%s\''\n", $10
81	}' )
82	pw_member_groups=$( f_get_member_groups "$user" )
83}
84
85# f_dialog_menu_user_list
86#
87# Allows the user to select a login from a list.
88#
89f_dialog_menu_user_list()
90{
91	local menu_list size
92	local hline="$hline_alnum_punc_tab_enter"
93
94	menu_list="
95		'X $msg_exit' ''
96	" # END-QUOTE
97
98	# Add users from passwd(5)
99	menu_list="$menu_list $( pw usershow -a | awk -F: '
100		!/^[[:space:]]*(#|$)/ {
101			printf "'\'%s\'\ \'%s\''\n", $1, $8
102		}'
103	)"
104
105	size=$( eval f_dialog_menu_size \
106	        	\"\$DIALOG_TITLE\"     \
107	        	\"\$DIALOG_BACKTITLE\" \
108	        	\"\"                   \
109	        	\"\$hline\"            \
110	        	$menu_list             )
111
112	local dialog_menu
113	dialog_menu=$( eval $DIALOG \
114		--clear --title \"\$DIALOG_TITLE\" \
115		--backtitle \"\$DIALOG_BACKTITLE\" \
116		--hline \"\$hline\"                \
117		--ok-label \"\$msg_ok\"            \
118		--cancel-label \"\$msg_cancel\"    \
119		--menu \"\" $size $menu_list       \
120		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
121	)
122	local retval=$?
123	setvar DIALOG_MENU_$$ "$dialog_menu"
124	return $retval
125}
126
127# f_dialog_input_member_groups [$member_groups]
128#
129# Allows the user to edit group memberships for a given user. If the user does
130# not cancel or press ESC, the $pw_member_groups variable will hold the newly-
131# configured value upon return.
132#
133f_dialog_input_member_groups()
134{
135	local msg="$( printf "$msg_member_of_groups" )"
136	local hline="$hline_alnum_tab_enter"
137
138	#
139	# Loop until the user provides taint-free/valid input
140	#
141	local size retval all_groups checklist_groups="" _member_groups="$1"
142	all_groups=$( pw groupshow -a | awk -F: '
143		!/^[[:space:]]*(#|$)/ {
144			printf "%s\n", $1
145                }'
146        )
147	for grp in $all_groups; do
148		checklist_groups="$checklist_groups $grp $grp"
149		if echo "$_member_groups" | grep -q "\<$grp\>"; then
150			checklist_groups="$checklist_groups on"
151		else
152			checklist_groups="$checklist_groups off"
153		fi
154	done
155
156	while :; do
157		size=$( eval f_dialog_radiolist_size \
158		        	\"\$DIALOG_TITLE\"     \
159		        	\"\$DIALOG_BACKTITLE\" \
160		        	\"\"                   \
161		        	\"\$hline\"            \
162		        	$checklist_groups      )
163		local dialog_inputbox
164		dialog_inputbox=$( eval $DIALOG \
165			--title \"\$DIALOG_TITLE\"         \
166			--backtitle \"\$DIALOG_BACKTITLE\" \
167			--separate-output                  \
168			--hline \"\$hline\"                \
169			--ok-label \"\$msg_ok\"            \
170			--cancel-label \"\$msg_cancel\"    \
171			--checklist \"\$msg\" $size        \
172			$checklist_groups                  \
173			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
174		)
175
176		retval=$?
177		setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox"
178		_member_groups=$( f_dialog_inputstr )
179
180		# Return if user has either pressed ESC or chosen Cancel/No
181		[ $retval -eq $SUCCESS ] || return $retval
182
183		#
184		# Validate each of the groups the user has entered
185		#
186		local group all_groups_valid=1
187		for group in $_member_groups; do
188			if ! f_quietly pw groupshow -n "$group"; then
189				f_show_msg "$msg_group_not_found" "$group"
190				all_groups_valid=
191				break
192			fi
193		done
194		[ "$all_groups_valid" ] || continue
195
196		pw_member_groups="$_member_groups"
197		break
198	done
199	save_flag=1
200
201	f_dprintf "pw_member_groups: [%s]->[%s]" \
202	          "$cur_pw_member_groups" "$pw_member_groups"
203
204	return $SUCCESS
205}
206
207# f_dialog_input_name [$name]
208#
209# Allows the user to enter a new username for a given user. If the user does
210# not cancel or press ESC, the $pw_name variable will hold the newly-configured
211# value upon return.
212#
213# If $cur_pw_name is defined, the user can enter that and by-pass error-
214# checking (allowing the user to "revert" to an old value without, for example,
215# being told that the username already exists).
216#
217f_dialog_input_name()
218{
219	#
220	# Loop until the user provides taint-free/valid input
221	#
222	local _name="$1" _input="$1"
223	while :; do
224
225		# Return if user has either pressed ESC or chosen Cancel/No
226		_input=$( f_dialog_input "$msg_login" "$_input" \
227		                         "$hline_alnum_tab_enter"
228		        ) || return
229
230		# Check for no-change
231		[ "$_input" = "$_name" ] && return $SUCCESS
232
233		# Check for reversion
234		if [ "$_input" = "$cur_pw_name" ]; then
235			pw_name="$cur_pw_name"
236			return $SUCCESS
237		fi
238
239		# Check for NULL entry
240		if [ ! "$_input" ]; then
241			f_show_msg "$msg_login_is_empty"
242			continue
243		fi
244
245		# Check for invalid entry
246		if ! echo "$_input" | grep -q "^[[:alpha:]]"; then
247			f_show_msg "$msg_login_must_start_with_letter"
248			continue
249		fi
250
251		# Check for duplicate entry
252		if f_quietly pw usershow -n "$_input"; then
253			f_show_msg "$msg_login_already_used" "$_input"
254			continue
255		fi
256
257		pw_name="$_input"
258		break
259	done
260	save_flag=1
261
262	f_dprintf "pw_name: [%s]->[%s]" "$cur_pw_name" "$pw_name"
263
264	return $SUCCESS
265}
266
267# f_dialog_input_password
268#
269# Prompt the user to enter a password (twice).
270#
271f_dialog_input_password()
272{
273	local hline="$hline_alnum_punc_tab_enter"
274	local msg size rmsg rsize
275
276	msg=$( printf "$msg_password" )
277	size=$( f_dialog_inputbox_size \
278	        	"$DIALOG_TITLE"     \
279	        	"$DIALOG_BACKTITLE" \
280	        	"$msg"              \
281	        	""                  \
282	        	"$hline"            )
283
284	rmsg=$( printf "$msg_reenter_password" )
285	rsize=$( f_dialog_inputbox_size \
286	        	"$DIALOG_TITLE"     \
287	        	"$DIALOG_BACKTITLE" \
288	        	"$rmsg"             \
289	        	""                  \
290	        	"$hline"            )
291
292	#
293	# Loop until the user provides taint-free/valid input
294	#
295	local retval _password1 _password2
296	while :; do
297		local dialog_inputbox
298		dialog_inputbox=$( eval $DIALOG \
299			--title \"\$DIALOG_TITLE\"         \
300			--backtitle \"\$DIALOG_BACKTITLE\" \
301			--hline \"\$hline\"                \
302			--ok-label \"\$msg_ok\"            \
303			--cancel-label \"\$msg_cancel\"    \
304			--insecure                         \
305			--passwordbox \"\$msg\" $size      \
306			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
307		)
308
309		retval=$?
310		setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox"
311		_password1=$( f_dialog_inputstr )
312
313		# Return if user has either pressed ESC or chosen Cancel/No
314		[ $retval -eq $SUCCESS ] || return $retval
315
316		dialog_inputbox=$( eval $DIALOG \
317			--title \"\$DIALOG_TITLE\"         \
318			--backtitle \"\$DIALOG_BACKTITLE\" \
319			--hline \"\$hline\"                \
320			--ok-label \"\$msg_ok\"            \
321			--cancel-label \"\$msg_cancel\"    \
322			--insecure                         \
323			--passwordbox \"\$rmsg\" $rsize    \
324			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
325		)
326
327		retval=$?
328		setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox"
329		_password2=$( f_dialog_inputstr )
330
331		# Return if user has either pressed ESC or chosen Cancel/No
332		[ $retval -eq $SUCCESS ] || return $retval
333
334		# Check for password mismatch
335		if [ "$_password1" != "$_password2" ]; then
336			f_show_msg "$msg_passwords_do_not_match"
337			continue
338		fi
339
340		# Check for NULL entry
341		if [ ! "$_password1" ]; then
342			f_dialog_yesno \
343				"$msg_disable_password_auth_for_account" ||
344				continue
345			pw_password_disable=1
346		else
347			pw_password_disable=
348		fi
349
350		pw_password="$_password1"
351		break
352	done
353	save_flag=1
354
355	f_dprintf "pw_password: [%s]->[%s]" "$cur_pw_password" "$pw_password"
356
357	return $SUCCESS
358}
359
360# f_dialog_input_gecos [$gecos]
361#
362# Allow the user to enter new GECOS information for a given user. This
363# information is commonly used to store the ``Full Name'' of the user. If the
364# user does not cancel or press ESC, the $pw_gecos variable will hold the
365# newly-configured value upon return.
366#
367f_dialog_input_gecos()
368{
369	local _input="$1"
370
371	# Return if user has either pressed ESC or chosen Cancel/No
372	_input=$( f_dialog_input "$msg_full_name" "$_input" \
373	                         "$hline_alnum_punc_tab_enter"
374	        ) || return
375
376	pw_gecos="$_input"
377	save_flag=1
378
379	f_dprintf "pw_gecos: [%s]->[%s]" "$cur_pw_gecos" "$pw_gecos"
380
381	return $SUCCESS
382}
383
384# f_dialog_input_uid [$uid]
385#
386# Allow the user to enter a new UID for a given user. If the user does not
387# cancel or press ESC, the $pw_uid variable will hold the newly-configured
388# value upon return.
389#
390f_dialog_input_uid()
391{
392	local _input="$1"
393
394	# Return if user has either pressed ESC or chosen Cancel/No
395	_input=$( f_dialog_input "$msg_user_id_leave_empty_for_default" \
396	                         "$_input" "$hline_num_tab_enter"
397	        ) || return
398
399	pw_uid="$_input"
400	save_flag=1
401
402	f_dprintf "pw_uid: [%s]->[%s]" "$cur_pw_uid" "$pw_uid"
403
404	return $SUCCESS
405}
406
407# f_dialog_input_gid [$gid]
408#
409# Allow the user to enter a new primary GID for a given user. If the user does
410# not cancel or press ESC, the $pw_gid variable will hold the newly-configured
411# value upon return.
412#
413f_dialog_input_gid()
414{
415	local _input="$1"
416
417	# Return if user has either pressed ESC or chosen Cancel/No
418	_input=$( f_dialog_input "$msg_group_id_leave_empty_for_default" \
419	                         "$_input" "$hline_num_tab_enter"
420	        ) || return
421
422	pw_gid="$_input"
423	save_flag=1
424
425	f_dprintf "pw_gid: [%s]->[%s]" "$cur_pw_gid" "$pw_gid"
426
427	return $SUCCESS
428}
429
430# f_dialog_input_class [$class]
431#
432# Allow the user to enter a new login class for a given user. If the user does
433# not cancel or press ESC, the $pw_class variable will hold the newly-
434# configured value upon return.
435#
436f_dialog_input_class()
437{
438	local _input="$1"
439
440	# Return if user has either pressed ESC or chosen Cancel/No
441	_input=$( f_dialog_input "$msg_login_class" "$_input" \
442	                         "$hline_alnum_tab_enter"
443	        ) || return
444
445	pw_class="$_input"
446	save_flag=1
447
448	f_dprintf "pw_class: [%s]->[%s]" "$cur_pw_class" "$pw_class"
449
450	return $SUCCESS
451}
452
453# f_dialog_input_change [$seconds]
454#
455# Allow the user to enter a date/time (in number-of-seconds since the `epoch')
456# for when a given user's password must be changed. If the user does not cancel
457# or press ESC, the $pw_password_expire variable will hold the newly-
458# configured value upon return.
459#
460f_dialog_input_change()
461{
462	local calendar_size timebox_size
463	local msg menu_size size retval _input="$1"
464	local hline="$hline_num_arrows_tab_enter"
465
466	local menu_list="
467		'1' '$msg_password_does_not_expire'
468		'2' '$msg_edit_date_time_with_a_calendar'
469		'3' '$msg_enter_number_of_days_into_the_future'
470		'4' '$msg_enter_value_manually'
471	" # END-QUOTE
472
473	#
474	# Loop until the user provides taint-free/cancellation-free input
475	#
476	while :; do
477		msg="$msg_password_expires_on"
478		menu_size=$( eval f_dialog_menu_size \
479		        	\"\$DIALOG_TITLE\"     \
480		        	\"\$DIALOG_BACKTITLE\" \
481		        	\"\$msg\"              \
482		        	\"\$hline\"            \
483		        	$menu_list             )
484
485		local dialog_menu
486		dialog_menu=$( eval $DIALOG \
487			--title \"\$DIALOG_TITLE\"         \
488			--backtitle \"\$DIALOG_BACKTITLE\" \
489			--hline \"\$hline\"                \
490			--ok-label \"\$msg_ok\"            \
491			--cancel-label \"\$msg_cancel\"    \
492			--menu \"\$msg\" $menu_size        \
493			$menu_list                         \
494			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
495		)
496	        retval=$?
497		setvar DIALOG_MENU_$$ "$dialog_menu"
498	        date_type=$( f_dialog_menutag )
499	        f_dprintf "retval=%u date_type=[%s]" $retval "$date_type"
500
501		# Return if user has either pressed ESC or chosen Cancel/No
502		[ $retval -eq $SUCCESS ] || return $retval
503
504		case "$date_type" in
505		1) # Password does not expire
506			_input=""
507			break ;;
508
509		2) # Edit date/time with a calendar
510			local _input_date _input_time ret_date ret_time
511
512			local secs="$_input"
513			{ f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs=
514			_input_date=$( date -j -f "%s" -- "$secs" \
515			               		"+%d %m %Y" 2> /dev/null )
516			calendar_size=$( f_dialog_calendar_size \
517			                 	"$DIALOG_TITLE"     \
518			                 	"$DIALOG_BACKTITLE" \
519			                 	"$msg"              \
520			                 	"$hline"            )
521			local dialog_inputbox
522			dialog_inputbox=$( eval $DIALOG \
523				--title \"\$DIALOG_TITLE\"          \
524				--backtitle \"\$DIALOG_BACKTITLE\"  \
525				--hline \"\$hline\"                 \
526				--ok-label \"\$msg_ok\"             \
527				--cancel-label \"\$msg_cancel\"     \
528				--calendar \"\$msg\" $calendar_size \
529				$_input_date                        \
530				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
531			)
532			retval=$?
533			setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox"
534			ret_date=$( f_dialog_inputstr )
535			f_dprintf "retval=%u ret_date=[%s]" $retval "$ret_date"
536
537			# Return to menu if either ESC or Cancel/No
538			[ $retval -eq $SUCCESS ] || continue
539
540			_input_time=
541			[ "$secs" ] && _input_time=$( date -j \
542				-f %s -- "$_input" "+%H %M %S" 2> /dev/null )
543			timebox_size=$( f_dialog_timebox_size \
544			                	"$DIALOG_TITLE"     \
545			                	"$DIALOG_BACKTITLE" \
546			                	"$msg"              \
547			                	"$hline"            )
548			local dialog_inputbox
549			dialog_inputbox=$( eval $DIALOG \
550				--title \"\$DIALOG_TITLE\"         \
551				--backtitle \"\$DIALOG_BACKTITLE\" \
552				--hline \"\$hline\"                \
553				--ok-label \"\$msg_ok\"            \
554				--cancel-label \"\$msg_cancel\"    \
555				--timebox \"\$msg\" $timebox_size  \
556				$_input_time                       \
557				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
558			)
559			retval=$?
560			setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox"
561			ret_time=$( f_dialog_inputstr )
562			f_dprintf "retval=%u ret_time=[%s]" $retval "$ret_time"
563
564			# Return to menu if either ESC or Cancel/No
565			[ $retval -eq $SUCCESS ] || continue
566
567			_input=$( date \
568			          	-j -f "%d/%m/%Y %T" \
569			          	-- "$ret_date $ret_time" \
570			          	+%s 2> /dev/null )
571			f_dprintf "_input=[%s]" "$_input"
572			break ;;
573
574		3) # Enter number of days into the future
575			local ret_days seconds="$( date +%s )"
576
577			f_isinteger "$_input" || _input=0
578			[ $_input -gt 0 -a $_input -gt $seconds ] &&
579				ret_days=$(( ( $_input - $seconds ) / 86400 ))
580			f_isinteger "$ret_days" &&
581				ret_days=$(( $ret_days + 1 ))
582
583			# Return to menu if either ESC or Cancel/No
584			ret_days=$( f_dialog_input \
585				"$msg_password_expires_in_how_many_days" \
586				"$ret_days" "$hline"
587			) || continue
588
589			# Taint-check the user's input
590			if ! f_isinteger "$ret_days"; then
591				f_show_msg "$msg_invalid_number_of_days"
592				continue
593			fi
594
595			f_dprintf "ret_days=[%s]" "$ret_days"
596			case "$ret_days" in
597			[-+]*) _input=$( date -v${ret_days}d +%s );;
598			    0) _input=$( date +%s );;
599			    *) _input=$( date -v+${ret_days}d +%s );;
600			esac
601			f_dprintf "_input=[%s]" "$_input"
602			break ;;
603
604		4) # Enter value manually
605			local ret_secs
606
607			msg=$( printf "$msg_number_of_seconds_since_epoch" \
608			              "$( date -r 1 "+%c %Z" )" )
609
610			# Return to menu if either ESC or Cancel/No
611			ret_secs=$( f_dialog_input \
612			            	"$msg" "$_input" "$hline" ) || continue
613
614			_input="$ret_secs"
615
616			# Taint-check the user's input
617			if ! f_isinteger "${_input:-0}"; then
618				f_show_msg "$msg_invalid_number_of_seconds"
619				continue
620			fi
621
622			f_dprintf "_input=[%s]" "$_input"
623			break ;;
624
625		esac
626
627	done # Loop forever
628
629	pw_password_expire="$_input"
630	save_flag=1
631
632	f_dprintf "pw_password_expire: [%s]->[%s]" \
633	          "$cur_pw_password_expire" "$pw_password_expire"
634
635	return $SUCCESS
636}
637
638# f_dialog_input_expire [$seconds]
639#
640# Allow the user to enter a date/time (in number-of-seconds since the `epoch')
641# for when a given user's account should become expired. If the user does not
642# cancel or press ESC, the $pw_account_expire variable will hold the newly-
643# configured value upon return.
644#
645f_dialog_input_expire()
646{
647	local calendar_size timebox_size
648	local msg menu_size size retval _input="$1"
649	local hline="$hline_num_arrows_tab_enter"
650
651	local menu_list="
652		'1' '$msg_account_does_not_expire'
653		'2' '$msg_edit_date_time_with_a_calendar'
654		'3' '$msg_enter_number_of_days_into_the_future'
655		'4' '$msg_enter_value_manually'
656	" # END-QUOTE
657
658	#
659	# Loop until the user provides taint-free/cancellation-free input
660	#
661	while :; do
662		msg="$msg_account_expires_on"
663		menu_size=$( eval f_dialog_menu_size \
664		        	\"\$DIALOG_TITLE\"     \
665		        	\"\$DIALOG_BACKTITLE\" \
666		        	\"\$msg\"              \
667		        	\"\$hline\"            \
668		        	$menu_list             )
669
670		local dialog_menu
671		dialog_menu=$( eval $DIALOG \
672			--title \"\$DIALOG_TITLE\"         \
673			--backtitle \"\$DIALOG_BACKTITLE\" \
674			--hline \"\$hline\"                \
675			--ok-label \"\$msg_ok\"            \
676			--cancel-label \"\$msg_cancel\"    \
677			--menu \"\$msg\" $menu_size        \
678			$menu_list                         \
679			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
680		)
681	        retval=$?
682		setvar DIALOG_MENU_$$ "$dialog_menu"
683	        date_type=$( f_dialog_menutag )
684	        f_dprintf "retval=%u date_type=[%s]" $retval "$date_type"
685
686		# Return if user has either pressed ESC or chosen Cancel/No
687		[ $retval -eq $SUCCESS ] || return $retval
688
689		case "$date_type" in
690		1) # Account does not expire
691			_input=""
692			break ;;
693
694		2) # Edit date/time with a calendar
695			local _input_date _input_time ret_date ret_time
696
697			local secs="$_input"
698			{ f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs=
699			_input_date=$( date -j -f "%s" -- "$secs" \
700			               		"+%d %m %Y" 2> /dev/null )
701			calendar_size=$( f_dialog_calendar_size \
702			                 	"$DIALOG_TITLE"     \
703			                 	"$DIALOG_BACKTITLE" \
704			                 	"$msg"              \
705			                 	"$hline"            )
706			local dialog_inputbox
707			dialog_inputbox=$( eval $DIALOG \
708				--title \"\$DIALOG_TITLE\"          \
709				--backtitle \"\$DIALOG_BACKTITLE\"  \
710				--hline \"\$hline\"                 \
711				--ok-label \"\$msg_ok\"             \
712				--cancel-label \"\$msg_cancel\"     \
713				--calendar \"\$msg\" $calendar_size \
714				$_input_date                        \
715				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
716			)
717			retval=$?
718			setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox"
719			ret_date=$( f_dialog_inputstr )
720			f_dprintf "retval=%u ret_date=[%s]" $retval "$ret_date"
721
722			# Return to menu if either ESC or Cancel/No
723			[ $retval -eq $SUCCESS ] || continue
724
725			_input_time=
726			[ "$secs" ] && _input_time=$( date -j \
727				-f %s -- "$_input" "+%H %M %S" 2> /dev/null )
728			timebox_size=$( f_dialog_timebox_size \
729			                	"$DIALOG_TITLE"     \
730			                	"$DIALOG_BACKTITLE" \
731			                	"$msg"              \
732			                	"$hline"            )
733			dialog_inputbox=$( eval $DIALOG \
734				--title \"\$DIALOG_TITLE\"         \
735				--backtitle \"\$DIALOG_BACKTITLE\" \
736				--hline \"\$hline\"                \
737				--ok-label \"\$msg_ok\"            \
738				--cancel-label \"\$msg_cancel\"    \
739				--timebox \"\$msg\" $timebox_size  \
740				$_input_time                       \
741				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
742			)
743			retval=$?
744			setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox"
745			ret_time=$( f_dialog_inputstr )
746			f_dprintf "retval=%u ret_time=[%s]" $retval "$ret_time"
747
748			# Return to menu if either ESC or Cancel/No
749			[ $retval -eq $SUCCESS ] || continue
750
751			_input=$( date \
752			          	-j -f "%d/%m/%Y %T" \
753			          	-- "$ret_date $ret_time" \
754			          	+%s 2> /dev/null )
755			f_dprintf "_input=[%s]" "$_input"
756			break ;;
757
758		3) # Enter number of days into the future
759			local ret_days seconds="$( date +%s )"
760
761			f_isinteger "$_input" || _input=0
762			[ $_input -gt 0 -a $_input -gt $seconds ] &&
763				ret_days=$(( ( $_input - $seconds ) / 86400 ))
764			f_isinteger "$ret_days" &&
765				ret_days=$(( $ret_days + 1 ))
766
767			# Return to menu if either ESC or Cancel/No
768			ret_days=$( f_dialog_input \
769				"$msg_account_expires_in_how_many_days" \
770				"$ret_days" "$hline"
771			) || continue
772
773			# Taint-check the user's input
774			if ! f_isinteger "$ret_days"; then
775				f_show_msg "$msg_invalid_number_of_days"
776				continue
777			fi
778
779			f_dprintf "ret_days=[%s]" "$ret_days"
780			case "$ret_days" in
781			[-+]*) _input=$( date -v${ret_days}d +%s );;
782			    0) _input=$( date +%s );;
783			    *) _input=$( date -v+${ret_days}d +%s );;
784			esac
785			f_dprintf "_input=[%s]" "$_input"
786			break ;;
787
788		4) # Enter value manually
789			local ret_secs
790
791			msg=$( printf "$msg_number_of_seconds_since_epoch" \
792			              "$( date -r 1 "+%c %Z" )" )
793
794			# Return to menu if either ESC or Cancel/No
795			ret_secs=$( f_dialog_input \
796			            	"$msg" "$_input" "$hline" ) || continue
797
798			_input="$ret_secs"
799
800			# Taint-check the user's input
801			if ! f_isinteger "${_input:-0}"; then
802				f_show_msg "$msg_invalid_number_of_seconds"
803				continue
804			fi
805
806			f_dprintf "_input=[%s]" "$_input"
807			break ;;
808
809		esac
810
811	done # Loop forever
812
813	pw_account_expire="$_input"
814	save_flag=1
815
816	f_dprintf "pw_account_expire: [%s]->[%s]" \
817	          "$cur_pw_account_expire" "$pw_account_expire"
818
819	return $SUCCESS
820}
821
822# f_dialog_input_home_dir [$home_dir]
823#
824# Allow the user to enter a new home directory for a given user. If the user
825# does not cancel or press ESC, the $pw_home_dir variable will hold the newly-
826# configured value upon return.
827#
828f_dialog_input_home_dir()
829{
830	local _input="$1"
831
832	# Return if user has either pressed ESC or chosen Cancel/No
833	_input=$( f_dialog_input "$msg_home_directory" "$_input" \
834	                         "$hline_alnum_punc_tab_enter"
835	        ) || return
836
837	pw_home_dir="$_input"
838	save_flag=1
839
840	f_dprintf "pw_home_dir: [%s]->[%s]" "$cur_pw_home_dir" "$pw_home_dir"
841
842	return $SUCCESS
843}
844
845# f_dialog_input_home_create
846#
847# Prompt the user to confirm creation of a given user's home directory. If the
848# user does not cancel (by choosing "No") or press ESC, the $pw_home_create
849# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return
850# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible.
851#
852f_dialog_input_home_create()
853{
854	local retval
855
856	f_dialog_yesno "$msg_create_home_directory"
857	retval=$?
858
859	if [ $retval -eq $SUCCESS ]; then
860		pw_home_create="$msg_yes"
861	else
862		pw_home_create="$msg_no"
863	fi
864	save_flag=1
865
866	f_dprintf "pw_home_create: [%s]->[%s]" \
867	          "$cur_pw_home_create" "$pw_home_create"
868
869	[ $retval -ne 255 ] # return failure if user pressed ESC
870}
871
872# f_dialog_input_group_delete
873#
874# Prompt the user to confirm deletion of a given user's primary group. If the
875# user does not cancel (by choosing "No") or press ESC, the $pw_group_delete
876# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return
877# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible.
878#
879f_dialog_input_group_delete()
880{
881	local retval
882
883	if f_isinteger "$pw_gid"; then
884		if [ $pw_gid -lt 1000 ]; then
885			f_dialog_noyes "$msg_delete_primary_group"
886		else
887			f_dialog_yesno "$msg_delete_primary_group"
888		fi
889	elif [ "$pw_gid" ]; then
890		local gid=0
891		gid=$( pw groupshow "$pw_gid" | awk -F: '{print $3}' )
892		if f_isinteger "$gid" && [ $gid -lt 1000 ]; then
893			f_dialog_noyes "$msg_delete_primary_group"
894		else
895			f_dialog_yesno "$msg_delete_primary_group"
896		fi
897	else
898		f_dialog_yesno "$msg_delete_primary_group"
899	fi
900	retval=$?
901
902	if [ $retval -eq $SUCCESS ]; then
903		pw_group_delete="$msg_yes"
904	else
905		pw_group_delete="$msg_no"
906	fi
907	save_flag=1
908
909	f_dprintf "pw_group_delete: [%s]->[%s]" \
910	          "$cur_pw_group_delete" "$pw_group_delete"
911
912	[ $retval -ne 255 ] # return failure if user pressed ESC
913}
914
915# f_dialog_input_home_delete
916#
917# Prompt the user to confirm deletion of a given user's home directory. If the
918# user does not cancel (by choosing "No") or press ESC, the $pw_home_delete
919# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return
920# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible.
921#
922f_dialog_input_home_delete()
923{
924	local retval
925
926	f_dialog_yesno "$msg_delete_home_directory"
927	retval=$?
928
929	if [ $retval -eq $SUCCESS ]; then
930		pw_home_delete="$msg_yes"
931	else
932		pw_home_delete="$msg_no"
933	fi
934	save_flag=1
935
936	f_dprintf "pw_home_delete: [%s]->[%s]" \
937	          "$cur_pw_home_delete" "$pw_home_delete"
938
939	[ $retval -ne 255 ] # return failure if user pressed ESC
940}
941
942# f_dialog_input_dotfiles_create
943#
944# Prompt the user to confirm population of a given user's home directory with
945# sample dotfiles. If the user does not cancel (by choosing "No") or press ESC,
946# the $pw_dotfiles_create variable will hold $msg_yes upon return, otherwise
947# $msg_no. Use these return variables ($msg_yes and $msg_no) for comparison to
948# be i18n-compatible.
949#
950f_dialog_input_dotfiles_create()
951{
952	local retval
953
954	f_dialog_yesno "$msg_create_dotfiles"
955	retval=$?
956
957	if [ $retval -eq $SUCCESS ]; then
958		pw_dotfiles_create="$msg_yes"
959	else
960		pw_dotfiles_create="$msg_no"
961	fi
962	save_flag=1
963
964	f_dprintf "pw_dotfiles_create: [%s]->[%s]" \
965	          "$cur_pw_dotfiles_create" "$pw_dotfiles_create"
966
967	[ $retval -ne 255 ] # return failure if user pressed ESC
968}
969
970# f_dialog_input_shell [$shell]
971#
972# Allow the user to select a new login shell for a given user. If the user does
973# not cancel or press ESC, the $pw_home_dir variable will hold the newly-
974# configured value upon return.
975#
976#
977f_dialog_input_shell()
978{
979	local size retval shells shell_list _input="$1"
980	local hline="$hline_arrows_space_tab_enter"
981	local prompt="$msg_select_login_shell"
982
983	shells=$( awk '!/^[[:space:]]*(#|$)/{print}' "$ETC_SHELLS" )
984	shell_list=$(
985		for shell in $shells; do
986			if [ "$shell" = "$_input" ]; then
987				echo "'$shell' '' 'on'"
988			else
989				echo "'$shell' '' 'off'"
990			fi
991		done
992	)
993
994	size=$( eval f_dialog_radiolist_size \
995	        	\"\$DIALOG_TITLE\"     \
996	        	\"\$DIALOG_BACKTITLE\" \
997	        	\"\$prompt\"           \
998	        	\"\$hline\"            \
999	        	$shell_list            )
1000
1001	local dialog_inputbox
1002	dialog_inputbox=$( eval $DIALOG \
1003		--title \"\$DIALOG_TITLE\"         \
1004		--backtitle \"\$DIALOG_BACKTITLE\" \
1005		--hline \"\$hline\"                \
1006		--ok-label \"\$msg_ok\"            \
1007		--cancel-label \"\$msg_cancel\"    \
1008		--radiolist \"\$prompt\" $size     \
1009		$shell_list                        \
1010		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
1011	)
1012
1013	retval=$?
1014	setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox"
1015	_input=$( f_dialog_inputstr )
1016
1017	# Return if user has either pressed ESC or chosen Cancel/No
1018	[ $retval -eq $SUCCESS ] || return $retval
1019
1020	pw_shell="$_input"
1021	save_flag=1
1022
1023	f_dprintf "pw_shell: [%s]->[%s]" "$cur_pw_shell" "$pw_shell"
1024
1025	return $SUCCESS
1026}
1027
1028fi # ! $_USERMGMT_USER_INPUT_SUBR
1029