3 # Display a form for editing a user, or creating a new user
5 require './user-lib.pl';
6 require 'timelocal.pl';
9 # Show header and get the user
12 $access{'ucreate'} || &error($text{'uedit_ecreate'});
13 &ui_print_header(undef, $text{'uedit_title2'}, "", "create_user");
16 @ulist = &list_users();
17 ($uinfo_hash) = grep { $_->{'user'} eq $n } @ulist;
18 $uinfo_hash || &error($text{'uedit_egone'});
19 %uinfo = %$uinfo_hash;
20 &can_edit_user(\%access, \%uinfo) || &error($text{'uedit_eedit'});
21 &ui_print_header(undef, $text{'uedit_title'}, "", "edit_user");
23 @tds = ( "width=30%" );
25 # build list of used shells
26 %shells = map { $_, 1 } split(/,/, $config{'shells'});
27 @shlist = ($config{'default_shell'} ? ( $config{'default_shell'} ) : ( ));
28 push(@shlist, "/bin/sh", "/bin/csh", "/bin/false") if ($shells{'fixed'});
29 &build_user_used(\%used, $shells{'passwd'} ? \@shlist : undef);
30 if ($shells{'shells'}) {
31 open(SHELLS, "/etc/shells");
35 push(@shlist, $_) if (/\S/);
41 print &ui_form_start("save_user.cgi", "post");
42 print &ui_hidden("old", $n) if ($n ne "");
43 print &ui_table_start($text{'uedit_details'}, "width=100%", 2, \@tds);
46 if ($n eq "" && $config{'new_user_group'} && $access{'gcreate'}) {
47 $onch = "newgid.value = user.value";
49 if ($access{'urename'} || $n eq "") {
50 print &ui_table_row(&hlink($text{'user'}, "user"),
51 &ui_textbox("user", $uinfo{'user'}, 40, 0, undef,
55 print &ui_table_row(&hlink($text{'user'}, "user"),
56 "<tt>".&html_escape($uinfo{'user'})."</tt>");
57 print &ui_hidden("user", $uinfo{'user'}),"\n";
62 # Existing user, just show field to edit
63 $uidfield = &ui_textbox("uid", $uinfo{'uid'}, 10);
66 # Work out which UID modes are available
68 $defuid = &allocate_uid(\%used);
69 if ($access{'autouid'}) {
70 push(@uidmodes, [ 1, $text{'uedit_uid_def'} ]);
72 if ($access{'calcuid'}) {
73 push(@uidmodes, [ 2, $text{'uedit_uid_calc'} ]);
75 if ($access{'useruid'}) {
76 push(@uidmodes, [ 0, &ui_textbox("uid", $defuid, 10) ]);
79 $uidfield = &ui_hidden("uid_def", $uidmodes[0]->[0]).
83 $uidfield = &ui_radio("uid_def", $config{'uid_mode'},
87 print &ui_table_row(&hlink($text{'uid'}, "uid"), $uidfield);
90 if ($config{'extra_real'}) {
91 # Has separate name, office, work and home phone parts
92 local @real = split(/,/, $uinfo{'real'}, 5);
93 print &ui_table_row(&hlink($text{'real'}, "real"),
94 &ui_textbox("real", $real[0], 40));
96 print &ui_table_row(&hlink($text{'office'}, "office"),
97 &ui_textbox("office", $real[1], 20));
99 print &ui_table_row(&hlink($text{'workph'}, "workph"),
100 &ui_textbox("workph", $real[2], 20));
102 print &ui_table_row(&hlink($text{'homeph'}, "homeph"),
103 &ui_textbox("homeph", $real[3], 20));
105 print &ui_table_row(&hlink($text{'extra'}, "extra"),
106 &ui_textbox("extra", $real[4], 20));
110 $uinfo{'real'} =~ s/,*$//; # Strip empty extra fields
111 print &ui_table_row(&hlink($text{'real'}, "real"),
112 &ui_textbox("real", $uinfo{'real'}, 40));
115 # Show input for home directory
116 if ($access{'autohome'}) {
117 # Automatic, cannot be changed
118 $homefield = $text{'uedit_auto'}.
119 ($n eq "" ? "" : " ( <tt>$uinfo{'home'}</tt> )" );
122 if ($config{'home_base'}) {
124 local $grp = &my_getgrgid($uinfo{'gid'});
125 local $hb = $n eq "" ||
126 &auto_home_dir($config{'home_base'},
127 $uinfo{'user'}, $grp) eq $uinfo{'home'};
128 $homefield = &ui_radio("home_base", $hb ? 1 : 0,
129 [ [ 1, $text{'uedit_auto'}."<br>" ],
130 [ 0, $text{'uedit_manual'}." ".
131 &ui_filebox("home", $hb ? "" : $uinfo{'home'},
132 40, 0, undef, undef, 1) ] ]);
135 # Allow any directory
136 $homefield = &ui_filebox("home", $uinfo{'home'}, 25, 0,
140 print &ui_table_row(&hlink($text{'home'}, "home"),
143 # Show shell drop-down
144 push(@shlist, $uinfo{'shell'}) if ($n ne "" && $uinfo{'shell'});
145 if ($access{'shells'} ne "*") {
146 # Limit to shells from ACL
147 @shlist = $n ne "" ? ($uinfo{'shell'}) : ();
148 push(@shlist, split(/\s+/, $access{'shells'}));
151 $shells = 1 if ($access{'noother'});
152 @shlist = &unique(@shlist);
153 if ($n ne "" && !$uinfo{'shell'}) {
155 push(@shlist, [ "", "<None>" ]);
157 push(@shlist, [ "*", $text{'uedit_other'} ]) if (!$shells);
158 $firstshell = ref($shlist[0]) ? $shlist[0]->[0] : $shlist[0];
159 print &ui_table_row(&hlink($text{'shell'}, "shell"),
160 &ui_select("shell", $n eq "" ? $config{'default_shell'} || $firstshell
162 \@shlist, 1, 0, 0, 0,
163 "onChange='form.othersh.disabled = form.shell.value != \"*\"'").
164 ($shells ? "" : &ui_filebox("othersh", undef, 40, 1)));
166 # Get the password, generate random if needed
167 $pass = $n ne "" ? $uinfo{'pass'} : $config{'lock_string'};
168 if ($n eq "" && $config{'random_password'}) {
169 $random_password = &generate_random_password();
172 # Check if temporary locking is supported
173 if (&supports_temporary_disable()) {
174 if ($n ne "" && $pass ne $config{'lock_string'} && $pass ne "") {
175 # Can disable if not already locked, or if a new account
177 if ($pass =~ /^\Q$disable_string\E/) {
179 $pass =~ s/^\Q$disable_string\E//;
187 # Show password field
188 $passmode = $pass eq "" && $random_password eq "" ? 0 :
189 $pass eq $config{'lock_string'} && $random_password eq "" ? 1 :
190 $random_password ne "" ? 3 :
191 $pass && $pass ne $config{'lock_string'} &&
192 $random_password eq "" ? 2 : -1;
193 $pffunc = $config{'passwd_stars'} ? \&ui_password : \&ui_textbox;
194 print &ui_table_row(&hlink($text{'pass'}, "pass"),
195 &ui_radio_table("passmode", $passmode,
196 [ [ 0, $config{'empty_mode'} ? $text{'none1'} : $text{'none2'} ],
197 [ 1, $text{'nologin'} ],
199 &$pffunc("pass", $config{'random_password'} && $n eq "" ?
200 $random_password : "", 15) ],
202 ( [ 2, $text{'nochange'},
203 &ui_hidden("encpass", $pass) ] ) :
204 ( [ 2, $text{'encrypted'},
205 &ui_textbox("encpass", $passmode == 2 ? $pass : "", 60) ] )
207 ($can_disable ? " ".&ui_checkbox("disable", 1,
208 $text{'uedit_disabled'}, $disabled) : "")
211 print &ui_table_end();
213 $pft = &passfiles_type();
214 if (($pft == 1 || $pft == 6) && $access{'peopt'}) {
215 # Additional user fields for BSD users
216 print &ui_table_start($text{'uedit_passopts'}, "width=100%", 4, \@tds);
219 if ($uinfo{'change'}) {
220 @tm = localtime($uinfo{'change'});
223 $cyear = $tm[5]+1900;
224 $chour = sprintf "%2.2d", $tm[2];
225 $cmin = sprintf "%2.2d", $tm[1];
228 &date_input($cday, $cmon, $cyear, 'change');
229 print &ui_table_row(&hlink($text{'change2'}, "change2"),
230 &date_input($cday, $cmon, $cyear, 'change').
231 " ".&ui_textbox("changeh", $chour, 3).
232 ":".&ui_textbox("changemi", $cmin, 3));
236 if ($config{'default_expire'} =~
237 /^(\d+)\/(\d+)\/(\d+)$/) {
245 elsif ($uinfo{'expire'}) {
246 @tm = localtime($uinfo{'expire'});
249 $eyear = $tm[5]+1900;
250 $ehour = sprintf "%2.2d", $tm[2];
251 $emin = sprintf "%2.2d", $tm[1];
253 print &ui_table_row(&hlink($text{'expire2'}, "expire2"),
254 &date_input($eday, $emon, $eyear, 'expire').
255 " ".&ui_textbox("expireh", $ehour, 3).
256 ":".&ui_textbox("expiremi", $emin, 3));
259 print &ui_table_row(&hlink($text{'class'}, "class"),
260 &ui_textbox("class", $uinfo{'class'}, 10));
262 print &ui_table_end();
264 elsif (($pft == 2 || $pft == 5) && $access{'peopt'}) {
265 # System has a shadow password file as well.. which means it supports
266 # password expiry and so on
267 print &ui_table_start($text{'uedit_passopts'}, "width=100%", 4, \@tds);
270 local $max = $n eq "" ? $config{'default_max'} : $uinfo{'max'};
271 print &ui_table_row(&hlink($text{'change'}, "change"),
272 ($uinfo{'change'} ? &make_date(timelocal(
273 gmtime($uinfo{'change'} * 60*60*24)),1) :
274 $n eq "" ? $text{'uedit_never'} :
275 $text{'uedit_unknown'}));
280 if ($config{'default_expire'} =~
281 /^(\d+)\/(\d+)\/(\d+)$/) {
287 elsif ($uinfo{'expire'}) {
288 @tm = localtime(timelocal(gmtime($uinfo{'expire'} *
292 $eyear = $tm[5]+1900;
294 print &ui_table_row(&hlink($text{'expire'}, "expire"),
295 &date_input($eday, $emon, $eyear, 'expire'));
298 # Ask at first login?
299 print &ui_table_row(&hlink($text{'ask'}, "ask"),
300 &ui_yesno_radio("ask", $uinfo{'change'} eq '0'));
303 # Minimum and maximum days for changing
304 print &ui_table_row(&hlink($text{'min'}, "min"),
305 &ui_textbox("min", $n eq "" ? $config{'default_min'} :
308 print &ui_table_row(&hlink($text{'max'}, "max"),
309 &ui_textbox("max", $n eq "" ? $config{'default_max'} :
313 # Warning and inactive days. Only available when full shadow
315 print &ui_table_row(&hlink($text{'warn'}, "warn"),
316 &ui_textbox("warn", $n eq "" ? $config{'default_warn'}
317 : $uinfo{'warn'}, 5));
319 print &ui_table_row(&hlink($text{'inactive'}, "inactive"),
320 &ui_textbox("inactive", $n eq "" ?
321 $config{'default_inactive'} :
322 $uinfo{'inactive'}, 5));
325 # Force change at next login
326 if (($max || $gconfig{'os_type'} =~ /-linux$/) && $pft == 2) {
328 &hlink($text{'uedit_forcechange'}, 'forcechange'),
329 &ui_yesno_radio("forcechange", 0));
332 print &ui_table_end();
334 elsif ($pft == 4 && $access{'peopt'}) {
335 # System has extra AIX password information
336 print &ui_table_start($text{'uedit_passopts'}, "width=100%", 4, \@tds);
338 # Last change date and time
339 print &ui_table_row(&hlink($text{'change'}, "change"),
340 ($uinfo{'change'} ? &make_date($uinfo{'change'}) :
341 $n eq "" ? $text{'uedit_never'} :
342 $text{'uedit_unknown'}));
344 if ($uinfo{'expire'} =~ /^(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/) {
357 $emon =~ s/0(\d)/$1/; # strip leading 0
358 print &ui_table_row(&hlink($text{'expire'}, "expire"),
359 &ui_radio("expire_def", $uinfo{'expire'} eq '' ? 1 :
360 $uinfo{'expire'} eq '0' ? 2 : 0,
361 [ [ 1, $text{'uedit_sys'} ],
362 [ 2, $text{'uedit_never'} ],
363 [ 0, &date_input($eday, $emon, $eyear, 'expire').
364 " ".&ui_textbox("expireh", $ehour, 3).
365 "/".&ui_textbox("expiremi", $emin, 3) ] ]), 3);
367 # Minimum and maximum ages in weeks
368 print &ui_table_row(&hlink($text{'min_weeks'}, "min_weeks"),
369 &ui_opt_textbox("min", $uinfo{'min'}, 5, $text{'uedit_sys'}), 3);
371 print &ui_table_row(&hlink($text{'max_weeks'}, "max_weeks"),
372 &ui_opt_textbox("max", $uinfo{'max'}, 5, $text{'uedit_sys'}), 3);
375 print &ui_table_row(&hlink($text{'warn'}, "warn"),
376 &ui_opt_textbox("warn", $uinfo{'warn'}, 5, $text{'uedit_sys'}), 3);
379 print &ui_table_row(&hlink($text{'flags'}, "flags"),
380 &ui_checkbox("flags", "admin", $text{'uedit_admin'},
381 $uinfo{'admin'})."<br>".
382 &ui_checkbox("flags", "admchg", $text{'uedit_admchg'},
383 $uinfo{'admchg'})."<br>".
384 &ui_checkbox("flags", "nocheck", $text{'uedit_nocheck'},
385 $uinfo{'nocheck'}), 3);
387 print &ui_table_end();
390 # Group memberships section
391 print &ui_table_start($text{'uedit_gmem'}, "width=100%", 4, \@tds);
396 if ($n eq "" && $access{'gcreate'}) {
397 # Has option to create a group
398 push(@groupopts, [ 2, $text{'uedit_samg'} ]);
399 push(@groupopts, [ 1, $text{'uedit_newg'},
400 &ui_textbox("newgid", undef, 20) ]);
401 $gidmode = $config{'new_user_group'} ? 2 : 0;
403 if ($access{'ugroups'} eq "*" || $access{'uedit_gmode'} >= 3) {
404 # Group can be chosen with popup window
407 if ($gconfig{'db_sizeuser'}) {
408 ($w, $h) = split(/x/, $gconfig{'db_sizeuser'});
410 push(@groupopts, [ 0, $text{'uedit_oldg'},
411 &ui_textbox("gid", $n eq "" ? $config{'default_group'} :
412 scalar(&my_getgrgid($uinfo{'gid'})), 13)." ".
413 &popup_window_button("my_group_chooser.cgi?multi=0", $w, $h,
414 1, [ [ "ifield", "gid", "group" ] ]) ]);
417 # From fixed menu of groups
418 $cg = $uinfo{'gid'} ? &my_getgrgid($uinfo{'gid'}) : undef;
419 @gl = &unique($cg ? ($cg) : (),
420 &split_quoted_string($access{'ugroups'}));
421 push(@groupopts, [ 0, $text{'uedit_oldg'},
422 &ui_select("gid", $cg, \@gl) ]);
424 if (@groupopts == 1) {
425 $groupfield = $groupopts[0]->[2];
428 $groupfield = &ui_radio_table("gidmode", $gidmode, \@groupopts);
430 print &ui_table_row(&hlink($text{'group'}, "group"), $groupfield, 3);
432 # Work out which secondary groups the user is in
433 if ($config{'secmode'} != 1) {
434 @defsecs = &split_quoted_string($config{'default_secs'});
435 @glist = &list_groups();
436 @glist = sort { $a->{'group'} cmp $b->{'group'} } @glist
437 if ($config{'sort_mode'});
439 foreach $g (@glist) {
440 @mems = split(/,/ , $g->{'members'});
441 $ismem = &indexof($uinfo{'user'}, @mems) >= 0;
443 $ismem = 1 if (&indexof($g->{'group'}, @defsecs) >= 0);
445 $ingroups{$g->{'group'}} = $ismem;
449 if ($config{'secmode'} == 0) {
450 # Show secondary groups with select menu
452 foreach $g (@glist) {
453 next if (!&can_use_group(\%access, $g->{'group'}) &&
454 !$ingroups{$g->{'group'}});
455 push(@canglist, [ $g->{'group'}, &html_escape($g->{'group'}) ]);
457 @ingroups = map { [ $_, $_ ] } sort { $a cmp $b }
458 grep { $ingroups{$_} } (keys %ingroups);
459 $secfield = &ui_multi_select("sgid", \@ingroups, \@canglist, 5, 1, 0,
460 $text{'uedit_allg'}, $text{'uedit_ing'});
462 elsif ($config{'secmode'} == 2) {
465 foreach $g (@glist) {
466 if ($ingroups{$g->{'group'}}) {
467 push(@insecs, $g->{'group'});
470 $secfield = &ui_textarea("sgid", join("\n", @insecs), 5, 20);
477 print &ui_table_row(&hlink($text{'uedit_2nd'}, "2nd"), $secfield, 3);
480 print &ui_table_end();
483 # Editing a user - show options for moving home directory, changing IDs
484 # and updating in other modules
485 if ($access{'movehome'} == 1 || $access{'chuid'} == 1 ||
486 $access{'chgid'} == 1 || $access{'mothers'} == 1) {
487 print &ui_table_start($text{'onsave'}, "width=100%", 2, \@tds);
489 # Move home directory
490 if ($access{'movehome'} == 1) {
492 &hlink($text{'uedit_movehome'}, "movehome"),
493 &ui_yesno_radio("movehome", 1));
496 # Change UID on files
497 if ($access{'chuid'} == 1) {
499 &hlink($text{'uedit_chuid'},"chuid"),
500 &ui_radio("chuid", 1,
501 [ [ 0, $text{'no'} ],
502 [ 1, $text{'home'} ],
503 [ 2, $text{'uedit_allfiles'} ] ]));
506 # Change GID on files
507 if ($access{'chgid'} == 1) {
509 &hlink($text{'uedit_chgid'},"chgid"),
510 &ui_radio("chgid", 1,
511 [ [ 0, $text{'no'} ],
512 [ 1, $text{'home'} ],
513 [ 2, $text{'uedit_allfiles'} ] ]));
516 # Modify in other modules
517 if ($access{'mothers'} == 1) {
519 &hlink($text{'uedit_mothers'},"others"),
520 &ui_yesno_radio("others",
521 $config{'default_other'} ? 1 : 0));
524 # Rename group, if the same and if editable
525 @ginfo = &my_getgrgid($uinfo{'gid'});
526 if ($ginfo[0] eq $uinfo{'user'}) {
527 ($group) = grep { $_->{'gid'} == $uinfo{'gid'} }
529 if (&can_edit_group(\%access, $group)) {
531 &hlink($text{'uedit_grename'},"grename"),
532 &ui_yesno_radio("grename", 1));
536 print &ui_table_end(),"<p>\n";
540 # Creating a user - show options for creating home directory, copying
541 # skel files and creating in other modules
542 if ($access{'makehome'} == 1 || $access{'copy'} == 1 ||
543 $access{'cothers'} == 1) {
544 print &ui_table_start($text{'uedit_oncreate'}, "width=100%",
548 if ($access{'makehome'} == 1) {
550 &hlink($text{'uedit_makehome'}, "makehome"),
551 &ui_yesno_radio("makehome", 1));
555 if ($config{'user_files'} =~ /\S/ && $access{'copy'} == 1) {
557 &hlink($text{'uedit_copy'}, "copy_files"),
558 &ui_yesno_radio("copy_files", 1));
561 # Create in other modules
562 if ($access{'cothers'} == 1) {
564 &hlink($text{'uedit_cothers'},"others"),
565 &ui_yesno_radio("others",
566 $config{'default_other'}));
569 print &ui_table_end();
574 # Buttons for saving and other actions
575 @buts = ( [ undef, $text{'save'} ] );
577 # List logins by user
578 push(@buts, [ "list", $text{'uedit_logins'} ]);
580 # Link to the mailboxes module, if installed
581 if (&foreign_available("mailboxes") &&
582 &foreign_installed("mailboxes", 1)) {
583 push(@buts, [ "mailboxes", $text{'uedit_mail'} ]);
586 # Link to Usermin for switching user
587 if (&foreign_available("usermin") &&
588 &foreign_installed("usermin", 1) &&
589 (%uacl = &get_module_acl("usermin") &&
590 $uacl{'sessions'})) {
591 # Link to Usermin module for switching to some user
592 &foreign_require("usermin", "usermin-lib.pl");
594 &usermin::get_usermin_miniserv_config(\%uminiserv);
595 if ($uminiserv{'session'}) {
596 push(@buts, [ "switch", $text{'uedit_swit'} ]);
601 if ($access{'udelete'}) {
602 push(@buts, [ "delete", $text{'delete'} ]);
604 print &ui_form_end(\@buts);
608 print &ui_form_end([ [ undef, $text{'create'} ] ]);
611 &ui_print_footer("index.cgi?mode=users", $text{'index_return'});