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