File: //scripts.20110531.215904.25158/updateuserdomains
#!/usr/bin/perl
# cpanel - updateuserdomains Copyright(c) 2010 cPanel, Inc.
# All rights Reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
BEGIN {
unshift @INC, '/usr/local/cpanel';
if ( @ARGV && $ARGV[0] && $ARGV[0] !~ m/^--/ && -e $ARGV[0] ) {
unshift @INC, $ARGV[0];
}
}
require 5.006;
# ignore our parent if it dies as this could result in
# a blank userdomains files
$SIG{'HUP'} = 'IGNORE';
$SIG{'INT'} = 'IGNORE';
my $CPUSER_DATA_CHECK_TIME = 300;
use strict;
use Cpanel::SafeFile ();
use IPC::Open3 ();
use Fcntl ();
use Cpanel::Sys ();
use Cpanel::Sys::Hostname ();
use Cpanel::CPAN::Sys::Hostname::Long ();
use Cpanel::FileUtils::TouchFile ();
use Cpanel::Config::LoadCpConf ();
use Cpanel::Validate::Username ();
use Cpanel::PwCache ();
use Cpanel::Logger ();
# we will trade speed for memory here no Cpanel :: Hostname
my $mailgid = ( getgrnam('mail') )[2] || ( getgrnam('mailnull') )[2] || 0;
my $hostname = Cpanel::Sys::Hostname::gethostname();
my $system = $^O || 'Linux';
my $cpanel = ( -d '/usr/local/cpanel' ? 1 : 0 );
my $cpconf = Cpanel::Config::LoadCpConf::loadcpconf();
my $per_domain_mailips = -e '/var/cpanel/per_domain_mailips' ? 1 : 0;
my $fast = $cpconf->{'maildir'} ? 1 : 0;
my $hasmaildir = $cpconf->{'maildir'} ? 1 : 0;
my $verbose = 0;
my $force = 0;
my $exim_dbmbuild;
my %permissions_list = (
'/etc/relayhosts' => 0644,
'/etc/relayhostsusers' => 0644,
'/etc/trueuserowners' => 0644,
'default' => 0640,
);
if (@ARGV) {
if ( grep( /fast/, @ARGV ) ) {
$fast = 1;
@ARGV = grep !/fast/, @ARGV;
}
if ( grep( /^--verbose$/, @ARGV ) ) {
$verbose = 1;
@ARGV = grep !/^--verbose$/, @ARGV;
}
if ( grep( /^--force$/, @ARGV ) ) {
$force = 1;
@ARGV = grep !/^--force$/, @ARGV;
}
}
# reset umask for update
umask 0022;
if ($cpanel) {
eval {
local $SIG{'__DIE__'};
local $SIG{'__WARN__'};
require Storable; #needed to prevent locking all cpanel users file (loadcpuserfile)
};
}
Cpanel::PwCache::no_uid_cache(); #uid cache only needed if we are going to make lots of getpwuid calls
Cpanel::PwCache::init_passwdless_pwcache();
################################################################################
# Begin cPanel specific code
################################################################################
if ($cpanel) {
if ( !-e '/etc/eximrejects' ) {
open( EXIMREJ, '>', '/etc/eximrejects' );
print EXIMREJ <<"REJECTMSG";
host_accept_relay: Host \$sender_fullhost is not permitted|
to relay through \$primary_hostname.|
Perhaps you have not logged into the pop/imap server in the last 30 minutes.|
You may also have been rejected because your ip address|
does not have a reverse DNS entry.
REJECTMSG
close(EXIMREJ);
}
my %READDB_TYPES = (
'SINGLE_HASH' => 0,
'HASH_ARRAY', => 1,
'REVERSE_HASH_ARRAY' => 2,
'HASH' => 3,
'REVERSE_HASH' => 4
);
my ( %NOCGI, %DEMO, %USER_DOMAINS, %USER_PW_DATA, %TRUE_USER_OWNERS, %TRUE_USER_DOMAINS, %DBOWNERS, %USER_PLANS, %USER_BWLIMITS );
my @KEYS = ( 'DOMAINS', 'DOMAIN', 'OWNER', 'NOCGI', 'DEMOUIDS', 'PLANS', 'BWLIMIT' );
my ( @OPTIMIZEDB, @NEEDSBUILD );
my ( %DB, %MTIMEDB );
my %DBLIST = (
'userdomains' => {
'key' => 'DOMAINS',
'order' => $READDB_TYPES{'REVERSE_HASH_ARRAY'},
'builddb' => 1,
},
'trueuserdomains' => {
'key' => 'DOMAIN',
'order' => $READDB_TYPES{'REVERSE_HASH'},
'builddb' => 1,
},
'trueuserowners' => {
'key' => 'OWNER',
'order' => $READDB_TYPES{'HASH'},
'builddb' => 1,
},
'dbowners' => {
'key' => 'DBOWNERS',
'order' => $READDB_TYPES{'HASH'},
'builddb' => 1,
},
'userplans' => {
'key' => 'PLANS',
'order' => $READDB_TYPES{'HASH'},
'builddb' => 1,
},
'userbwlimits' => {
'key' => 'BWLIMIT',
'order' => $READDB_TYPES{'HASH'},
'builddb' => 0,
},
'demouids' => {
'key' => 'DEMOUIDS',
'order' => $READDB_TYPES{'REVERSE_HASH'},
'builddb' => 1,
},
'nocgiusers' => {
'key' => 'NOCGI',
'order' => $READDB_TYPES{'SINGLE_HASH'},
'builddb' => 1,
},
);
foreach my $db ( keys %DBLIST ) {
readdb( \%DB, \%MTIMEDB, $db, $DBLIST{$db}->{'key'}, $DBLIST{$db}->{'order'} );
}
my $needs_update = ( $force ? 1 : 0 );
my $custom_mail_helo;
my $custom_mail_ips;
my @DBS = ( 'userdomains', 'nocgiusers', 'trueuserdomains', 'mail_reverse_dns', 'trueuserowners', 'demouids', 'demousers', 'demodomains', 'domainusers', 'localdomains', 'dbowners', 'userplans', 'userbwlimits', 'secondarymx', 'remotedomains', 'backupmxhosts', 'spammeripblocks', 'trustedmailhosts', 'skipsmtpcheckhosts', 'senderverifybypasshosts' );
if ( !-e '/var/cpanel/custom_mailips' || !-e '/etc/mailips' ) {
push @DBS, 'mailips';
$custom_mail_ips = 0;
}
else {
$custom_mail_ips = 1;
}
if ( !-e '/var/cpanel/custom_mailhelo' || !-e '/etc/mailhelo' ) {
push @DBS, 'mailhelo';
$custom_mail_helo = 0;
}
else {
$custom_mail_helo = 1;
}
foreach my $db ( map { '/etc/' . $_ } @DBS ) {
if ( !-e $db ) {
if ($verbose) { print "Missing $db file forced full rebuild.\n"; }
my @path = split( '/', $db );
pop @path;
my $dest_dir = join '/', @path;
if ( $dest_dir && -d $dest_dir ) {
Cpanel::FileUtils::TouchFile::touchfile($db);
}
}
chown( 0, $mailgid, $db );
chmod( _permissions($db), $db );
}
if ( -z '/etc/localdomains' ) {
if ( open( my $local_domains_fh, '>>', '/etc/localdomains' ) ) {
print {$local_domains_fh} $hostname . "\n";
}
}
foreach my $db ( '/etc/relayhosts', '/etc/relayhostsusers' ) {
if ( !-e $db ) { Cpanel::FileUtils::TouchFile::touchfile($db); }
chmod( _permissions($db), $db );
}
my $now = time();
%USER_BWLIMITS = %{ $DB{'BWLIMIT'} } if ref $DB{'BWLIMIT'};
%USER_PLANS = %{ $DB{'PLANS'} } if ref $DB{'PLANS'};
%DBOWNERS = %{ $DB{'DBOWNERS'} } if ref $DB{'DBOWNERS'};
%TRUE_USER_OWNERS = %{ $DB{'OWNER'} } if ref $DB{'OWNER'};
%DEMO = %{ $DB{'DEMOUIDS'} } if ref $DB{'DEMOUIDS'};
%NOCGI = %{ $DB{'NOCGI'} } if ref $DB{'NOCGI'};
%TRUE_USER_DOMAINS = map { $DB{'DOMAIN'}{$_} => $_ } keys %{ $DB{'DOMAIN'} } if ref $DB{'DOMAIN'};
my ( $user, $uid, $gid, $homedir, $size, $userfilemtime, $goodmtime, $cpuserfile, @DOMAINS );
my $pwcache_ref = Cpanel::PwCache::fetch_pwcache();
my $oldest_db_mtime;
my $mtime;
foreach my $db ( keys %MTIMEDB ) {
$mtime = $MTIMEDB{$db} || 0;
if ( !defined $oldest_db_mtime || $mtime < $oldest_db_mtime ) {
$oldest_db_mtime = $mtime;
}
}
my $this_version_mtime = ( stat('/scripts/updateuserdomains') )[9];
#
# If the oldest_db_mtime is older then now = ok otherwise we have a timewarp
# If the mtime of updateuserdomains is older then the oldest db = ok otherwise updateuserdomains has been updated and we do a full rebuild
#
my $database_is_valid = ( $oldest_db_mtime <= $now && $this_version_mtime < $oldest_db_mtime ) ? 1 : 0;
print "updateuserdomains database validity : $database_is_valid\n" if $verbose;
my %RESERVED_USERNAMES = map { $_ => undef } ( 'cpanel', Cpanel::Validate::Username::list_reserved_usernames() );
foreach my $pwref ( grep { !exists $RESERVED_USERNAMES{ $_->[0] } && $_->[2] >= 500 } @$pwcache_ref ) {
( $user, $uid, $gid, $homedir ) = ( (@$pwref)[ 0, 2, 3, 7 ] );
( $goodmtime, $size, $userfilemtime ) = ( 1, ( stat "/var/cpanel/users/$user" )[ 7, 9 ] );
next if !$size;
$DBOWNERS{$user} = _add_dbowner_to_cpuserfile($user);
# user file has to be at least an hour better
# and timewarm safe
# and be in the db
if ( !$database_is_valid
|| ( $userfilemtime + $CPUSER_DATA_CHECK_TIME ) > $oldest_db_mtime
|| !exists $DB{'DOMAIN'}{$user} ) {
$needs_update = 1;
if ($verbose) { print "$user has missing or newer data, forcing full load of their user data. !(( $userfilemtime + $CPUSER_DATA_CHECK_TIME ) > $oldest_db_mtime)\n"; }
require Cpanel::Config::LoadCpUserFile;
$cpuserfile = Cpanel::Config::LoadCpUserFile::load($user);
@DOMAINS = ( ref $cpuserfile->{'DOMAINS'} eq 'ARRAY' ? @{ $cpuserfile->{'DOMAINS'} } : () );
delete $TRUE_USER_DOMAINS{ $DB{'DOMAIN'}{$user} };
$TRUE_USER_DOMAINS{ $cpuserfile->{'DOMAIN'} } = $user;
foreach my $domain ( $cpuserfile->{'DOMAIN'}, @DOMAINS ) {
next if ( !$domain || $domain eq '' || !Cpanel::Sys::valid_wild_domainname( $domain, 1 ) );
if ( !exists $USER_DOMAINS{$domain} ) {
$USER_DOMAINS{$domain} = $user;
}
elsif ( $USER_DOMAINS{$domain} ne $user ) {
Cpanel::Logger::logger(
{
'message' => "domain conflict: $domain: /var/cpanel/users/${user} contains a " . ( ( $domain eq $cpuserfile->{'DOMAIN'} ) ? 'MAIN ' : '' ) . "domain already owned by $USER_DOMAINS{$domain}",
'level' => 'warn',
'backtrace' => 0,
'service' => 'updateuserdomains',
'output' => 1,
}
);
next;
}
}
$USER_PLANS{$user} = ( $cpuserfile->{'PLAN'} || 'default' );
$USER_BWLIMITS{$user} = ( $cpuserfile->{'BWLIMIT'} || '0' );
$TRUE_USER_OWNERS{$user} = ( $cpuserfile->{'OWNER'} || 'root' );
if ( $cpuserfile->{'DEMO'} && $cpuserfile->{'DEMO'} eq '1' ) {
$DEMO{$user} = $uid;
}
else {
delete $DEMO{$user};
}
if ( exists $cpuserfile->{'HASCGI'} && $cpuserfile->{'HASCGI'} eq '0' ) {
$NOCGI{$user} = undef;
}
else {
delete $NOCGI{$user};
}
if ( !$cpuserfile->{'DOMAIN'} ) {
Cpanel::Logger::logger(
{
'message' => "user $user: missing DNS= line in /var/cpanel/users/${user}",
'level' => 'warn',
'backtrace' => 0,
'service' => 'updateuserdomains',
'output' => 1,
}
);
}
}
else {
foreach my $domain ( @{ $DB{'DOMAINS'}{$user} } ) {
if ( exists $USER_DOMAINS{$domain} ) {
if ( $USER_DOMAINS{$domain} ne $user ) {
$needs_update = 1;
Cpanel::Logger::logger(
{
'message' => "domain conflict: /var/cpanel/users/${user} contains a" . ( $domain eq $cpuserfile->{'DOMAIN'} ) . "domain already owned by $USER_DOMAINS{$domain}",
'level' => 'warn',
'backtrace' => 0,
'service' => 'updateuserdomains',
'output' => 1,
}
);
next;
}
}
$USER_DOMAINS{$domain} = $user;
}
}
}
# Cleanup step to eliminate references to deleted accounts.
my $is_valid_user = { map { $_->[0] => 1 } @$pwcache_ref };
$needs_update = 1
if delete @TRUE_USER_OWNERS{ grep { !$is_valid_user->{$_} } keys %TRUE_USER_OWNERS };
$needs_update = 1
if delete @USER_PLANS{ grep { !$is_valid_user->{$_} } keys %USER_PLANS };
$needs_update = 1
if delete @DBOWNERS{ grep { !$is_valid_user->{$_} } keys %DBOWNERS };
$needs_update = 1
if delete @USER_BWLIMITS{ grep { !$is_valid_user->{$_} } keys %USER_BWLIMITS };
if ( exists $USER_DOMAINS{$hostname} ) {
print "== WORKAROUND ENABLED ==\n";
print "Serious Problem -- This should never happen!!\n";
print "The hostname ($hostname) is owned by the user $USER_DOMAINS{$hostname}\n";
print "== WORKAROUND ENABLED ==\n";
delete $USER_DOMAINS{$hostname};
$needs_update = 1;
}
if ($needs_update) {
print "Updating databases.\n" if $verbose;
require Cpanel::DIp; #lazy load when needed
my @WRITTEN_DBS;
if ( sysopen( my $userdomains_fh, '/etc/.userdomains', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/userdomains') ) ) {
push @WRITTEN_DBS, 'userdomains';
chown( 0, $mailgid, '/etc/.userdomains' );
print {$userdomains_fh} join( "\n", map { $_ . ': ' . $USER_DOMAINS{$_} } keys %USER_DOMAINS ) . ( scalar keys %USER_DOMAINS ? "\n" : '' );
print {$userdomains_fh} "*: nobody\n";
close($userdomains_fh);
}
if ( sysopen( my $mailips_fh, '/etc/.mailips', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/mailips') )
&& sysopen( my $mailhelo_fh, '/etc/.mailhelo', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/mailhelo') )
&& sysopen( my $mailrdns_fh, '/etc/.mail_reverse_dns', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/mail_reverse_dns') ) ) {
push @WRITTEN_DBS, 'mail_reverse_dns';
push @WRITTEN_DBS, 'mailips' if !$custom_mail_ips;
push @WRITTEN_DBS, 'mailhelo' if !$custom_mail_helo;
chown( 0, $mailgid, '/etc/.mailips', '/etc/.mailhelo', '/etc/.mail_reverse_dns' );
my $mainip = Cpanel::DIp::getmainserverip();
my %SEEN_RDNS_IP;
my %TRUE_DOMAIN_USERS;
my %IP_OWNER_MAP;
print {$mailrdns_fh} "$mainip: $hostname\n";
if ($per_domain_mailips) {
require Cpanel::DomainIp; #lazy load when needed
require Cpanel::Config::userdata::Cache; #lazy load when needed
require Cpanel::Reseller; #lazy load when needed
my $userdata = Cpanel::Config::userdata::Cache::load_cache();
foreach my $reseller ( 'root', Cpanel::Reseller::getresellerslist() ) {
my $shared_ip_ref = Cpanel::DIp::getsharedipslist($reseller);
next if ( !$shared_ip_ref || !ref $shared_ip_ref );
foreach my $ip (@$shared_ip_ref) {
$IP_OWNER_MAP{$reseller} = $ip;
last;
}
}
foreach my $domain ( keys %TRUE_USER_DOMAINS ) {
$TRUE_DOMAIN_USERS{ $TRUE_USER_DOMAINS{$domain} } = $domain;
}
foreach my $domain ( keys %USER_DOMAINS ) {
my $ip = $userdata->{$domain}->[5];
$ip =~ s/:\d+$//; # Strip off port number
next if ( !$ip || $ip eq $mainip );
if ( Cpanel::DIp::isdedicatedip($ip) ) {
print {$mailips_fh} $domain . ': ' . $ip . "\n";
print {$mailhelo_fh} $domain . ': ' . $TRUE_DOMAIN_USERS{ $USER_DOMAINS{$domain} } . "\n";
if ( !exists $SEEN_RDNS_IP{$ip} ) {
print {$mailrdns_fh} $ip . ': ' . $TRUE_DOMAIN_USERS{ $USER_DOMAINS{$domain} } . "\n";
$SEEN_RDNS_IP{$ip} = 1;
}
}
else {
my $owner = $TRUE_USER_OWNERS{ $USER_DOMAINS{$domain} };
if ( exists $IP_OWNER_MAP{$owner} ) {
print {$mailips_fh} $domain . ': ' . $IP_OWNER_MAP{$owner} . "\n";
if ( exists $TRUE_DOMAIN_USERS{$owner} && $TRUE_DOMAIN_USERS{$owner} ) {
print {$mailhelo_fh} $domain . ': ' . $TRUE_DOMAIN_USERS{$owner} . "\n";
if ( !exists $SEEN_RDNS_IP{$ip} ) {
print {$mailrdns_fh} $ip . ': ' . $TRUE_DOMAIN_USERS{$owner} . "\n";
$SEEN_RDNS_IP{$ip} = 1;
}
}
}
}
}
}
close($mailips_fh);
close($mailhelo_fh);
close($mailrdns_fh);
}
if ( sysopen( my $userbwlimits_fh, '/etc/.userbwlimits', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/userbwlimits') ) ) {
push @WRITTEN_DBS, 'userbwlimits';
chown( 0, $mailgid, '/etc/.userbwlimits' );
print {$userbwlimits_fh} "#userbwlimits v1\n";
print {$userbwlimits_fh} join( "\n", map { $_ . ': ' . $USER_BWLIMITS{$_} } keys %USER_BWLIMITS ) . ( scalar keys %USER_BWLIMITS ? "\n" : '' );
close($userbwlimits_fh);
}
if ( sysopen( my $dbowners_fh, '/etc/.dbowners', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/dbowners') ) ) {
push @WRITTEN_DBS, 'dbowners';
chown( 0, $mailgid, '/etc/.dbowners' );
print {$dbowners_fh} "#dbowners v1\n";
print {$dbowners_fh} join( "\n", map { $_ . ': ' . $DBOWNERS{$_} } keys %DBOWNERS ) . ( scalar keys %DBOWNERS ? "\n" : '' );
close($dbowners_fh);
}
if ( sysopen( my $userplans_fh, '/etc/.userplans', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/userplans') ) ) {
push @WRITTEN_DBS, 'userplans';
chown( 0, $mailgid, '/etc/.userplans' );
print {$userplans_fh} "#userplans v1\n";
print {$userplans_fh} join( "\n", map { $_ . ': ' . $USER_PLANS{$_} } keys %USER_PLANS ) . ( scalar keys %USER_PLANS ? "\n" : '' );
close($userplans_fh);
}
if ( sysopen( my $trueuserowners_fh, '/etc/.trueuserowners', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/trueuserowners') ) ) {
push @WRITTEN_DBS, 'trueuserowners';
chown( 0, $mailgid, '/etc/.trueuserowners' );
print {$trueuserowners_fh} "#userowners v1\n";
print {$trueuserowners_fh} join( "\n", map { $_ . ': ' . $TRUE_USER_OWNERS{$_} } keys %TRUE_USER_OWNERS ) . ( scalar keys %TRUE_USER_OWNERS ? "\n" : '' );
close($trueuserowners_fh);
}
if ( sysopen( my $trueuserdomains_fh, '/etc/.trueuserdomains', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/trueuserdomains') ) ) {
push @WRITTEN_DBS, 'trueuserdomains';
chown( 0, $mailgid, '/etc/.trueuserdomains' );
print {$trueuserdomains_fh} join( "\n", map { $_ . ': ' . $TRUE_USER_DOMAINS{$_} } keys %TRUE_USER_DOMAINS ) . ( scalar keys %TRUE_USER_DOMAINS ? "\n" : '' );
close($trueuserdomains_fh);
}
if ( sysopen( my $domainusers_fh, '/etc/.domainusers', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/domainusers') ) ) {
push @WRITTEN_DBS, 'domainusers';
chown( 0, $mailgid, '/etc/.domainusers' );
print {$domainusers_fh} join( "\n", map { $TRUE_USER_DOMAINS{$_} . ': ' . $_ } keys %TRUE_USER_DOMAINS ) . ( scalar keys %TRUE_USER_DOMAINS ? "\n" : '' );
close($domainusers_fh);
}
if ( sysopen( my $demousers_fh, '/etc/.demousers', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/demousers') ) ) {
push @WRITTEN_DBS, 'demousers';
chown( 0, $mailgid, '/etc/.demousers' );
print {$demousers_fh} join( "\n", keys %DEMO ) . ( scalar keys %DEMO ? "\n" : '' );
close($demousers_fh);
}
if ( sysopen( my $nocgiusers_fh, '/etc/.nocgiusers', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/nocgiusers') ) ) {
push @WRITTEN_DBS, 'nocgiusers';
chown( 0, $mailgid, '/etc/.nocgiusers' );
print {$nocgiusers_fh} join( "\n", keys %NOCGI ) . ( scalar keys %NOCGI ? "\n" : '' );
close($nocgiusers_fh);
}
if ( sysopen( my $demouids_fh, '/etc/.demouids', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/demouids') ) ) {
push @WRITTEN_DBS, 'demouids';
chown( 0, $mailgid, '/etc/.demouids' );
print {$demouids_fh} join( "\n", map { $DEMO{$_} . ': ' . $_ } keys %DEMO ) . ( scalar keys %DEMO ? "\n" : '' );
close($demouids_fh);
}
if ( sysopen( my $demodomains_fh, '/etc/.demodomains', &Fcntl::O_WRONLY | &Fcntl::O_TRUNC | &Fcntl::O_CREAT, _permissions('/etc/demodomains') ) ) {
push @WRITTEN_DBS, 'demodomains';
chown( 0, $mailgid, '/etc/.demodomains' );
print {$demodomains_fh} join( "\n", grep { $DEMO{ $USER_DOMAINS{$_} } } keys %USER_DOMAINS ) . ( scalar keys %DEMO ? "\n" : '' );
close($demodomains_fh);
}
if ( !$hasmaildir ) {
### DEPRCATE -- MBOX SUPPORT -- REMOVE ME?
my $buildvmail = $force;
if ( !-e '/etc/vmail/' ) {
mkdir( '/etc/vmail/', 0755 );
chown 0, 0, '/etc/vmail';
$buildvmail = 1;
}
elsif ( ( stat('/etc/passwd') )[9] > ( stat('/etc/vmail') )[9] ) {
$buildvmail = 1;
}
if ( !$hasmaildir && $buildvmail ) {
print "Rebuilding /etc/vmail ...\n" if $verbose;
foreach my $pwref (@$pwcache_ref) {
( $user, $uid, $gid, $homedir ) = (@$pwref)[ 0, 2, 3, 7 ];
$USER_PW_DATA{$user} = [ $uid, $gid, $homedir ];
}
foreach my $domain ( keys %USER_DOMAINS ) {
next if !$domain;
my $user = $USER_DOMAINS{$domain};
my ( $uid, $gid, $homedir ) = @{ $USER_PW_DATA{$user} };
if ( $homedir && $uid ) {
print "Updating domain $domain for UID $uid\n" if $verbose;
if ( readlink( '/etc/vmail/passwd.' . $domain ) ne $homedir . '/etc/' . $domain . '/passwd' ) {
unlink '/etc/vmail/passwd.' . $domain;
symlink $homedir . '/etc/' . $domain . '/passwd', '/etc/vmail/passwd.' . $domain;
}
if ( readlink( '/etc/vmail/shadow.' . $domain ) ne $homedir . '/etc/' . $domain . '/shadow' ) {
unlink '/etc/vmail/shadow.' . $domain;
symlink $homedir . '/etc/' . $domain . '/shadow', '/etc/vmail/shadow.' . $domain;
}
if ( !-e '/etc/vmail/vhost.' . $domain ) {
unless ( Cpanel::FileUtils::TouchFile::touchfile( '/etc/vmail/vhost.' . $domain ) ) {
Cpanel::Logger::logger(
{
'message' => "Unable to write /etc/vmail/vhost.$domain: $!",
'level' => 'warn',
'backtrace' => 0,
'service' => 'updateuserdomains',
'output' => 1,
}
);
}
}
unlink '/etc/vmail/uid.' . $domain;
if ( open my $vmail_uid_fh, '>', '/etc/vmail/uid.' . $domain ) {
print {$vmail_uid_fh} $uid;
close $vmail_uid_fh;
}
else {
Cpanel::Logger::logger(
{
'message' => "Unable to write /etc/vmail/uid.$domain: $!",
'level' => 'warn',
'backtrace' => 0,
'service' => 'updateuserdomains',
'output' => 1,
}
);
}
unlink '/etc/vmail/gid.' . $domain;
if ( open my $vmail_gid_fh, '>', '/etc/vmail/gid.' . $domain ) {
print {$vmail_gid_fh} $gid;
close $vmail_gid_fh;
}
else {
Cpanel::Logger::logger(
{
'message' => "Unable to write /etc/vmail/gid.$domain: $!",
'level' => 'warn',
'backtrace' => 0,
'service' => 'updateuserdomains',
'output' => 1,
}
);
}
}
else {
print "Skipping domain $domain. Not associated with a system account.\n" if $verbose;
}
}
utime $now, $now, '/etc/vmail';
print "Done rebuilding /etc/vmail.\n" if $verbose;
}
}
foreach my $db (@WRITTEN_DBS) {
my $safecount = 0;
while ( -e '/etc/' . $db . '.back' ) {
$safecount++;
sleep 1;
last if $safecount > 10; # wait up to ten seconds for another updateuserdomains
}
if ( !-e '/etc/.' . $db ) {
Cpanel::Logger::logger(
{
'message' => "Unable to update /etc/.${db}: $!",
'level' => 'warn',
'backtrace' => 0,
'service' => 'updateuserdomains',
'output' => 1,
}
);
next;
}
# -z '/etc/.' . $db -- we want to do it even if its zero because the utime cache will never work otherwise
next unless ( rename '/etc/' . $db, '/etc/' . $db . '.back' );
unless ( rename '/etc/.' . $db, '/etc/' . $db ) {
Cpanel::Logger::logger(
{
'message' => "Unable to rename /etc/.${db}: $!",
'level' => 'warn',
'backtrace' => 0,
'service' => 'updateuserdomains',
'output' => 1,
}
);
unless ( rename '/etc/' . $db . '.back', '/etc/' . $db ) {
Cpanel::Logger::logger(
{
'message' => "Filesystem error, unable to rename /etc/$db.back: $!",
'level' => 'warn',
'backtrace' => 0,
'service' => 'updateuserdomains',
'output' => 1,
}
);
unless ( Cpanel::FileUtils::TouchFile::touchfile( '/etc/' . $db ) ) {
Cpanel::Logger::logger(
{
'message' => "Unable to touch /etc/$db: $!",
'level' => 'warn',
'backtrace' => 0,
'service' => 'updateuserdomains',
'output' => 1,
}
);
}
}
}
unless ( unlink '/etc/' . $db . '.back' ) {
Cpanel::Logger::logger(
{
'message' => "Unable to unlink intermdiary file /etc/$db.back: $!",
'level' => 'warn',
'backtrace' => 0,
'service' => 'updateuserdomains',
'output' => 1,
}
);
}
utime $now, $now, '/etc/' . $db;
push @NEEDSBUILD, $db;
push @OPTIMIZEDB, '/etc/' . $db;
}
}
else {
print "Not updating databases.\n" if $verbose;
}
require Cpanel::FSOptimize;
# update http env's passwd
if ( $system =~ m/linux/i && -e '/etc/chroothttpd' ) {
system '/usr/local/cpanel/bin/chroothttpd', '--updatefiles';
}
if ( -x '/scripts/postupdateuserdomains' ) {
system '/scripts/postupdateuserdomains', @ARGV;
}
if ( !$fast && -e '/usr/local/cpanel/Cpanel.pm' ) {
if ( Cpanel::FSOptimize::has_chattr() ) {
system '/scripts/convertemails', '--quiet';
}
else {
exec '/scripts/convertemails', '--quiet';
}
}
foreach my $db (@NEEDSBUILD) {
_builddb($db);
}
if ( @OPTIMIZEDB && Cpanel::FSOptimize::has_chattr() ) {
my $chattr_bin = Cpanel::FSOptimize::get_chattr_bin();
if ( !$chattr_bin ) { last; }
exec $chattr_bin, '-R', '+A', @OPTIMIZEDB;
}
exit; #not reached
}
################################################################################
# End cPanel specific code
################################################################################
if ( -e '/var/db/dsm/conf/domainmap' ) {
my @PW = ();
Cpanel::PwCache::setpwent();
open( my $trueuserdomains_fh, '>>', '/etc/trueuserdomains' );
while ( @PW = Cpanel::PwCache::getpwent() ) {
if ( -e "$PW[7]/public_html" ) {
my $homedir = "$PW[7]/public_html";
if ( -l $homedir ) {
$homedir = readlink($homedir);
}
$homedir =~ /([^\/]+)$/;
my $dns = $1;
my $lcdns = lc($1);
print {$trueuserdomains_fh} "${lcdns}: $PW[0]\n";
}
}
close($trueuserdomains_fh);
Cpanel::PwCache::endpwent();
exit;
}
if ( -e '/etc/mail/mailertable' ) {
my %ALAU;
open( my $trueuserdomains_fh, '>>', '/etc/trueuserdomains' );
open( MT, '<', '/etc/mail/mailertable' );
while (<MT>) {
chomp();
my ( $domain, $user ) = split( /\s+/, $_ );
$user =~ s/^procmail://g;
next if ( $ALAU{$user} ne '' );
print {$trueuserdomains_fh} "${domain}: ${user}\n";
$ALAU{$user} = 1;
}
close($trueuserdomains_fh);
}
my $sitelookup = '/usr/local/bin/sitelookup';
if ( -d '/home/virtual' && -e $sitelookup ) {
require Cpanel::FileFormat::Ini;
#
# Death is an appropriate alternative to a failed and miserable life at this
# point in the script's execution.
#
open( my $trueuserdomains_fh, '>', '/etc/trueuserdomains' ) or die('Unable to open /etc/trueuserdomains for writing');
open( my $domainips_fh, '>', '/etc/domainips' ) or die('Unable to open /etc/domainips for writing');
opendir( VH, "/home/virtual" ) or die('Unable to open /home/virtual for site indexing');
my @FILES = grep( /^site\d+/, readdir(VH) );
closedir(VH);
my %seen = ();
foreach my $site (@FILES) {
my ( $ul, $user, $dns );
my $sitedir = '/home/virtual/' . $site . "/fst";
open( DNS, "$sitedir/etc/HOSTNAME" );
chomp( $dns = <DNS> );
close(DNS);
my $sitelookup_data = '';
if ( open( my $sitelookup_h, '-|', "$sitelookup -s $site" ) ) {
while ( my $line = <$sitelookup_h> ) {
$sitelookup_data .= $line;
}
}
else {
print 'Unable to open ' . $sitelookup . ' for reading' . "\n";
exit;
}
my @sitelookup_data = split /\n/, $sitelookup_data;
$user = ( split ',', $sitelookup_data[0] )[-1];
$seen{$user}++;
## only append the incrementing counter if there are duplicates. in other words,
## don't append the counter for the first one named $user.
unless ( $seen{$user} == 1 ) {
$user .= $seen{$user};
}
$user .= "^$site";
my $ipinfo_file = "/home/virtual/$site/info/current/ipinfo";
if ( -f $ipinfo_file ) {
my $data = eval { Cpanel::FileFormat::Ini->read_file($ipinfo_file)->{'DEFAULT'} } || {};
if ( $data->{'namebased'} eq '0' && $data->{'ipaddrs'} ) {
my @ips;
if ( $data->{'ipaddrs'} =~ /^\[([0-9'\., ]+)\]$/ ) {
push @ips, map { s/^'(.*)'$/$1/; $_ } split( /\s*,\s*/, $1 );
}
foreach (@ips) {
printf( $domainips_fh "%s: %s\n", $_, ( split( ',', $sitelookup_data[0] ) )[0] );
}
}
}
print {$trueuserdomains_fh} "$dns: $user\n";
}
close($domainips_fh);
close($trueuserdomains_fh);
}
my $psa1 = 0;
if ( -e '/etc/psa/.psa.shadow' || -e '/usr/local/plesk/admin/conf/admin.conf' ) {
if ( -e '/etc/psa/.psa.shadow' ) {
open( PSAPASS, '/etc/psa/.psa.shadow' );
}
else {
$psa1 = 1;
open( PSAPASS, '/usr/local/plesk/admin/conf/admin.conf' );
}
my $adminpass = <PSAPASS>;
chomp($adminpass);
close(PSAPASS);
open( RC, '>', '/root/.my.cnf' );
print RC <<"MYCNF";
[client]
user=admin
pass='$adminpass'
MYCNF
close(RC);
chmod( 0600, '/root/.my.cnf' );
my $psaversion = getpsaversion();
my @LOGINLIST = ();
my %LOGINS = ();
my %DOMAINS = ();
if ( $psaversion >= 5 ) {
@LOGINLIST = split( /\n/, psasqlcmd('select dom_id,sys_user_id from hosting;') );
}
else {
@LOGINLIST = split( /\n/, psasqlcmd('select dom_id,login from hosting;') );
}
my @SYSUSERLIST = split( /\n/, psasqlcmd('select hosting.dom_id,sys_users.login from sys_users LEFT JOIN hosting ON (sys_users.id=hosting.sys_user_id);') );
foreach (@SYSUSERLIST) {
s/\n//g;
my ( $id, $login ) = split( /[\s\t]+/, $_ );
$LOGINS{$id} = $login;
}
my @DOMLIST = split( /\n/, psasqlcmd('select id,name from domains;') );
foreach (@LOGINLIST) {
s/\n//g;
my ( $id, $login ) = split( /[\s\t]+/, $_ );
next if ( $LOGINS{$id} ne '' );
if ( $login =~ /^\d+$/ ) {
$login += 10000;
$login = ( Cpanel::PwCache::getpwuid($login) )[0];
}
$LOGINS{$id} = $login;
}
foreach (@DOMLIST) {
s/\n//g;
my ( $id, $login ) = split( /[\s\t]+/, $_ );
$DOMAINS{$id} = $login;
}
open( my $trueuserdomains_fh, '>>', '/etc/trueuserdomains' );
foreach my $id ( keys %LOGINS ) {
if ( $DOMAINS{$id} ne '' ) {
print {$trueuserdomains_fh} "$DOMAINS{$id}: $LOGINS{$id}\n";
}
}
close($trueuserdomains_fh);
}
if ( -d '/usr/local/directadmin' ) {
open( my $trueuserdomains_fh, ">>", "/etc/trueuserdomains" );
opendir( my $users_h, '/usr/local/directadmin/data/users/' );
my @users = grep { !/\.\.?/ } readdir $users_h;
foreach my $user (@users) {
my $config = get_user_config($user);
my $domain = $config->{'domain'};
my $name = $config->{'name'};
print {$trueuserdomains_fh} "$domain: $name\n";
}
close $trueuserdomains_fh;
}
my $hostdir = hostdir();
if ( -d $hostdir ) {
open( my $trueuserdomains_fh, ">>", "/etc/trueuserdomains" );
my $row = spherasqlcmd( 'sddb', 'SELECT key, domain FROM account' );
if (@$row) {
foreach my $data (@$row) {
my $name = $data->{'key'};
my $domain = $data->{'domain'};
print {$trueuserdomains_fh} "$domain: $name\n";
}
}
else {
eval { require XML::Parser };
if ( !$@ ) {
my $xml = XML::Parser->new(
Handlers => {
Start => sub {
my ( $e, $el, %attr ) = @_;
return unless $el eq 'account';
my $domain = $attr{'domain'};
my $name = $attr{'name'};
print {$trueuserdomains_fh} "$domain: $name\n";
}
}
);
$xml->parsefile( "$hostdir/conf/" . 'hostdir.xml' );
}
}
}
sub hostdir {
my $scripts = `rpm -q ServerDirector --scripts`;
my $conf = '';
foreach my $line ( split /\n/, $scripts ) {
if ( $line =~ /\s+(\/.*sphera.conf)/ ) {
$conf = $1;
last;
}
}
my $hostdir;
if ( open( my $fh, '<', $conf ) ) {
my $line = readline($fh);
my @hostdir = split /\s/, $line;
$hostdir = $hostdir[1];
close $fh;
}
return $hostdir;
}
sub psasqlcmd {
my ($cmd) = @_;
my ($result);
my $mysql = find_mysql();
my $pid = IPC::Open3::open3( \*WTRFH, \*RDRFH, ">&STDERR", $mysql, '-N', ( $psa1 ? 'plesk' : 'psa' ) );
print WTRFH "$cmd\n";
close(WTRFH);
while (<RDRFH>) {
$result .= $_;
}
close(RDRFH);
waitpid( $pid, 0 );
return ($result);
}
sub find_mysql {
my ($self) = @_;
my @LOC = qw( /usr/local/psa/mysql/bin/mysql /usr/bin/mysql /usr/sbin/mysql /usr/local/plesk/mysql/bin/mysql /usr/local/bin/mysql );
foreach my $loc (@LOC) {
if ( -e $loc ) { return $loc; }
}
}
sub getpsaversion {
my $psadir = getpsadir();
my $version = 0;
if ( $psadir && open( my $fh, '<', "${psadir}/version" ) ) {
while ( my $line = <$fh> ) {
if ( $line =~ /^(\d+)/ ) {
$version = $1;
last;
}
}
close($fh);
}
return $version;
}
sub getpsadir {
foreach my $dir ( "/usr/local/psa", "/usr/local/plesk" ) {
return $dir if -d $dir;
}
}
sub readdb {
my ( $rDB, $rMTIMEDB, $db, $keyname, $order ) = @_;
$rMTIMEDB->{$keyname} = ( stat( '/etc/' . $db ) )[9];
if ( open my $db_fh, '<', '/etc/' . $db ) {
if ( $order && $order == 3 ) {
%{ $rDB->{$keyname} } = map { /^([^\#\:]+):\s*(\S.*?)\s*$/ ? ( $1 => $2 ) : () } (<$db_fh>);
#while ( readline($db_fh) ) {
# if (/^([^\#\:]+):\s*(\S.*?)\s*$/) {
# $rDB->{$keyname}->{$1} = $2; #field0 = field1
# }
#}
}
elsif ( $order && $order == 4 ) {
%{ $rDB->{$keyname} } = map { /^([^\#\:]+):\s*(\S.*?)\s*$/ ? ( $2 => $1 ) : () } (<$db_fh>);
#while ( readline($db_fh) ) {
# if (/^([^\#\:]+):\s*(\S.*?)\s*$/) {
# $rDB->{$keyname}->{$2} = $1; #field1 = field0
# }
#}
}
elsif ( $order && $order == 1 ) {
#while ( readline($db_fh) ) {
# if (/^([^\#\:]+):\s*(\S.*?)\s*$/) {
# push( @{ $rDB->{$keyname}{$1} }, $2 );
# }
#}
foreach (<$db_fh>) {
if (/^([^\#\:]+):\s*(\S.*?)\s*$/) {
push( @{ $rDB->{$keyname}{$1} }, $2 );
}
}
}
elsif ($order) {
#while ( readline($db_fh) ) {
# if (/^([^\#\:]+):\s*(\S.*?)\s*$/) {
# push( @{ $rDB->{$keyname}{$2} }, $1 );
# }
#}
foreach (<$db_fh>) {
if (/^([^\#\:]+):\s*(\S.*?)\s*$/) {
push( @{ $rDB->{$keyname}{$2} }, $1 );
}
}
}
else {
local $/;
%{ $rDB->{$keyname} } = map { $_ => 1 } split( /\n/, readline($db_fh) ); #field0 = 1
delete @{ $rDB->{$keyname} }{ '', grep { m/^#/ } keys %{ $rDB->{$keyname} } };
}
close $db_fh;
}
else {
Cpanel::Logger::logger(
{
'message' => "Unable to read /etc/$db: $!",
'level' => 'warn',
'backtrace' => 0,
'service' => 'updateuserdomains',
'output' => $verbose,
}
);
return;
}
}
sub get_user_config {
my ($user) = @_;
my $conf = '/usr/local/directadmin/data/users/' . $user . '/user.conf';
my $data = {};
if ( open( my $fh, '<', $conf ) ) {
while ( my $line = <$fh> ) {
chomp($line);
my ( $key, $value ) = split /=/, $line;
$data->{$key} = $value;
}
close $fh;
}
return $data;
}
sub findbin {
my $binname = shift;
my %OPTS = @_;
foreach my $path ( @{ $OPTS{'path'} } ) {
if ( -x $path . '/' . $binname ) {
return $path . '/' . $binname;
}
}
}
sub _builddb {
my $db = shift;
# not yet used and might actually be slower since exims caching is so well written
#
#
return;
if ( !$exim_dbmbuild ) {
$exim_dbmbuild = findbin( 'exim_dbmbuild', 'path' => [ '/usr/sbin', '/usr/local/sbin', '/usr/bin', '/usr/local/bin' ] );
}
if ( my $pid = fork() ) {
waitpid( $pid, 0 );
}
else {
umask 0127;
open( STDOUT, '>', '/dev/null' );
$) = $mailgid;
exec $exim_dbmbuild, '/etc/' . $db, '/etc/' . $db . '.db';
exit 0;
}
chown( 0, $mailgid, '/etc/' . $db . '.db' );
chmod( _permissions( '/etc/' . $db ), '/etc/' . $db . '.db' );
}
sub spherasqlcmd {
my ( $db, $stmt ) = @_;
my $psql = '/usr/bin/psql';
local $ENV{'PGHOST'} = _find_pg_socket();
my $pg_user = pg_user();
my $pid = IPC::Open3::open3( my $w, my $r, my $err, $psql, $db, '-A', '-R', '-+++-', '-U', $pg_user );
print {$w} $stmt;
close $w;
my $result;
while ( my $line = <$r> ) {
$result .= $line;
}
close $r;
waitpid( $pid, 0 );
return _create_hashref($result);
}
sub pg_user {
my $pid = IPC::Open3::open3( my $w, my $r, '', 'ps', '-axu' );
close $w;
my $pg_user;
while ( my $line = <$r> ) {
chomp($line);
if ( $line =~ /postmaster|postgres/ ) {
my @items = split /\s+/, $line;
$pg_user = $items[0];
last if $pg_user;
}
}
close $r;
waitpid( $pid, 0 );
return $pg_user;
}
sub _create_hashref {
my ($string_data) = @_;
my $record_separator_qr = qr{\Q-+++-\E};
my @records = split m{$record_separator_qr}, $string_data;
chomp @records;
pop @records;
my @field_names = map lc, split( /\|/, shift(@records) );
my @result_set;
foreach my $row (@records) {
my @row = split /\|/, $row;
my $results = {};
my $count = 0;
foreach my $field (@field_names) {
$results->{$field} = $row[$count];
$count++;
}
push @result_set, $results;
}
return \@result_set;
}
sub _find_pg_socket {
my $conf = '/home/hostdir/sphera_db/data/postgresql.conf';
my $socket_dir_key = 'unix_socket_directory';
my $pghost;
if ( open( my $fh, '<', $conf ) ) {
while ( my $line = <$fh> ) {
next if $line =~ /^\#/;
if ( $line =~ /^$socket_dir_key/ ) {
( undef, $pghost ) = split /\s+?\=\s+?/, $line, 2;
chomp($pghost);
$pghost =~ s/^\'|\'$//g;
}
}
}
return $pghost;
}
sub _permissions {
my $db = shift;
return exists $permissions_list{$db} ? $permissions_list{$db} : $permissions_list{'default'};
}
# This sub adds the DBOWNER key to the cpuser file.
# We cannot guarantee that Cpanel::Config::CpUserGuard
# will be installed in in Cpanel/ when updateuserdomains
# is run in upcp so load the old modules if CpUserGuard
# can't be found.
sub _add_dbowner_to_cpuserfile {
my ($user) = @_;
require Cpanel::Config::LoadCpUserFile;
my $cpuserfile = Cpanel::Config::LoadCpUserFile::load($user);
if (exists $cpuserfile->{'DBOWNER'}) {
return $cpuserfile->{'DBOWNER'};
}
eval { require Cpanel::Config::CpUserGuard; };
if ( !$@ ) {
my $cpuser_guard = Cpanel::Config::CpUserGuard->new($user);
$cpuser_guard->{'data'}->{'DBOWNER'} = $user;
$cpuser_guard->save();
}
else {
require Cpanel::Config::SaveCpUserFile;
$cpuserfile->{'DBOWNER'} = $user;
Cpanel::Config::SaveCpUserFile::save( $user, $cpuserfile );
}
return $user;
}