1263150Sdteskeif [ ! "$_USERMGMT_USER_SUBR" ]; then _USERMGMT_USER_SUBR=1
2263150Sdteske#
3263150Sdteske# Copyright (c) 2012 Ron McDowell
4263150Sdteske# Copyright (c) 2012-2014 Devin Teske
5263150Sdteske# All rights reserved.
6263150Sdteske#
7263150Sdteske# Redistribution and use in source and binary forms, with or without
8263150Sdteske# modification, are permitted provided that the following conditions
9263150Sdteske# are met:
10263150Sdteske# 1. Redistributions of source code must retain the above copyright
11263150Sdteske#    notice, this list of conditions and the following disclaimer.
12263150Sdteske# 2. Redistributions in binary form must reproduce the above copyright
13263150Sdteske#    notice, this list of conditions and the following disclaimer in the
14263150Sdteske#    documentation and/or other materials provided with the distribution.
15263150Sdteske#
16263150Sdteske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17263150Sdteske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18263150Sdteske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19263150Sdteske# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20263150Sdteske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21263150Sdteske# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22263150Sdteske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23263150Sdteske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24263150Sdteske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25263150Sdteske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26263150Sdteske# SUCH DAMAGE.
27263150Sdteske#
28263150Sdteske# $FreeBSD$
29263150Sdteske#
30263150Sdteske############################################################ INCLUDES
31263150Sdteske
32263150SdteskeBSDCFG_SHARE="/usr/share/bsdconfig"
33263150Sdteske. $BSDCFG_SHARE/common.subr || exit 1
34263150Sdteskef_dprintf "%s: loading includes..." usermgmt/user.subr
35263150Sdteskef_include $BSDCFG_SHARE/dialog.subr
36263150Sdteskef_include $BSDCFG_SHARE/strings.subr
37263150Sdteskef_include $BSDCFG_SHARE/usermgmt/group_input.subr
38263150Sdteskef_include $BSDCFG_SHARE/usermgmt/user_input.subr
39263150Sdteske
40263150SdteskeBSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt"
41263150Sdteskef_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr
42263150Sdteske
43263150Sdteske############################################################ CONFIGURATION
44263150Sdteske
45263150Sdteske# set some reasonable defaults if /etc/adduser.conf does not exist.
46263150Sdteske[ -f /etc/adduser.conf ] && f_include /etc/adduser.conf
47263150Sdteske: ${defaultclass:=""}
48263150Sdteske: ${defaultshell:="/bin/sh"}
49263150Sdteske: ${homeprefix:="/home"}
50263150Sdteske: ${passwdtype:="yes"}
51263150Sdteske: ${udotdir:="/usr/share/skel"}
52263150Sdteske: ${uexpire:=""}
53263150Sdteske	# Default account expire time. Format is similar to upwexpire variable.
54263150Sdteske: ${ugecos:="User &"}
55263150Sdteske: ${upwexpire:=""}
56263150Sdteske	# The default password expiration time. Format of the date is either a
57263150Sdteske	# UNIX time in decimal, or a date in dd-mmm-yy[yy] format, where dd is
58263150Sdteske	# the day, mmm is the month in either numeric or alphabetic format, and
59263150Sdteske	# yy[yy] is either a two or four digit year. This variable also accepts
60263150Sdteske	# a relative date in the form of n[mhdwoy] where n is a decimal, octal
61263150Sdteske	# (leading 0) or hexadecimal (leading 0x) digit followed by the number
62263150Sdteske	# of Minutes, Hours, Days, Weeks, Months or Years from the current date
63263150Sdteske	# at which the expiration time is to be set.
64263150Sdteske
65263150Sdteske#
66263150Sdteske# uexpire and upwexpire from adduser.conf(5) differ only slightly from what
67263150Sdteske# pw(8) accepts as `date' argument(s); pw(8) requires a leading `+' for the
68263150Sdteske# relative date syntax (n[mhdwoy]).
69263150Sdteske#
70263150Sdteskecase "$uexpire" in *[mhdwoy])
71263150Sdteske	f_isinteger "${uexpire%[mhdwoy]}" && uexpire="+$uexpire"
72263150Sdteskeesac
73263150Sdteskecase "$upwexpire" in *[mhdwoy])
74263150Sdteske	f_isinteger "${upwexpire%[mhdwoy]}" && upwexpire="+$upwexpire"
75263150Sdteskeesac
76263150Sdteske
77263150Sdteske############################################################ FUNCTIONS
78263150Sdteske
79263150Sdteske# f_user_create_homedir $user
80263150Sdteske#
81263150Sdteske# Create home directory for $user.
82263150Sdteske#
83263150Sdteskef_user_create_homedir()
84263150Sdteske{
85263150Sdteske	local funcname=f_user_create_homedir
86263150Sdteske	local user="$1"
87263150Sdteske
88263150Sdteske	[ "$user" ] || return $FAILURE
89263150Sdteske
90263150Sdteske	local user_account_expire user_class user_gecos user_gid user_home_dir
91263150Sdteske	local user_member_groups user_name user_password user_password_expire
92263150Sdteske	local user_shell user_uid # Variables created by f_input_user() below
93263150Sdteske	f_input_user "$user" || return $FAILURE
94263150Sdteske
95263150Sdteske	f_dprintf "Creating home directory \`%s' for user \`%s'" \
96263150Sdteske	          "$user_home_dir" "$user"
97263150Sdteske
98263150Sdteske	local _user_gid _user_home_dir _user_uid
99263150Sdteske	f_shell_escape "$user_gid"      _user_gid
100263150Sdteske	f_shell_escape "$user_home_dir" _user_home_dir
101263150Sdteske	f_shell_escape "$user_uid"      _user_uid
102263150Sdteske	f_eval_catch $funcname mkdir "mkdir -p '%s'" "$_user_home_dir" ||
103263150Sdteske		return $FAILURE
104263150Sdteske	f_eval_catch $funcname chown "chown '%i:%i' '%s'" \
105263150Sdteske		"$_user_uid" "$_user_gid" "$_user_home_dir" || return $FAILURE
106263150Sdteske}
107263150Sdteske
108263150Sdteske# f_user_copy_dotfiles $user
109263150Sdteske#
110263150Sdteske# Copy `skel' dot-files from $udotdir (global inherited from /etc/adduser.conf)
111263150Sdteske# to the home-directory of $user. Attempts to create the home-directory first
112263150Sdteske# if it doesn't exist.
113263150Sdteske#
114263150Sdteskef_user_copy_dotfiles()
115263150Sdteske{
116263150Sdteske	local funcname=f_user_copy_dotfiles
117263150Sdteske	local user="$1"
118263150Sdteske
119263150Sdteske	[ "$udotdir" ] || return $FAILURE
120263150Sdteske	[ "$user"    ] || return $FAILURE
121263150Sdteske
122263150Sdteske	local user_account_expire user_class user_gecos user_gid user_home_dir
123263150Sdteske	local user_member_groups user_name user_password user_password_expire
124263150Sdteske	local user_shell user_uid # Variables created by f_input_user() below
125263150Sdteske	f_input_user "$user" || return $FAILURE
126263150Sdteske
127263150Sdteske	f_dprintf "Copying dot-files from \`%s' to \`%s'" \
128263150Sdteske	          "$udotdir" "$user_home_dir"
129263150Sdteske
130263150Sdteske	# Attempt to create the home directory if it doesn't exist
131263150Sdteske	[ -d "$user_home_dir" ] ||
132263150Sdteske		f_user_create_homedir "$user" || return $FAILURE
133263150Sdteske
134263150Sdteske	local _user_gid _user_home_dir _user_uid
135263150Sdteske	f_shell_escape "$user_gid"      _user_gid
136263150Sdteske	f_shell_escape "$user_home_dir" _user_home_dir
137263150Sdteske	f_shell_escape "$user_uid"      _user_uid
138263150Sdteske
139263150Sdteske	local - # Localize `set' to this function
140263150Sdteske	set +f # Enable glob pattern-matching for paths
141263150Sdteske	cd "$udotdir" || return $FAILURE
142263150Sdteske
143263150Sdteske	local _file file retval
144263150Sdteske	for file in dot.*; do
145263150Sdteske		[ -e "$file" ] || continue # no-match
146263150Sdteske
147263150Sdteske		f_shell_escape "$file" "_file"
148263150Sdteske		f_eval_catch $funcname cp "cp -n '%s' '%s'" \
149263150Sdteske			"$_file" "$_user_home_dir/${_file#dot}"
150263150Sdteske		retval=$?
151263150Sdteske		[ $retval -eq $SUCCESS ] || break
152263150Sdteske		f_eval_catch $funcname chown \
153263150Sdteske			"chown -h '%i:%i' '%s'" \
154263150Sdteske			"$_user_uid" "$_user_gid" \
155263150Sdteske			"$_user_home_dir/${_file#dot}"
156263150Sdteske		retval=$?
157263150Sdteske		[ $retval -eq $SUCCESS ] || break
158263150Sdteske	done
159263150Sdteske
160263150Sdteske	cd -
161263150Sdteske	return $retval
162263150Sdteske}
163263150Sdteske
164263150Sdteske# f_user_add [$user]
165263150Sdteske#
166263150Sdteske# Create a login account. If both $user (as a first argument) and $VAR_USER are
167263150Sdteske# unset or NULL and we are running interactively, prompt the end-user to enter
168263150Sdteske# the name of a new login account and (if $VAR_NO_CONFIRM is unset or NULL)
169263150Sdteske# prompt the end-user to answer some questions about the new account. Variables
170263150Sdteske# that can be used to script user input:
171263150Sdteske#
172263150Sdteske# 	VAR_USER [Optional if running interactively]
173263150Sdteske# 		The login to add. Ignored if given non-NULL first-argument.
174263150Sdteske# 	VAR_USER_ACCOUNT_EXPIRE [Optional]
175263150Sdteske# 		The account expiration time. Format is similar to
176263150Sdteske# 		VAR_USER_PASSWORD_EXPIRE variable below. Default is to never
177263150Sdteske# 		expire the account.
178263150Sdteske# 	VAR_USER_DOTFILES_CREATE [Optional]
179263150Sdteske# 		If non-NULL, populate the user's home directory with the
180263150Sdteske# 		template files found in $udotdir (`/usr/share/skel' default).
181263150Sdteske# 	VAR_USER_GECOS [Optional]
182263150Sdteske# 		Often the full name of the account holder. Default is NULL.
183263150Sdteske# 	VAR_USER_GID [Optional]
184263150Sdteske# 		Numerical primary-group ID to use. If NULL or unset, the group
185263150Sdteske# 		ID is automatically chosen.
186263150Sdteske# 	VAR_USER_GROUPS [Optional]
187263150Sdteske# 		Comma-separated list of additional groups to which the user is
188263150Sdteske# 		a member of. Default is NULL (no additional groups).
189263150Sdteske# 	VAR_USER_HOME [Optional]
190263150Sdteske# 		The home directory to set. If NULL or unset, the home directory
191263150Sdteske# 		is automatically calculated.
192263150Sdteske# 	VAR_USER_HOME_CREATE [Optional]
193263150Sdteske# 		If non-NULL, create the user's home directory if it doesn't
194263150Sdteske# 		already exist.
195263150Sdteske# 	VAR_USER_LOGIN_CLASS [Optional]
196263150Sdteske# 		Login class to use when creating the login. Default is NULL.
197263150Sdteske# 	VAR_USER_PASSWORD [Optional]
198263150Sdteske# 		Unencrypted password to use. If unset or NULL, password
199263150Sdteske# 		authentication for the login is disabled.
200263150Sdteske# 	VAR_USER_PASSWORD_EXPIRE [Optional]
201263150Sdteske# 		The password expiration time. Format of the date is either a
202263150Sdteske# 		UNIX time in decimal, or a date in dd-mmm-yy[yy] format, where
203263150Sdteske# 		dd is the day, mmm is the month in either numeric or alphabetic
204263150Sdteske# 		format, and yy[yy] is either a two or four digit year. This
205263150Sdteske# 		variable also accepts a relative date in the form of +n[mhdwoy]
206263150Sdteske# 		where n is a decimal, octal (leading 0) or hexadecimal (leading
207263150Sdteske# 		0x) digit followed by the number of Minutes, Hours, Days,
208263150Sdteske# 		Weeks, Months or Years from the current date at which the
209263150Sdteske# 		expiration time is to be set. Default is to never expire the
210263150Sdteske# 		account password.
211263150Sdteske# 	VAR_USER_SHELL [Optional]
212263150Sdteske# 		Path to login shell to use. Default is `/bin/sh'.
213263150Sdteske# 	VAR_USER_UID [Optional]
214263150Sdteske# 		Numerical user ID to use. If NULL or unset, the user ID is
215263150Sdteske# 		automatically chosen.
216263150Sdteske#
217263150Sdteske# Returns success if the user account was successfully created.
218263150Sdteske#
219263150Sdteskef_user_add()
220263150Sdteske{
221263150Sdteske	local funcname=f_user_add
222263150Sdteske	local title # Calculated below
223263150Sdteske	local alert=f_show_msg no_confirm=
224263150Sdteske
225263150Sdteske	f_getvar $VAR_NO_CONFIRM no_confirm
226263150Sdteske	[ "$no_confirm" ] && alert=f_show_info
227263150Sdteske
228263150Sdteske	local input
229263150Sdteske	f_getvar 3:-\$$VAR_USER input "$1"
230263150Sdteske
231263150Sdteske	#
232263150Sdteske	# NB: pw(8) has a ``feature'' wherein `-n name' can be taken as UID
233263150Sdteske	# instead of name. Work-around is to also pass `-u UID' at the same
234263150Sdteske	# time (any UID will do; but `-1' is appropriate for this context).
235263150Sdteske	#
236263150Sdteske	if [ "$input" ] && f_quietly pw usershow -n "$input" -u -1; then
237263150Sdteske		f_show_err "$msg_login_already_used" "$input"
238263150Sdteske		return $FAILURE
239263150Sdteske	fi
240263150Sdteske
241263150Sdteske	local user_name="$input"
242263150Sdteske	while f_interactive && [ ! "$user_name" ]; do
243263150Sdteske		f_dialog_input_name user_name "$user_name" ||
244263150Sdteske			return $SUCCESS
245263150Sdteske		[ "$user_name" ] ||
246263150Sdteske			f_show_err "$msg_please_enter_a_user_name"
247263150Sdteske	done
248263150Sdteske	if [ ! "$user_name" ]; then
249263150Sdteske		f_show_err "$msg_no_user_specified"
250263150Sdteske		return $FAILURE
251263150Sdteske	fi
252263150Sdteske
253263150Sdteske	local user_account_expire user_class user_gecos user_gid user_home_dir
254263150Sdteske	local user_member_groups user_password user_password_expire user_shell
255263150Sdteske	local user_uid user_dotfiles_create= user_home_create=
256263150Sdteske	f_getvar $VAR_USER_ACCOUNT_EXPIRE-\$uexpire    user_account_expire
257263150Sdteske	f_getvar $VAR_USER_DOTFILES_CREATE:+\$msg_yes  user_dotfiles_create
258263150Sdteske	f_getvar $VAR_USER_GECOS-\$ugecos              user_gecos
259263150Sdteske	f_getvar $VAR_USER_GID                         user_gid
260263150Sdteske	f_getvar $VAR_USER_GROUPS                      user_member_groups
261263150Sdteske	f_getvar $VAR_USER_HOME:-\${homeprefix%/}/\$user_name \
262263150Sdteske	                                               user_home_dir
263263150Sdteske	f_getvar $VAR_USER_HOME_CREATE:+\$msg_yes      user_home_create
264263150Sdteske	f_getvar $VAR_USER_LOGIN_CLASS-\$defaultclass  user_class
265263150Sdteske	f_getvar $VAR_USER_PASSWORD                    user_password
266263150Sdteske	f_getvar $VAR_USER_PASSWORD_EXPIRE-\$upwexpire user_password_expire
267263150Sdteske	f_getvar $VAR_USER_SHELL-\$defaultshell        user_shell
268263150Sdteske	f_getvar $VAR_USER_UID                         user_uid
269263150Sdteske
270263150Sdteske	# Create home-dir if no script-override and does not exist
271263150Sdteske	f_isset $VAR_USER_HOME_CREATE || [ -d "$user_home_dir" ] ||
272263150Sdteske		user_home_create="$msg_yes"
273263150Sdteske	# Copy dotfiles if home-dir creation is desired, does not yet exist,
274263150Sdteske	# and no script-override has been set
275263150Sdteske	f_isset $VAR_USER_DOTFILES_CREATE ||
276263150Sdteske		[ "$user_home_create" != "$msg_yes" ] ||
277263150Sdteske		[ -d "$user_home_dir" ] || user_dotfiles_create="$msg_yes"
278263150Sdteske	# Create home-dir if copying dotfiles but home-dir does not exist
279263150Sdteske	[ "$user_dotfiles_create" -a ! -d "$user_home_dir" ] &&
280263150Sdteske		user_home_create="$msg_yes"
281263150Sdteske
282263150Sdteske	# Set flags for meaningful NULL values if-provided
283263150Sdteske	local no_account_expire= no_password_expire= null_gecos= null_members=
284263150Sdteske	local user_password_disable=
285263150Sdteske	f_isset $VAR_USER_ACCOUNT_EXPIRE &&
286263150Sdteske		[ ! "$user_account_expire"  ] && no_account_expire=1
287263150Sdteske	f_isset $VAR_USER_GECOS &&
288263150Sdteske		[ ! "$user_gecos"           ] && null_gecos=1
289263150Sdteske	f_isset $VAR_USER_GROUPS &&
290263150Sdteske		[ ! "$user_member_groups"   ] && null_members=1
291263150Sdteske	f_isset $VAR_USER_PASSWORD &&
292263150Sdteske		[ ! "$user_password"        ] && user_password_disable=1
293263150Sdteske	f_isset $VAR_USER_PASSWORD_EXPIRE &&
294263150Sdteske		[ ! "$user_password_expire" ] && no_password_expire=1
295263150Sdteske
296263150Sdteske	if f_interactive && [ ! "$no_confirm" ]; then
297263150Sdteske		f_dialog_noyes \
298263150Sdteske			"$msg_use_default_values_for_all_account_details"
299263150Sdteske		retval=$?
300263150Sdteske		if [ $retval -eq $DIALOG_ESC ]; then
301263150Sdteske			return $SUCCESS
302263150Sdteske		elif [ $retval -ne $DIALOG_OK ]; then
303263150Sdteske			#
304263150Sdteske			# Ask series of questions to pre-fill the editor screen
305263150Sdteske			#
306263150Sdteske			# Defaults used in each dialog should allow the user to
307263150Sdteske			# simply hit ENTER to proceed, because cancelling any
308263150Sdteske			# single dialog will cause them to be returned to the
309263150Sdteske			# previous menu.
310263150Sdteske			#
311263150Sdteske
312263150Sdteske			f_dialog_input_gecos user_gecos "$user_gecos" ||
313263150Sdteske				return $FAILURE
314263150Sdteske			if [ "$passwdtype" = "yes" ]; then
315263150Sdteske				f_dialog_input_password user_password \
316263150Sdteske					user_password_disable ||
317263150Sdteske					return $FAILURE
318263150Sdteske			fi
319263150Sdteske			f_dialog_input_uid user_uid "$user_uid" ||
320263150Sdteske				return $FAILURE
321263150Sdteske			f_dialog_input_gid user_gid "$user_gid" ||
322263150Sdteske				return $FAILURE
323263150Sdteske			f_dialog_input_member_groups user_member_groups \
324263150Sdteske				"$user_member_groups" || return $FAILURE
325263150Sdteske			f_dialog_input_class user_class "$user_class" ||
326263150Sdteske				return $FAILURE
327263150Sdteske			f_dialog_input_expire_password user_password_expire \
328263150Sdteske				"$user_password_expire" || return $FAILURE
329263150Sdteske			f_dialog_input_expire_account user_account_expire \
330263150Sdteske				"$user_account_expire" || return $FAILURE
331263150Sdteske			f_dialog_input_home_dir user_home_dir \
332263150Sdteske				"$user_home_dir" || return $FAILURE
333263150Sdteske			if [ ! -d "$user_home_dir" ]; then
334263150Sdteske				f_dialog_input_home_create user_home_create ||
335263150Sdteske					return $FAILURE
336263150Sdteske				if [ "$user_home_create" = "$msg_yes" ]; then
337263150Sdteske					f_dialog_input_dotfiles_create \
338263150Sdteske						user_dotfiles_create ||
339263150Sdteske						return $FAILURE
340263150Sdteske				fi
341263150Sdteske			fi
342263150Sdteske			f_dialog_input_shell user_shell "$user_shell" ||
343263150Sdteske				return $FAILURE
344263150Sdteske		fi
345263150Sdteske	fi
346263150Sdteske
347263150Sdteske	#
348263150Sdteske	# Loop until the user decides to Exit, Cancel, or presses ESC
349263150Sdteske	#
350263150Sdteske	title="$msg_add $msg_user: $user_name"
351263150Sdteske	if f_interactive; then
352263150Sdteske		local mtag retval defaultitem=
353263150Sdteske		while :; do
354263150Sdteske			f_dialog_title "$title"
355263150Sdteske			f_dialog_menu_user_add "$defaultitem"
356263150Sdteske			retval=$?
357263150Sdteske			f_dialog_title_restore
358263150Sdteske			f_dialog_menutag_fetch mtag
359263150Sdteske			f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
360263150Sdteske			defaultitem="$mtag"
361263150Sdteske
362263150Sdteske			# Return if user either pressed ESC or chose Cancel/No
363263150Sdteske			[ $retval -eq $DIALOG_OK ] || return $FAILURE
364263150Sdteske
365263150Sdteske			case "$mtag" in
366263150Sdteske			X) # Add/Exit
367263150Sdteske			   local var
368263150Sdteske			   for var in account_expire class gecos gid home_dir \
369263150Sdteske			   	member_groups name password_expire shell uid \
370263150Sdteske			   ; do
371263150Sdteske			   	local _user_$var
372263150Sdteske			   	eval f_shell_escape \"\$user_$var\" _user_$var
373263150Sdteske			   done
374263150Sdteske
375263150Sdteske			   local cmd="pw useradd -n '$_user_name'"
376263150Sdteske			   [ "$user_gid"   ] && cmd="$cmd -g '$_user_gid'"
377263150Sdteske			   [ "$user_shell" ] && cmd="$cmd -s '$_user_shell'"
378263150Sdteske			   [ "$user_uid"   ] && cmd="$cmd -u '$_user_uid'"
379263150Sdteske			   [ "$user_account_expire" -o \
380263150Sdteske			     "$no_account_expire" ] &&
381263150Sdteske			   	cmd="$cmd -e '$_user_account_expire'"
382263150Sdteske			   [ "$user_class" -o "$null_class" ] &&
383263150Sdteske			   	cmd="$cmd -L '$_user_class'"
384263150Sdteske			   [ "$user_gecos" -o "$null_gecos" ] &&
385263150Sdteske			   	cmd="$cmd -c '$_user_gecos'"
386263150Sdteske			   [ "$user_home_dir" ] &&
387263150Sdteske			   	cmd="$cmd -d '$_user_home_dir'"
388263150Sdteske			   [ "$user_member_groups" ] &&
389263150Sdteske			   	cmd="$cmd -G '$_user_member_groups'"
390263150Sdteske			   [ "$user_password_expire" -o \
391263150Sdteske			     "$no_password_expire" ] &&
392263150Sdteske			   	cmd="$cmd -p '$_user_password_expire'"
393263150Sdteske
394263150Sdteske			   # Execute the command
395263150Sdteske			   if [ "$user_password_disable" ]; then
396263150Sdteske			   	f_eval_catch $funcname pw '%s -h -' "$cmd"
397263150Sdteske			   elif [ "$user_password" ]; then
398263150Sdteske			   	echo "$user_password" | f_eval_catch \
399263150Sdteske			   		$funcname pw '%s -h 0' "$cmd"
400263150Sdteske			   else
401263150Sdteske			   	f_eval_catch $funcname pw '%s' "$cmd"
402263150Sdteske			   fi || continue
403263150Sdteske
404263150Sdteske			   # Create home directory if desired
405263150Sdteske			   [ "${user_home_create:-$msg_no}" != "$msg_no" ] &&
406263150Sdteske			   	f_user_create_homedir "$user_name"
407263150Sdteske
408263150Sdteske			   # Copy dotfiles if desired
409263150Sdteske			   [ "${user_dotfiles_create:-$msg_no}" != \
410263150Sdteske			     "$msg_no" ] && f_user_copy_dotfiles "$user_name"
411263150Sdteske
412263150Sdteske			   break # to success
413263150Sdteske			   ;;
414263150Sdteske			1) # Login (prompt for new login name)
415263150Sdteske			   f_dialog_input_name input "$user_name" ||
416263150Sdteske			   	continue
417263150Sdteske			   if f_quietly pw usershow -n "$input" -u -1; then
418263150Sdteske			   	f_show_err "$msg_login_already_used" "$input"
419263150Sdteske			   	continue
420263150Sdteske			   fi
421263150Sdteske			   user_name="$input"
422263150Sdteske			   title="$msg_add $msg_user: $user_name"
423263150Sdteske			   user_home_dir="${homeprefix%/}/$user_name"
424263150Sdteske			   ;;
425263150Sdteske			2) # Full Name
426263150Sdteske			   f_dialog_input_gecos user_gecos "$user_gecos" &&
427263150Sdteske			   	[ ! "$user_gecos" ] && null_gecos=1 ;;
428263150Sdteske			3) # Password
429263150Sdteske			   f_dialog_input_password \
430263150Sdteske			   	user_password user_password_disable ;;
431263150Sdteske			4) # User ID
432263150Sdteske			   f_dialog_input_uid user_uid "$user_uid" ;;
433263150Sdteske			5) # Group ID
434263150Sdteske			   f_dialog_input_gid user_gid "$user_gid" ;;
435263150Sdteske			6) # Member of Groups
436263150Sdteske			   f_dialog_input_member_groups \
437263150Sdteske			   	user_member_groups "$user_member_groups" &&
438263150Sdteske			   	[ ! "$user_member_groups" ] &&
439263150Sdteske			   	null_members=1 ;;
440263150Sdteske			7) # Login Class
441263150Sdteske			   f_dialog_input_class user_class "$user_class" &&
442263150Sdteske			   	[ ! "$user_class" ] && null_class=1 ;;
443263150Sdteske			8) # Password Expires On
444263150Sdteske			   f_dialog_input_expire_password \
445263150Sdteske			   	user_password_expire "$user_password_expire" &&
446263150Sdteske			   	[ ! "$user_password_expire" ] &&
447263150Sdteske			   	no_password_expire=1 ;;
448263150Sdteske			9) # Account Expires On
449263150Sdteske			   f_dialog_input_expire_account \
450263150Sdteske			   	user_account_expire "$user_account_expire" &&
451263150Sdteske			   	[ ! "$user_account_expire" ] &&
452263150Sdteske			   	no_account_expire=1 ;;
453263150Sdteske			A) # Home Directory
454263150Sdteske			   f_dialog_input_home_dir \
455263150Sdteske			   	user_home_dir "$user_home_dir" ;;
456263150Sdteske			B) # Shell
457263150Sdteske			   f_dialog_input_shell user_shell "$user_shell" ;;
458263150Sdteske			C) # Create Home Directory?
459263150Sdteske			   if [ "${user_home_create:-$msg_no}" != "$msg_no" ]
460263150Sdteske			   then
461263150Sdteske			   	user_home_create="$msg_no"
462263150Sdteske			   else
463263150Sdteske			   	user_home_create="$msg_yes"
464263150Sdteske			   fi ;;
465263150Sdteske			D) # Create Dotfiles?
466263150Sdteske			   if [ "${user_dotfiles_create:-$msg_no}" != \
467263150Sdteske			        "$msg_no" ]
468263150Sdteske			   then
469263150Sdteske			   	user_dotfiles_create="$msg_no"
470263150Sdteske			   else
471263150Sdteske			   	user_dotfiles_create="$msg_yes"
472263150Sdteske			   fi ;;
473263150Sdteske			esac
474263150Sdteske		done
475263150Sdteske	else
476263150Sdteske		local var
477263150Sdteske		for var in account_expire class gecos gid home_dir \
478263150Sdteske			member_groups name password_expire shell uid \
479263150Sdteske		; do
480263150Sdteske			local _user_$var
481263150Sdteske			eval f_shell_escape \"\$user_$var\" _user_$var
482263150Sdteske		done
483263150Sdteske
484263150Sdteske		# Form the command
485263150Sdteske		local cmd="pw useradd -n '$_user_name'"
486263150Sdteske		[ "$user_gid"      ] && cmd="$cmd -g '$_user_gid'"
487263150Sdteske		[ "$user_home_dir" ] && cmd="$cmd -d '$_user_home_dir'"
488263150Sdteske		[ "$user_shell"    ] && cmd="$cmd -s '$_user_shell'"
489263150Sdteske		[ "$user_uid"      ] && cmd="$cmd -u '$_user_uid'"
490263150Sdteske		[ "$user_account_expire" -o "$no_account_expire" ] &&
491263150Sdteske			cmd="$cmd -e '$_user_account_expire'"
492263150Sdteske		[ "$user_class" -o "$null_class" ] &&
493263150Sdteske			cmd="$cmd -L '$_user_class'"
494263150Sdteske		[ "$user_gecos" -o "$null_gecos" ] &&
495263150Sdteske			cmd="$cmd -c '$_user_gecos'"
496263150Sdteske		[ "$user_member_groups" -o "$null_members" ] &&
497263150Sdteske			cmd="$cmd -G '$_user_member_groups'"
498263150Sdteske		[ "$user_password_expire" -o "$no_password_expire" ] &&
499263150Sdteske			cmd="$cmd -p '$_user_password_expire'"
500263150Sdteske
501263150Sdteske		# Execute the command
502263150Sdteske		local retval err
503263150Sdteske		if [ "$user_password_disable" ]; then
504263150Sdteske			f_eval_catch -k err $funcname pw '%s -h -' "$cmd"
505263150Sdteske		elif [ "$user_password" ]; then
506263150Sdteske			err=$( echo "$user_password" | f_eval_catch -de \
507263150Sdteske				$funcname pw '%s -h 0' "$cmd" 2>&1 )
508263150Sdteske		else
509263150Sdteske			f_eval_catch -k err $funcname pw '%s' "$cmd"
510263150Sdteske		fi
511263150Sdteske		retval=$?
512263150Sdteske		if [ $retval -ne $SUCCESS ]; then
513263150Sdteske			f_show_err "%s" "$err"
514263150Sdteske			return $retval
515263150Sdteske		fi
516263150Sdteske
517263150Sdteske		# Create home directory if desired
518263150Sdteske		[ "${user_home_create:-$msg_no}" != "$msg_no" ] &&
519263150Sdteske			f_user_create_homedir "$user_name"
520263150Sdteske
521263150Sdteske		# Copy dotfiles if desired
522263150Sdteske		[ "${user_dotfiles_create:-$msg_no}" != "$msg_no" ] &&
523263150Sdteske			f_user_copy_dotfiles "$user_name"
524263150Sdteske	fi
525263150Sdteske
526263150Sdteske	f_dialog_title "$title"
527263150Sdteske	$alert "$msg_login_added"
528263150Sdteske	f_dialog_title_restore
529263150Sdteske	[ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1
530263150Sdteske
531263150Sdteske	return $SUCCESS
532263150Sdteske}
533263150Sdteske
534263150Sdteske# f_user_delete [$user]
535263150Sdteske#
536263150Sdteske# Delete a user. If both $user (as a first argument) and $VAR_USER are unset or
537263150Sdteske# NULL and we are running interactively, prompt the end-user to select a user
538263150Sdteske# account from a list of those available. Variables that can be used to script
539263150Sdteske# user input:
540263150Sdteske#
541263150Sdteske# 	VAR_USER [Optional if running interactively]
542263150Sdteske# 		The user to delete. Ignored if given non-NULL first-argument.
543263150Sdteske#
544263150Sdteske# Returns success if the user account was successfully deleted.
545263150Sdteske#
546263150Sdteskef_user_delete()
547263150Sdteske{
548263150Sdteske	local funcname=f_user_delete
549263150Sdteske	local title # Calculated below
550263150Sdteske	local alert=f_show_msg no_confirm=
551263150Sdteske
552263150Sdteske	f_getvar $VAR_NO_CONFIRM no_confirm
553263150Sdteske	[ "$no_confirm" ] && alert=f_show_info
554263150Sdteske
555263150Sdteske	local input
556263150Sdteske	f_getvar 3:-\$$VAR_USER input "$1"
557263150Sdteske
558263150Sdteske	if f_interactive && [ ! "$input" ]; then
559263150Sdteske		f_dialog_menu_user_list || return $SUCCESS
560263150Sdteske		f_dialog_menutag_fetch input
561263150Sdteske		[ "$input" = "X $msg_exit" ] && return $SUCCESS
562263150Sdteske	elif [ ! "$input" ]; then
563263150Sdteske		f_show_err "$msg_no_user_specified"
564263150Sdteske		return $FAILURE
565263150Sdteske	fi
566263150Sdteske
567263150Sdteske	local user_account_expire user_class user_gecos user_gid user_home_dir
568263150Sdteske	local user_member_groups user_name user_password user_password_expire
569263150Sdteske	local user_shell user_uid # Variables created by f_input_user() below
570263150Sdteske	if [ "$input" ] && ! f_input_user "$input"; then
571263150Sdteske		f_show_err "$msg_login_not_found" "$input"
572263150Sdteske		return $FAILURE
573263150Sdteske	fi
574263150Sdteske
575263150Sdteske	local user_group_delete= user_home_delete=
576263150Sdteske	f_getvar $VAR_USER_GROUP_DELETE:-\$msg_no user_group_delete
577263150Sdteske	f_getvar $VAR_USER_HOME_DELETE:-\$msg_no  user_home_delete
578263150Sdteske
579263150Sdteske	# Attempt to translate user GID into a group name
580263150Sdteske	local user_group
581263150Sdteske	if user_group=$( pw groupshow -g "$user_gid" 2> /dev/null ); then
582263150Sdteske		user_group="${user_group%%:*}"
583263150Sdteske		# Default to delete the primary group if no script-override and
584263150Sdteske		# exists with same name as the user (same logic used by pw(8))
585263150Sdteske		f_isset $VAR_USER_GROUP_DELETE ||
586263150Sdteske			[ "$user_group" != "$user_name" ] ||
587263150Sdteske			user_group_delete="$msg_yes"
588263150Sdteske	fi
589263150Sdteske
590263150Sdteske	#
591263150Sdteske	# Loop until the user decides to Exit, Cancel, or presses ESC
592263150Sdteske	#
593263150Sdteske	title="$msg_delete $msg_user: $user_name"
594263150Sdteske	if f_interactive; then
595263150Sdteske		local mtag retval defaultitem=
596263150Sdteske		while :; do
597263150Sdteske			f_dialog_title "$title"
598263150Sdteske			f_dialog_menu_user_delete "$user_name" "$defaultitem"
599263150Sdteske			retval=$?
600263150Sdteske			f_dialog_title_restore
601263150Sdteske			f_dialog_menutag_fetch mtag
602263150Sdteske			f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
603263150Sdteske			defaultitem="$mtag"
604263150Sdteske
605263150Sdteske			# Return if user either pressed ESC or chose Cancel/No
606263150Sdteske			[ $retval -eq $DIALOG_OK ] || return $FAILURE
607263150Sdteske
608263150Sdteske			case "$mtag" in
609263150Sdteske			X) # Delete/Exit
610263150Sdteske			   f_shell_escape "$user_uid" _user_uid
611263150Sdteske
612263150Sdteske			   # Save group information in case pw(8) deletes it
613263150Sdteske			   # and we wanted to keep it (to be restored below)
614263150Sdteske			   if [ "${user_group_delete:-$msg_no}" = "$msg_no" ]
615263150Sdteske			   then
616263150Sdteske			   	local v vars="gid members name password"
617263150Sdteske			   	for v in $vars; do local group_$var; done
618263150Sdteske			   	f_input_group "$user_group"
619263150Sdteske
620263150Sdteske			   	# Remove user-to-delete from group members
621263150Sdteske			   	# NB: Otherwise group restoration could fail
622263150Sdteske			   	local name length=0 _members=
623263150Sdteske			  	while [ $length -ne ${#group_members} ]; do
624263150Sdteske			   		name="${group_members%%,*}"
625263150Sdteske			   		[ "$name" != "$user_name" ] &&
626263150Sdteske			   			_members="$_members,$name"
627263150Sdteske			   		length=${#group_members}
628263150Sdteske			   		group_members="${group_members#*,}"
629263150Sdteske			   	done
630263150Sdteske			   	group_members="${_members#,}"
631263150Sdteske
632263150Sdteske			   	# Create escaped variables for f_eval_catch()
633263150Sdteske			   	for v in $vars; do
634263150Sdteske			   		local _group_$v
635263150Sdteske			   		eval f_shell_escape \
636263150Sdteske			   			\"\$group_$v\" _group_$v
637263150Sdteske			   	done
638263150Sdteske			   fi
639263150Sdteske
640263150Sdteske			   # Delete the user (if asked to delete home directory
641263150Sdteske			   # display [X]dialog notification to show activity)
642263150Sdteske			   local cmd="pw userdel -u '$_user_uid'"
643263150Sdteske			   if [ "$user_home_delete" = "$msg_yes" -a \
644263150Sdteske			        "$USE_XDIALOG" ]
645263150Sdteske			   then
646263150Sdteske			   	local err
647263150Sdteske			   	err=$(
648263150Sdteske			   		exec 9>&1
649263150Sdteske			   		f_eval_catch -e $funcname pw \
650263150Sdteske			   		  "%s -r" "$cmd" \
651263150Sdteske			   		  >&$DIALOG_TERMINAL_PASSTHRU_FD 2>&9 |
652263150Sdteske			   		  f_xdialog_info \
653263150Sdteske			   		  	"$msg_deleting_home_directory"
654263150Sdteske			   	)
655263150Sdteske			   	[ ! "$err" ]
656263150Sdteske			   elif [ "$user_home_delete" = "$msg_yes" ]; then
657263150Sdteske			   	f_dialog_info "$msg_deleting_home_directory"
658263150Sdteske			   	f_eval_catch $funcname pw '%s -r' "$cmd"
659263150Sdteske			   else
660263150Sdteske			   	f_eval_catch $funcname pw '%s' "$cmd"
661263150Sdteske			   fi || continue
662263150Sdteske
663263150Sdteske			   #
664263150Sdteske			   # pw(8) may conditionally delete the primary group,
665263150Sdteske			   # which may not be what is desired.
666263150Sdteske			   #
667263150Sdteske			   # If we've been asked to delete the group and pw(8)
668263150Sdteske			   # chose not to, delete it. Otherwise, if we're told
669263150Sdteske			   # to NOT delete the group, we may need to restore it
670263150Sdteske			   # since pw(8) doesn't have a flag to tell `userdel'
671263150Sdteske			   # to not delete the group.
672263150Sdteske			   # 
673263150Sdteske			   # NB: If primary group and user have different names
674263150Sdteske			   # the group may not have been deleted (again, see PR
675263150Sdteske			   # 169471 and SVN r263114 for details).
676263150Sdteske			   #
677263150Sdteske			   if [ "${user_group_delete:-$msg_no}" != "$msg_no" ]
678263150Sdteske			   then
679263150Sdteske			   	f_quietly pw groupshow -g "$user_gid" &&
680263150Sdteske			   	f_eval_catch $funcname pw \
681263150Sdteske			   		"pw groupdel -g '%s'" "$_user_gid"
682263150Sdteske			   elif ! f_quietly pw groupshow -g "$group_gid" &&
683263150Sdteske			        [ "$group_name" -a "$group_gid" ]
684263150Sdteske			   then
685263150Sdteske			   	# Group deleted by pw(8), so restore it
686263150Sdteske			   	local cmd="pw groupadd -n '$_group_name'"
687263150Sdteske			   	cmd="$cmd -g '$_group_gid'"
688263150Sdteske			   	cmd="$cmd -M '$_group_members'"
689263150Sdteske
690263150Sdteske			   	# Get the group password (pw(8) groupshow does
691263150Sdteske			  	# NOT provide this (even if running privileged)
692263150Sdteske			   	local group_password_enc
693263150Sdteske			   	group_password_enc=$( getent group | awk -F: '
694263150Sdteske			   		!/^[[:space:]]*(#|$)/ && \
695263150Sdteske			   		    $1 == ENVIRON["group_name"] && \
696263150Sdteske			   		    $3 == ENVIRON["group_gid"] && \
697263150Sdteske			   		    $4 == ENVIRON["group_members"] \
698263150Sdteske			   		{ print $2; exit }
699263150Sdteske			   	' )
700263150Sdteske			   	if [ "$group_password_enc" ]; then
701263150Sdteske			   		echo "$group_password_enc" |
702263150Sdteske			   			f_eval_catch $funcname \
703263150Sdteske			   				pw '%s -H 0' "$cmd"
704263150Sdteske			   	else
705263150Sdteske			   		f_eval_catch $funcname \
706263150Sdteske			   			pw '%s -h -' "$cmd"
707263150Sdteske			   	fi
708263150Sdteske			   fi
709263150Sdteske
710263150Sdteske			   break # to success
711263150Sdteske			   ;;
712263150Sdteske			1) # Login (select different login from list)
713263150Sdteske			   f_dialog_menu_user_list "$user_name" || continue
714263150Sdteske			   f_dialog_menutag_fetch mtag
715263150Sdteske
716263150Sdteske			   [ "$mtag" = "X $msg_exit" ] && continue
717263150Sdteske
718263150Sdteske			   if ! f_input_user "$mtag"; then
719263150Sdteske			   	f_show_err "$msg_login_not_found" "$mtag"
720263150Sdteske			   	# Attempt to fall back to previous selection
721263150Sdteske			   	f_input_user "$input" || return $FAILURE
722263150Sdteske			   else
723263150Sdteske			   	input="$mtag"
724263150Sdteske			   fi
725263150Sdteske			   title="$msg_delete $msg_user: $user_name"
726263150Sdteske			   ;;
727263150Sdteske			C) # Delete Primary Group?
728263150Sdteske			   if [ "${user_group_delete:-$msg_no}" != "$msg_no" ]
729263150Sdteske			   then
730263150Sdteske			   	user_group_delete="$msg_no"
731263150Sdteske			   else
732263150Sdteske			   	user_group_delete="$msg_yes"
733263150Sdteske			   fi ;;
734263150Sdteske			D) # Delete Home Directory?
735263150Sdteske			   if [ "${user_home_delete:-$msg_no}" != "$msg_no" ]
736263150Sdteske			   then
737263150Sdteske			   	user_home_delete="$msg_no"
738263150Sdteske			   else
739263150Sdteske			   	user_home_delete="$msg_yes"
740263150Sdteske			   fi ;;
741263150Sdteske			esac
742263150Sdteske		done
743263150Sdteske	else
744263150Sdteske		f_shell_escape "$user_uid" _user_uid
745263150Sdteske
746263150Sdteske		# Save group information in case pw(8) deletes it
747263150Sdteske		# and we wanted to keep it (to be restored below)
748263150Sdteske		if [ "${user_group_delete:-$msg_no}" = "$msg_no" ]; then
749263150Sdteske			local v vars="gid members name password"
750263150Sdteske			for v in $vars; do local group_$v; done
751263150Sdteske			f_input_group "$user_group"
752263150Sdteske
753263150Sdteske			# Remove user we're about to delete from group members
754263150Sdteske			# NB: Otherwise group restoration could fail
755263150Sdteske			local name length=0 _members=
756263150Sdteske			while [ $length -ne ${#group_members} ]; do
757263150Sdteske				name="${group_members%%,*}"
758263150Sdteske				[ "$name" != "$user_name" ] &&
759263150Sdteske					_members="$_members,$name"
760263150Sdteske				length=${#group_members}
761263150Sdteske				group_members="${group_members#*,}"
762263150Sdteske			done
763263150Sdteske			group_members="${_members#,}"
764263150Sdteske
765263150Sdteske			# Create escaped variables for later f_eval_catch()
766263150Sdteske			for v in $vars; do
767263150Sdteske				local _group_$v
768263150Sdteske				eval f_shell_escape \"\$group_$v\" _group_$v
769263150Sdteske			done
770263150Sdteske		fi
771263150Sdteske
772263150Sdteske		# Delete the user (if asked to delete home directory
773263150Sdteske		# display [X]dialog notification to show activity)
774263150Sdteske		local err cmd="pw userdel -u '$_user_uid'"
775263150Sdteske		if [ "$user_home_delete" = "$msg_yes" -a "$USE_XDIALOG" ]; then
776263150Sdteske			err=$(
777263150Sdteske				exec 9>&1
778263150Sdteske				f_eval_catch -de $funcname pw \
779263150Sdteske					'%s -r' "$cmd" 2>&9 | f_xdialog_info \
780263150Sdteske					"$msg_deleting_home_directory"
781263150Sdteske			)
782263150Sdteske			[ ! "$err" ]
783263150Sdteske		elif [ "$user_home_delete" = "$msg_yes" ]; then
784263150Sdteske			f_dialog_info "$msg_deleting_home_directory"
785263150Sdteske			f_eval_catch -k err $funcname pw '%s -r' "$cmd"
786263150Sdteske		else
787263150Sdteske			f_eval_catch -k err $funcname pw '%s' "$cmd"
788263150Sdteske		fi
789263150Sdteske		local retval=$?
790263150Sdteske		if [ $retval -ne $SUCCESS ]; then
791263150Sdteske			f_show_err "%s" "$err"
792263150Sdteske			return $retval
793263150Sdteske		fi
794263150Sdteske
795263150Sdteske		#
796263150Sdteske		# pw(8) may conditionally delete the primary group, which may
797263150Sdteske		# not be what is desired.
798263150Sdteske		#
799263150Sdteske		# If we've been asked to delete the group and pw(8) chose not
800263150Sdteske		# to, delete it. Otherwise, if we're told to NOT delete the
801263150Sdteske		# group, we may need to restore it since pw(8) doesn't have a
802263150Sdteske		# flag to tell `userdel' to not delete the group.
803263150Sdteske		# 
804263150Sdteske		# NB: If primary group and user have different names the group
805263150Sdteske		# may not have been deleted (again, see PR 169471 and SVN
806263150Sdteske		# r263114 for details).
807263150Sdteske		#
808263150Sdteske		if [ "${user_group_delete:-$msg_no}" != "$msg_no" ]
809263150Sdteske		then
810263150Sdteske			f_quietly pw groupshow -g "$user_gid" &&
811263150Sdteske			f_eval_catch $funcname pw \
812263150Sdteske				"pw groupdel -g '%s'" "$_user_gid"
813263150Sdteske		elif ! f_quietly pw groupshow -g "$group_gid" &&
814263150Sdteske		     [ "$group_name" -a "$group_gid" ]
815263150Sdteske		then
816263150Sdteske			# Group deleted by pw(8), so restore it
817263150Sdteske			local cmd="pw groupadd -n '$_group_name'"
818263150Sdteske			cmd="$cmd -g '$_group_gid'"
819263150Sdteske			cmd="$cmd -M '$_group_members'"
820263150Sdteske			local group_password_enc
821263150Sdteske			group_password_enc=$( getent group | awk -F: '
822263150Sdteske				!/^[[:space:]]*(#|$)/ && \
823263150Sdteske				    $1 == ENVIRON["group_name"] && \
824263150Sdteske				    $3 == ENVIRON["group_gid"] && \
825263150Sdteske				    $4 == ENVIRON["group_members"] \
826263150Sdteske				{ print $2; exit }
827263150Sdteske			' )
828263150Sdteske			if [ "$group_password_enc" ]; then
829263150Sdteske				echo "$group_password_enc" |
830263150Sdteske					f_eval_catch $funcname \
831263150Sdteske						pw '%s -H 0' "$cmd"
832263150Sdteske			else
833263150Sdteske				f_eval_catch $funcname \
834263150Sdteske					pw '%s -h -' "$cmd"
835263150Sdteske			fi
836263150Sdteske		fi
837263150Sdteske	fi
838263150Sdteske
839263150Sdteske	f_dialog_title "$title"
840263150Sdteske	$alert "$msg_login_deleted"
841263150Sdteske	f_dialog_title_restore
842263150Sdteske	[ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1
843263150Sdteske
844263150Sdteske	return $SUCCESS
845263150Sdteske}
846263150Sdteske
847263150Sdteske# f_user_edit [$user]
848263150Sdteske#
849263150Sdteske# Modify a login account. If both $user (as a first argument) and $VAR_USER are
850263150Sdteske# unset or NULL and we are running interactively, prompt the end-user to select
851263150Sdteske# a login account from a list of those available. Variables that can be used to
852263150Sdteske# script user input:
853263150Sdteske#
854263150Sdteske# 	VAR_USER [Optional if running interactively]
855263150Sdteske# 		The login to modify. Ignored if given non-NULL first-argument.
856263150Sdteske# 	VAR_USER_ACCOUNT_EXPIRE [Optional]
857263150Sdteske# 		The account expiration time. Format is similar to
858263150Sdteske# 		VAR_USER_PASSWORD_EXPIRE variable below. If unset, account
859263150Sdteske# 		expiry is unchanged. If set but NULL, account expiration is
860263150Sdteske# 		disabled (same as setting a value of `0').
861263150Sdteske# 	VAR_USER_DOTFILES_CREATE [Optional]
862263150Sdteske# 		If non-NULL, re-populate the user's home directory with the
863263150Sdteske# 		template files found in $udotdir (`/usr/share/skel' default).
864263150Sdteske# 	VAR_USER_GECOS [Optional]
865263150Sdteske# 		Often the full name of the account holder. If unset, the GECOS
866263150Sdteske# 		field is unmodified. If set but NULL, the field is blanked.
867263150Sdteske# 	VAR_USER_GID [Optional]
868263150Sdteske# 		Numerical primary-group ID to set. If NULL or unset, the group
869263150Sdteske# 		ID is unchanged.
870263150Sdteske# 	VAR_USER_GROUPS [Optional]
871263150Sdteske# 		Comma-separated list of additional groups to which the user is
872263150Sdteske# 		a member of. If set but NULL, group memberships are reset (this
873263150Sdteske# 		login will not be a member of any additional groups besides the
874263150Sdteske# 		primary group). If unset, group membership is unmodified.
875263150Sdteske# 	VAR_USER_HOME [Optional]
876263150Sdteske# 		The home directory to set. If NULL or unset, the home directory
877263150Sdteske# 		is unchanged.
878263150Sdteske# 	VAR_USER_HOME_CREATE [Optional]
879263150Sdteske# 		If non-NULL, create the user's home directory if it doesn't
880263150Sdteske# 		already exist.
881263150Sdteske# 	VAR_USER_LOGIN_CLASS [Optional]
882263150Sdteske# 		Login class to set. If unset, the login class is unchanged. If
883263150Sdteske# 		set but NULL, the field is blanked.
884263150Sdteske# 	VAR_USER_PASSWORD [Optional]
885263150Sdteske# 		Unencrypted password to set. If unset, the login password is
886263150Sdteske# 		unmodified. If set but NULL, password authentication for the
887263150Sdteske# 		login is disabled.
888263150Sdteske# 	VAR_USER_PASSWORD_EXPIRE [Optional]
889263150Sdteske# 		The password expiration time. Format of the date is either a
890263150Sdteske# 		UNIX time in decimal, or a date in dd-mmm-yy[yy] format, where
891263150Sdteske# 		dd is the day, mmm is the month in either numeric or alphabetic
892263150Sdteske# 		format, and yy[yy] is either a two or four digit year. This
893263150Sdteske# 		variable also accepts a relative date in the form of +n[mhdwoy]
894263150Sdteske# 		where n is a decimal, octal (leading 0) or hexadecimal (leading
895263150Sdteske# 		0x) digit followed by the number of Minutes, Hours, Days,
896263150Sdteske# 		Weeks, Months or Years from the current date at which the
897263150Sdteske# 		expiration time is to be set. If unset, password expiry is
898263150Sdteske# 		unchanged. If set but NULL, password expiration is disabled
899263150Sdteske# 		(same as setting a value of `0').
900263150Sdteske# 	VAR_USER_SHELL [Optional]
901263150Sdteske# 		Path to login shell to set. If NULL or unset, the shell is
902263150Sdteske# 		unchanged.
903263150Sdteske# 	VAR_USER_UID [Optional]
904263150Sdteske# 		Numerical user ID to set. If NULL or unset, the user ID is
905263150Sdteske# 		unchanged.
906263150Sdteske#
907263150Sdteske# Returns success if the user account was successfully modified.
908263150Sdteske#
909263150Sdteskef_user_edit()
910263150Sdteske{
911263150Sdteske	local funcname=f_user_edit
912263150Sdteske	local title # Calculated below
913263150Sdteske	local alert=f_show_msg no_confirm=
914263150Sdteske
915263150Sdteske	f_getvar $VAR_NO_CONFIRM no_confirm
916263150Sdteske	[ "$no_confirm" ] && alert=f_show_info
917263150Sdteske
918263150Sdteske	local input
919263150Sdteske	f_getvar 3:-\$$VAR_USER input "$1"
920263150Sdteske
921263150Sdteske	#
922263150Sdteske	# NB: pw(8) has a ``feature'' wherein `-n name' can be taken as UID
923263150Sdteske	# instead of name. Work-around is to also pass `-u UID' at the same
924263150Sdteske	# time (any UID will do; but `-1' is appropriate for this context).
925263150Sdteske	#
926263150Sdteske	if [ "$input" ] && ! f_quietly pw usershow -n "$input" -u -1; then
927263150Sdteske		f_show_err "$msg_login_not_found" "$input"
928263150Sdteske		return $FAILURE
929263150Sdteske	fi
930263150Sdteske
931263150Sdteske	if f_interactive && [ ! "$input" ]; then
932263150Sdteske		f_dialog_menu_user_list || return $SUCCESS
933263150Sdteske		f_dialog_menutag_fetch input
934263150Sdteske		[ "$input" = "X $msg_exit" ] && return $SUCCESS
935263150Sdteske	elif [ ! "$input" ]; then
936263150Sdteske		f_show_err "$msg_no_user_specified"
937263150Sdteske		return $FAILURE
938263150Sdteske	fi
939263150Sdteske
940263150Sdteske	local user_account_expire user_class user_gecos user_gid user_home_dir
941263150Sdteske	local user_member_groups user_name user_password user_password_expire
942263150Sdteske	local user_shell user_uid # Variables created by f_input_user() below
943263150Sdteske	if ! f_input_user "$input"; then
944263150Sdteske		f_show_err "$msg_login_not_found" "$input"
945263150Sdteske		return $FAILURE
946263150Sdteske	fi
947263150Sdteske
948263150Sdteske	#
949263150Sdteske	# Override values probed by f_input_user() with desired values
950263150Sdteske	#
951263150Sdteske	f_isset $VAR_USER_GID   && f_getvar $VAR_USER_GID   user_gid
952263150Sdteske	f_isset $VAR_USER_HOME  && f_getvar $VAR_USER_HOME  user_home_dir
953263150Sdteske	f_isset $VAR_USER_SHELL && f_getvar $VAR_USER_SHELL user_shell
954263150Sdteske	f_isset $VAR_USER_UID   && f_getvar $VAR_USER_UID   user_uid
955263150Sdteske	local user_dotfiles_create= user_home_create=
956263150Sdteske	f_getvar $VAR_USER_DOTFILES_CREATE:+\$msg_yes user_dotfiles_create
957263150Sdteske	f_getvar $VAR_USER_HOME_CREATE:+\$msg_yes     user_home_create
958263150Sdteske	local no_account_expire=
959263150Sdteske	if f_isset $VAR_USER_ACCOUNT_EXPIRE; then
960263150Sdteske		f_getvar $VAR_USER_ACCOUNT_EXPIRE user_account_expire
961263150Sdteske		[ "$user_account_expire" ] || no_account_expire=1
962263150Sdteske	fi
963263150Sdteske	local null_gecos=
964263150Sdteske	if f_isset $VAR_USER_GECOS; then
965263150Sdteske		f_getvar $VAR_USER_GECOS user_gecos
966263150Sdteske		[ "$user_gecos" ] || null_gecos=1
967263150Sdteske	fi
968263150Sdteske	local null_members=
969263150Sdteske	if f_isset $VAR_USER_GROUPS; then
970263150Sdteske		f_getvar $VAR_USER_GROUPS user_member_groups
971263150Sdteske		[ "$user_member_groups" ] || null_members=1
972263150Sdteske	fi
973263150Sdteske	local null_class=
974263150Sdteske	if f_isset $VAR_USER_LOGIN_CLASS; then
975263150Sdteske		f_getvar $VAR_USER_LOGIN_CLASS user_class
976263150Sdteske		[ "$user_class" ] || null_class=1
977263150Sdteske	fi
978263150Sdteske	local user_password_disable=
979263150Sdteske	if f_isset $VAR_USER_PASSWORD; then
980263150Sdteske		f_getvar $VAR_USER_PASSWORD user_password
981263150Sdteske		[ "$user_password" ] || user_password_disable=1
982263150Sdteske	fi
983263150Sdteske	local no_password_expire=
984263150Sdteske	if f_isset $VAR_USER_PASSWORD_EXPIRE; then
985263150Sdteske		f_getvar $VAR_USER_PASSWORD_EXPIRE user_password_expire
986263150Sdteske		[ "$user_password_expire" ] || no_password_expire=1
987263150Sdteske	fi
988263150Sdteske
989263150Sdteske	#
990263150Sdteske	# Loop until the user decides to Exit, Cancel, or presses ESC
991263150Sdteske	#
992263150Sdteske	title="$msg_edit_view $msg_user: $user_name"
993263150Sdteske	if f_interactive; then
994263150Sdteske		local mtag retval defaultitem=
995263150Sdteske		while :; do
996263150Sdteske			f_dialog_title "$title"
997263150Sdteske			f_dialog_menu_user_edit "$defaultitem"
998263150Sdteske			retval=$?
999263150Sdteske			f_dialog_title_restore
1000263150Sdteske			f_dialog_menutag_fetch mtag
1001263150Sdteske			f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
1002263150Sdteske			defaultitem="$mtag"
1003263150Sdteske
1004263150Sdteske			# Return if user either pressed ESC or chose Cancel/No
1005263150Sdteske			[ $retval -eq $DIALOG_OK ] || return $FAILURE
1006263150Sdteske
1007263150Sdteske			case "$mtag" in
1008263150Sdteske			X) # Save/Exit
1009263150Sdteske			   local var
1010263150Sdteske			   for var in account_expire class gecos gid home_dir \
1011263150Sdteske			   	member_groups name password_expire shell uid \
1012263150Sdteske			   ; do
1013263150Sdteske			   	local _user_$var
1014263150Sdteske			   	eval f_shell_escape \"\$user_$var\" _user_$var
1015263150Sdteske			   done
1016263150Sdteske
1017263150Sdteske			   local cmd="pw usermod -n '$_user_name'"
1018263150Sdteske			   [ "$user_gid"   ] && cmd="$cmd -g '$_user_gid'"
1019263150Sdteske			   [ "$user_shell" ] && cmd="$cmd -s '$_user_shell'"
1020263150Sdteske			   [ "$user_uid"   ] && cmd="$cmd -u '$_user_uid'"
1021263150Sdteske			   [ "$user_account_expire" -o \
1022263150Sdteske			     "$no_account_expire" ] &&
1023263150Sdteske			   	cmd="$cmd -e '$_user_account_expire'"
1024263150Sdteske			   [ "$user_class" -o "$null_class" ] &&
1025263150Sdteske			   	cmd="$cmd -L '$_user_class'"
1026263150Sdteske			   [ "$user_gecos" -o "$null_gecos" ] &&
1027263150Sdteske			   	cmd="$cmd -c '$_user_gecos'"
1028263150Sdteske			   [ "$user_home_dir"  ] &&
1029263150Sdteske			   	cmd="$cmd -d '$_user_home_dir'"
1030263150Sdteske			   [ "$user_member_groups" -o "$null_members" ] &&
1031263150Sdteske			   	cmd="$cmd -G '$_user_member_groups'"
1032263150Sdteske			   [ "$user_password_expire" -o \
1033263150Sdteske			     "$no_password_expire" ] &&
1034263150Sdteske			   	cmd="$cmd -p '$_user_password_expire'"
1035263150Sdteske
1036263150Sdteske			   # Execute the command
1037263150Sdteske			   if [ "$user_password_disable" ]; then
1038263150Sdteske			   	f_eval_catch $funcname pw '%s -h -' "$cmd"
1039263150Sdteske			   elif [ "$user_password" ]; then
1040263150Sdteske			   	echo "$user_password" | f_eval_catch \
1041263150Sdteske			   		$funcname pw '%s -h 0' "$cmd"
1042263150Sdteske			   else
1043263150Sdteske			   	f_eval_catch $funcname pw '%s' "$cmd"
1044263150Sdteske			   fi || continue
1045263150Sdteske
1046263150Sdteske			   # Create home directory if desired
1047263150Sdteske			   [ "${user_home_create:-$msg_no}" != "$msg_no" ] &&
1048263150Sdteske			   	f_user_create_homedir "$user_name"
1049263150Sdteske
1050263150Sdteske			   # Copy dotfiles if desired
1051263150Sdteske			   [ "${user_dotfiles_create:-$msg_no}" != \
1052263150Sdteske			     "$msg_no" ] && f_user_copy_dotfiles "$user_name"
1053263150Sdteske
1054263150Sdteske			   break # to success
1055263150Sdteske			   ;;
1056263150Sdteske			1) # Login (select different login from list)
1057263150Sdteske			   f_dialog_menu_user_list "$user_name" || continue
1058263150Sdteske			   f_dialog_menutag_fetch mtag
1059263150Sdteske
1060263150Sdteske			   [ "$mtag" = "X $msg_exit" ] && continue
1061263150Sdteske
1062263150Sdteske			   if ! f_input_user "$mtag"; then
1063263150Sdteske			   	f_show_err "$msg_login_not_found" "$mtag"
1064263150Sdteske			   	# Attempt to fall back to previous selection
1065263150Sdteske			   	f_input_user "$input" || return $FAILURE
1066263150Sdteske			   else
1067263150Sdteske			   	input="$mtag"
1068263150Sdteske			   fi
1069263150Sdteske			   title="$msg_edit_view $msg_user: $user_name"
1070263150Sdteske			   ;;
1071263150Sdteske			2) # Full Name
1072263150Sdteske			   f_dialog_input_gecos user_gecos "$user_gecos" &&
1073263150Sdteske			   	[ ! "$user_gecos" ] && null_gecos=1 ;;
1074263150Sdteske			3) # Password
1075263150Sdteske			   f_dialog_input_password \
1076263150Sdteske			   	user_password user_password_disable ;;
1077263150Sdteske			4) # User ID
1078263150Sdteske			   f_dialog_input_uid user_uid "$user_uid" ;;
1079263150Sdteske			5) # Group ID
1080263150Sdteske			   f_dialog_input_gid user_gid "$user_gid" ;;
1081263150Sdteske			6) # Member of Groups
1082263150Sdteske			   f_dialog_input_member_groups \
1083263150Sdteske			   	user_member_groups "$user_member_groups" &&
1084263150Sdteske			   	[ ! "$user_member_groups" ] &&
1085263150Sdteske			   	null_members=1 ;;
1086263150Sdteske			7) # Login Class
1087263150Sdteske			   f_dialog_input_class user_class "$user_class" &&
1088263150Sdteske			   	[ ! "$user_class" ] && null_class=1 ;;
1089263150Sdteske			8) # Password Expires On
1090263150Sdteske			   f_dialog_input_expire_password \
1091263150Sdteske			   	user_password_expire "$user_password_expire" &&
1092263150Sdteske			   	[ ! "$user_password_expire" ] &&
1093263150Sdteske			   	no_password_expire=1 ;;
1094263150Sdteske			9) # Account Expires On
1095263150Sdteske			   f_dialog_input_expire_account \
1096263150Sdteske			   	user_account_expire "$user_account_expire" &&
1097263150Sdteske			   	[ ! "$user_account_expire" ] &&
1098263150Sdteske			   	no_account_expire=1 ;;
1099263150Sdteske			A) # Home Directory
1100263150Sdteske			   f_dialog_input_home_dir \
1101263150Sdteske			   	user_home_dir "$user_home_dir" ;;
1102263150Sdteske			B) # Shell
1103263150Sdteske			   f_dialog_input_shell user_shell "$user_shell" ;;
1104263150Sdteske			C) # Create Home Directory?
1105263150Sdteske			   if [ "${user_home_create:-$msg_no}" != "$msg_no" ]
1106263150Sdteske			   then
1107263150Sdteske			   	user_home_create="$msg_no"
1108263150Sdteske			   else
1109263150Sdteske			   	user_home_create="$msg_yes"
1110263150Sdteske			   fi ;;
1111263150Sdteske			D) # Create Dotfiles?
1112263150Sdteske			   if [ "${user_dotfiles_create:-$msg_no}" != \
1113263150Sdteske			        "$msg_no" ]
1114263150Sdteske			   then
1115263150Sdteske			   	user_dotfiles_create="$msg_no"
1116263150Sdteske			   else
1117263150Sdteske			   	user_dotfiles_create="$msg_yes"
1118263150Sdteske			   fi ;;
1119263150Sdteske			esac
1120263150Sdteske		done
1121263150Sdteske	else
1122263150Sdteske		local var
1123263150Sdteske		for var in account_expire class gecos gid home_dir \
1124263150Sdteske			member_groups name password_expire shell uid \
1125263150Sdteske		; do
1126263150Sdteske			local _user_$var
1127263150Sdteske			eval f_shell_escape \"\$user_$var\" _user_$var
1128263150Sdteske		done
1129263150Sdteske
1130263150Sdteske		# Form the command
1131263150Sdteske		local cmd="pw usermod -n '$_user_name'"
1132263150Sdteske		[ "$user_gid"      ] && cmd="$cmd -g '$_user_gid'"
1133263150Sdteske		[ "$user_home_dir" ] && cmd="$cmd -d '$_user_home_dir'"
1134263150Sdteske		[ "$user_shell"    ] && cmd="$cmd -s '$_user_shell'"
1135263150Sdteske		[ "$user_uid"      ] && cmd="$cmd -u '$_user_uid'"
1136263150Sdteske		[ "$user_account_expire" -o "$no_account_expire" ] &&
1137263150Sdteske			cmd="$cmd -e '$_user_account_expire'"
1138263150Sdteske		[ "$user_class" -o "$null_class" ] &&
1139263150Sdteske			cmd="$cmd -L '$_user_class'"
1140263150Sdteske		[ "$user_gecos" -o "$null_gecos" ] &&
1141263150Sdteske			cmd="$cmd -c '$_user_gecos'"
1142263150Sdteske		[ "$user_member_groups" -o "$null_members" ] &&
1143263150Sdteske			cmd="$cmd -G '$_user_member_groups'"
1144263150Sdteske		[ "$user_password_expire" -o "$no_password_expire" ] &&
1145263150Sdteske			cmd="$cmd -p '$_user_password_expire'"
1146263150Sdteske
1147263150Sdteske		# Execute the command
1148263150Sdteske		local retval err
1149263150Sdteske		if [ "$user_password_disable" ]; then
1150263150Sdteske			f_eval_catch -k err $funcname pw '%s -h -' "$cmd"
1151263150Sdteske		elif [ "$user_password" ]; then
1152263150Sdteske			err=$( echo "$user_password" | f_eval_catch -de \
1153263150Sdteske				$funcname pw '%s -h 0' "$cmd" 2>&1 )
1154263150Sdteske		else
1155263150Sdteske			f_eval_catch -k err $funcname pw '%s' "$cmd"
1156263150Sdteske		fi
1157263150Sdteske		retval=$?
1158263150Sdteske		if [ $retval -ne $SUCCESS ]; then
1159263150Sdteske			f_show_err "%s" "$err"
1160263150Sdteske			return $retval
1161263150Sdteske		fi
1162263150Sdteske
1163263150Sdteske		# Create home directory if desired
1164263150Sdteske		[ "${user_home_create:-$msg_no}" != "$msg_no" ] &&
1165263150Sdteske			f_user_create_homedir "$user_name"
1166263150Sdteske
1167263150Sdteske		# Copy dotfiles if desired
1168263150Sdteske		[ "${user_dotfiles_create:-$msg_no}" != "$msg_no" ] &&
1169263150Sdteske			f_user_copy_dotfiles "$user_name"
1170263150Sdteske	fi
1171263150Sdteske
1172263150Sdteske	f_dialog_title "$title"
1173263150Sdteske	$alert "$msg_login_updated"
1174263150Sdteske	f_dialog_title_restore
1175263150Sdteske	[ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1
1176263150Sdteske
1177263150Sdteske	return $SUCCESS
1178263150Sdteske}
1179263150Sdteske
1180263150Sdteske############################################################ MAIN
1181263150Sdteske
1182263150Sdteskef_dprintf "%s: Successfully loaded." usermgmt/user.subr
1183263150Sdteske
1184263150Sdteskefi # ! $_USERMGMT_USER_SUBR
1185