Handle hostnames with upper-case letters
[webmin.git] / useradmin / save_user.cgi
1 #!/usr/local/bin/perl
2 # save_user.cgi
3 # Saves or creates a new user. If the changes require moving of the user's
4 # home directory or changing file ownerships, do that as well
5
6 require './user-lib.pl';
7 require 'timelocal.pl';
8 &error_setup($text{'usave_err'});
9 &ReadParse();
10
11 # Check for buttons at end of user form, redirect if needed
12 if ($in{'list'}) {
13         &redirect("list_logins.cgi?username=".&urlize($in{'old'}));
14         return;
15         }
16 elsif ($in{'mailboxes'}) {
17         &redirect("../mailboxes/list_mail.cgi?user=".&urlize($in{'old'}));
18         return;
19         }
20 elsif ($in{'switch'}) {
21         &redirect("../usermin/switch.cgi?user=".&urlize($in{'old'}));
22         return;
23         }
24 elsif ($in{'delete'}) {
25         &redirect("delete_user.cgi?user=".&urlize($in{'old'}));
26         return;
27         }
28
29 # Build list of used UIDs and GIDs
30 &build_user_used(\%used);
31 &build_group_used(\%used) if ($config{'new_user_gid'});
32 &build_group_used(\%gused);
33
34 # Strip out \n characters in inputs
35 $in{'real'} =~ s/\r|\n//g;
36 $in{'user'} =~ s/\r|\n//g;
37 $in{'user'} =~ s/\0.*$//;       # some people reports \0 in usernames!
38 $in{'pass'} =~ s/\r|\n//g;
39 $in{'encpass'} =~ s/\r|\n//g;
40 $in{'home'} =~ s/\r|\n//g;
41 $in{'gid'} =~ s/\r|\n//g;
42 $in{'uid'} =~ s/\r|\n//g;
43 $in{'uid'} = int($in{'uid'});
44 $in{'othersh'} =~ s/\r|\n//g;
45
46 # Validate username
47 $user{'user'} = $in{'user'};
48 $in{'user'} =~ /^[^:\t]+$/ ||
49         &error(&text('usave_ebadname', $in{'user'}));
50 $err = &check_username_restrictions($in{'user'});
51 &error($err) if ($err);
52
53 &lock_user_files();
54 @ulist = &list_users();
55 @glist = &list_groups();
56 if ($in{'old'} ne "") {
57         # Get old user info
58         ($ouser_hash) = grep { $_->{'user'} eq $in{'old'} } @ulist;
59         $ouser_hash || &error($text{'uedit_egone'});
60         %ouser = %$ouser_hash;
61         if (!$access{'urename'} && $ouser{'user'} ne $user{'user'}) {
62                 &error($text{'usave_erename'});
63                 }
64         $user{'olduser'} = $ouser{'user'};
65         if ($user{'user'} ne $ouser{'user'}) {
66                 foreach $ou (@ulist) {
67                         &error(&text('usave_einuse', $in{'user'}))
68                                 if ($ou->{'user'} eq $in{'user'});
69                         }
70                 $access{'uedit_mode'} == 2 && &error($text{'usave_erename'});
71                 $renaming = 1;
72                 }
73         &can_edit_user(\%access, \%ouser) || &error($text{'usave_eedit'});
74         }
75 else {
76         # check new user details
77         $access{'ucreate'} || &error($text{'usave_ecreate'});
78         foreach $ou (@ulist) {
79                 &error(&text('usave_einuse', $in{'user'}))
80                         if ($ou->{'user'} eq $in{'user'});
81                 }
82         }
83 if (($in{'old'} eq '' || $user{'user'} ne $ouser{'user'}) &&
84     $config{'alias_check'} && &foreign_check("sendmail")) {
85         # Check if the new username conflicts with a sendmail alias
86         &foreign_require("sendmail", "sendmail-lib.pl");
87         &foreign_require("sendmail", "aliases-lib.pl");
88         local $conf = &foreign_call("sendmail", "get_sendmailcf");
89         local $afiles = &foreign_call("sendmail", "aliases_file", $conf);
90         foreach $a (&foreign_call("sendmail", "list_aliases", $afiles)) {
91                 &error(&text('usave_einuse_a', $in{'user'}))
92                         if ($a->{'name'} eq $in{'user'});
93                 }
94         }
95
96 # Validate and store basic inputs
97 if (!$in{'uid_def'} || $in{'old'} ne '') {
98         # Only do UID checks if not automatic
99         $in{'uid'} =~ /^\-?[0-9]+$/ || &error(&text('usave_euid', $in{'uid'}));
100         if (!%ouser || $ouser{'uid'} != $in{'uid'}) {
101                 !$access{'lowuid'} || $in{'uid'} >= $access{'lowuid'} ||
102                         &error(&text('usave_elowuid', $access{'lowuid'}));
103                 !$access{'hiuid'} || $in{'uid'} <= $access{'hiuid'} ||
104                         &error(&text('usave_ehiuid', $access{'hiuid'}));
105                 }
106         if (!$access{'uuid'} && %ouser && $ouser{'uid'} != $in{'uid'}) {
107                 &error($text{'usave_euuid'});
108                 }
109         if (!$access{'umultiple'}) {
110                 foreach $ou (@ulist) {
111                         if ($ou->{'uid'} == $in{'uid'} &&
112                             $ou->{'user'} ne $ouser{'user'}) {
113                                 &error(&text('usave_euidused',
114                                              $ou->{'user'}, $in{'uid'}));
115                                 }
116                         }
117                 }
118         }
119 elsif ( $in{'uid_def'} eq '1' ) {
120         # Can assign UID here
121         $in{'uid'} = int($config{'base_uid'} > $access{'lowuid'} ?
122                          $config{'base_uid'} : $access{'lowuid'});
123         while($used{$in{'uid'}}) {
124                 $in{'uid'}++;
125                 }
126         if ($access{'hiuid'} && $in{'uid'} > $access{'hiuid'}) {
127                 # Out of UIDs!
128                 &error($text{'usave_ealluid'});
129                 }
130         }
131
132 elsif ( $in{'uid_def'} eq '2' ) {
133         # Can calculate UID here
134         if ( $config{'uid_calc'} ) {
135             $in{'uid'} = &mkuid($in{'user'});
136         } else {
137             $in{'uid'} = &berkeley_cksum($in{'user'});
138         }
139         &error("Unable to calculate UID, invalid user name specified") if ( $in{'uid'} lt 0 );
140         while($used{$in{'uid'}}) {
141                 $in{'uid'}++;
142                 }
143         if ($access{'hiuid'} && $in{'uid'} > $access{'hiuid'}) {
144                 # Out of UIDs!
145                 &error($text{'usave_ealluid'});
146                 }
147         }
148
149 $in{'real'} =~ /^[^:]*$/ || &error(&text('usave_ereal', $in{'real'}));
150 if ($in{'shell'} eq "*") { $in{'shell'} = $in{'othersh'}; }
151 if ($access{'shells'} ne "*") {
152         if (&indexof($in{'shell'}, split(/\s+/, $access{'shells'})) < 0 &&
153             (!%ouser || $in{'shell'} ne $ouser{'shell'})) {
154                 &error(&text('usave_eshell', $in{'shell'}));
155                 }
156         }
157 $user{'uid'} = $in{'uid'};
158 if ($in{'old'} ne "" || !$in{'gidmode'}) {
159         # Selecting existing group
160         $user{'gid'} = &my_getgrnam($in{'gid'});
161         if ($user{'gid'} eq "") { &error(&text('usave_egid', $in{'gid'})); }
162         $grp = $in{'gid'};
163         }
164 else {
165         # Creating a new group
166         $access{'gcreate'} || &error($text{'usave_egcreate'});
167         if ($in{'gidmode'} == 2) {
168                 # New group has same name as user
169                 $in{'newgid'} = $in{'user'};
170                 }
171         else {
172                 # New group has arbitrary name
173                 $in{'newgid'} =~ /^[^: \t]+$/ ||
174                         &error(&text('gsave_ebadname', $in{'newgid'}));
175                 }
176         foreach $og (@glist) {
177                 &error(&text('usave_einuseg', $in{'newgid'}))
178                         if ($og->{'group'} eq $in{'newgid'});
179                 }
180         $grp = $in{'newgid'};
181         }
182 if ($config{'extra_real'}) {
183         $in{'real'} =~ /^[^:,]*$/ || &error(&text('usave_ereal', $in{'real'}));
184         $in{'office'} =~ /^[^:,]*$/ || &error($text{'usave_eoffice'});
185         $in{'workph'} =~ /^[^:,]*$/ || &error($text{'usave_eworkph'});
186         $in{'homeph'} =~ /^[^:,]*$/ || &error($text{'usave_ehomeph'});
187         $user{'real'} = join(",", $in{'real'}, $in{'office'}, $in{'workph'},
188                                   $in{'homeph'});
189         $user{'real'} .= ",$in{'extra'}" if ($in{'extra'});
190         }
191 else {
192         $user{'real'} = $in{'real'};
193         }
194
195 if ($config{'real_base'} &&
196     $user{'home'} eq &auto_home_dir($config{'home_base'},
197                                     $ouser{'user'}, $grp)) {
198         # Work out old real home
199         $old_real_home = &auto_home_dir($config{'real_base'},
200                                         $ouser{'user'}, $grp);
201         }
202 if ($access{'autohome'}) {
203         # Home directory is forced to automatic
204         if ($in{'new'} || $ouser{'user'} ne $user{'user'}) {
205                 $user{'home'} = &auto_home_dir($access{'home'}, $in{'user'},
206                                                $grp);
207                 $real_home = &auto_home_dir($config{'real_base'}, $in{'user'},
208                                             $grp) if ($config{'real_base'});
209                 }
210         else {
211                 $user{'home'} = $ouser{'home'};
212                 }
213         }
214 elsif ($config{'home_base'} && $in{'home_base'}) {
215         # Automatic home directory option chosen
216         $user{'home'} = &auto_home_dir($config{'home_base'}, $in{'user'},
217                                        $grp);
218         $real_home = &auto_home_dir($config{'real_base'}, $in{'user'},
219                                     $grp) if ($config{'real_base'});
220         }
221 else {
222         # Manual home directory chosen
223         $user{'home'} = $in{'home'};
224         }
225 $real_home ||= $user{'home'};
226 if (!$access{'autohome'}) {
227         $user{'home'} =~ /^\// || &error(&text('usave_ehome', $in{'home'}));
228         if (!&is_under_directory($access{'home'}, $user{'home'})) {
229                 &error(&text('usave_ehomepath', $user{'home'}));
230                 }
231         }
232 $user{'shell'} = $in{'shell'};
233 @sgnames = $config{'secmode'} == 2 ? &split_quoted_string($in{'sgid'})
234                                    : split(/\r?\n/, $in{'sgid'});
235 foreach $gname (@sgnames) {
236         $ingroup{$gname}++;
237         $gid = &my_getgrnam($gname);
238         defined($gid) || &error(&text('usave_esgname', $gname));
239         push(@sgids, $gid);
240         }
241
242 if ($access{'ugroups'} ne "*") {
243         if ($in{'old'} ne "") {
244                 # existing users can only be added to or removed from
245                 # allowed groups
246                 if ($ouser{'gid'} != $user{'gid'}) {
247                         &can_use_group(\%access, $in{'gid'}) ||
248                                 &error(&text('usave_eprimary', $in{'gid'}));
249                         local $og = &my_getgrgid($ouser{'gid'});
250                         &can_use_group(\%access, $og) ||
251                                 &error(&text('usave_eprimaryr', $og));
252                         }
253                 foreach $g (@glist) {
254                         local @mems = split(/,/ , $g->{'members'});
255                         local $idx = &indexof($ouser{'user'}, @mems);
256                         if ($ingroup{$g->{'group'}} && $idx<0 &&
257                             !&can_use_group(\%access, $g->{'group'})) {
258                                 &error(&text('usave_esecondary',
259                                              $g->{'group'}));
260                                 }
261                         elsif (!$ingroup{$g->{'group'}} && $idx>=0 &&
262                                !&can_use_group(\%access, $g->{'group'})) {
263                                 &error(&text('usave_esecondaryr',
264                                              $g->{'group'}));
265                                 }
266                         }
267                 }
268         elsif (!$in{'gidmode'}) {
269                 # new users can only be added to allowed groups
270                 # This is skipped if we are creating a new group for
271                 # new users
272                 &can_use_group(\%access, $in{'gid'}) ||
273                         &error(&text('usave_eprimary', $in{'gid'}));
274                 foreach $gname (@sgnames) {
275                         &can_use_group(\%access, $gname) ||
276                                 &error(&text('usave_esecondary', $group));
277                         }
278                 }
279         }
280
281 # Store password input
282 if ($in{'passmode'} == 0) {
283         # Password is blank
284         if (!$config{'empty_mode'}) {
285                 local $err = &check_password_restrictions("", $user{'user'});
286                 &error($err) if ($err);
287                 }
288         $user{'pass'} = "";
289         }
290 elsif ($in{'passmode'} == 1) {
291         # Password is locked
292         $user{'pass'} = $config{'lock_string'};
293         }
294 elsif ($in{'passmode'} == 2) {
295         # Specific encrypted password entered, or possibly no change
296         $user{'pass'} = $in{'encpass'};
297         }
298 elsif ($in{'passmode'} == 3) {
299         # Normal password entered - check restrictions
300         local $err = &check_password_restrictions($in{'pass'}, $user{'user'});
301         &error($err) if ($err);
302         $user{'pass'} = &encrypt_password($in{'pass'});
303         }
304 if (&supports_temporary_disable() &&
305     $in{'disable'} && ($in{'passmode'} == 2 || $in{'passmode'} == 3)) {
306         $user{'pass'} = $disable_string.$user{'pass'};
307         }
308
309 $pft = &passfiles_type();
310 if ($pft == 2 || $pft == 5) {
311         if ($access{'peopt'}) {
312                 # Validate shadow-password inputs
313                 $in{'min'} =~ /^\-?[0-9]*$/ ||
314                         &error(&text('usave_emin', $in{'min'}));
315                 $in{'max'} =~ /^\-?[0-9]*$/ ||
316                         &error(&text('usave_emax', $in{'max'}));
317                 $user{'min'} = $in{'min'};
318                 $user{'max'} = $in{'max'};
319                 if ($pft == 2) {
320                         if ($in{'expired'} ne "" && $in{'expirem'} ne ""
321                             && $in{'expirey'} ne "") {
322                                 eval { $expire = timelocal(0, 0, 12,
323                                                         $in{'expired'},
324                                                         $in{'expirem'}-1,
325                                                         $in{'expirey'}-1900); };
326                                 if ($@) { &error($text{'usave_eexpire'}); }
327                                 $expire = int($expire / (60*60*24));
328                                 }
329                         else { $expire = ""; }
330                         $user{'expire'} = $expire;
331                         $in{'warn'} =~ /^\-?[0-9]*$/ ||
332                             &error(&text('usave_ewarn', $in{'warn'}));
333                         $in{'inactive'} =~ /^\-?[0-9]*$/ ||
334                             &error(&text('usave_einactive', $in{'inactive'}));
335                         $user{'warn'} = $in{'warn'};
336                         $user{'inactive'} = $in{'inactive'};
337                         }
338                 }
339         else {
340                 $user{'expire'} = $ouser{'expire'};
341                 $user{'min'} = $ouser{'min'};
342                 $user{'max'} = $ouser{'max'};
343                 if ($pft == 2) {
344                         $user{'warn'} = $ouser{'warn'};
345                         $user{'inactive'} = $ouser{'inactive'};
346                         }
347                 }
348         $daynow = int(time() / (60*60*24));
349         $user{'change'} = $in{'forcechange'} ? 0 :
350                           $pft == 5 && $in{'ask'} ? 0 :
351                           !%ouser ? $daynow :
352                           $in{'passmode'} == 3 ? $daynow :
353                           $in{'passmode'} == 2 &&
354                           $user{'pass'} ne $ouser{'pass'} ? $daynow :
355                                                             $ouser{'change'};
356         }
357 elsif ($pft == 1 || $pft == 6) {
358         if ($access{'peopt'}) {
359                 # Validate BSD-password inputs
360                 if ($in{'expired'} ne "" && $in{'expirem'} ne ""
361                     && $in{'expirey'} ne "") {
362                         eval { $expire = timelocal(59, $in{'expiremi'},
363                                                    $in{'expireh'},
364                                                    $in{'expired'},
365                                                    $in{'expirem'}-1,
366                                                    $in{'expirey'}-1900); };
367                         if ($@) { &error($text{'usave_eexpire'}); }
368                         }
369                 else { $expire = ""; }
370                 if ($in{'changed'} ne "" && $in{'changem'} ne ""
371                     && $in{'changey'} ne "") {
372                         eval { $change = timelocal(59, $in{'changemi'},
373                                                    $in{'changeh'},
374                                                    $in{'changed'},
375                                                    $in{'changem'}-1,
376                                                    $in{'changey'}-1900); };
377                         if ($@) { &error($text{'usave_echange'}); }
378                         }
379                 else { $change = ""; }
380                 $in{'class'} =~ /^([^: ]*)$/ ||
381                         &error(&text('usave_eclass', $in{'class'}));
382                 $user{'expire'} = $expire;
383                 $user{'change'} = $change;
384                 $user{'class'} = $in{'class'};
385                 }
386         else {
387                 $user{'expire'} = $ouser{'expire'};
388                 $user{'change'} = $ouser{'change'};
389                 $user{'class'} = $ouser{'class'};
390                 }
391         }
392 elsif ($pft == 4) {
393         # Validate AIX-style password inputs
394         if ($in{'expire_def'} == 1) {
395                 # System default expiry date
396                 $expire = undef;
397                 }
398         elsif ($in{'expire_def'} == 2) {
399                 # Never expires
400                 $expire = "0";
401                 }
402         else {
403                 # Add a leading zero if only 1 digit long
404                 $in{'expirem'} =~ /^\d+$/ && $in{'expired'} =~ /^\d+$/ &&
405                    $in{'expireh'} =~ /^\d+$/ && $in{'expiremi'} =~ /^\d+$/ &&
406                    $in{'expirey'} =~ /^\d+$/ || &error($text{'usave_eexpire'});
407                 $in{'expirem'} =~ s/^(\d)$/0$1/;
408                 $in{'expired'} =~ s/^(\d)$/0$1/;
409                 $in{'expireh'} =~ s/^(\d)$/0$1/;
410                 $in{'expiremi'} =~ s/^(\d)$/0$1/;
411                 
412                 # Only use the last two digits of the year
413                 $in{'expirey'} =~ s/^\d\d(\d\d)$/$1/;
414                 
415                 # If the user didn't choose the hour and min make them 01
416                 $in{'expireh'} = "01" if $in{'expireh'} eq "";
417                 $in{'expiremi'} = "01" if $in{'expiremi'} eq "";
418                 $expire="$in{'expirem'}$in{'expired'}$in{'expireh'}$in{'expiremi'}$in{'expirey'}";
419                 }
420         if ($access{'peopt'}) {
421                 $user{'admin'} = $in{'flags'} =~ /admin/;
422                 $user{'admchg'} = $in{'flags'} =~ /admchg/;
423                 $user{'nocheck'} = $in{'flags'} =~ /nocheck/;
424                 $user{'expire'} = $expire;
425                 $user{'min'} = $in{'min_def'} ? undef : $in{'min'};
426                 $user{'max'} = $in{'max_def'} ? undef : $in{'max'};
427                 $user{'warn'} = $in{'warn_def'} ? undef : $in{'warn'};
428                 }
429         else {
430                 $user{'admin'} = $ouser{'admin'};
431                 $user{'admchg'} = $ouser{'admchg'};
432                 $user{'nocheck'} = $ouser{'nocheck'};
433                 $user{'expire'} = $ouser{'expire'};
434                 $user{'min'} = $ouser{'min'};
435                 $user{'max'} = $ouser{'max'};
436                 $user{'warn'} = $ouser{'warn'};
437                 }
438         $user{'change'} = !%ouser ? time() :
439                           $in{'passmode'} == 3 ? time() :
440                           $user{'pass'} ne $ouser{'pass'} ? time() :
441                                                             $ouser{'change'};
442         }
443
444 if (%ouser) {
445         # We are changing an existing user
446         if ($ouser{'uid'} != $user{'uid'}) {
447                 $changing_uid = 1;
448                 }
449         if ($ouser{'gid'} != $user{'gid'}) {
450                 $changing_gid = 1;
451                 }
452         if ($ouser{'home'} ne $user{'home'}) {
453                 $changing_homedir = 1;
454                 }
455         $in{'old'} = $ouser{'user'};
456
457         # Force defaults for save options if necessary
458         $in{'movehome'} = !$access{'movehome'} if ($access{'movehome'} != 1);
459         $in{'chuid'} = !$access{'chuid'} if ($access{'chuid'} != 1);
460         $in{'chgid'} = !$access{'chgid'} if ($access{'chgid'} != 1);
461         $in{'others'} = !$access{'mothers'} if ($access{'mothers'} != 1);
462
463         # Run the pre-change command
464         &set_user_envs(\%user, 'MODIFY_USER',
465                        $in{'passmode'} == 3 ? $in{'pass'} : "", \@sgids);
466         $merr = &making_changes();
467         &error(&text('usave_emaking', "<tt>$merr</tt>")) if (defined($merr));
468
469         # Move the home directory if needed
470         if ($changing_homedir && $in{'movehome'}) {
471                 &error($text{'usave_efromroot'}) if ($ouser{'home'} eq "/");
472                 &error($text{'usave_etoroot'}) if ($user{'home'} eq "/");
473                 if (-d $ouser{'home'} && !-e $user{'home'}) {
474                         # Move home directory if the old one exists and
475                         # the new one does not.
476                         if ($real_home && $old_real_home) {
477                                 # Move real home dir
478                                 $out = &backquote_logged(
479                                         "mv ".quotemeta($old_real_home)." ".
480                                               quotemeta($real_home)." 2>&1");
481                                 }
482                         else {
483                                 $out = &backquote_logged(
484                                         "mv ".quotemeta($ouser{'home'})." ".
485                                               quotemeta($user{'home'})." 2>&1");
486                                 }
487                         if ($?) { &error(&text('usave_emove', $out)); }
488                         }
489                 }
490
491         # Change GID on files if needed
492         if ($changing_gid && $in{'chgid'}) {
493                 if ($in{'chgid'} == 1) {
494                         &recursive_change($user{'home'}, $ouser{'uid'},
495                                           $ouser{'gid'}, -1, $user{'gid'});
496                         }
497                 else {
498                         &recursive_change("/", $ouser{'uid'},
499                                           $ouser{'gid'}, -1, $user{'gid'});
500                         }
501                 }
502
503         # Change UID on files if needed
504         if ($changing_uid && $in{'chuid'}) {
505                 if ($in{'chuid'} == 1) {
506                         &recursive_change($user{'home'}, $ouser{'uid'},
507                                           -1, $user{'uid'}, -1);
508                         }
509                 else {
510                         &recursive_change("/", $ouser{'uid'},
511                                           -1, $user{'uid'}, -1);
512                         }
513                 }
514
515         # Update user details
516         $user{'passmode'} = $in{'passmode'};
517         if ($in{'passmode'} == 2 && $user{'pass'} eq $ouser{'pass'}) {
518                 # not changing password
519                 $user{'passmode'} = 4;
520                 }
521         $user{'plainpass'} = $in{'pass'} if ($in{'passmode'} == 3);
522         &modify_user(\%ouser, \%user);
523
524         # Rename group if needed and if possible
525         if ($user{'user'} ne $ouser{'user'} &&
526             $user{'gid'} == $ouser{'gid'} &&
527             $in{'grename'}) {
528                 ($group) = grep { $_->{'gid'} == $user{'gid'} } &list_groups();
529                 if ($group->{'group'} eq $ouser{'user'} &&
530                     &can_edit_group(\%access, $group)) {
531                         # Do the rename
532                         $ogroup = { %$group };
533                         $group->{'group'} = $user{'user'};
534                         &modify_group($ogroup, $group);
535                         }
536                 }
537         }
538 else {
539         # Force defaults for save options if necessary
540         $in{'makehome'} = !$access{'makehome'} if ($access{'makehome'} != 1);
541         $in{'copy_files'} = !$access{'copy'} if ($access{'copy'} != 1 &&
542                                                  $config{'user_files'} =~ /\S/);
543         $in{'others'} = !$access{'cothers'} if ($access{'cothers'} != 1);
544
545         # Run the pre-change command
546         &set_user_envs(\%user, 'CREATE_USER',
547                        $in{'passmode'} == 3 ? $in{'pass'} : "", \@sgids);
548         $merr = &making_changes();
549         &error(&text('usave_emaking', "<tt>$merr</tt>")) if (defined($merr));
550
551         # Create the home directory
552         if ($in{'makehome'}) {
553                 &create_home_directory(\%user, $real_home);
554                 $made_home = 1;
555                 }
556
557         if ($in{'gidmode'}) {
558                 # New group for the new user ..
559                 if ($config{'new_user_gid'}) {
560                         # gid is the same as the uid
561                         $newgid = $user{'uid'};
562                         }
563                 else {
564                         # find the first free GID above the base
565                         $newgid = int($config{'base_gid'} > $access{'lowgid'} ?
566                                       $config{'base_gid'} : $access{'lowgid'});
567                         while($gused{$newgid}) {
568                                 $newgid++;
569                                 }
570                         }
571
572                 # create a new group for this user
573                 $created_group = $group{'group'} = $in{'newgid'};
574                 $user{'gid'} = $group{'gid'} = $newgid;
575                 &create_group(\%group);
576                 $created_group = \%group;
577                 }
578
579         if ($made_home) {
580                 &set_ownership_permissions($user{'uid'}, $user{'gid'},
581                                            undef, $real_home) ||
582                         &error(&text('usave_echown', $!));
583                 }
584
585         # Save user details
586         $user{'passmode'} = $in{'passmode'};
587         $user{'plainpass'} = $in{'pass'} if ($in{'passmode'} == 3);
588         &create_user(\%user);
589
590         # Copy files into user's directory
591         if ($in{'copy_files'} && $in{'makehome'}) {
592                 local $uf = &get_skel_directory(\%user, $in{'gid'});
593                 &copy_skel_files($uf, $real_home, $user{'uid'}, $user{'gid'});
594                 }
595         }
596
597 if ($config{'secmode'} != 1) {
598         # Update secondary groups
599         foreach $g (@glist) {
600                 @mems = split(/,/ , $g->{'members'});
601                 if ($renaming) {
602                         $idx = &indexof($ouser{'user'}, @mems);
603                         if ($ingroup{$g->{'group'}} && $idx<0) {
604                                 # Need to add to the group
605                                 push(@mems, $user{'user'});
606                                 }
607                         elsif (!$ingroup{$g->{'group'}} && $idx>=0) {
608                                 # Need to remove from the group
609                                 splice(@mems, $idx, 1);
610                                 }
611                         elsif ($idx >= 0) {
612                                 # Need to rename in group
613                                 $mems[$idx] = $user{'user'};
614                                 }
615                         else { next; }
616                         }
617                 else {
618                         $idx = &indexof($user{'user'}, @mems);
619                         if ($ingroup{$g->{'group'}} && $idx<0) {
620                                 # Need to add to the group
621                                 push(@mems, $user{'user'});
622                                 }
623                         elsif (!$ingroup{$g->{'group'}} && $idx>=0) {
624                                 # Need to remove from the group
625                                 splice(@mems, $idx, 1);
626                                 }
627                         else { next; }
628                         }
629                 %newg = %$g;
630                 $newg{'members'} = join(',', @mems);
631                 &modify_group($g, \%newg);
632                 }
633         }
634 &unlock_user_files();
635 &made_changes();
636
637 # Run other modules' scripts
638 if ($in{'others'}) {
639         $error_must_die = 1;
640         eval {
641                 if (%ouser) {
642                         &other_modules("useradmin_modify_user", \%user,\%ouser);
643                         }
644                 else {
645                         &other_modules("useradmin_create_user", \%user);
646                         }
647                 if ($created_group) {
648                         &other_modules("useradmin_create_group",
649                                        $created_group);
650                         }
651                 };
652         $error_must_die = 0;
653         $others_err = $@;
654         }
655
656 delete($in{'pass'});
657 delete($in{'encpass'});
658 &webmin_log(%ouser ? 'modify' : 'create', 'user', $in{'user'}, \%in);
659 &webmin_log('create', 'group', $created_group->{'group'}, \%in)
660         if ($created_group);
661
662 # Bounce back to the list, if everything worked
663 &error(&text('usave_eothers', $others_err)) if ($others_err);
664 &redirect("index.cgi?mode=users");
665
666