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