1if [ ! "$_USERMGMT_GROUP_INPUT_SUBR" ]; then _USERMGMT_GROUP_INPUT_SUBR=1
2#
3# Copyright (c) 2012 Ron McDowell
4# Copyright (c) 2012-2014 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#
29############################################################ INCLUDES
30
31BSDCFG_SHARE="/usr/share/bsdconfig"
32. $BSDCFG_SHARE/common.subr || exit 1
33f_dprintf "%s: loading includes..." usermgmt/group_input.subr
34f_include $BSDCFG_SHARE/dialog.subr
35f_include $BSDCFG_SHARE/strings.subr
36
37BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt"
38f_include_lang $BSDCFG_LIBE/include/messages.subr
39f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr
40
41############################################################ FUNCTIONS
42
43# f_input_group $group
44#
45# Given $group name or id, create the environment variables group_name,
46# group_gid, and group_members (and group_password is reset to NULL).
47#
48f_input_group()
49{
50	local funcname=f_input_group
51	local group="$1"
52
53	f_dprintf "$funcname: Getting info for group \`%s'" "$group"
54	eval "$( pw groupshow "$group" 2> /dev/null | awk -F: '
55	function set_value(var, value) {
56		gsub(/'\''/, "'\''\\'\'\''", value)
57		printf "group_%s='\'%s\''\n", var, value
58	}
59	{
60		found = $1 != ""
61		set_value("name",     $1)
62		set_value("password", "")
63		set_value("gid",      $3)
64		set_value("members",  $4)
65		exit
66	}
67	END { if (!found) print "false" }' )"
68}
69
70# f_dialog_menu_group_list [$default]
71#
72# Allows the user to select a group from a list. Optionally, if present and
73# non-NULL, initially highlight $default group.
74#
75f_dialog_menu_group_list()
76{
77	local prompt=
78	local menu_list="
79		'X $msg_exit' ''
80	" # END-QUOTE
81	local defaultitem="$1"
82	local hline="$hline_alnum_punc_tab_enter"
83
84	# Add groups from group(5)
85	menu_list="$menu_list $( pw groupshow -a | awk -F: '
86		function mprint(tag, item) {
87			gsub(/'\''/, "'\''\\'\'\''", tag)
88			gsub(/'\''/, "'\''\\'\'\''", item)
89			printf "'\'%s\'\ \'%s\''\n", tag, item
90		}
91		!/^[[:space:]]*(#|$)/ { mprint($1, $1) }
92	' )"
93
94	local height width rows
95	eval f_dialog_menu_size height width rows \
96	                        \"\$DIALOG_TITLE\"     \
97	                        \"\$DIALOG_BACKTITLE\" \
98	                        \"\$prompt\"           \
99	                        \"\$hline\"            \
100	                        $menu_list
101
102	local menu_choice
103	menu_choice=$( eval $DIALOG \
104		--title \"\$DIALOG_TITLE\"         \
105		--backtitle \"\$DIALOG_BACKTITLE\" \
106		--hline \"\$hline\"                \
107		--ok-label \"\$msg_ok\"            \
108		--cancel-label \"\$msg_cancel\"    \
109		--default-item \"\$defaultitem\"   \
110		--menu \"\$prompt\"                \
111		$height $width $rows               \
112		$menu_list                         \
113		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
114	)
115	local retval=$?
116	f_dialog_menutag_store -s "$menu_choice"
117	return $retval
118}
119
120# f_dialog_input_group_name $var_to_set [$group_name]
121#
122# Allows the user to enter a name for a new group. If the user does not cancel
123# or press ESC, the $var_to_set variable will hold the newly-configured value
124# upon return.
125#
126f_dialog_input_group_name()
127{
128	local __var_to_set="$1" __name="$2"
129
130	#
131	# Loop until the user provides taint-free/valid input
132	#
133	local __input="$__name"
134	while :; do
135		# Return if user has either pressed ESC or chosen Cancel/No
136		f_dialog_input __input "$msg_group" "$__input" \
137		               "$hline_alnum_tab_enter" || return $?
138
139		# Check for no-change
140		if [ "$__input" = "$__name" ]; then
141			setvar "$__var_to_set" "$__input"
142			return $DIALOG_OK
143		fi
144
145		# Check for NULL entry
146		if [ ! "$__input" ]; then
147			f_show_msg "$msg_group_is_empty"
148			continue
149		fi
150
151		# Check for invalid entry
152		case "$__input" in [!a-zA-Z]*)
153			f_show_msg "$msg_group_must_start_with_letter"
154			continue
155		esac
156
157		# Check for duplicate entry
158		if f_quietly pw groupshow -n "$__input"; then
159			f_show_msg "$msg_group_already_used" "$__input"
160			continue
161		fi
162
163		setvar "$__var_to_set" "$__input"
164		break
165	done
166
167	return $DIALOG_OK
168}
169
170# f_dialog_input_group_password $var_to_set $dvar_to_set
171#
172# Prompt the user to enter a password (twice). If the user does not cancel or
173# press ESC, $var_to_set will hold the confirmed user entry. Otherwise, if the
174# user cancels or enters a NULL password (twice), they are given the choice to
175# disable password authentication for the given group, wherein $dvar_to_set has
176# a value of 1 to indicate password authentication should be disabled.
177#
178f_dialog_input_group_password()
179{
180	local __var_to_set="$1" __dvar_to_set="$2"
181	local __prompt1="$msg_group_password"
182	local __prompt2="$msg_reenter_group_password"
183	local __hline="$hline_alnum_punc_tab_enter"
184
185	local __height1 __width1
186	f_dialog_inputbox_size __height1 __width1 \
187	        	"$DIALOG_TITLE"     \
188	        	"$DIALOG_BACKTITLE" \
189	        	"$__prompt1"        \
190	        	""                  \
191	        	"$__hline"
192
193	local __height2 __width2
194	f_dialog_inputbox_size __height2 __width2 \
195	        	"$DIALOG_TITLE"     \
196	        	"$DIALOG_BACKTITLE" \
197	        	"$__prompt2"        \
198	        	""                  \
199	        	"$__hline"
200
201	#
202	# Loop until the user provides taint-free/valid input
203	#
204	local __retval __password1 __password2
205	while :; do
206		__password1=$( $DIALOG \
207			--title "$DIALOG_TITLE"         \
208			--backtitle "$DIALOG_BACKTITLE" \
209			--hline "$__hline"              \
210			--ok-label "$msg_ok"            \
211			--cancel-label "$msg_cancel"    \
212			--insecure                      \
213			--passwordbox "$__prompt1"      \
214			$__height1 $__width1            \
215			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
216		)
217		__retval=$?
218		debug= f_dialog_line_sanitize __password1
219
220		# Return if user has either pressed ESC or chosen Cancel/No
221		[ $__retval -eq $DIALOG_OK ] || return $__retval
222
223		__password2=$( $DIALOG \
224			--title "$DIALOG_TITLE"         \
225			--backtitle "$DIALOG_BACKTITLE" \
226			--hline "$__hline"              \
227			--ok-label "$msg_ok"            \
228			--cancel-label "$msg_cancel"    \
229			--insecure                      \
230			--passwordbox "$__prompt2"      \
231			$__height2 $__width2            \
232			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
233		)
234		__retval=$?
235		debug= f_dialog_line_sanitize __password2
236
237		# Return if user has either pressed ESC or chosen Cancel/No
238		[ $__retval -eq $DIALOG_OK ] || return $__retval
239
240		# Check for password mismatch
241		if [ "$__password1" != "$__password2" ]; then
242			f_show_msg "$msg_group_passwords_do_not_match"
243			continue
244		fi
245
246		# Check for NULL entry
247		if [ ! "$__password1" ]; then
248			f_dialog_yesno "$msg_disable_password_auth_for_group"
249			__retval=$?
250			if [ $__retval -eq $DIALOG_ESC ]; then
251				return $__retval
252			elif [ $__retval -eq $DIALOG_OK ]; then
253				setvar "$__dvar_to_set" 1
254			else
255				continue # back to password prompt
256			fi
257		else
258			setvar "$__dvar_to_set" ""
259		fi
260
261		setvar "$__var_to_set" "$__password1"
262		break
263	done
264
265	return $DIALOG_OK
266}
267
268# f_dialog_input_group_gid $var_to_set [$group_gid]
269#
270# Allow the user to enter a new GID for a given group. If the user does not
271# cancel or press ESC, the $var_to_set variable will hold the newly-configured
272# value upon return.
273#
274f_dialog_input_group_gid()
275{
276	local __var_to_set="$1" __input="$2"
277
278	# Return if user has either pressed ESC or chosen Cancel/No
279	f_dialog_input __input "$msg_group_id_leave_empty_for_default" \
280	               "$__input" "$hline_num_tab_enter" || return $?
281
282	setvar "$__var_to_set" "$__input"
283	return $DIALOG_OK
284}
285
286# f_dialog_input_group_members $var_to_set [$group_members]
287#
288# Allow the user to modify a list of members for a given group. If the user
289# does not cancel or press ESC, the $var_to_set variable will hold the newly-
290# configured value upon return.
291#
292f_dialog_input_group_members()
293{
294	local __var_to_set="$1" __input="$2"
295	local __prompt="$msg_group_members:"
296	local __menu_list="
297		'X' '$msg_continue'
298		'1' '$msg_select_group_members_from_list'
299		'2' '$msg_enter_group_members_manually'
300	" # END-QUOTE
301	local __defaultitem=
302	local __hline="$hline_num_arrows_tab_enter"
303
304	local __mheight __mwidth __mrows
305	eval f_dialog_menu_size __mheight __mwidth __mrows \
306	                        \"\$DIALOG_TITLE\"     \
307	                        \"\$DIALOG_BACKTITLE\" \
308	                        \"\$__prompt\"         \
309	                        \"\$__hline\"          \
310	                        $__menu_list
311
312	local __menu_choice __retval
313	while :; do
314		__menu_choice=$( eval $DIALOG \
315			--title \"\$DIALOG_TITLE\"         \
316			--backtitle \"\$DIALOG_BACKTITLE\" \
317			--hline \"\$__hline\"              \
318			--ok-label \"\$msg_ok\"            \
319			--cancel-label \"\$msg_cancel\"    \
320			--default-item \"\$__defaultitem\" \
321			--menu \"\$__prompt\"              \
322			$__mheight $__mwidth $__mrows      \
323			$__menu_list                       \
324			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
325		)
326		__retval=$?
327		f_dialog_data_sanitize __menu_choice
328		__defaultitem="$__menu_choice"
329		f_dprintf "retval=%u menu_choice=[%s]" \
330		          $__retval "$__menu_choice"
331
332		# Return if user has either pressed ESC or chosen Cancel/No
333		[ $__retval -eq $DIALOG_OK ] || return $__retval
334
335		local __group_members
336		case "$__menu_choice" in
337		X) # Exit
338			break ;;
339		1) # Select Group Members from a list
340			local __check_list= # Calculated below
341			local __user_list __u __user __length=0
342			__user_list=$( pw usershow -a |
343				awk -F: '!/^[[:space:]]*(#|$)/{print $1}' )
344			while [ $__length -ne ${#__user_list} ]; do
345				__u="${__user_list%%$NL*}" # First line
346				f_shell_escape "$__u" __user
347
348				# Format of a checklist entry: tag item status
349				__check_list="$__check_list '$__user' ''"
350				case "$__input" in
351				"$__u"|"$__u",*|*,"$__u",*|*,"$__u")
352					__check_list="$__check_list on" ;;
353				*)
354					__check_list="$__check_list off"
355				esac
356
357				__length=${#__user_list}
358				__user_list="${__user_list#*$NL}" # Kill line
359			done
360
361			local __cheight __cwidth __crows
362			eval f_dialog_checklist_size \
363				__cheight __cwidth __crows \
364				\"\$DIALOG_TITLE\"     \
365				\"\$DIALOG_BACKTITLE\" \
366				\"\$__prompt\"         \
367				\"\$__hline\"          \
368				$__check_list
369			__group_members=$( eval $DIALOG \
370				--title \"\$DIALOG_TITLE\"         \
371				--backtitle \"\$DIALOG_BACKTITLE\" \
372				--separate-output                  \
373				--hline \"\$__hline\"              \
374				--ok-label \"\$msg_ok\"            \
375				--cancel-label \"\$msg_cancel\"    \
376				--checklist \"\$__prompt\"         \
377				$__cheight $__cwidth $__crows      \
378				$__check_list                      \
379				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
380			) || continue
381				# Return to previous menu if user either
382				# pressed ESC or chose Cancel/No
383			f_dialog_data_sanitize __group_members
384
385			#
386			# Convert the newline separated list into a comma-
387			# separated one so that if the user switches over to
388			# manual editing, list reflects checklist selections
389			#
390			f_replaceall "$__group_members" "[$NL]" "," __input
391			;;
392		2) # Enter Group Members manually
393			local __prompt2="$msg_group_members"
394			__prompt2="$__prompt2 ($msg_separated_by_commas)"
395
396			f_dialog_input __group_members \
397			               "$__prompt2" "$__input" \
398			               "$hline_num_tab_enter" || continue
399				# Return to previous menu if user either
400				# pressed ESC or chose Cancel/No
401
402			__input="$__group_members"
403			;;
404		esac
405	done
406
407	setvar "$__var_to_set" "$__input"
408	return $DIALOG_OK
409}
410
411# f_dialog_menu_group_add [$defaultitem]
412#
413# Present a menu detailing the properties of a group that is about to be added.
414# The user's menu choice is available using f_dialog_menutag_fetch(). Returns
415# success unless the user chose Cancel or pressed ESC. Data to display is taken
416# from environment variables group_name, group_gid, and group_members. If
417# $defaultitem is present and non-NULL, initially highlight the item in the
418# menu.
419#
420f_dialog_menu_group_add()
421{
422	local prompt="$msg_save_exit_or_cancel"
423	local menu_list # Calculated below
424	local defaultitem="$1"
425	local hline="$hline_arrows_tab_enter"
426
427	# Localize potentially hostile variables and escape their values
428	# to the local variable (see f_shell_escape() of `strings.subr')
429	local var
430	for var in gid members name; do
431		local _group_$var
432		eval f_shell_escape \"\$group_$var\" _group_$var
433	done
434
435	menu_list="
436		'X' '$msg_add/$msg_exit'
437		'1' '$msg_group: $_group_name'
438		'2' '$msg_password: -----'
439		'3' '$msg_group_id: $_group_gid'
440		'4' '$msg_group_members: $_group_members'
441	" # END-QUOTE
442
443	local height width rows
444	eval f_dialog_menu_size height width rows \
445	                        \"\$DIALOG_TITLE\"     \
446	                        \"\$DIALOG_BACKTITLE\" \
447	                        \"\$prompt\"           \
448	                        \"\$hline\"            \
449	                        $menu_list
450
451	local menu_choice
452	menu_choice=$( eval $DIALOG \
453		--title \"\$DIALOG_TITLE\"         \
454		--backtitle \"\$DIALOG_BACKTITLE\" \
455		--hline \"\$hline\"                \
456		--ok-label \"\$msg_ok\"            \
457		--cancel-label \"\$msg_cancel\"    \
458		--default-item \"\$defaultitem\"   \
459		--menu \"\$prompt\"                \
460		$height $width $rows               \
461		$menu_list                         \
462		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
463	)
464	local retval=$?
465	f_dialog_data_sanitize menu_choice
466	f_dialog_menutag_store "$menu_choice"
467	return $retval
468}
469
470# f_dialog_menu_group_delete $group [$defaultitem]
471#
472# Present a menu detailing the properties of a group that is about to be
473# deleted. The user's menu choice is available using f_dialog_menutag_fetch().
474# Returns success unless the user chose Cancel or pressed ESC. Data to display
475# is populated automatically from the system accounting database for the given
476# $group argument. If $defaultitem is present and non-NULL, initially highlight
477# the item in the menu.
478#
479f_dialog_menu_group_delete()
480{
481	local prompt="$msg_delete_exit_or_cancel"
482	local menu_list # Calculated below
483	local defaultitem="$2"
484	local hline="$hline_arrows_tab_enter"
485
486	local group_name group_password group_gid group_members
487	f_input_group "$1"
488
489	# Localize potentially hostile variables and escape their values
490	# to the local variable (see f_shell_escape() of `strings.subr')
491	local var
492	for var in gid members name; do
493		local _group_$var
494		eval f_shell_escape \"\$group_$var\" _group_$var
495	done
496
497	menu_list="
498		'X' '$msg_delete/$msg_exit'
499		'1' '$msg_group: $_group_name'
500		'-' '$msg_password: -----'
501		'-' '$msg_group_id: $_group_gid'
502		'-' '$msg_group_members: $_group_members'
503	" # END-QUOTE
504
505	local height width rows
506	eval f_dialog_menu_size height width rows \
507	                        \"\$DIALOG_TITLE\"     \
508	                        \"\$DIALOG_BACKTITLE\" \
509	                        \"\$prompt\"           \
510	                        \"\$hline\"            \
511	                        $menu_list
512
513	local menu_choice
514	menu_choice=$( eval $DIALOG \
515		--title \"\$DIALOG_TITLE\"         \
516		--backtitle \"\$DIALOG_BACKTITLE\" \
517		--hline \"\$hline\"                \
518		--ok-label \"\$msg_ok\"            \
519		--cancel-label \"\$msg_cancel\"    \
520		--default-item \"\$defaultitem\"   \
521		--menu \"\$prompt\"                \
522		$height $width $rows               \
523		$menu_list                         \
524		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
525	)
526	local retval=$?
527	f_dialog_data_sanitize menu_choice
528	f_dialog_menutag_store "$menu_choice"
529	return $retval
530}
531
532# f_dialog_menu_group_edit [$defaultitem]
533#
534# Present a menu detailing the properties of a group that is about to be
535# modified. The user's menu choice is available using f_dialog_menutag_fetch().
536# Returns success unless the user chose Cancel or pressed ESC. Data to display
537# is taken from environment variables group_name, group_gid, and group_members.
538# If $defaultitem is present and non-NULL, initially highlight the item in the
539# menu.
540#
541f_dialog_menu_group_edit()
542{
543	local prompt="$msg_save_exit_or_cancel"
544	local menu_list # Calculated below
545	local defaultitem="$1"
546	local hline="$hline_arrows_tab_enter"
547
548	# Localize potentially hostile variables and escape their values
549	# to the local variable (see f_shell_escape() of `strings.subr')
550	local var
551	for var in gid members name; do
552		local _group_$var
553		eval f_shell_escape \"\$group_$var\" _group_$var
554	done
555
556	menu_list="
557		'X' '$msg_save/$msg_exit'
558		'1' '$msg_group: $_group_name'
559		'2' '$msg_password: -----'
560		'3' '$msg_group_id: $_group_gid'
561		'4' '$msg_group_members: $_group_members'
562	" # END-QUOTE
563
564	local height width rows
565	eval f_dialog_menu_size height width rows \
566	                        \"\$DIALOG_TITLE\"     \
567	                        \"\$DIALOG_BACKTITLE\" \
568	                        \"\$prompt\"           \
569	                        \"\$hline\"            \
570	                        $menu_list
571
572	local menu_choice
573	menu_choice=$( eval $DIALOG \
574		--title \"\$DIALOG_TITLE\"         \
575		--backtitle \"\$DIALOG_BACKTITLE\" \
576		--hline \"\$hline\"                \
577		--ok-label \"\$msg_ok\"            \
578		--cancel-label \"\$msg_cancel\"    \
579		--default-item \"\$defaultitem\"   \
580		--menu \"\$prompt\"                \
581		$height $width $rows               \
582		$menu_list                         \
583		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
584	)
585	local retval=$?
586	f_dialog_data_sanitize menu_choice
587	f_dialog_menutag_store "$menu_choice"
588	return $retval
589}
590
591############################################################ MAIN
592
593f_dprintf "%s: Successfully loaded." usermgmt/group_input.subr
594
595fi # ! $_USERMGMT_GROUP_INPUT_SUBR
596