\n";
print &ui_form_start("save_user.cgi");
print &ui_hidden("dom", $in{'dom'});
print &ui_hidden("old", $in{'old'});
print &ui_hidden("delete", 1);
# Count up home directory size
local ($mailsz) = &mail_file_size($user);
local ($msg, $homesz);
if ($user->{'nocreatehome'} || !$user->{'home'}) {
$msg = 'user_rusurew';
}
else {
($homesz) = &disk_usage_kb($user->{'home'});
$msg = $mailsz && $homesz && !&mail_under_home() ?
'user_rusure' :'user_rusureh';
}
print "",&text($msg, "$in{'old'}",
&nice_size($mailsz),
&nice_size($homesz*1024),
"$user->{'home'}"),"
\n";
# Check for home directory clash
if (!$user->{'nocreatehome'} && $user->{'home'} &&
!$user->{'webowner'}) {
local @hclash = grep {
(&same_file($_->{'home'}, $user->{'home'}) ||
&is_under_directory($user->{'home'},
$_->{'home'})) &&
$_ ne $user } @users;
if (@hclash) {
print "",&text('user_hclash',
join(" ", map { "$_->{'user'}" }
@hclash)),"
\n";
}
}
print &ui_form_end([ [ "confirm", $text{'user_deleteok'} ] ]);
print "
\n";
if ($d) {
&ui_print_footer("list_users.cgi?dom=$in{'dom'}",
$text{'users_return'});
}
else {
&ui_print_footer("", $text{'index_return'});
}
exit;
}
}
else {
# Saving or creating, so verify inputs
if ($in{'new'} && $d) {
($mleft, $mreason, $mmax) = &count_feature("mailboxes");
$mleft == 0 && &error($text{'user_emailboxlimit'});
}
if (!$mailbox) {
if (!$config{'allow_upper'}) {
$in{'mailuser'} = lc($in{'mailuser'});
}
$in{'mailuser'} =~ s/^\s+//;
$in{'mailuser'} =~ s/\s+$//;
$err = &valid_mailbox_name($in{'mailuser'});
$olderr = $in{'new'} ? undef
: &valid_mailbox_name($user->{'user'});
&error($err) if ($err && !$olderr);
if (&supports_firstname()) {
$in{'firstname'} =~ /^[^:\r\n]*$/ ||
&error($text{'user_efirstname'});
$user->{'firstname'} = $in{'firstname'};
$in{'surname'} =~ /^[^:\r\n]*$/ ||
&error($text{'user_esurname'});
$user->{'surname'} = $in{'surname'};
}
$in{'real'} =~ /^[^:\r\n]*$/ ||
&error($text{'user_ereal'});
$user->{'real'} = $in{'real'};
if (!$in{'new'} && $in{'mailpass_def'} == 1) {
# Password not being changed
$user->{'passmode'} = 4;
}
elsif ($in{'mailpass_def'} == 2) {
# No passwored accepted
$need_password_check = 0;
delete($user->{'plainpass'});
$user->{'pass'} = '*';
$user->{'passmode'} = 1;
}
else {
# Either password is being changed, or this is new user
$user->{'plainpass'} =
&parse_new_password("mailpass", 1);
$need_password_check = 1;
$user->{'pass'} = &encrypt_user_password(
$user, $user->{'plainpass'});
$user->{'passmode'} = 3;
&set_pass_change($user);
}
if (!$user->{'alwaysplain'}) {
# Disable account if requested
&set_pass_disable($user, $in{'disable'});
}
if (!$user->{'noquota'}) {
# Check and save quota inputs
$qedit = &can_mailbox_quota();
@defmquota = split (/ /, $tmpl->{'defmquota'});
$pd = $d->{'parent'} ? &get_domain($d->{'parent'}) : $d;
if (&has_home_quotas() && $qedit) {
# Use entered quota
if ( $in{'quota'} eq -1 ) {
$in{'quota'} = $in{'otherquota'};
}
$in{'quota_def'} ||
$in{'quota'} =~ /^[0-9\.]+$/ ||
&error($text{'user_equota'});
$user->{'quota'} = $in{'quota_def'} ? 0 :
"a_parse("quota", "home");
!$user->{'quota'} || !$pd->{'quota'} ||
$user->{'quota'} <= $pd->{'quota'} ||
&error(&text('user_eoverquota',
&nice_size($pd->{'quota'}*
"a_bsize("home"))));
}
elsif (&has_home_quotas() && $in{'new'}) {
# Use default
$user->{'quota'} = $defmquota[0];
}
if (&has_mail_quotas() && $qedit) {
if ( $in{'mquota'} eq -1 ) {
$in{'mquota'} = $in{'othermquota'};
}
$in{'mquota_def'} ||
$in{'mquota'} =~ /^[0-9\.]+$/ ||
&error($text{'user_equota'});
$user->{'mquota'} = $in{'mquota_def'} ? 0 :
"a_parse("mquota", "mail");
!$user->{'mquota'} || !$pd->{'mquota'} ||
$user->{'mquota'} <= $pd->{'mquota'} ||
&error(&text('user_eovermquota',
&nice_size($pd->{'mquota'}*
"a_bsize("mail"))));
}
elsif (&has_mail_quotas() && $in{'new'}) {
# Use default
$user->{'mquota'} = $defmquota[0];
}
}
if ($d) {
# Save list of allowed databases
local ($db, @dbs);
foreach $db (split(/\r?\n/, $in{'dbs'})) {
local ($type, $name) = split(/_/, $db, 2);
push(@dbs, { 'type' => $type,
'name' => $name });
}
$user->{'dbs'} = \@dbs;
}
}
else {
# For a domain owner, the password is never changed here
$user->{'passmode'} = 4;
}
# Save extra email addresses
%oldextra = ( );
if (!$in{'new'}) {
%oldextra = map { $_, 1 } @{$old{'extraemail'}};
}
$eu = $mailbox ? $d->{'user'} : $in{'mailuser'};
@extra = split(/\s+/, $in{'extra'});
%donextra = ( );
foreach $e (@extra) {
$e = lc($e);
if ($d && $e =~ /^([^\@ \t]+$)$/) {
$e = "$e\@$d->{'dom'}";
}
if ($e !~ /^(\S*)\@(\S+)$/) {
&error(&text('user_eextra1', $e));
}
if ($e eq $eu."\@".$d->{'dom'}) {
&error(&text('user_eextra5', $e));
}
local ($eu, $ed) = ($1, $2);
$ed = &parse_domain_name($ed);
local $edom = &get_domain_by("dom", $ed);
$edom && $edom->{'mail'} || &error(&text('user_eextra2', $ed));
&can_edit_domain($edom) || $oldextra{$e} ||
&error(&text('user_eextra3', $ed));
!$edom->{'alias'} || !$edom->{'aliascopy'} ||
&error(&text('user_eextra7', $ed));
$e = $eu."\@".$ed;
$donextra{lc($e)}++ && &error(&text('user_eextra6', $e));
}
$user->{'extraemail'} = \@extra;
# Check if extras would exceed limit
($mleft, $mreason, $mmax) = &count_feature("aliases");
if ($mleft >= 0 &&
$mleft - @extra + (%old ? @{$old{'extraemail'}} : 0) < 0) {
&error($text{'alias_ealiaslimit'});
}
# Save primary email address
if ($d && !$user->{'noprimary'}) {
$user->{'email'} = $in{'mailbox'} ? $eu."\@".$d->{'dom'}
: undef;
}
# Save recovery address
if (!$mailbox && ($in{'recovery_def'} || $in{'recovery'})) {
$in{'recovery_def'} || $in{'recovery'} =~ /^\S+\@\S+$/ ||
&error($text{'user_erecovery'});
$user->{'recovery'} = $in{'recovery_def'} ? ""
: $in{'recovery'};
}
# Get the email address to send new/updated mailbox, for the mailbox
# itself. Email may also be sent to the reseller and domain owner
if ($in{'new'} && &will_send_user_email($d, 1) &&
!$in{'newmail_def'} && $in{'newmail'}) {
$in{'newmail'} =~ /^\S+$/ || &error($text{'user_enewmail'});
$newmailto = $in{'newmail'};
}
elsif (!$in{'new'} && &will_send_user_email($d, 0) &&
!$in{'remail_def'} && $in{'remail'}) {
$in{'remail'} =~ /^\S+$/ || &error($text{'user_eremail'});
$newmailto = $in{'remail'};
}
if (!$mailbox && !$user->{'fixedhome'} && !$user->{'brokenhome'}) {
# Find home
if (&can_mailbox_home($user) &&
$d && $d->{'home'} && !$in{'home_def'}) {
$in{'home'} =~ /\.\.\// && &error($text{'user_ehome'});
$in{'home'} =~ /^\// && &error($text{'user_ehome2'});
if ($user->{'webowner'}) {
# Custom home directory for web FTP user
if (!$in{'home'}) {
$home = $d->{'home'};
}
else {
$home = &public_html_dir($d)."/".
$in{'home'};
}
}
else {
# Custom home directory for mailbox user
&error($text{'user_ehome'})
if ($in{'home'} =~ /^\s*$/);
$home = "$d->{'home'}/$in{'home'}";
}
$user->{'maybecreatehome'} = 1;
}
elsif ($d) {
if ($user->{'webowner'}) {
# Auto home directory for web FTP user
$home = &public_html_dir($d);
}
else {
# Auto home directory for mailbox user
$home = "$d->{'home'}/$config{'homes_dir'}/".
$in{'mailuser'};
}
}
else {
# Auto home directory for local user
$home = &useradmin::auto_home_dir(
$home_base, $in{'mailuser'},
$config{'localgroup'});
}
# Make sure home exists, for web owner user
if ($user->{'webowner'} && !-d $home) {
&error(&text('user_ehomeexists', &html_escape($home)));
}
}
# Update secondary groups
%cangroups = map { $_, 1 } (&allowed_secondary_groups($d),
@{$user->{'secs'}});
@secs = split(/\0/, $in{'groups'});
foreach my $g (@secs) {
$cangroups{$g} || &error(&text('user_egroup', $g));
}
$user->{'secs'} = [ @secs ];
# Update no-spam flag
if ($config{'spam'} && $d->{'spam'}) {
$user->{'nospam'} = $in{'nospam'};
}
# Work out full email address, perhaps with real name
if ($user->{'real'}) {
$fullemail = '"'.$user->{'real'}.'" <'.$user->{'email'}.'>';
}
else {
$fullemail = $user->{'email'};
}
# Create or update the user
my $sshkey_mode = $in{'sshkey_mode'} == 2 ? 1 : 0;
$emailmailbox = 0;
if ($in{'new'}) {
# Set new user parameters
if (!$user->{'webowner'}) {
# UID needs to be unique
$user->{'uid'} = &allocate_uid(\%taken);
}
else {
# UID is same as domain for Qmail users and web owners
$user->{'uid'} = $d->{'uid'};
}
$user->{'gid'} = $d ? $d->{'gid'} :
getgrnam($config{'localgroup'});
# Check for clash within this domain
($clash) = grep { $_->{'user'} eq $in{'mailuser'} } @users;
$clash && &error($text{'user_eclash2'});
if (&can_mailbox_ftp()) {
# Shell can be set to one that's allowed for FTP
&check_available_shell($in{'shell'}, 'mailbox')
|| &error($text{'user_eshell'});
$user->{'shell'} = $in{'shell'};
}
else {
# If the shell cannot be edited, always use
# the default.
$user->{'shell'} =
&default_available_shell('mailbox');
}
if (!$user->{'fixedhome'} && !$user->{'brokenhome'}) {
$user->{'home'} = $home;
}
if (($utaken{$in{'mailuser'}} || ($d && $config{'append'})) &&
!$user->{'noappend'}) {
# Need to append domain name
if ($d) {
# Add group name
$user->{'user'} = &userdom_name(
$in{'mailuser'},$d);
}
else {
# No domain to add, so give up!
&error($text{'user_eclash2'});
}
}
else {
# Username is as entered
$user->{'user'} = $in{'mailuser'};
}
if ($d) {
# Check for a Unix clash
$mclash = &check_clash($in{'mailuser'}, $d->{'dom'});
if ($utaken{$user->{'user'}} ||
$user->{'email'} && $mclash ||
!$user->{'email'} && $mclash == 2) {
&error($text{'user_eclash'});
}
}
# Check if any extras clash
foreach $e (@extra) {
$e =~ /^(\S*)\@(\S+)$/;
if (&check_clash($1, $2)) {
&error(&text('user_eextra4', $e));
}
}
# Check if the name is too long
if ($lerr = &too_long($user->{'user'})) {
&error($lerr);
}
# Check if home directory already exists
if (-e $home && !$user->{'nocreatehome'} &&
!$user->{'maybecreatehome'}) {
&error(&text('user_emkhome', $home));
}
if (!$user->{'noalias'}) {
# Save alias
if ($in{'simplemode'} eq 'simple') {
# From simple form
$simple = &get_simple_alias($d, $user, 1);
&parse_simple_form($simple, \%in, $d, 1, 1, 1,
$user->{'user'});
$simple->{'from'} = $fullemail;
$user->{'user_extra'} = &replace_atsign(
$user->{'user'}) if (&need_extra_user($user));
&save_simple_alias($d, $user, $simple);
if (@{$user->{'to'}} == 1 &&
$simple->{'tome'}) {
# If forwarding is just to the user's
# mailbox, then that is like no
# forwarding at all
$user->{'to'} = undef;
}
}
else {
# From complex form
@values = &parse_alias(undef, $user->{'user'},
undef, "user", $d);
$user->{'to'} = @values ? \@values : undef;
}
# Check for alias loop
&check_email_to_loop();
}
# Now we have the username, check the password
if ($need_password_check) {
$user->{'plainpass'} eq '' &&
&error($text{'setup_eemptypass'});
$perr = &check_password_restrictions($user, 0, $d);
&error($perr) if ($perr);
}
# Validate plugins
foreach $f (&list_mail_plugins()) {
$err = &plugin_call($f, "mailbox_validate", $user,
\%old, \%in, $in{'new'}, $d);
&error($err) if ($err);
}
# Validate if extra database user exists
# which can be merged with Unix user
my $extra_db_user = &get_extra_db_user($d, $user->{'user'});
my $extra_web_user = &get_extra_web_user($d, $user->{'user'});
if (($extra_db_user || $extra_web_user) && !$in{'confirm'}) {
# Confirm suppression first
&ui_print_header(&domain_in($d), $text{'user_createovertitle'}, "");
my @save_user_hids;
foreach my $key (keys %in) {
push(@save_user_hids, [$key, $in{$key}]);
}
my $createoverdesc = $extra_db_user && $extra_web_user ?
"dbweb" : $extra_db_user ? "db" : "web";
my $createoverdescm = $extra_db_user && $extra_web_user ? 2 : 1;
print &ui_confirmation_form(
"save_user.cgi",
&text("user_createoverdesc$createoverdesc",
"