xref: /freebsd/usr.sbin/bsdconfig/usermgmt/share/user_input.subr (revision a98ff317388a00b992f1bf8404dee596f9383f5e)
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_show_msg "$msg_login_is_empty"
246			continue
247		fi
248
249		# Check for invalid entry
250		if ! echo "$_input" | grep -q "^[[:alpha:]]"; then
251			f_show_msg "$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_show_msg "$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_show_msg "$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_show_msg "$msg_invalid_number_of_seconds"
614				continue
615			fi
616
617			f_dprintf "_input=[%s]" "$_input"
618			break ;;
619
620		esac
621
622	done # Loop forever
623
624	pw_password_expire="$_input"
625	save_flag=1
626
627	f_dprintf "pw_password_expire: [%s]->[%s]" \
628	          "$cur_pw_password_expire" "$pw_password_expire"
629
630	return $SUCCESS
631}
632
633# f_dialog_input_expire_account [$seconds]
634#
635# Allow the user to enter a date/time (in number-of-seconds since the `epoch')
636# for when a given user's account should become expired. If the user does not
637# cancel or press ESC, the $pw_account_expire variable will hold the newly-
638# configured value upon return.
639#
640f_dialog_input_expire_account()
641{
642	local prompt="$msg_account_expires_on"
643	local menu_list="
644		'1' '$msg_account_does_not_expire'
645		'2' '$msg_edit_date_time_with_a_calendar'
646		'3' '$msg_enter_number_of_days_into_the_future'
647		'4' '$msg_enter_value_manually'
648	" # END-QUOTE
649	local hline="$hline_num_arrows_tab_enter"
650	local retval _input="$1"
651
652	local mheight mwidth mrows
653	eval f_dialog_menu_size mheight mwidth mrows \
654	                        \"\$DIALOG_TITLE\"     \
655	                        \"\$DIALOG_BACKTITLE\" \
656	                        \"\$prompt\"           \
657	                        \"\$hline\"            \
658	                        $menu_list
659	local cheight cwidth
660	f_dialog_calendar_size cheight cwidth \
661	                       "$DIALOG_TITLE"     \
662	                       "$DIALOG_BACKTITLE" \
663	                       "$prompt"           \
664	                       "$hline"
665	local theight twidth
666	f_dialog_timebox_size theight twidth \
667	                      "$DIALOG_TITLE"     \
668	                      "$DIALOG_BACKTITLE" \
669	                      "$prompt"           \
670	                      "$hline"
671
672	#
673	# Loop until the user provides taint-free/cancellation-free input
674	#
675	local date_type defaultitem=
676	while :; do
677		date_type=$( eval $DIALOG \
678			--title \"\$DIALOG_TITLE\"         \
679			--backtitle \"\$DIALOG_BACKTITLE\" \
680			--hline \"\$hline\"                \
681			--default-item \"\$defaultitem\"   \
682			--ok-label \"\$msg_ok\"            \
683			--cancel-label \"\$msg_cancel\"    \
684			--menu \"\$prompt\"                \
685			$mheight $mwidth $mrows            \
686			$menu_list                         \
687			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
688		)
689		retval=$?
690		f_dialog_data_sanitize date_type
691		defaultitem="$date_type"
692		f_dprintf "retval=%u date_type=[%s]" $retval "$date_type"
693
694		# Return if user has either pressed ESC or chosen Cancel/No
695		[ $retval -eq $SUCCESS ] || return $retval
696
697		case "$date_type" in
698		1) # Account does not expire
699			_input=""
700			break ;;
701
702		2) # Edit date/time with a calendar
703			local _input_date _input_time ret_date ret_time
704
705			local secs="$_input"
706			{ f_isinteger "$secs" && [ $secs -gt 0 ]; } || secs=
707			_input_date=$( date -j -f "%s" -- "$secs" \
708			               		"+%d %m %Y" 2> /dev/null )
709			ret_date=$( 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 \"\$prompt\"             \
716				$cheight $cwidth                    \
717				$_input_date                        \
718				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
719			)
720			retval=$?
721			f_dialog_data_sanitize ret_date
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			ret_time=$( eval $DIALOG \
731				--title \"\$DIALOG_TITLE\"         \
732				--backtitle \"\$DIALOG_BACKTITLE\" \
733				--hline \"\$hline\"                \
734				--ok-label \"\$msg_ok\"            \
735				--cancel-label \"\$msg_cancel\"    \
736				--timebox \"\$prompt\"             \
737				$theight $twidth                   \
738				$_input_time                       \
739				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
740			)
741			retval=$?
742			f_dialog_data_sanitize ret_time
743			f_dprintf "retval=%u ret_time=[%s]" $retval "$ret_time"
744
745			# Return to menu if either ESC or Cancel/No
746			[ $retval -eq $SUCCESS ] || continue
747
748			_input=$( date \
749			          	-j -f "%d/%m/%Y %T" \
750			          	-- "$ret_date $ret_time" \
751			          	+%s 2> /dev/null )
752			f_dprintf "_input=[%s]" "$_input"
753			break ;;
754
755		3) # Enter number of days into the future
756			local ret_days seconds="$( date +%s )"
757
758			f_isinteger "$_input" || _input=0
759			[ $_input -gt 0 -a $_input -gt $seconds ] &&
760				ret_days=$(( ( $_input - $seconds ) / 86400 ))
761			f_isinteger "$ret_days" &&
762				ret_days=$(( $ret_days + 1 ))
763
764			# Return to menu if either ESC or Cancel/No
765			f_dialog_input ret_days \
766				"$msg_account_expires_in_how_many_days" \
767				"$ret_days" "$hline" || continue
768
769			# Taint-check the user's input
770			if ! f_isinteger "$ret_days"; then
771				f_show_msg "$msg_invalid_number_of_days"
772				continue
773			fi
774
775			f_dprintf "ret_days=[%s]" "$ret_days"
776			case "$ret_days" in
777			[-+]*) _input=$( date -v${ret_days}d +%s ) ;;
778			    0) _input=$( date +%s ) ;;
779			    *) _input=$( date -v+${ret_days}d +%s ) ;;
780			esac
781			f_dprintf "_input=[%s]" "$_input"
782			break ;;
783
784		4) # Enter value manually
785			local msg ret_secs
786			msg=$( printf "$msg_number_of_seconds_since_epoch" \
787			              "$( date -r 1 "+%c %Z" )" )
788
789			# Return to menu if either ESC or Cancel/No
790			f_dialog_input ret_secs "$msg" \
791			               "$_input" "$hline" || continue
792
793			_input="$ret_secs"
794
795			# Taint-check the user's input
796			if ! f_isinteger "${_input:-0}"; then
797				f_show_msg "$msg_invalid_number_of_seconds"
798				continue
799			fi
800
801			f_dprintf "_input=[%s]" "$_input"
802			break ;;
803
804		esac
805
806	done # Loop forever
807
808	pw_account_expire="$_input"
809	save_flag=1
810
811	f_dprintf "pw_account_expire: [%s]->[%s]" \
812	          "$cur_pw_account_expire" "$pw_account_expire"
813
814	return $SUCCESS
815}
816
817# f_dialog_input_home_dir [$home_dir]
818#
819# Allow the user to enter a new home directory for a given user. If the user
820# does not cancel or press ESC, the $pw_home_dir variable will hold the newly-
821# configured value upon return.
822#
823f_dialog_input_home_dir()
824{
825	local _input="$1"
826
827	# Return if user has either pressed ESC or chosen Cancel/No
828	f_dialog_input _input "$msg_home_directory" "$_input" \
829	               "$hline_alnum_punc_tab_enter" || return
830
831	pw_home_dir="$_input"
832	save_flag=1
833
834	f_dprintf "pw_home_dir: [%s]->[%s]" "$cur_pw_home_dir" "$pw_home_dir"
835
836	return $SUCCESS
837}
838
839# f_dialog_input_home_create
840#
841# Prompt the user to confirm creation of a given user's home directory. If the
842# user does not cancel (by choosing "No") or press ESC, the $pw_home_create
843# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return
844# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible.
845#
846f_dialog_input_home_create()
847{
848	local retval
849
850	f_dialog_yesno "$msg_create_home_directory"
851	retval=$?
852
853	if [ $retval -eq $SUCCESS ]; then
854		pw_home_create="$msg_yes"
855	else
856		pw_home_create="$msg_no"
857	fi
858	save_flag=1
859
860	f_dprintf "pw_home_create: [%s]->[%s]" \
861	          "$cur_pw_home_create" "$pw_home_create"
862
863	[ $retval -ne 255 ] # return failure if user pressed ESC
864}
865
866# f_dialog_input_group_delete
867#
868# Prompt the user to confirm deletion of a given user's primary group. If the
869# user does not cancel (by choosing "No") or press ESC, the $pw_group_delete
870# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return
871# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible.
872#
873f_dialog_input_group_delete()
874{
875	local retval
876
877	if f_isinteger "$pw_gid"; then
878		if [ $pw_gid -lt 1000 ]; then
879			f_dialog_noyes "$msg_delete_primary_group"
880		else
881			f_dialog_yesno "$msg_delete_primary_group"
882		fi
883	elif [ "$pw_gid" ]; then
884		local gid=0
885		gid=$( pw groupshow "$pw_gid" | awk -F: '{print $3}' )
886		if f_isinteger "$gid" && [ $gid -lt 1000 ]; then
887			f_dialog_noyes "$msg_delete_primary_group"
888		else
889			f_dialog_yesno "$msg_delete_primary_group"
890		fi
891	else
892		f_dialog_yesno "$msg_delete_primary_group"
893	fi
894	retval=$?
895
896	if [ $retval -eq $SUCCESS ]; then
897		pw_group_delete="$msg_yes"
898	else
899		pw_group_delete="$msg_no"
900	fi
901	save_flag=1
902
903	f_dprintf "pw_group_delete: [%s]->[%s]" \
904	          "$cur_pw_group_delete" "$pw_group_delete"
905
906	[ $retval -ne 255 ] # return failure if user pressed ESC
907}
908
909# f_dialog_input_home_delete
910#
911# Prompt the user to confirm deletion of a given user's home directory. If the
912# user does not cancel (by choosing "No") or press ESC, the $pw_home_delete
913# variable will hold $msg_yes upon return, otherwise $msg_no. Use these return
914# variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible.
915#
916f_dialog_input_home_delete()
917{
918	local retval
919
920	f_dialog_yesno "$msg_delete_home_directory"
921	retval=$?
922
923	if [ $retval -eq $SUCCESS ]; then
924		pw_home_delete="$msg_yes"
925	else
926		pw_home_delete="$msg_no"
927	fi
928	save_flag=1
929
930	f_dprintf "pw_home_delete: [%s]->[%s]" \
931	          "$cur_pw_home_delete" "$pw_home_delete"
932
933	[ $retval -ne 255 ] # return failure if user pressed ESC
934}
935
936# f_dialog_input_dotfiles_create
937#
938# Prompt the user to confirm population of a given user's home directory with
939# sample dotfiles. If the user does not cancel (by choosing "No") or press ESC,
940# the $pw_dotfiles_create variable will hold $msg_yes upon return, otherwise
941# $msg_no. Use these return variables ($msg_yes and $msg_no) for comparison to
942# be i18n-compatible.
943#
944f_dialog_input_dotfiles_create()
945{
946	local retval
947
948	f_dialog_yesno "$msg_create_dotfiles"
949	retval=$?
950
951	if [ $retval -eq $SUCCESS ]; then
952		pw_dotfiles_create="$msg_yes"
953	else
954		pw_dotfiles_create="$msg_no"
955	fi
956	save_flag=1
957
958	f_dprintf "pw_dotfiles_create: [%s]->[%s]" \
959	          "$cur_pw_dotfiles_create" "$pw_dotfiles_create"
960
961	[ $retval -ne 255 ] # return failure if user pressed ESC
962}
963
964# f_dialog_input_shell [$shell]
965#
966# Allow the user to select a new login shell for a given user. If the user does
967# not cancel or press ESC, the $pw_home_dir variable will hold the newly-
968# configured value upon return.
969#
970#
971f_dialog_input_shell()
972{
973	local _input="$1"
974	local prompt="$msg_select_login_shell"
975	local radio_list= # Calculated below
976	local hline="$hline_arrows_space_tab_enter"
977
978	#
979	# Generate the radiolist of shells
980	#
981	local shell
982	for shell in $( awk '!/^[[:space:]]*(#|$)/{print}' "$ETC_SHELLS" ); do
983		# Format of a radiolist menu entry is "tag item status"
984		if [ "$shell" = "$_input" ]; then
985			radio_list="$radio_list '$shell' '' 'on'"
986		else
987			radio_list="$radio_list '$shell' '' 'off'"
988		fi
989	done
990
991	local height width rows
992	eval f_dialog_radiolist_size height width rows \
993	                             \"\$DIALOG_TITLE\"     \
994	                             \"\$DIALOG_BACKTITLE\" \
995	                             \"\$prompt\"           \
996	                             \"\$hline\"            \
997	                             $radio_list
998
999	_input=$( eval $DIALOG \
1000		--title \"\$DIALOG_TITLE\"         \
1001		--backtitle \"\$DIALOG_BACKTITLE\" \
1002		--hline \"\$hline\"                \
1003		--ok-label \"\$msg_ok\"            \
1004		--cancel-label \"\$msg_cancel\"    \
1005		--radiolist \"\$prompt\"           \
1006		$height $width $rows               \
1007		$radio_list                        \
1008		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
1009	) || return $?
1010		# Return if user either pressed ESC or chose Cancel/No
1011	f_dialog_data_sanitize _input
1012
1013	pw_shell="$_input"
1014	save_flag=1
1015
1016	f_dprintf "pw_shell: [%s]->[%s]" "$cur_pw_shell" "$pw_shell"
1017
1018	return $SUCCESS
1019}
1020
1021############################################################ MAIN
1022
1023f_dprintf "%s: Successfully loaded." usermgmt/user_input.subr
1024
1025fi # ! $_USERMGMT_USER_INPUT_SUBR
1026