# Copyright 1999-2012. Parallels IP Holdings GmbH. All Rights Reserved. package PleskStructure; use PleskVersion; use XmlNode; # IP address is the pair (address, type) my %ipType; #ip -> 'shared'|'exclusive' my %clientDomains; #clientName -> domainName my %clientIds; #clientId -> clientName my %clientGuids; #clientId -> clientGuid my %clientChilds; #clientId -> clientName my %domainIp; #domainName -> @(ip1, [ip2], ...) my %domainIds; #domainName -> id my %domainAsciiNames; #domainId -> domainAsciiName my %domain2Client; #domainId -> clientId my %clientData; #clientName -> { 'id'-> $id, 'type' -> $type, 'parent' -> $parent, 'ip' -> @ip, 'guid' -> $guid } my %resellerPlans; #resellerId -> planId my $adminName; my $dbh; my %userLogins; #login -> %users my %roleIds; #id -> %roles my %sites2Subscriptions; #siteName -> subscriptionName my %serviceNodesIps; #ip -> remote node id my %serviceNodes; # id -> output of command service_node --describe-for-pmm-deployer my %registeredDbServers; #dbServerId -> { 'admin' => $admin, 'password' => $password } my $databaseRegistrarUtilFailed = 0; ## public interface sub init { $dbh = shift; if ( PleskVersion::isSmb() ) { __initSmb(); } elsif ( PleskVersion::atLeast( 11, 1, 0 ) ) { __init111(); } elsif ( PleskVersion::atLeast( 10, 0, 0 ) ) { __init100(); } elsif (PleskVersion::atLeast(9, 0, 0)) { __init90(); } else { __init60(); } } sub getClients { my( @ret ); foreach my $clientName( keys %clientData ){ push @ret, $clientName if ${clientData}{$clientName}{'type'} eq 'client'; } return @ret; } sub getResellers { my( @ret ); foreach my $clientName( keys %clientData ){ push @ret, $clientName if ${clientData}{$clientName}{'type'} eq 'reseller'; } return @ret; } sub getResellersFromId{ my ( $ids ) = @_; my ( @ret, @wrong ); foreach my $id( @{$ids} ){ my $login = getClientNameFromId( $id ); if( $login ){ if( getClientType( $login ) eq 'reseller' ) { push @ret, $login; } else{ push @wrong, $id; } } else{ push @wrong, $id; } } @{$ids} = @wrong; return @ret; } sub getResellerPlanId { my ($id) = @_; return (exists $resellerPlans{$id}) ? $resellerPlans{$id} : undef; } sub getClientsFromId{ my( $ids )= @_; my ( @ret, @wrong ); foreach my $id( @{$ids} ){ my $login = getClientNameFromId( $id ); if( $login ){ if( getClientType( $login ) eq 'client' ) { push @ret, $login; } else{ push @wrong, $id; } } else{ push @wrong, $id; } } @{$ids} = @wrong; return @ret; } sub getDomainsFromId{ my ( $ids ) = @_; my ( @ret, @wrong ); foreach my $id( @{$ids} ){ my $domain = getDomainNameFromId( $id ); if( $domain ){ push @ret, $domain; } else{ push @wrong, $id; } } @{$ids} = @wrong; return @ret; } sub getAdminClients { if (PleskVersion::atLeast(9, 0, 0)) { my @ret = getMyClients( $adminName ); #push @ret, $adminName; return @ret; } else{ return getClients(); } } sub getAdminDomains { if (PleskVersion::atLeast(9, 0, 0)) { return getDomainsForClient( $adminName ); } else{ my @ret; return @ret; } } sub getAdminName{ return $adminName; } sub getAdminId{ return getClientId( $adminName ) if $adminName; return; } sub getAdminGuid{ return getClientGuid( $adminName ) if $adminName; return "00000000-0000-0000-0000-000000000000"; } sub getClientsCount{ my ( $parentName ) = @_; my $count = 0; if( $parentName ){ my $parentId = $clientData{$parentName}{'id'}; foreach my $clientName( keys %clientData ){ if( $clientData{$clientName}{'parent'} == $parentId ){ $count += 1; if( $clientData{$clientName}{'type'} eq 'reseller' ){ $count += getClientsCount( $clientData{'id'} ); } } } } else{ $count = scalar( keys %clientData ); } return $count; } sub getDomainsCount{ my ($parentName ) = @_; my $count = 0; if( $parentName ){ my $parentId = $clientData{$parentName}{'id'}; foreach my $clientName( keys %clientData ){ if( $clientData{$clientName}{'parent'} == $parentId ){ $count += scalar( getDomainsForClient( $clientName ) ); if( $clientData{$clientName}{'type'} eq 'reseller' ){ $count += getDomainsCount( $clientData{'id'} ); } } elsif( $clientData{$clientName}{'id'} == $parentId ){ $count += scalar( getDomainsForClient( $clientName ) ); } } } else{ foreach my $clientName( keys %clientDomains ){ $count += scalar( getDomainsForClient( $clientName ) ) } } return $count; } sub PleskStructure::sortClients{ my ( $a, $b ) = @_; return $a cmp $b if $clientData{$a}{'type'} eq $clientData{$b}{'type'}; return 1 if $clientData{$a}{'type'} eq 'reseller'; return -1 if $clientData{$b}{'type'} eq 'reseller'; return 1 if $clientData{$a}{'type'} eq 'admin'; return -1 if $clientData{$b}{'type'} eq 'admin'; die "Cannot compare clients '$a', '$b'. Invalid types!"; } sub getMyClients { my ( $parentName ) = @_; my( @ret ); die "Cannot find client '$parentName'!" if not exists $clientData{$parentName}; my $parentId = $clientData{$parentName}{'id'}; return @{$clientChilds{$parentId}} if exists $clientChilds{$parentId}; return @ret; } sub getClientType{ my( $clientName ) = @_; die "Cannot determine client type. Cannot find client '$clientName'!" if not exists $clientData{$clientName}; my $ret = $clientData{$clientName}{'type'}; $ret = 'client' if not $ret; return $ret; } sub getDomainNameFromId{ my( $domainId ) = @_; my ( $name, $data ); my $ret = ''; while ( ($name, $data) = each %domainIds) { $ret = $name if $data == $domainId; } return $ret; } sub getDomainAsciiNameFromId{ my( $domainId ) = @_; return $domainAsciiNames{$domainId}; } sub getClientNameFromId{ my( $clientId ) = @_; return $clientIds{$clientId}; } sub getClientGuidFromId{ my( $clientId ) = @_; return $clientGuids{$clientId}; } sub getClientParentId{ my( $clientName ) = @_; return $clientData{$clientName}{'parent'}; } sub getClientId{ my( $clientName ) = @_; return $clientData{$clientName}{'id'}; } sub getDomainId{ my( $domainName ) = @_; return $domainIds{$domainName}; } sub getClientGuid{ my( $clientName ) = @_; return $clientData{$clientName}{'guid'}; } sub getDomains { return keys %domainIp; } sub getDomainsForClient { my ($clientName) = @_; return @{ $clientDomains{$clientName} }; } sub getClientIdForDomainId { my ($domainId) = @_; return $domain2Client{$domainId}; } sub getClientIps { my ($clientName) = @_; return %{$clientData{$clientName}{'ip'}}; } sub getClientIpType { my ($clientName, $ip) = @_; return ${$clientData{$clientName}{'ip'}}{$ip} if exists ${$clientData{$clientName}{'ip'}}{$ip}; return $ipType{ $ip }; } # May return undef in the case of disabled domain without hosting sub getDomainIp { my ($domainName) = @_; return $domainIp{$domainName}; } sub isExclusiveIp { my ($ip) = @_; return unless defined $ip; return $ipType{$ip} eq "exclusive"; } sub getIpType { my ($ip) = @_; return unless defined $ip; return $ipType{$ip}; } sub getIp4DomainWoHosting { my ($domainId, $preferredIp, $typePtr) = @_; return unless defined $preferredIp; my @result; my $clientId = $domain2Client{$domainId}; my %clientIps = getClientIps(getClientNameFromId($clientId)); if ( keys %clientIps ) { if( grep $_ eq $preferredIp, keys %clientIps) { ${$typePtr} = $clientIps{$preferredIp}; push @result, $preferredIp; return \@result; } foreach my $ip ( keys %clientIps ) { if ( $clientIps{$ip} eq 'shared' ) { ${$typePtr} = 'shared'; push @result, $ip; return \@result; } } foreach my $ip ( keys %clientIps ) { ${$typePtr} = 'exclusive'; push @result, $ip; return \@result; } } ${$typePtr} = 'shared'; push @result, $preferredIp; return \@result; } sub getUserLogins { my( $ownerLogin ) = @_; return unless exists $userLogins{$ownerLogin}; return keys %{$userLogins{$ownerLogin}}; } sub getRoleIds { my( $ownerLogin ) = @_; return unless exists $roleIds{$ownerLogin}; return keys %{$roleIds{$ownerLogin}}; } ## Plesk Smb sub __initSmb { __init90(); __populateSmbUsers(); __populateSmbRoles(); } ## Plesk 11.1 - sub __init111 { __init100(); __populateServiceNodes(); } ## Plesk 10.0 - sub __init100 { __init90(); __populateUsers(); __populateRoles(); __populateSites(); __populateResellerPlans(); } sub __populateResellerPlans { my $sql = "SELECT s.object_id, t.id FROM Subscriptions AS s ". "INNER JOIN PlansSubscriptions AS p ON s.id = p.subscription_id ". "INNER JOIN Templates AS t ON t.id = p.plan_id ". "WHERE s.object_type = 'client' AND t.owner_id = (SELECT id FROM clients WHERE type = 'admin' LIMIT 0,1) AND t.type = 'reseller'"; if ($dbh->execute($sql)) { while (my $row = $dbh->fetchrow()) { $resellerPlans{$row->[0]} = $row->[1]; } $dbh->finish(); } } ## Plesk 9.0 - sub __init90 { __populateIpsFromIPAddressesTable9(); __populateClientIpPool9(); __populateDomains(); } sub __populateIpsFromIPAddressesTable9 { my $row; if($dbh->execute("SELECT ip_address, id FROM IP_Addresses")) { while ( $row = $dbh->fetchrow() ) { $ipType{ $row->[0] } = 'shared'; } } $dbh->finish(); } sub __populateClientIpPool9 { my $sql = "SELECT c.id, c.login, p.ip_address, c.type, c.parent_id, pl.type, c.guid FROM clients c " . "LEFT JOIN ip_pool pl ON c.pool_id = pl.id " . "LEFT JOIN IP_Addresses p ON pl.ip_address_id = p.id"; if ( $dbh->execute($sql) ) { my $row; while ($row = $dbh->fetchrow()) { my ($id, $clientName, $ipAddress, $cltype, $clparent, $iptype, $guid ) = @{$row}; $clientIds{$id} = $clientName; $clientGuids{$id} = $guid; $clientData{$clientName} = { 'id' => $id, 'type' => $cltype, 'parent' => $clparent, 'ip' => {}, 'guid' => $guid } if not exists $clientData{$clientName}; ${$clientData{$clientName}{'ip'}}{$ipAddress} = $iptype if ($ipAddress); if( $cltype eq 'admin' ){ $adminName = $clientName; $ipType{$ipAddress} = $iptype; } } } foreach my $clientName( keys %clientData ){ my $clparent = $clientData{$clientName}{'parent'}; if( $clparent ) { $clientChilds{$clparent} = () if not exists $clientChilds{$clparent}; push @{$clientChilds{$clparent}}, $clientName; } } $dbh->finish(); } ## Plesk 6.0 - 8.0 sub __init60 { __populateIpsFromIPAddressesTable(); __populateClientIpPool(); __populateDomains(); } ## common sub __populateUsers { my $sql = "SELECT smb_users.login, clients.login FROM smb_users, clients WHERE smb_users.ownerId = clients.id"; if($dbh->execute($sql)) { my $row; while ( $row = $dbh->fetchrow() ) { my $userLogin = $row->[0]; my $ownerLogin = $row->[1]; $userLogins{$ownerLogin}->{$userLogin} = 1; } } $dbh->finish(); } sub __populateSmbUsers { my @logins = @{DAL::selectLoginFromSmbUsers()}; foreach my $login (@logins) { $userLogins{'admin'}->{$login} = 1; } } sub __populateRoles { my $sql = "SELECT smb_roles.id, clients.login FROM smb_roles, clients WHERE smb_roles.ownerId = clients.id"; if($dbh->execute($sql)) { my $row; while ( $row = $dbh->fetchrow() ) { my $roleId = $row->[0]; my $ownerLogin = $row->[1]; $roleIds{$ownerLogin}->{$roleId} = 1; } } $dbh->finish(); } sub __populateSmbRoles { my @roles = @{DAL::selectSmbRoles()}; foreach my $role (@roles) { $roleIds{'admin'}->{$role->{'id'}} = 1; } } sub __populateIpsFromIPAddressesTable { my $row; if($dbh->execute("SELECT ip_address, type, id FROM IP_Addresses")) { while ( $row = $dbh->fetchrow() ) { $ipType{ $row->[0] } = $row->[1]; } } $dbh->finish(); } sub __populateClientIpPool { my $sql = "SELECT c.id, c.login, p.ip_address FROM clients c " . "LEFT JOIN Repository r ON c.pool_id = r.rep_id " . "LEFT JOIN IP_Addresses p ON r.component_id = p.id"; $dbh->execute($sql); my $row; while ($row = $dbh->fetchrow()) { my ($id, $clientName, $ipAddress) = @{$row}; $clientData{$clientName} = { 'id' => $id, 'type' => 'client', 'parent' => 0, ip => {} } if not exists $clientData{$clientName}; ${$clientData{$clientName}{'ip'}}{$ipAddress} = $ipType{$ipAddress} if $ipAddress; $clientIds{$id} = $clientName; } $dbh->finish(); } sub __populateDomains { my $domainNameColumn = 'displayName'; my $sql; if ( PleskVersion::atLeast( 10, 2, 0 ) and not PleskVersion::isSmb() ) { $sql = "SELECT DISTINCT domains.$domainNameColumn, IP_Addresses.ip_address FROM DomainServices, IpAddressesCollections, IP_Addresses, domains" . " WHERE DomainServices.dom_id = domains.id AND DomainServices.ipCollectionId = IpAddressesCollections.ipCollectionId" . " AND IpAddressesCollections.ipAddressId = IP_Addresses.id AND domains.webspace_id = 0 "; } elsif ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) { $sql = "SELECT d.$domainNameColumn, p.ip_address FROM domains d, IP_Addresses p, hosting h " . "WHERE p.id = h.ip_address_id AND h.dom_id = d.id AND d.webspace_id = 0"; } else { $sql = "SELECT d.$domainNameColumn, p.ip_address FROM domains d, IP_Addresses p, hosting h " . "WHERE p.id = h.ip_address_id AND h.dom_id = d.id"; } if($dbh->execute($sql)) { my $row; while ( $row = $dbh->fetchrow() ) { my ( $domainName, $ipAddress ) = @{$row}; if (!exists($domainIp{$domainName})) { @{$domainIp{$domainName}} = ($ipAddress); }else { push @{$domainIp{$domainName}}, $ipAddress; } } } $dbh->finish(); if ( !PleskVersion::atLeast( 10, 2, 0 ) ) { if ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) { $sql = "SELECT d.$domainNameColumn, p.ip_address FROM domains d, IP_Addresses p, forwarding f " . "WHERE p.id = f.ip_address_id AND f.dom_id = d.id AND d.webspace_id = 0"; } else { $sql = "SELECT d.$domainNameColumn, p.ip_address FROM domains d, IP_Addresses p, forwarding f " . "WHERE p.id = f.ip_address_id AND f.dom_id = d.id"; } if($dbh->execute($sql)) { while ( $row = $dbh->fetchrow() ) { my ( $domainName, $ipAddress ) = @{$row}; if (!exists($domainIp{$domainName})) { @{$domainIp{$domainName}} = ($ipAddress); }else { push @{$domainIp{$domainName}}, $ipAddress; } } } $dbh->finish(); } if ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) { $sql = "SELECT d.$domainNameColumn FROM domains d WHERE htype = 'none' AND webspace_id = 0"; } else { $sql = "SELECT d.$domainNameColumn FROM domains d WHERE htype = 'none'"; } if($dbh->execute($sql)) { while ( $row = $dbh->fetchrow() ) { $domainIp{ $row->[0] } = undef; } } $dbh->finish(); if ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) { $sql = "SELECT d.$domainNameColumn, d.name, c.login, c.id, d.id dom_id FROM clients c LEFT JOIN domains d ON d.cl_id = c.id WHERE d.webspace_id = 0"; } else { $sql = "SELECT d.$domainNameColumn, d.name, c.login, c.id, d.id dom_id FROM clients c LEFT JOIN domains d ON d.cl_id = c.id"; } if($dbh->execute($sql)) { while ( $row = $dbh->fetchrow() ) { my ( $domainName, $domainAsciiName, $clientName, $clientId, $domainId ) = @{$row}; $domainIds{$domainName} = $domainId; $domainAsciiNames{$domainId} = $domainAsciiName; $domain2Client{$domainId} = $clientId; if ( !exists( $clientDomains{$clientName} ) ) { $clientDomains{$clientName} = []; } if ($domainName) { push @{ $clientDomains{$clientName} }, $domainName; } } } $dbh->finish(); } sub __populateSites { my $sql = "SELECT a.displayName, b.displayName FROM domains a INNER JOIN (SELECT id, displayName FROM domains) b WHERE a.webspace_id = b.id"; if($dbh->execute($sql)) { while ( $row = $dbh->fetchrow() ) { my ($site, $subscription) = @{$row}; $sites2Subscriptions{$site} = $subscription; } } $dbh->finish(); } sub getSites { return \%sites2Subscriptions; } sub __populateServiceNodes { eval {require XML::Simple; 1;}; my $serviceNodeCommand = AgentConfig::getServiceNodeCommand(); my $cmd = $serviceNodeCommand . " --describe-for-pmm-deployer"; Logging::debug("Exec: " . $cmd); my $result = `$cmd`; chomp($result); my $xs = XML::Simple->new(ForceArray => 1); my $ref = $xs->XMLin($result, KeyAttr => []); foreach my $serviceNode (@{$ref->{'service-node'}}) { if ($serviceNode->{'local'} eq 'false') { $serviceNodes->{$serviceNode->{'database-id'}} = $serviceNode; foreach my $ipaddress (@{$serviceNode->{'ip-address'}}) { $serviceNodesIps{$ipaddress} = $serviceNode->{'database-id'}; } } } } sub isRemoteIp { my $ip = shift; return exists($serviceNodesIps{$ip}); } sub getRemoteNodeId { my $ip = shift; return $serviceNodesIps{$ip}; } sub getCommunicationIp { my $ip = shift; my $serviceNodeId = getRemoteNodeId($ip); return $serviceNodes->{$serviceNodeId}->{'communication-ip-address'}; } sub getServiceNodeProperty { my ($serviceNodeId, $propertyName) = @_; my $serviceNode = $serviceNodes->{$serviceNodeId}; foreach my $properties (@{$serviceNode->{'properties'}}) { foreach my $property (@{$properties->{'property'}}) { if ($property->{'name'}[0] eq $propertyName) { return $property->{'value'}[0]; } } } } sub __populateDbServers { my $util = AgentConfig::getDatabaseRegistrarUtil(); my $cmd = $util . " --get-all-registered-servers"; Logging::debug(" Exec: " . $cmd); my $result = `$cmd`; chomp($result); my $retCode = $? >> 8; if( $retCode != 0 ) { Logging::error( "Unable to get list of db servers (ErrorCode: $retCode, STDOUT:$result)." ,'UtilityError'); $databaseRegistrarUtilFailed = 1; return; } my @db_servers; eval($result); if ( $@ ) { Logging::error( "Unable to parse db servers from utility output ($@):\n $result", 'UtilityError' ); return; } foreach my $dbServer (@db_servers) { my $xmlNode = HelpFuncs::hash2XmlNode($dbServer, 1); $registeredDbServers{ $xmlNode->getChild('id')->getText() } = { 'admin' => $xmlNode->getChild('admin')->getText(), 'password' => $xmlNode->getChild('password')->getText() }; } } sub getDbServerAdminPassword { my ( $dbServer ) = @_; my $dbServerId = $dbServer->{'id'}; if ( PleskVersion::atLeast( 11, 0, 4 ) ) { if ($databaseRegistrarUtilFailed) { return undef; } if ( !%registeredDbServers ) { __populateDbServers(); } return $registeredDbServers{ $dbServerId }{'password'}; } else { if ( $dbServer->{'type'} eq "mysql" and $dbServer->{'host'} eq 'localhost' ) { $dbPasswd = AgentConfig::get('password'); } else { $dbPasswd = $dbServer->{'admin_password'}; } return $dbPasswd; } } 1; # Local Variables: # mode: cperl # cperl-indent-level: 2 # indent-tabs-mode: nil # tab-width: 4 # End: