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