]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - usr.sbin/bsdconfig/usermgmt/share/user_input.subr
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / usr.sbin / bsdconfig / usermgmt / share / user_input.subr
1 if [ ! "$_USERMGMT_USER_INPUT_SUBR" ]; then _USERMGMT_USER_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 # $FreeBSD$
29 #
30 ############################################################ INCLUDES
31
32 BSDCFG_SHARE="/usr/share/bsdconfig"
33 . $BSDCFG_SHARE/common.subr || exit 1
34 f_dprintf "%s: loading includes..." usermgmt/user_input.subr
35 f_include $BSDCFG_SHARE/dialog.subr
36 f_include $BSDCFG_SHARE/strings.subr
37
38 BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt"
39 f_include_lang $BSDCFG_LIBE/include/messages.subr
40 f_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 $var_to_set $user
52 #
53 # Get a list of additional groups $user is a member of in group(5).
54 #
55 f_get_member_groups()
56 {
57         f_replaceall "$( pw groupshow -a | awk -F: -v user="$2" '{
58                 if (!split($4, users, /,/)) next
59                 for (u in users) if (users[u] == user) { print $1; next }
60         }' )" "[$NL]" "," "$1"
61 }
62
63 # f_input_user $user
64 #
65 # Given $user name or id, create the environment variables user_name, user_uid,
66 # user_gid, user_class, user_password_expire, user_account_expire, user_gecos,
67 # user_home_dir, user_shell, and user_member_groups (and user_password is reset
68 # to NULL).
69 #
70 f_input_user()
71 {
72         local funcname=f_input_user
73         local user="$1"
74
75         f_dprintf "$funcname: Getting info for user \`%s'" "$user"
76         eval "$( pw usershow "$user" 2> /dev/null | awk -F: '
77         function set_value(var, value) {
78                 gsub(/'\''/, "'\''\\'\'\''", value)
79                 printf "user_%s='\'%s\''\n", var, value
80         }
81         {
82                 found = $1 != ""
83                 set_value("name",            $1 )
84                 set_value("password",        "" )
85                 set_value("uid",             $3 )
86                 set_value("gid",             $4 )
87                 set_value("class",           $5 )
88                 set_value("password_expire", $6 )
89                 set_value("account_expire",  $7 )
90                 set_value("gecos",           $8 )
91                 set_value("home_dir",        $9 )
92                 set_value("shell",           $10)
93                 exit
94         }
95         END { if (!found) print "false" }' )"
96         local retval=$?
97
98         f_dprintf "$funcname: Getting group memberships for user \`%s'" "$user"
99         f_get_member_groups user_member_groups "$user"
100
101         return $retval
102 }
103
104 # f_dialog_menu_user_list [$default]
105 #
106 # Allows the user to select a login from a list. Optionally, if present and
107 # non-NULL, initially highlight $default user.
108 #
109 f_dialog_menu_user_list()
110 {
111         local prompt=
112         local menu_list="
113                 'X $msg_exit' ''
114         " # END-QUOTE
115         local defaultitem="$1"
116         local hline="$hline_alnum_punc_tab_enter"
117
118         # Add users from passwd(5)
119         menu_list="$menu_list $( pw usershow -a | awk -F: '
120                 function mprint(tag, item) {
121                         gsub(/'\''/, "'\''\\'\'\''", tag)
122                         gsub(/'\''/, "'\''\\'\'\''", item)
123                         printf "'\'%s\'\ \'%s\''\n", tag, item
124                 }
125                 !/^[[:space:]]*(#|$)/ { mprint($1, $8) }
126         ' )"
127
128         local height width rows
129         eval f_dialog_menu_size height width rows \
130                                 \"\$DIALOG_TITLE\"     \
131                                 \"\$DIALOG_BACKTITLE\" \
132                                 \"\$prompt\"           \
133                                 \"\$hline\"            \
134                                 $menu_list
135
136         local menu_choice
137         menu_choice=$( eval $DIALOG \
138                 --title \"\$DIALOG_TITLE\"         \
139                 --backtitle \"\$DIALOG_BACKTITLE\" \
140                 --hline \"\$hline\"                \
141                 --ok-label \"\$msg_ok\"            \
142                 --cancel-label \"\$msg_cancel\"    \
143                 --default-item \"\$defaultitem\"   \
144                 --menu \"\$prompt\"                \
145                 $height $width $rows               \
146                 $menu_list                         \
147                 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
148         )
149         local retval=$?
150         f_dialog_menutag_store -s "$menu_choice"
151         return $retval
152 }
153
154 # f_dialog_input_member_groups $var_to_set [$member_groups]
155 #
156 # Allows the user to edit group memberships for a given user. If the user does
157 # not cancel or press ESC, the $var_to_set variable will hold the newly-
158 # configured value upon return.
159 #
160 f_dialog_input_member_groups()
161 {
162         local __var_to_set="$1" __input="$2"
163         local __prompt="$msg_member_of_groups"
164         local __menu_list="
165                 'X' '$msg_continue'
166                 '1' '$msg_select_groups_from_list'
167                 '2' '$msg_enter_groups_manually'
168         " # END-QUOTE
169         local __defaultitem=
170         local __hline="$hline_alnum_space_tab_enter"
171
172         local __mheight __mwidth __mrows
173         eval f_dialog_menu_size __mheight __mwidth __mrows \
174                                 \"\$DIALOG_TITLE\"     \
175                                 \"\$DIALOG_BACKTITLE\" \
176                                 \"\$__prompt\"         \
177                                 \"\$__hline\"          \
178                                 $__menu_list
179
180         local __menu_choice __retval
181         while :; do
182                 __menu_choice=$( eval $DIALOG \
183                         --title \"\$DIALOG_TITLE\"         \
184                         --backtitle \"\$DIALOG_BACKTITLE\" \
185                         --hline \"\$__hline\"              \
186                         --ok-label \"\$msg_ok\"            \
187                         --cancel-label \"\$msg_cancel\"    \
188                         --default-item \"\$__defaultitem\" \
189                         --menu \"\$__prompt\"              \
190                         $__mheight $__mwidth $__mrows      \
191                         $__menu_list                       \
192                         2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
193                 )
194                 __retval=$?
195                 f_dialog_data_sanitize __menu_choice
196                 __defaultitem="$__menu_choice"
197                 f_dprintf "retval=%u menu_choice=[%s]" \
198                           $__retval "$__menu_choice"
199
200                 # Return if user has either pressed ESC or chosen Cancel/No
201                 [ $__retval -eq $DIALOG_OK ] || return $__retval
202
203                 local __member_groups
204                 case "$__menu_choice" in
205                 X) # Exit
206                         break ;;
207                 1) # Select Groups from a list
208                         local __check_list= # Calculated below
209                         local __group_list __g __grp __length=0
210                         __group_list=$( pw groupshow -a |
211                                 awk -F: '!/^[[:space:]]*(#|$)/{print $1}' )
212                         while [ $__length -ne ${#__group_list} ]; do
213                                 __g="${__group_list%%$NL*}" # First line
214                                 f_shell_escape "$__g" __grp
215
216                                 # Format of a checklist entry: tag item status
217                                 # NB: Setting both tag/item to group name below
218                                 __check_list="$__check_list '$__grp' '$__grp'"
219                                 case "$__input" in
220                                 "$__g"|"$__g",*|*,"$__g",*|*,"$__g")
221                                         __check_list="$__check_list on" ;;
222                                 *)
223                                         __check_list="$__check_list off"
224                                 esac
225
226                                 __length=${#__group_list}
227                                 __group_list="${__group_list#*$NL}" # Kill line
228                         done
229
230                         local __cheight __cwidth __crows
231
232                         eval f_dialog_checklist_size \
233                                 __cheight __cwidth __crows \
234                                 \"\$DIALOG_TITLE\"     \
235                                 \"\$DIALOG_BACKTITLE\" \
236                                 \"\$__prompt\"         \
237                                 \"\$__hline\"          \
238                                 $__check_list
239                         __member_groups=$( eval $DIALOG \
240                                 --title \"\$DIALOG_TITLE\"         \
241                                 --backtitle \"\$DIALOG_BACKTITLE\" \
242                                 --separate-output                  \
243                                 --hline \"\$__hline\"              \
244                                 --ok-label \"\$msg_ok\"            \
245                                 --cancel-label \"\$msg_cancel\"    \
246                                 --checklist \"\$__prompt\"         \
247                                 $__cheight $__cwidth $__crows      \
248                                 $__check_list                      \
249                                 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
250                         ) || continue
251                                 # Return to previous menu if user either
252                                 # pressed ESC or chose Cancel/No
253                         f_dialog_data_sanitize __member_groups
254
255                         #
256                         # Convert the newline separated list into a comma-
257                         # separated one so that if the user switches over to
258                         # manual editing, list reflects checklist selections
259                         #
260                         f_replaceall "$__member_groups" "[$NL]" "," __input
261                         ;;
262                 2) # Enter Groups manually
263                         local __prompt2="$msg_groups"
264                         __prompt2="$__prompt2 ($msg_separated_by_commas)"
265
266                         f_dialog_input __member_groups \
267                                        "$__prompt2" "$__input" \
268                                        "$hline_num_tab_enter" || continue
269                                 # Return to previous menu if user either
270                                 # pressed ESC or chose Cancel/No
271
272                         #
273                         # Validate each of the groups the user has entered
274                         #
275                         local __all_groups_valid=1 __grp __grp_list
276                         f_replaceall "$__member_groups" "," " " __grp_list
277                         for __grp in $__grp_list; do
278                                 if ! f_quietly pw groupshow -n "$__grp"; then
279                                         f_show_msg "$msg_group_not_found" \
280                                                    "$__grp"
281                                         __all_groups_valid=
282                                         break
283                                 fi
284                         done
285                         [ "$__all_groups_valid" ] || continue
286
287                         __input="$__member_groups" 
288                         ;;
289                 esac
290         done
291
292         setvar "$__var_to_set" "$__input"
293         return $DIALOG_OK
294 }
295
296 # f_dialog_input_name $var_to_set [$name]
297 #
298 # Allows the user to enter a new username for a given user. If the user does
299 # not cancel or press ESC, the $var_to_set variable will hold the newly-
300 # configured value upon return.
301 #
302 f_dialog_input_name()
303 {
304         local __var_to_set="$1" __name="$2"
305
306         #
307         # Loop until the user provides taint-free/valid input
308         #
309         local __input="$__name"
310         while :; do
311                 # Return if user has either pressed ESC or chosen Cancel/No
312                 f_dialog_input __input "$msg_login" "$__input" \
313                                "$hline_alnum_tab_enter" || return $?
314
315                 # Check for no-change
316                 if [ "$__input" = "$__name" ]; then
317                         setvar "$__var_to_set" "$__input"
318                         return $DIALOG_OK
319                 fi
320
321                 # Check for NULL entry
322                 if [ ! "$__input" ]; then
323                         f_show_msg "$msg_login_is_empty"
324                         continue
325                 fi
326
327                 # Check for invalid entry
328                 case "$__input" in [!a-zA-Z]*)
329                         f_show_msg "$msg_login_must_start_with_letter"
330                         continue
331                 esac
332
333                 # Check for duplicate entry
334                 if f_quietly pw usershow -n "$__input"; then
335                         f_show_msg "$msg_login_already_used" "$__input"
336                         continue
337                 fi
338
339                 setvar "$__var_to_set" "$__input"
340                 break
341         done
342
343         return $DIALOG_OK
344 }
345
346 # f_dialog_input_password $var_to_set $dvar_to_set
347 #
348 # Prompt the user to enter a password (twice). If the user does not cancel or
349 # press ESC, $var_to_set will hold the confirmed user entry. Otherwise, if the
350 # user cancels or enters a NULL password (twice), they are given the choice to
351 # disable password authentication for the given login, wherein $dvar_to_set has
352 # a value of 1 to indicate password authentication should be disabled.
353 #
354 f_dialog_input_password()
355 {
356         local __var_to_set="$1" __dvar_to_set="$2"
357         local __prompt1="$msg_password"
358         local __prompt2="$msg_reenter_password"
359         local __hline="$hline_alnum_punc_tab_enter"
360
361         local __height1 __width1
362         f_dialog_inputbox_size __height1 __width1 \
363                                "$DIALOG_TITLE"     \
364                                "$DIALOG_BACKTITLE" \
365                                "$__prompt1"        \
366                                ""                  \
367                                "$__hline"
368         local __height2 __width2
369         f_dialog_inputbox_size __height2 __width2 \
370                                "$DIALOG_TITLE"     \
371                                "$DIALOG_BACKTITLE" \
372                                "$__prompt2"        \
373                                ""                  \
374                                "$__hline"
375
376         #
377         # Loop until the user provides taint-free/valid input
378         #
379         local __retval __password1 __password2
380         while :; do
381                 __password1=$( $DIALOG \
382                         --title "$DIALOG_TITLE"         \
383                         --backtitle "$DIALOG_BACKTITLE" \
384                         --hline "$__hline"              \
385                         --ok-label "$msg_ok"            \
386                         --cancel-label "$msg_cancel"    \
387                         --insecure                      \
388                         --passwordbox "$__prompt1"      \
389                         $__height1 $__width1            \
390                         2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
391                 ) || return $?
392                         # Return if user either pressed ESC or chose Cancel/No
393                 debug= f_dialog_line_sanitize __password1
394
395                 __password2=$( $DIALOG \
396                         --title "$DIALOG_TITLE"         \
397                         --backtitle "$DIALOG_BACKTITLE" \
398                         --hline "$__hline"              \
399                         --ok-label "$msg_ok"            \
400                         --cancel-label "$msg_cancel"    \
401                         --insecure                      \
402                         --passwordbox "$__prompt2"      \
403                         $__height2 $__width2            \
404                         2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
405                 ) || return $?
406                         # Return if user either pressed ESC or chose Cancel/No
407                 debug= f_dialog_line_sanitize __password2
408
409                 # Check for password mismatch
410                 if [ "$__password1" != "$__password2" ]; then
411                         f_show_msg "$msg_passwords_do_not_match"
412                         continue
413                 fi
414
415                 # Check for NULL entry
416                 if [ ! "$__password1" ]; then
417                         f_dialog_yesno "$msg_disable_password_auth_for_account"
418                         __retval=$?
419                         if [ $__retval -eq $DIALOG_ESC ]; then
420                                 return $__retval
421                         elif [ $__retval -eq $DIALOG_OK ]; then
422                                 setvar "$__dvar_to_set" 1
423                         else
424                                 continue # back to password prompt
425                         fi
426                 else
427                         setvar "$__dvar_to_set" ""
428                 fi
429
430                 setvar "$__var_to_set" "$__password1"
431                 break
432         done
433
434         return $DIALOG_OK
435 }
436
437 # f_dialog_input_gecos $var_to_set [$gecos]
438 #
439 # Allow the user to enter new GECOS information for a given user. This
440 # information is commonly used to store the ``Full Name'' of the user. If the
441 # user does not cancel or press ESC, the $var_to_set variable will hold the
442 # newly-configured value upon return.
443 #
444 f_dialog_input_gecos()
445 {
446         local __var_to_set="$1" __input="$2"
447
448         # Return if user has either pressed ESC or chosen Cancel/No
449         f_dialog_input __input "$msg_full_name" "$__input" \
450                        "$hline_alnum_punc_tab_enter" || return $?
451
452         setvar "$__var_to_set" "$__input"
453         return $DIALOG_OK
454 }
455
456 # f_dialog_input_uid $var_to_set [$uid]
457 #
458 # Allow the user to enter a new UID for a given user. If the user does not
459 # cancel or press ESC, the $var_to_set variable will hold the newly-configured
460 # value upon return.
461 #
462 f_dialog_input_uid()
463 {
464         local __var_to_set="$1" __input="$2"
465
466         # Return if user has either pressed ESC or chosen Cancel/No
467         f_dialog_input __input "$msg_user_id_leave_empty_for_default" \
468                        "$__input" "$hline_num_tab_enter" || return $?
469
470         setvar "$__var_to_set" "$__input"
471         return $DIALOG_OK
472 }
473
474 # f_dialog_input_gid $var_to_set [$gid]
475 #
476 # Allow the user to enter a new primary GID for a given user. If the user does
477 # not cancel or press ESC, the $var_to_set variable will hold the newly-
478 # configured value upon return.
479 #
480 f_dialog_input_gid()
481 {
482         local __var_to_set="$1" __input="$2"
483
484         # Return if user has either pressed ESC or chosen Cancel/No
485         f_dialog_input __input "$msg_group_id_leave_empty_for_default" \
486                        "$__input" "$hline_num_tab_enter" || return $?
487
488         setvar "$__var_to_set" "$__input"
489         return $DIALOG_OK
490 }
491
492 # f_dialog_input_class $var_to_set [$class]
493 #
494 # Allow the user to enter a new login class for a given user. If the user does
495 # not cancel or press ESC, the $var_to_set variable will hold the newly-
496 # configured value upon return.
497 #
498 f_dialog_input_class()
499 {
500         local __var_to_set="$1" __input="$2"
501
502         # Return if user has either pressed ESC or chosen Cancel/No
503         f_dialog_input __input "$msg_login_class" "$__input" \
504                        "$hline_alnum_tab_enter" || return $?
505
506         setvar "$__var_to_set" "$__input"
507         return $DIALOG_OK
508 }
509
510 # f_dialog_input_expire_password $var_to_set [$seconds]
511 #
512 # Allow the user to enter a date/time (in number-of-seconds since the `epoch')
513 # for when a given user's password must be changed. If the user does not cancel
514 # or press ESC, the $var_to_set variable will hold the newly-configured value
515 # upon return.
516 #
517 f_dialog_input_expire_password()
518 {
519         local __var_to_set="$1" __input="$2"
520         local __prompt="$msg_password_expires_on"
521         local __menu_list="
522                 '1' '$msg_password_does_not_expire'
523                 '2' '$msg_edit_date_time_with_a_calendar'
524                 '3' '$msg_enter_value_manually'
525         " # END-QUOTE
526         local __defaultitem= # Calculated below
527         local __hline="$hline_num_arrows_tab_enter"
528
529         local __mheight __mwidth __mrows
530         eval f_dialog_menu_size __mheight __mwidth __mrows \
531                                 \"\$DIALOG_TITLE\"     \
532                                 \"\$DIALOG_BACKTITLE\" \
533                                 \"\$__prompt\"         \
534                                 \"\$__hline\"          \
535                                 $__menu_list
536         local __cheight __cwidth
537         f_dialog_calendar_size __cheight __cwidth \
538                                "$DIALOG_TITLE"     \
539                                "$DIALOG_BACKTITLE" \
540                                "$__prompt"         \
541                                "$__hline"
542         local __theight __twidth
543         f_dialog_timebox_size __theight __twidth \
544                               "$DIALOG_TITLE"     \
545                               "$DIALOG_BACKTITLE" \
546                               "$__prompt"         \
547                               "$__hline"
548
549         #
550         # Loop until the user provides taint-free/cancellation-free input
551         #
552         local __retval __date_type
553         while :; do
554                 __date_type=$( eval $DIALOG \
555                         --title \"\$DIALOG_TITLE\"         \
556                         --backtitle \"\$DIALOG_BACKTITLE\" \
557                         --hline \"\$__hline\"              \
558                         --default-item \"\$__defaultitem\" \
559                         --ok-label \"\$msg_ok\"            \
560                         --cancel-label \"\$msg_cancel\"    \
561                         --menu \"\$__prompt\"              \
562                         $__mheight $__mwidth $__mrows      \
563                         $__menu_list                       \
564                         2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
565                 )
566                 __retval=$?
567                 f_dialog_data_sanitize __date_type
568                 __defaultitem="$__date_type"
569                 f_dprintf "retval=%u date_type=[%s]" $__retval "$__date_type"
570
571                 # Return if user has either pressed ESC or chosen Cancel/No
572                 [ $__retval -eq $DIALOG_OK ] || return $__retval
573
574                 case "$__date_type" in
575                 1) # Password does not expire
576                         __input= break ;;
577
578                 2) # Edit date/time with a calendar
579                         local __input_date __input_time __ret_date __ret_time
580
581                         local __seconds="$__input"
582                         { f_isinteger "$__seconds" && [ $__seconds -gt 0 ]; } ||
583                                 __seconds=
584                         __input_date=$( date -j -f "%s" -- "$__seconds" \
585                                                 "+%d %m %Y" 2> /dev/null )
586                         __ret_date=$( eval $DIALOG \
587                                 --title \"\$DIALOG_TITLE\"          \
588                                 --backtitle \"\$DIALOG_BACKTITLE\"  \
589                                 --hline \"\$__hline\"               \
590                                 --ok-label \"\$msg_ok\"             \
591                                 --cancel-label \"\$msg_cancel\"     \
592                                 --calendar \"\$__prompt\"           \
593                                 $__cheight $__cwidth                \
594                                 $__input_date                       \
595                                 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
596                         )
597                         __retval=$?
598                         f_dialog_data_sanitize __ret_date
599                         f_dprintf "retval=%u ret_date=[%s]" \
600                                   $__retval "$__ret_date"
601
602                         # Return to menu if either ESC or Cancel/No
603                         [ $__retval -eq $DIALOG_OK ] || continue
604
605                         __input_time=
606                         [ "$__seconds" ] && __input_time=$( date -j \
607                                 -f %s -- "$__input" "+%H %M %S" 2> /dev/null )
608                         __ret_time=$( eval $DIALOG \
609                                 --title \"\$DIALOG_TITLE\"         \
610                                 --backtitle \"\$DIALOG_BACKTITLE\" \
611                                 --hline \"\$__hline\"              \
612                                 --ok-label \"\$msg_ok\"            \
613                                 --cancel-label \"\$msg_cancel\"    \
614                                 --timebox \"\$__prompt\"           \
615                                 $__theight $__twidth               \
616                                 $__input_time                      \
617                                 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
618                         )
619                         __retval=$?
620                         f_dialog_data_sanitize __ret_time
621                         f_dprintf "retval=%u ret_time=[%s]" \
622                                   $__retval "$__ret_time"
623
624                         # Return to menu if either ESC or Cancel/No
625                         [ $__retval -eq $DIALOG_OK ] || continue
626
627                         __input=$( date -j -f "%d/%m/%Y %T" -- \
628                                 "$__ret_date $__ret_time" +%s 2> /dev/null )
629                         f_dprintf "input=[%s]" "$__input"
630                         break ;;
631
632                 3) # Enter value manually
633                         local __msg __new_input
634                         f_sprintf __msg "$msg_password_expire_manual_edit" \
635                                         "$( date -r 0 "+%c %Z" )"
636
637                         # Return to menu if either ESC or Cancel/No
638                         f_dialog_input __new_input \
639                                 "$__msg" "$__input" "$__hline" || continue
640
641                         __input="$__new_input"
642                         f_dprintf "input=[%s]" "$__input"
643                         break ;;
644
645                 esac
646
647         done # Loop forever
648
649         setvar "$__var_to_set" "$__input"
650         return $DIALOG_OK
651 }
652
653 # f_dialog_input_expire_account $var_to_set [$seconds]
654 #
655 # Allow the user to enter a date/time (in number-of-seconds since the `epoch')
656 # for when a given user's account should become expired. If the user does not
657 # cancel or press ESC, the $var_to_set variable will hold the newly-configured
658 # value upon return.
659 #
660 f_dialog_input_expire_account()
661 {
662         local __var_to_set="$1" __input="$2"
663         local __prompt="$msg_account_expires_on"
664         local __menu_list="
665                 '1' '$msg_account_does_not_expire'
666                 '2' '$msg_edit_date_time_with_a_calendar'
667                 '3' '$msg_enter_value_manually'
668         " # END-QUOTE
669         local __defaultitem= # Calculated below
670         local __hline="$hline_num_arrows_tab_enter"
671
672         local __mheight __mwidth __mrows 
673         eval f_dialog_menu_size __mheight __mwidth __mrows \
674                                 \"\$DIALOG_TITLE\"     \
675                                 \"\$DIALOG_BACKTITLE\" \
676                                 \"\$__prompt\"         \
677                                 \"\$__hline\"          \
678                                 $__menu_list
679         local __cheight __cwidth
680         f_dialog_calendar_size __cheight __cwidth \
681                                "$DIALOG_TITLE"     \
682                                "$DIALOG_BACKTITLE" \
683                                "$__prompt"         \
684                                "$__hline"
685         local __theight __twidth
686         f_dialog_timebox_size __theight __twidth \
687                               "$DIALOG_TITLE"     \
688                               "$DIALOG_BACKTITLE" \
689                               "$__prompt"         \
690                               "$__hline"
691
692         #
693         # Loop until the user provides taint-free/cancellation-free input
694         #
695         local __retval __date_type
696         while :; do
697                 __date_type=$( eval $DIALOG \
698                         --title \"\$DIALOG_TITLE\"         \
699                         --backtitle \"\$DIALOG_BACKTITLE\" \
700                         --hline \"\$__hline\"              \
701                         --default-item \"\$__defaultitem\" \
702                         --ok-label \"\$msg_ok\"            \
703                         --cancel-label \"\$msg_cancel\"    \
704                         --menu \"\$__prompt\"              \
705                         $__mheight $__mwidth $__mrows      \
706                         $__menu_list                       \
707                         2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
708                 )
709                 __retval=$?
710                 f_dialog_data_sanitize __date_type
711                 __defaultitem="$__date_type"
712                 f_dprintf "retval=%u date_type=[%s]" $__retval "$__date_type"
713
714                 # Return if user has either pressed ESC or chosen Cancel/No
715                 [ $__retval -eq $DIALOG_OK ] || return $__retval
716
717                 case "$__date_type" in
718                 1) # Account does not expire
719                         __input= break ;;
720
721                 2) # Edit date/time with a calendar
722                         local __input_date __input_time __ret_date __ret_time
723
724                         local __seconds="$__input"
725                         { f_isinteger "$__seconds" && [ $__seconds -gt 0 ]; } ||
726                                 __seconds=
727                         __input_date=$( date -j -f "%s" -- "$__seconds" \
728                                                 "+%d %m %Y" 2> /dev/null )
729                         __ret_date=$( eval $DIALOG \
730                                 --title \"\$DIALOG_TITLE\"          \
731                                 --backtitle \"\$DIALOG_BACKTITLE\"  \
732                                 --hline \"\$__hline\"               \
733                                 --ok-label \"\$msg_ok\"             \
734                                 --cancel-label \"\$msg_cancel\"     \
735                                 --calendar \"\$__prompt\"           \
736                                 $__cheight $__cwidth                \
737                                 $__input_date                       \
738                                 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
739                         )
740                         __retval=$?
741                         f_dialog_data_sanitize __ret_date
742                         f_dprintf "retval=%u ret_date=[%s]" \
743                                   $__retval "$__ret_date"
744
745                         # Return to menu if either ESC or Cancel/No
746                         [ $__retval -eq $DIALOG_OK ] || continue
747
748                         __input_time=
749                         [ "$__seconds" ] && __input_time=$( date -j \
750                                 -f %s -- "$__input" "+%H %M %S" 2> /dev/null )
751                         __ret_time=$( eval $DIALOG \
752                                 --title \"\$DIALOG_TITLE\"         \
753                                 --backtitle \"\$DIALOG_BACKTITLE\" \
754                                 --hline \"\$__hline\"              \
755                                 --ok-label \"\$msg_ok\"            \
756                                 --cancel-label \"\$msg_cancel\"    \
757                                 --timebox \"\$__prompt\"           \
758                                 $__theight $__twidth               \
759                                 $__input_time                      \
760                                 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
761                         )
762                         __retval=$?
763                         f_dialog_data_sanitize __ret_time
764                         f_dprintf "retval=%u ret_time=[%s]" \
765                                   $__retval "$__ret_time"
766
767                         # Return to menu if either ESC or Cancel/No
768                         [ $__retval -eq $DIALOG_OK ] || continue
769
770                         __input=$( date -j -f "%d/%m/%Y %T" -- \
771                                 "$ret_date $ret_time" +%s 2> /dev/null )
772                         f_dprintf "input=[%s]" "$__input"
773                         break ;;
774
775                 3) # Enter value manually
776                         local __msg __new_input
777                         f_sprintf __msg "$msg_account_expire_manual_edit" \
778                                         "$( date -r 0 "+%c %Z" )"
779
780                         # Return to menu if either ESC or Cancel/No
781                         f_dialog_input __new_input \
782                                 "$__msg" "$__input" "$__hline" || continue
783
784                         __input="$__new_input"
785                         f_dprintf "input=[%s]" "$__input"
786                         break ;;
787
788                 esac
789
790         done # Loop forever
791
792         setvar "$__var_to_set" "$__input"
793         return $DIALOG_OK
794 }
795
796 # f_dialog_input_home_dir $var_to_set [$home_dir]
797 #
798 # Allow the user to enter a new home directory for a given login. If the user
799 # does not cancel or press ESC, the $var_to_set variable will hold the newly-
800 # configured value upon return.
801 #
802 f_dialog_input_home_dir()
803 {
804         local __var_to_set="$1" __input="$2"
805
806         # Return if user has either pressed ESC or chosen Cancel/No
807         f_dialog_input __input "$msg_home_directory" "$__input" \
808                        "$hline_alnum_punc_tab_enter" || return $?
809
810         setvar "$__var_to_set" "$__input"
811         return $DIALOG_OK
812 }
813
814 # f_dialog_input_home_create $var_to_set
815 #
816 # Prompt the user to confirm creation of a given login's home directory. If the
817 # user does not cancel (by choosing "No") or press ESC, the $var_to_set
818 # variable will hold $msg_yes upon return, otherwise $msg_no. Use these return
819 # variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible.
820 #
821 f_dialog_input_home_create()
822 {
823         local __var_to_set="$1"
824
825         f_dialog_yesno "$msg_create_home_directory"
826         local __retval=$?
827
828         if [ $__retval -eq $DIALOG_OK ]; then
829                 setvar "$__var_to_set" "$msg_yes"
830         else
831                 setvar "$__var_to_set" "$msg_no"
832         fi
833
834         [ $__retval -ne $DIALOG_ESC ] # return failure if user pressed ESC
835 }
836
837 # f_dialog_input_group_delete $var_to_set [$group]
838 #
839 # Prompt the user to confirm deletion of a given login's primary group. If the
840 # user does not cancel (by choosing "No") or press ESC, the $var_to_set
841 # variable will hold $msg_yes upon return, otherwise $msg_no. Use these return
842 # variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible.
843 #
844 f_dialog_input_group_delete()
845 {
846         local __var_to_set="$1" __group="$2"
847
848         if f_isinteger "$__group"; then
849                 if [ $__group -lt 1000 ]; then
850                         f_dialog_noyes "$msg_delete_primary_group"
851                 else
852                         f_dialog_yesno "$msg_delete_primary_group"
853                 fi
854         elif [ "$__group" ]; then
855                 local __gid=0
856                 __gid=$( pw groupshow "$__group" | awk -F: '{print $3}' )
857                 if f_isinteger "$__gid" && [ $__gid -lt 1000 ]; then
858                         f_dialog_noyes "$msg_delete_primary_group"
859                 else
860                         f_dialog_yesno "$msg_delete_primary_group"
861                 fi
862         else
863                 f_dialog_yesno "$msg_delete_primary_group"
864         fi
865         local __retval=$?
866
867         if [ $__retval -eq $DIALOG_OK ]; then
868                 setvar "$__var_to_set" "$msg_yes"
869         else
870                 setvar "$__var_to_set" "$msg_no"
871         fi
872
873         [ $__retval -ne $DIALOG_ESC ] # return failure if user pressed ESC
874 }
875
876 # f_dialog_input_home_delete $var_to_set
877 #
878 # Prompt the user to confirm deletion of a given login's home directory. If the
879 # user does not cancel (by choosing "No") or press ESC, the $var_to_set
880 # variable will hold $msg_yes upon return, otherwise $msg_no. Use these return
881 # variables ($msg_yes and $msg_no) for comparisons to be i18n-compatible.
882 #
883 f_dialog_input_home_delete()
884 {
885         local __var_to_set="$1"
886
887         f_dialog_yesno "$msg_delete_home_directory"
888         local __retval=$?
889
890         if [ $__retval -eq $DIALOG_OK ]; then
891                 setvar "$__var_to_set" "$msg_yes"
892         else
893                 setvar "$__var_to_set" "$msg_no"
894         fi
895
896         [ $__retval -ne $DIALOG_ESC ] # return failure if user pressed ESC
897 }
898
899 # f_dialog_input_dotfiles_create $var_to_set
900 #
901 # Prompt the user to confirm population of a given login's home directory with
902 # sample dotfiles. If the user does not cancel (by choosing "No") or press ESC,
903 # the $var_to_set variable will hold $msg_yes upon return, otherwise $msg_no.
904 # Use these return variables ($msg_yes and $msg_no) for comparison to be i18n-
905 # compatible.
906 #
907 f_dialog_input_dotfiles_create()
908 {
909         local __var_to_set="$1"
910
911         f_dialog_yesno "$msg_create_dotfiles"
912         local __retval=$?
913
914         if [ $__retval -eq $DIALOG_OK ]; then
915                 setvar "$__var_to_set" "$msg_yes"
916         else
917                 setvar "$__var_to_set" "$msg_no"
918         fi
919
920         [ $__retval -ne $DIALOG_ESC ] # return failure if user pressed ESC
921 }
922
923 # f_dialog_input_shell $var_to_set [$shell]
924 #
925 # Allow the user to select a new shell for a given login. If the user does not
926 # cancel or press ESC, the $var_to_set variable will hold the newly-configured
927 # value upon return.
928 #
929 f_dialog_input_shell()
930 {
931         local __funcname=f_dialog_input_shell
932         local __var_to_set="$1" __input="$2"
933         local __prompt="$msg_select_login_shell"
934         local __radio_list= # Calculated below
935         local __defaultitem="$2"
936         local __hline="$hline_arrows_space_tab_enter"
937
938         #
939         # Generate the radiolist of shells
940         #
941         local __shell_list __s __shell __length=0
942         f_eval_catch -k __shell_list $__funcname awk "awk '%s' \"%s\"" \
943                 '!/^[[:space:]]*(#|$)/{print}' "$ETC_SHELLS" || return $FAILURE
944         while [ $__length -ne ${#__shell_list} ]; do
945                 __s="${__shell_list%%$NL*}" # First line
946                 f_shell_escape "$__s" __shell
947
948                 # Format of a radiolist entry: tag item status
949                 if [ "$__s" = "$__input" ]; then
950                         __radio_list="$__radio_list '$__shell' '' 'on'"
951                 else
952                         __radio_list="$__radio_list '$__shell' '' 'off'"
953                 fi
954
955                 __length=${#__shell_list}
956                 __shell_list="${__shell_list#*$NL}" # Kill line
957         done
958
959         local __height __width __rows
960         eval f_dialog_radiolist_size __height __width __rows \
961                                      \"\$DIALOG_TITLE\"     \
962                                      \"\$DIALOG_BACKTITLE\" \
963                                      \"\$__prompt\"         \
964                                      \"\$__hline\"          \
965                                      $__radio_list
966
967         __input=$( eval $DIALOG \
968                 --title \"\$DIALOG_TITLE\"         \
969                 --backtitle \"\$DIALOG_BACKTITLE\" \
970                 --hline \"\$__hline\"              \
971                 --ok-label \"\$msg_ok\"            \
972                 --cancel-label \"\$msg_cancel\"    \
973                 --default-item \"\$__defaultitem\" \
974                 --radiolist \"\$__prompt\"         \
975                 $__height $__width $__rows         \
976                 $__radio_list                      \
977                 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
978         ) || return $?
979                 # Return if user either pressed ESC or chose Cancel/No
980         f_dialog_data_sanitize __input
981
982         setvar "$__var_to_set" "$__input"
983         return $DIALOG_OK
984 }
985
986 # f_dialog_menu_user_add [$defaultitem]
987 #
988 # Present a menu detailing the properties of a login that is about to be added.
989 # The user's menu choice is available using f_dialog_menutag_fetch(). Returns
990 # success unless the user chose Cancel or pressed ESC. Data to display is taken
991 # from environment variables user_account_expire, user_class,
992 # user_dotfiles_create, user_gecos, user_gid, user_home_create, user_home_dir,
993 # user_member_groups, user_name, user_password_expire, user_shell, and
994 # user_uid. If $defaultitem is present and non-NULL, initially highlight the
995 # item in the menu.
996 #
997 f_dialog_menu_user_add()
998 {
999         local funcname=f_dialog_menu_user_add
1000         local prompt="$msg_save_exit_or_cancel"
1001         local menu_list # Calculated below
1002         local defaultitem="$1"
1003         local hline="$hline_arrows_tab_enter"
1004
1005         # Attempt to convert numeric UNIX time to calendar date/time
1006         local user_account_expires_on=
1007         if f_isinteger "$user_account_expire"; then
1008                 [ "$user_account_expire" -ne 0 ] && user_account_expires_on=$(
1009                         date -r "$user_account_expire" "+%F %T %Z"
1010                 )
1011         else
1012                 user_account_expires_on="$user_account_expire"
1013         fi
1014         local user_password_expires_on=
1015         if f_isinteger "$user_password_expire"; then
1016                 [ $user_password_expire -ne 0 ] && user_password_expires_on=$(
1017                         date -r "$user_password_expire" "+%F %T %Z"
1018                 )
1019         else
1020                 user_password_expires_on="$user_password_expire"
1021         fi
1022
1023         # Attempt to translate a numeric GID into `number (name)'
1024         if f_isinteger "$user_gid"; then
1025                 local user_group
1026                 user_group=$( pw groupshow -g "$user_gid" 2> /dev/null ) &&
1027                         user_gid="$user_gid (${user_group%%:*})"
1028         fi
1029
1030         # Localize potentially hostile variables and escape their values
1031         # to the local variable (see f_shell_escape() of `strings.subr')
1032         local var
1033         for var in account_expires_on class dotfiles_create gecos gid \
1034                 home_create home_dir member_groups name password_expires_on \
1035                 shell uid \
1036         ; do
1037                 local _user_$var
1038                 eval f_shell_escape \"\$user_$var\" _user_$var
1039         done
1040
1041         menu_list="
1042                 'X' '$msg_add/$msg_exit'
1043                 '1' '$msg_login: $_user_name'
1044                 '2' '$msg_full_name: $_user_gecos'
1045                 '3' '$msg_password: -----'
1046                 '4' '$msg_user_id: $_user_uid'
1047                 '5' '$msg_group_id: $_user_gid'
1048                 '6' '$msg_member_of_groups: $_user_member_groups'
1049                 '7' '$msg_login_class: $_user_class'
1050                 '8' '$msg_password_expires_on: $_user_password_expires_on'
1051                 '9' '$msg_account_expires_on: $_user_account_expires_on'
1052                 'A' '$msg_home_directory: $_user_home_dir'
1053                 'B' '$msg_shell: $_user_shell'
1054         " # END-QUOTE
1055         case "$user_home_dir" in
1056         /|/nonexistent|/var/empty) menu_list="$menu_list
1057                 '-' '$msg_create_home_directory: $msg_n_a'
1058                 '-' '$msg_create_dotfiles: $msg_n_a'
1059            " # END-QUOTE
1060            ;;
1061         *) if [ -d "$user_home_dir" ]; then menu_list="$menu_list
1062                 '-' '$msg_create_home_directory: $msg_n_a'
1063                 'D' '$msg_create_dotfiles: ${_user_dotfiles_create:-$msg_no}'
1064            " # END-QUOTE
1065            else menu_list="$menu_list
1066                 'C' '$msg_create_home_directory: ${_user_home_create:-$msg_no}'
1067                 'D' '$msg_create_dotfiles: ${_user_dotfiles_create:-$msg_no}'
1068            " # END-QUOTE
1069            fi
1070         esac
1071
1072         local height width rows
1073         eval f_dialog_menu_size height width rows \
1074                                 \"\$DIALOG_TITLE\"     \
1075                                 \"\$DIALOG_BACKTITLE\" \
1076                                 \"\$prompt\"           \
1077                                 \"\$hline\"            \
1078                                 $menu_list
1079
1080         local menu_choice
1081         menu_choice=$( eval $DIALOG \
1082                 --title \"\$DIALOG_TITLE\"         \
1083                 --backtitle \"\$DIALOG_BACKTITLE\" \
1084                 --hline \"\$hline\"                \
1085                 --ok-label \"\$msg_ok\"            \
1086                 --cancel-label \"\$msg_cancel\"    \
1087                 --default-item \"\$defaultitem\"   \
1088                 --keep-tite                        \
1089                 --menu \"\$prompt\"                \
1090                 $height $width $rows               \
1091                 $menu_list                         \
1092                 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
1093         )
1094         local retval=$?
1095         f_dialog_data_sanitize menu_choice
1096         f_dialog_menutag_store "$menu_choice"
1097         return $retval
1098 }
1099
1100 # f_dialog_menu_user_delete $user [$defaultitem]
1101 #
1102 # Present a menu detailing the properties of a login that is about to be
1103 # deleted. The user's menu choice is available using f_dialog_menutag_fetch().
1104 # Returns success unless the user chose Cancel or pressed ESC. Data to display
1105 # is populated automatically from the system accounting database for the given
1106 # $user argument with the exception of two environment variables:
1107 # user_group_delete and user_home_delete. If $defaultitem is present and non-
1108 # NULL, initially highlight the item in the menu.
1109 #
1110 f_dialog_menu_user_delete()
1111 {
1112         local prompt="$msg_delete_exit_or_cancel"
1113         local menu_list # Calculated below
1114         local defaultitem="$2"
1115         local hline="$hline_arrows_tab_enter"
1116
1117         local user_name user_password user_uid user_gid user_class
1118         local user_password_expire user_account_expire user_gecos
1119         local user_home_dir user_shell user_member_groups
1120         f_input_user "$1"
1121
1122         # Attempt to convert numeric UNIX time to calendar date/time
1123         local user_account_expires_on=
1124         if f_isinteger "$user_account_expire"; then
1125                 [ "$user_account_expire" -ne 0 ] && user_account_expires_on=$(
1126                         date -r "$user_account_expire" "+%F %T %Z"
1127                 )
1128         else
1129                 user_account_expires_on="$user_account_expire"
1130         fi
1131         local user_password_expires_on=
1132         if f_isinteger "$user_password_expire"; then
1133                 [ $user_password_expire -ne 0 ] && user_password_expires_on=$(
1134                         date -r "$user_password_expire" "+%F %T %Z"
1135                 )
1136         else
1137                 user_password_expires_on="$user_password_expire"
1138         fi
1139
1140         # Attempt to translate a numeric GID into `number (name)'
1141         if f_isinteger "$user_gid"; then
1142                 local user_group
1143                 user_group=$( pw groupshow -g "$user_gid" 2> /dev/null ) &&
1144                         user_gid="$user_gid (${user_group%%:*})"
1145         fi
1146
1147         # Localize potentially hostile variables and escape their values
1148         # to the local variable (see f_shell_escape() of `strings.subr')
1149         local var
1150         for var in account_expires_on class gecos gid group_delete \
1151                 home_delete home_dir member_groups name password_expires_on \
1152                 shell uid \
1153         ; do
1154                 local _user_$var
1155                 eval f_shell_escape \"\$user_$var\" _user_$var
1156         done
1157
1158         menu_list="
1159                 'X' '$msg_delete/$msg_exit'
1160                 '1' '$msg_login: $_user_name'
1161                 '-' '$msg_full_name: $_user_gecos'
1162                 '-' '$msg_password: -----'
1163                 '-' '$msg_user_id: $_user_uid'
1164                 '-' '$msg_group_id: $_user_gid'
1165                 '-' '$msg_group_members: $_user_member_groups'
1166                 '-' '$msg_login_class: $_user_class'
1167                 '-' '$msg_password_expires_on: $_user_password_expires_on'
1168                 '-' '$msg_account_expires_on: $_user_account_expires_on'
1169                 '-' '$msg_home_directory: $_user_home_dir'
1170                 '-' '$msg_shell: $_user_shell'
1171         " # END-QUOTE
1172         if f_quietly pw groupshow -g "$user_gid"; then menu_list="$menu_list
1173                 'C' '$msg_delete_primary_group: ${_user_group_delete:-$msg_no}'
1174         " # END-QUOTE
1175         else menu_list="$menu_list
1176                 '-' '$msg_delete_primary_group: $msg_n_a'
1177         " # END-QUOTE
1178         fi
1179         case "$user_home_dir" in
1180         /|/nonexistent|/var/empty) menu_list="$menu_list
1181                 '-' '$msg_delete_home_directory: $msg_n_a'
1182            " # END-QUOTE
1183            ;;
1184         *) if [ -d "$user_home_dir" ]; then menu_list="$menu_list
1185                 'D' '$msg_delete_home_directory: ${_user_home_delete:-$msg_no}'
1186            " # END-QUOTE
1187            else menu_list="$menu_list
1188                 '-' '$msg_delete_home_directory: $msg_n_a'
1189            " # END-QUOTE
1190            fi
1191         esac
1192
1193         local height width rows
1194         eval f_dialog_menu_size height width rows \
1195                                 \"\$DIALOG_TITLE\"     \
1196                                 \"\$DIALOG_BACKTITLE\" \
1197                                 \"\$prompt\"           \
1198                                 \"\$hline\"            \
1199                                 $menu_list
1200
1201         local menu_choice
1202         menu_choice=$( eval $DIALOG \
1203                 --title \"\$DIALOG_TITLE\"         \
1204                 --backtitle \"\$DIALOG_BACKTITLE\" \
1205                 --hline \"\$hline\"                \
1206                 --ok-label \"\$msg_ok\"            \
1207                 --cancel-label \"\$msg_cancel\"    \
1208                 --default-item \"\$defaultitem\"   \
1209                 --keep-tite                        \
1210                 --menu \"\$prompt\"                \
1211                 $height $width $rows               \
1212                 $menu_list                         \
1213                 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
1214         )
1215         local retval=$?
1216         f_dialog_data_sanitize menu_choice
1217         f_dialog_menutag_store "$menu_choice"
1218         return $retval
1219 }
1220
1221 # f_dialog_menu_user_edit [$defaultitem]
1222 #
1223 # Present a menu detailing the properties of a login that is about to be
1224 # modified. The user's menu choice is available using f_dialog_menutag_fetch().
1225 # Returns success unless the user chose Cancel or pressed ESC. Data to display
1226 # is taken from environment variables user_account_expire, user_class,
1227 # user_dotfiles_create, user_gecos, user_gid, user_home_create, user_home_dir,
1228 # user_member_groups, user_name, user_password_expire, user_shell, and
1229 # user_uid. If $defaultitem is present and non-NULL, initially highlight the
1230 # item in the menu.
1231 #
1232 f_dialog_menu_user_edit()
1233 {
1234         local prompt="$msg_save_exit_or_cancel"
1235         local menu_list # Calculated below
1236         local defaultitem="$1"
1237         local hline="$hline_arrows_tab_enter"
1238
1239         # Attempt to convert numeric UNIX time to calendar date/time
1240         local user_account_expires_on=
1241         if f_isinteger "$user_account_expire"; then
1242                 [ "$user_account_expire" -ne 0 ] && user_account_expires_on=$(
1243                         date -r "$user_account_expire" "+%F %T %Z"
1244                 )
1245         else
1246                 user_account_expires_on="$user_account_expire"
1247         fi
1248         local user_password_expires_on=
1249         if f_isinteger "$user_password_expire"; then
1250                 [ $user_password_expire -ne 0 ] && user_password_expires_on=$(
1251                         date -r "$user_password_expire" "+%F %T %Z"
1252                 )
1253         else
1254                 user_password_expires_on="$user_password_expire"
1255         fi
1256
1257         # Attempt to translate a numeric GID into `number (name)'
1258         if f_isinteger "$user_gid"; then
1259                 local user_group
1260                 user_group=$( pw groupshow -g "$user_gid" 2> /dev/null ) &&
1261                         user_gid="$user_gid (${user_group%%:*})"
1262         fi
1263
1264         # Localize potentially hostile variables and escape their values
1265         # to the local variable (see f_shell_escape() of `strings.subr')
1266         local var
1267         for var in account_expires_on class dotfiles_create gecos gid \
1268                 home_create home_dir member_groups name password_expires_on \
1269                 shell uid \
1270         ; do
1271                 local _user_$var
1272                 eval f_shell_escape \"\$user_$var\" _user_$var
1273         done
1274
1275         menu_list="
1276                 'X' '$msg_save/$msg_exit'
1277                 '1' '$msg_login: $_user_name'
1278                 '2' '$msg_full_name: $_user_gecos'
1279                 '3' '$msg_password: -----'
1280                 '4' '$msg_user_id: $_user_uid'
1281                 '5' '$msg_group_id: $_user_gid'
1282                 '6' '$msg_member_of_groups: $_user_member_groups'
1283                 '7' '$msg_login_class: $_user_class'
1284                 '8' '$msg_password_expires_on: $_user_password_expires_on'
1285                 '9' '$msg_account_expires_on: $_user_account_expires_on'
1286                 'A' '$msg_home_directory: $_user_home_dir'
1287                 'B' '$msg_shell: $_user_shell'
1288         " # END-QUOTE
1289         case "$user_home_dir" in
1290         /|/nonexistent|/var/empty) menu_list="$menu_list
1291                 '-' '$msg_create_home_directory: $msg_n_a'
1292                 '-' '$msg_create_dotfiles: $msg_n_a'
1293            " # END-QUOTE
1294            ;;
1295         *) if [ -d "$user_home_dir" ]; then menu_list="$menu_list
1296                 '-' '$msg_create_home_directory: $msg_n_a'
1297                 'D' '$msg_create_dotfiles: ${_user_dotfiles_create:-$msg_no}'
1298            " # END-QUOTE
1299            else menu_list="$menu_list
1300                 'C' '$msg_create_home_directory: ${_user_home_create:-$msg_no}'
1301                 'D' '$msg_create_dotfiles: ${_user_dotfiles_create:-$msg_no}'
1302            " # END-QUOTE
1303            fi
1304         esac
1305
1306         local height width rows
1307         eval f_dialog_menu_size height width rows \
1308                                 \"\$DIALOG_TITLE\"     \
1309                                 \"\$DIALOG_BACKTITLE\" \
1310                                 \"\$prompt\"           \
1311                                 \"\$hline\"            \
1312                                 $menu_list
1313
1314         local menu_choice
1315         menu_choice=$( eval $DIALOG \
1316                 --title \"\$DIALOG_TITLE\"         \
1317                 --backtitle \"\$DIALOG_BACKTITLE\" \
1318                 --hline \"\$hline\"                \
1319                 --ok-label \"\$msg_ok\"            \
1320                 --cancel-label \"\$msg_cancel\"    \
1321                 --default-item \"\$defaultitem\"   \
1322                 --keep-tite                        \
1323                 --menu \"\$prompt\"                \
1324                 $height $width $rows               \
1325                 $menu_list                         \
1326                 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
1327         )
1328         local retval=$?
1329         f_dialog_data_sanitize menu_choice
1330         f_dialog_menutag_store "$menu_choice"
1331         return $retval
1332 }
1333
1334 ############################################################ MAIN
1335
1336 f_dprintf "%s: Successfully loaded." usermgmt/user_input.subr
1337
1338 fi # ! $_USERMGMT_USER_INPUT_SUBR