package PleskX;

#
# Agent interface:
#
# ::checkHost()
#
# ::new($storagePolicy, $dumpStatusPolicy, $agentsSharedDir)
#
# ->setDumpType(FULL [default] | SHALLOW | CONFIGURATION | ONLY_MAIL)
#
# ->selectDomains(@domains)
# ->selectClients(@clients)
# ->selectAll()
# ->selectAllDomains()
# ->selectServerSettings()
#
# ->excludeDomains(@domains)
# ->excludeClients(@clients)
#
# ->dump()
#
# Plesk agent interface:
#
# ->setDescription()
#

use strict;

use PleskVersion;
use PleskMaxVersion;
use PleskStructure;
use Status;
use SiteApp;
use SiteApp10;
use Mailman;
use AgentConfig;
use StopWatch;
use Logging;

#require "agent.include.pl";
# makePasswordNode, generateDumpId, XmlNode, makeXmlNode,
# makeMIMEBase64, urlDecode, addPermissionNode, addMailuserPermissionNode,
# getSqlList

use vars qw|@ISA|;

sub new {
  my $self = {};
  bless( $self, shift );
  $self->_init(@_);
  return $self;
}

sub checkHost {
  my @problems;
  if ( !AgentConfig::init() ) {
    push @problems, "Unable to find /etc/psa/psa.conf configuration file";
  }
  else {
    eval { PleskVersion::init( AgentConfig::get('PRODUCT_ROOT_D') ) };
    if ($@) {
      push @problems, $@;
    }
    else {
      if ( !PleskVersion::isSupported() ) {
        if ( PleskVersion::atLeast( 2, 5, 0 ) ) {
          push @problems,
              "Migration from version >= "
            . PleskMaxVersion::get()
            . " is not supported";
        }
        else {
          push @problems, "Migration from version < 2.5.0 is not supported";
        }
      }
    }

    if ( !defined AgentConfig::iconvBin() ) {
      push @problems, "No 'iconv' binary found on the host";
    }
  }

  return @problems;
}

sub getDomainRoot {
  my ($self, $domainAsciiName) = @_;
  my $domainRoot = AgentConfig::get("HTTPD_VHOSTS_D") . "/$domainAsciiName";
  return $domainRoot;
}

sub getDefaultWebspaceRoot {
  getDomainRoot(@_);
}

sub getWebspaceRoot {
  my ($self, $domainAsciiName) = @_;
  my $domainRoot = $self->getDomainRoot($domainAsciiName);
  return $domainRoot unless ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() );

  my $home;
  my $sql = "SELECT s.home FROM sys_users s, hosting h, domains d WHERE h.sys_user_id=s.id AND d.id=h.dom_id AND d.name='$domainAsciiName'";
  if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
    $home = $ptrRow->[0];
  }
  $self->{dbh}->finish();

  if ($home ne $domainRoot) {
    return $home;
  }

  return $domainRoot;
}

sub getSubroutineRef {
  my $self = shift;
  my $name = shift;
  my $ret = \&{ref($self->{packer}) . "::" . $name};
  return $ret;

}

# --- Public instance methods ---

sub _init {
  my ( $self, $storagePolicy, $dumpStatusPolicy, $agentsSharedDir, $skipLogs ) =
    @_;

  $self->{collectStatistics} = $self->getCollectStatistics();
  if ($self->{collectStatistics})
  {
    $self->{stopWatch} = StopWatch->new();
    $self->{stopWatch}->createMarker("all");

    use StopWatchPacker;
    $self->{packer} = StopWatchPacker->new( PleskMaxVersion::get(), $storagePolicy );
  }
  else
  {
    use Packer;
    $self->{packer} = Packer->new( PleskMaxVersion::get(), $storagePolicy );
  }
  $self->{dump_status} = $dumpStatusPolicy;
  $self->{skip_logs}   = $skipLogs;
  $self->{configuration_dump} = undef;
  $self->{shallow_dump}       = undef;
  $self->{only_mail_dump}     = undef;
  $self->{only_hosting_dump}  = undef;
  $self->{description}        = undef;
  $self->{suspend}            = undef;
  $self->{suspendedDomain}    = undef;
  $self->{get_size}           = undef;
  $self->{dump_vhost}         = 0;
  $self->{dump_full_mail}     = 0;
  $self->{dumped_domains}     = [];
  $self->{dumped_clients}     = [];
  
  #-----
  # Array of custom buttons IDs which are dumped within site applications
  #-----
  $self->{skip_custom_buttons} = [];

  AgentConfig::init()
    or die "Error: Plesk config file 'psa.conf' is not found\n";
  AgentConfig::setSharedDir($agentsSharedDir);

  AgentConfig::tarBin();    # Pre-caching of values
  AgentConfig::mysqlBin();

  PleskVersion::init( AgentConfig::get('PRODUCT_ROOT_D') );

  $self->{base64} = Packer::makeMIMEBase64();

  $self->{dbh} = Db::Connection::getConnection(
    'type'      => 'mysql',
    'user'      => 'admin',
    'password'  => AgentConfig::get('password'),
    'name'      => 'psa',
    'host'      => 'localhost',
    'utf8names' => PleskVersion::atLeast( 8, 0, 0 )
  );

  if ($self->{collectStatistics})
  {
    $self->{dbh}->startCollectStatistics();
  }

  PleskStructure::init( $self->{dbh} );

  if ( PleskVersion::atLeast( 7, 1, 0 ) ) {
    Encoding::setDefaultEncoding("UTF-8");
  }
}

sub getCollectStatistics {
  my $envVal = $ENV{'COLLECT_BACKUP_STAT'};
  if ($envVal)
  {
    return $envVal;
  }
  return 0;
}

sub turnOffMigrationMode{
  my ($self) = @_;
  $self->{packer}->turnOffMigrationMode();
}

sub setDumpWholeVHost{
  my ($self) = @_;
  $self->{dump_vhost}         = 1;
}

sub setDumpWholeMail{
  my ($self) = @_;
  $self->{dump_full_mail}     = 1;
}

sub setSizeMode{
  my ($self) = @_;
  $self->{get_size}           = 1;
  Logging::setVerbosity(4);
}

sub setContentTransport{
  my $self = shift;
  my $contentTransportType = shift;
  $self->{packer}->setContentTransport($contentTransportType, @_)
}


use vars qw|$FULL $SHALLOW $CONFIGURATION $ONLY_MAIL $ONLY_HOSTING $INCLUDE_APP_DISTRIB|;
$FULL          = 0;
$SHALLOW       = 1;
$CONFIGURATION = 2;
$ONLY_MAIL     = 4;
$ONLY_HOSTING  = 8;
$INCLUDE_APP_DISTRIB = 16;

sub setBackupProfileFileName{
 my ($self, $profileName, $profileId ) = @_;
 die "Invalid backup profile name '$profileName'\n" if index( $profileName, '/'  )>=0;
 $self->{packer}->setBackupProfileFileName( $profileName, $profileId ) if $profileName;
}

sub setBackupOwnerGuid{
 my ($self, $ownerGuid, $ownertype ) = @_;
 $ownerGuid = PleskStructure::getAdminGuid() if not $ownerGuid;
 $ownertype = 'server' if not $ownertype;
 $self->{packer}->setBackupOwnerGuid( $ownerGuid, $ownertype );
}

sub getBackupOwnerGuid{
 my ($self) = @_;
 $self->{packer}->getBackupOwnerGuid();
}

sub getMainServerIp{
  AgentConfig::init()  or die "Error: Plesk config file 'psa.conf' is not found\n";
  PleskVersion::init( AgentConfig::get('PRODUCT_ROOT_D') );
   my $dbh = Db::Connection::getConnection(
    'type'      => 'mysql',
    'user'      => 'admin',
    'password'  => AgentConfig::get('password'),
    'name'      => 'psa',
    'host'      => 'localhost',
    'utf8names' => PleskVersion::atLeast( 8, 0, 0 )
  );

  PleskStructure::fastInit( $dbh );
  $dbh->disconnect();
  return PleskStructure::getDefaultIp();

}

sub setDumpType {
  my ( $self, $type ) = @_;

  if ( $type & $SHALLOW )       { $self->{shallow_dump}       = 1; }
  if ( $type & $CONFIGURATION ) { $self->{configuration_dump} = 1; }
  if ( $type & $ONLY_MAIL ) {
    $self->{only_mail_dump}    = 1;
    $self->{only_hosting_dump} = undef;
  }
  if ( $type & $ONLY_HOSTING ) {
    $self->{only_hosting_dump} = 1;
    $self->{only_mail_dump}    = undef;
  }
  
  if ( $type & $INCLUDE_APP_DISTRIB) {
	$self->{include_app_distrib} = 1;
  }
}

sub setDescription {
  my ( $self, $description ) = @_;
  $self->{description} = $description;
}

sub selectDomains {
  my ( $self, @inputDomains ) = @_;
  @inputDomains = sort { $a cmp $b } @inputDomains;

  my @pleskDomains = sort { $a cmp $b } PleskStructure::getDomains();
  my @missingDomains = arrayDifference( \@inputDomains, \@pleskDomains );

  if (@missingDomains) {
    my $msg = "The following domains were not found on the host: "  . ( join ",", @missingDomains );
    print STDERR "$msg\n";
    Packer::printToError( $msg );
    @inputDomains = arrayIntersection( \@inputDomains, \@pleskDomains );
  }
  $self->{domains} = \@inputDomains if @inputDomains;
}

sub selectDomainsById {
  my ( $self, @inputDomains ) = @_;

  my @pleskDomains = PleskStructure::getDomainsFromId( \@inputDomains );
  if( scalar(@inputDomains)>0 ){
    my $msg = "The following domain id's were not found on the host: " . ( join ",", @inputDomains );
    print STDERR "$msg\n";
    Packer::printToError( $msg );
  }
  $self->selectDomains( @pleskDomains ) if @pleskDomains;
}

sub selectClients {
  my ( $self, @inputClients ) = @_;
  @inputClients = sort { $a cmp $b } @inputClients;

  my @pleskClients = sort { $a cmp $b } PleskStructure::getClients();
  my @missingClients = arrayDifference( \@inputClients, \@pleskClients );

  if (@missingClients) {
    my $msg = "The following clients were not found on the host: " . ( join ",", @missingClients );
    print STDERR "$msg\n";
    Packer::printToError( $msg );
    @inputClients = arrayIntersection( \@inputClients, \@pleskClients );
  }
  $self->_selectClients( \@inputClients ) if @inputClients;
}

sub selectClientsById {
  my ( $self, @inputClients ) = @_;

  my @pleskClients = PleskStructure::getClientsFromId( \@inputClients);
  if( scalar(@inputClients)>0 ){
    my $msg = "The following clients id's were not found on the host: " . ( join ",", @inputClients );
    print STDERR "$msg\n";
    Packer::printToError( $msg );
  }
  $self->selectClients( @pleskClients ) if @pleskClients;
}

sub selectResellers {
  my ( $self, @inputResellers ) = @_;
  @inputResellers = sort { $a cmp $b } @inputResellers;

  my @pleskResellers = sort { $a cmp $b } PleskStructure::getResellers();
  my @missingResellers = arrayDifference( \@inputResellers, \@pleskResellers );

  if (@missingResellers) {
    my $msg = "The following resellers were not found on the host: " . ( join ",", @missingResellers );
    print STDERR "$msg\n";
    Packer::printToError( $msg );
    @inputResellers = arrayIntersection( \@inputResellers, \@pleskResellers );
  }
  $self->_selectClients( \@inputResellers ) if @inputResellers;
}

sub selectResellersById {
  my ( $self, @inputResellers ) = @_;

  my @pleskResellers = PleskStructure::getResellersFromId( \@inputResellers);
  if( scalar(@inputResellers)>0 ){
    my $msg =  "The following reseller id's were not found on the host: " . ( join ",", @inputResellers );
    print STDERR "$msg\n";
    Packer::printToError( $msg );
  }
  $self->selectResellers( @pleskResellers ) if @pleskResellers;
}

sub _selectClients {
  my( $self, $logins ) = @_;
  my @clients;
  my @resellers;
  my $admin = 0;
  foreach my $client(@{$logins}){
    my $clType = PleskStructure::getClientType( $client );
    push @clients, $client if $clType eq 'client';
    push @resellers, $client if $clType eq 'reseller';
    $admin = 1 if $clType eq 'admin';
  }
  $self->{resellers} = \@resellers if scalar(@resellers)>0;
  $self->{clients} = \@clients if scalar(@clients)>0;
  $self->{dump_all} = 1 if $admin;
}

sub excludeDomains {
  my ( $self, @inputDomains ) = @_;
  @inputDomains =  PleskStructure::getDomains() if scalar(@inputDomains)==0;
  @inputDomains = sort { $a cmp $b } @inputDomains;

  $self->{exclude_domains} = \@inputDomains;
  $self->{domains} = [arrayDifference($self->{domains}, \@inputDomains)] if exists $self->{domains};
}

sub excludeClients {
  my ( $self, @inputClients ) = @_;
  @inputClients =  PleskStructure::getClients() if scalar(@inputClients)==0;
  @inputClients = sort { $a cmp $b } @inputClients;

  $self->{exclude_clients} = \@inputClients;
  $self->{clients} = [arrayDifference($self->{clients}, \@inputClients)] if exists $self->{clients};
}

sub excludeResellers {
  my ( $self, @inputResellers ) = @_;
  @inputResellers = PleskStructure::getResellers() if scalar(@inputResellers)==0;
  @inputResellers = sort { $a cmp $b } @inputResellers;

  $self->{exclude_resellers} = \@inputResellers;
  $self->{resellers} = [arrayDifference($self->{resellers}, \@inputResellers)] if exists $self->{resellers};
}

sub domainExcluded{
 my ($self, $name ) = @_;
 if( exists  $self->{exclude_domains} ){
   foreach my $domain( @{$self->{exclude_domains}} ){
     return 1 if $domain eq $name;
   }
 }
 return 0;
}

sub clientExcluded{
 my ($self, $name ) = @_;
 if( exists  $self->{exclude_clients} ){
   foreach my $client( @{$self->{exclude_clients}} ){
     return 1 if $client eq $name;
   }
 }
 return 0;
}

sub resellerExcluded{
 my ($self, $name ) = @_;
 if( exists  $self->{exclude_resellers} ){
   foreach my $reseller( @{$self->{exclude_resellers}} ){
     return 1 if $reseller eq $name;
   }
 }
 return 0;
}

sub selectAll {
  my ($self) = @_;
  #my @pleskClients = sort PleskStructure::sortClients PleskStructure::getAdminClients();
  #$self->{clients} = \@pleskClients;
  $self->{dump_all} = 1;
}

sub selectAllResellers {
  my ($self) = @_;

  my @pleskResellers =  sort { $a cmp $b } PleskStructure::getResellers();
  $self->{resellers} = \@pleskResellers;
}

sub selectAllClients {
  my ($self) = @_;

  my @pleskClients =  sort { $a cmp $b } PleskStructure::getClients();
  $self->{clients} = \@pleskClients;
}

sub selectAllDomains {
  my ($self) = @_;

  my @pleskDomains =  sort { $a cmp $b } PleskStructure::getDomains();
  $self->{domains} = \@pleskDomains;
}

sub selectServerSettings {
  my ($self) = @_;
  $self->{server_settings} = 1;
}

sub getSuspendCommand{
  return AgentConfig::get("PRODUCT_ROOT_D") . "/bin/sw-engine-pleskrun " . AgentConfig::get("PRODUCT_ROOT_D") . "/admin/plib/backup/suspend_handler/SuspendHandlerRunner.php";
}

sub getLicenseCommand {
  return AgentConfig::get("PRODUCT_ROOT_D") . "/bin/sw-engine-pleskrun " . AgentConfig::get("PRODUCT_ROOT_D") . "/admin/plib/api-cli/license.php";

}

sub setSuspend{
  my ( $self, $suspend, $suspendSid ) = @_;
  $self->{suspend} = undef;
  if( $suspend ){
     if( $suspendSid ){
        $self->{suspend} = 1;
        $self->{suspendSid} = $suspendSid;
     }
     else{
        Logging::error( "Suspend session id is unkwnown. Suspend will be unavailable." );
     }
  }
}

sub suspendDomain{
  my ($self, $domain ) = @_;
  return if not $self->{suspend};
  $self->unSuspendDomain();
  Logging::info( "Suspend domain '$domain'" );
  my $cmd = getSuspendCommand() . " suspend $domain $self->{suspendSid}";
  Logging::info( "Exec: $cmd" );
  my $ret = `$cmd`;
  my $retCode = $? >> 8;
  if( $retCode!=0 ){
    Logging::error( "Cannot suspend domain  '$domain' (ErrorCode: $retCode, STDOUT:$ret) [Error:$!]. This is not fatal error!" );
    return;
  }
  $self->{suspendedDomain} = $domain;
}

sub unSuspendDomain{
  my ($self ) = @_;
  return if not $self->{suspend};
  return if not $self->{suspendedDomain};
  my $domain = $self->{suspendedDomain};
  Logging::info( "Resume domain '$domain'" );
  my $cmd = getSuspendCommand() . " unsuspend $domain $self->{suspendSid}";
  Logging::info( "Exec: $cmd" );
   my $ret = `$cmd`;
   my $retCode = $? >> 8;
   if( $retCode!=0 ){
     Logging::error( "Cannot resume domain  '$domain' (ErrorCode: $retCode, STDOUT:$ret) [Error:$!]." );
     Logging::error( "The domain '$domain' may be inaccessible after backup. Please, resume it manually!" );
  }
  $self->{suspendedDomain} = undef;
}

sub Cleanup{
  my $self = shift;
  $self->unSuspendDomain();
}

sub dump {
  my ($self) = @_;

  $self->{packer}->tunOffContent() if $self->{configuration_dump};

  my $panelMode = $self->getPanelMode();
  
  $self->{packer}->setRoot( $self->{description}, $self->{configuration_dump} ? 0: 1, defined($panelMode) ? $panelMode : PleskVersion::isSmb() ? 'smb' : 'panel' );

  if( exists $self->{dump_all} ) {
    $self->createFullDump();

    if (PleskVersion::isSmb() && $self->{configuration_dump}) {
        $self->addAllRoles();
        $self->addAllUsers();
    }

    $self->makeServerNode() if exists $self->{server_settings};
    $self->{packer}->addServerNodeToDump();
  }
  else{
    my $done;
    $self->beginWriteStatus();
    if (exists $self->{resellers}){
        $self->createResellersDump();
        $done = 1;
    }
    if (exists $self->{clients}) {
        $self->createClientsDump();
        $done = 1;
    }
    if ( exists $self->{domains} ) {
        $self->createDomainsDump();
        $done = 1;
    }
    $self->{packer}->addRootRoles();
    $self->{packer}->addRootUsers();
    $self->{packer}->addRootDomains();
    if( exists $self->{server_settings} ){
        $self->makeServerNode();
        $done = 1;
    }
    $self->{packer}->addServerNodeToDump();
    $self->{dump_status}->finishObjects();
    die "No objects to dump found" if not $done;
  }

  $self->unSuspendDomain();
  my $ret = $self->{packer}->finish();

  if ($self->{collectStatistics})
  {
    $self->writeStatistics();
  }

  return $ret;
}

sub getSize {
  my ($self) = @_;
  Logging::info("Get backup size for selected objects");

  if ( $self->{configuration_dump} ) {
    Logging::info("Get backup size finished. Configuration-only mode detected. Backup size is reported as 0 bytes");
    return 0;
  }

  my $size = 0;
  if ( exists $self->{dump_all} ) {
    my @resellers = sort { $a cmp $b } PleskStructure::getResellers();
    @resellers = $self->filterSelectedResellers( \@resellers );
    foreach my $reseller ( @resellers ) {
      $size += $self->getSelectedResellerBackupSize( $reseller );
    }
    my @clients = sort { $a cmp $b } PleskStructure::getAdminClients();
    @clients = $self->filterSelectedClients( \@clients );
    foreach my $client ( @clients ) {
      if( PleskStructure::getClientType( $client ) eq 'client' ){
        $size += $self->getSelectedClientBackupSize( $client );
      }
    }
    my @adminDomains = PleskStructure::getAdminDomains();
    @adminDomains = $self->filterSelectedDomains( \@adminDomains );
    foreach my $domainName(@adminDomains) {
      $size += $self->getDomainBackupSize( $domainName );
    }
  }
  elsif ( exists $self->{resellers} ){
    foreach my $reseller ( @{ $self->{resellers} } ) {
      $size += $self->getSelectedResellerBackupSize( $reseller );
    }
  }
  elsif ( exists $self->{clients} ) {
    foreach my $client ( @{ $self->{clients} } ) {
      $size += $self->getSelectedClientBackupSize( $client );
    }
  }
  elsif ( exists $self->{domains} ) {
    foreach my $domain ( @{$self->{domains}} ) {
      $size += $self->getDomainBackupSize( $domain );
    }
  }

  if( exists $self->{server_settings} ){
      $size += $self->getServerSettingsBackupSize();
  }

  Logging::info("Get backup size finished. Backup size of selected objects is $size bytes");
  return $size;
}

sub getSelectedResellerBackupSize {
  my ($self, $reseller) = @_;
  my $size = 0;

  $size += $self->getSelectedClientBackupSize($reseller);

  my @myclients = sort { $a cmp $b } PleskStructure::getMyClients($reseller);
  @myclients = $self->filterSelectedClients( \@myclients );
  foreach my $client ( @myclients ) {
    $size += $self->getSelectedClientBackupSize( $client );
  }
  return $size;
}

sub getSelectedClientBackupSize {
  my ($self, $client ) = @_;
  my $size = 0;

  my @mydomains = sort { $a cmp $b } PleskStructure::getDomainsForClient($client);
  @mydomains = $self->filterSelectedDomains( \@mydomains );
  $size += $self->getClientBackupSize($client);
  foreach my $domain ( @mydomains ) {
    $size += $self->getDomainBackupSize( $domain );
  }
  return $size;
}

sub getDomainBackupSize {
  my ($self, $domain ) = @_;
  my $size = 0;
  Logging::info("Get backup size for domain '$domain'");
  if ( $self->{configuration_dump} ) {
    Logging::info("Get backup size for domain '$domain' finished. Configuration-only mode detected. Size is $size bytes.");
    return 0;
  }

  my $domainId = PleskStructure::getDomainId($domain);
  if ( not defined $domainId ) {
    Logging::error("Failed to get domain id for '$domain'");
    return 0;
  }

  my $sql = "SELECT name FROM domains WHERE id=$domainId";
  my $domainAsciiName;
  if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
    $domainAsciiName = $ptrRow->[0];
  }
  $self->{dbh}->finish();

  if ( not defined $domainAsciiName ) {
    Logging::error("Failed to get domain name for '$domain'");
    return 0;
  }

  if ( !$self->{only_hosting_dump} and !$self->{only_mail_dump} ) {
    $size += $self->getDomainDbSize($domain, $domainId);
  }

  if ( !$self->{only_hosting_dump} ) {
    $size += $self->getDomainMailSize($domain, $domainAsciiName, $domainId);
    $size += $self->getDomainMaillistsSize($domain, $domainId);
  }

  if ( !$self->{only_mail_dump} ) {
    $size += $self->getDomainTomcatSize($domain, $domainId, $domainAsciiName);
    $size += $self->getDomainPhostingSize($domain, $domainId, $domainAsciiName);
  }

  if ( !$self->{only_mail_dump} and PleskVersion::atLeast( 7, 1, 0 ) ) {
    $size += $self->getDomainCustomButtonsSize($domain, $domainId);
  }

  Logging::info("Get backup size for domain '$domain' finished. Size is $size bytes.");
  return $size;
}

sub getDomainCustomButtonsSize{
  my ($self, $domain, $domainId ) = @_;
  my $size = 0;
  Logging::debug("Domain '$domain': ". (caller(0))[3]." started.");

  my @buttonIds = $self->getCustomButtonIdsByOwner71('domain-admin', $domainId);
  foreach my $buttonId ( @buttonIds ) {
    $size += $self->getCustomButtonSize( $buttonId ) if defined $buttonId;
  }

  Logging::debug("Domain '$domain': ". (caller(0))[3]." finished. Size is $size bytes.");
  return $size;
}

sub getDomainDbSize {
  my ($self, $domain, $domainId ) = @_;
  my $size = 0;
  Logging::debug("Domain '$domain': ". (caller(0))[3]." started.");

  my $sql = "SELECT dbases FROM disk_usage WHERE dom_id=$domainId";
  if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
    $size += $ptrRow->[0];
  }
  $self->{dbh}->finish();

  Logging::debug("Domain '$domain': ". (caller(0))[3]." finished. Size is $size bytes.");
  return $size;
}

sub getDomainPhostingSize{
  my ($self, $domain, $domainId, $domainAsciiName ) = @_;
  my $size = 0;
  Logging::debug("Domain '$domain': ". (caller(0))[3]." started.");

  my $webspaceRoot = $self->getWebspaceRoot($domainAsciiName);
  $size += $self->getCidSize($webspaceRoot);

  my $domainRoot = $self->getDomainRoot($domainAsciiName);
  $size += $self->getDomainAnonFtpSize($domain, $domainId, $domainAsciiName);

  Logging::debug("Domain '$domain': ". (caller(0))[3]." finished. Size is $size bytes.");
  return $size;
}

sub getDomainAnonFtpSize {
  my ($self, $domain, $domainId, $domainAsciiName ) = @_;
  my $size = 0;
  Logging::debug("Domain '$domain': ". (caller(0))[3]." started.");

  my $sql   = "SELECT * FROM anon_ftp WHERE dom_id=$domainId";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      my $domainRoot = $self->getDomainRoot($domainAsciiName);
      if ( -d $domainRoot ) {
        my $pub_path      = "$domainRoot/anon_ftp/pub";
        my $incoming_path = "$domainRoot/anon_ftp/incoming";
        $size += $self->getCidSize($pub_path);
        $size += $self->getCidSize($incoming_path);
      }
    }
  }
  $self->{dbh}->finish();

  Logging::debug("Domain '$domain': ". (caller(0))[3]." finished. Size is $size bytes.");
  return $size;
}

sub getDomainTomcatSize{
  my ($self, $domain, $domainId, $domainAsciiName ) = @_;
  my $size = 0;
  Logging::debug("Domain '$domain': ". (caller(0))[3]." started.");

  if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
    my $tomcatServiceStatus = $self->getDomainServiceStatus( $domainId, 'tomcat' );
    if (defined $tomcatServiceStatus) {
      my $sql = "SELECT wa.name FROM WebApps wa LEFT JOIN DomainServices ds ON wa.domain_service_id=ds.id WHERE ds.dom_id = $domainId";
      my ( @webapps );
      if ( $self->{dbh}->execute_rownum($sql) ) {
        while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
          push (@webapps, $ptrRow->[0] );
        }
      }
      $self->{dbh}->finish();
      my $warDir = AgentConfig::get("CATALINA_HOME") . "/psa-wars/$domainAsciiName";
      foreach my $webapp ( @webapps ) {
        $size += $self->getCidSize("$warDir/$webapp.war");
      }
    }
  }

  Logging::debug("Domain '$domain': ". (caller(0))[3]." finished. Size is $size bytes.");
  return $size;
}

sub getDomainMailSize{
  my ($self, $domain, $domainAsciiName, $domainId ) = @_;
  my $size = 0;
  Logging::debug("Domain '$domain': ". (caller(0))[3]." started.");

  my $mailNamesPath = getPleskMailnamesDir() . "/$domainAsciiName/";
  # inclides mailboxes, attachments and spamassassin content
  if ( -e $mailNamesPath ) {
    $size += $self->getCidSize($mailNamesPath);
  }

  if ( PleskVersion::atLeast( 7, 1, 0 ) ) {
    my ( @mailIds );
    my $sql = "SELECT id FROM mail WHERE dom_id = $domainId";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
        push (@mailIds, $ptrRow->[0]);
      }
    }
    $self->{dbh}->finish();
  }

  Logging::debug("Domain '$domain': ". (caller(0))[3]." finished. Size is $size bytes.");
  return $size;
}

sub getDomainMaillistsSize {
  my ($self, $domain, $domainId ) = @_;
  my $size = 0;
  Logging::debug("Domain '$domain': ". (caller(0))[3]." started.");

  if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
    my $archiveDir = AgentConfig::get("MAILMAN_VAR_D") . "/archives/private";
    my $sql = "SELECT id,name,status FROM MailLists WHERE dom_id=$domainId";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
        my $datafile = $archiveDir . "/" . $ptrRow->[1] . ".mbox/" . $ptrRow->[1] . ".mbox";
        if ( -f $datafile ) {
          $size += $self->getCidSize($datafile);
        }
        else {
          Logging::debug("file '$datafile' doesn't exist" );
        }
      }
    }
    $self->{dbh}->finish();
  }

  Logging::debug("Domain '$domain': ". (caller(0))[3]." finished. Size is $size bytes.");
  return $size;
}

sub getClientBackupSize {
  my ($self, $client ) = @_;
  my $size = 0;
  Logging::info("Get backup size for client '$client'");

  if ( $self->{configuration_dump} ) {
    Logging::info("Get backup size for client '$client' finished. Configuration-only mode detected. Size is $size bytes.");
    return 0;
  }

  my $clientId = PleskStructure::getClientId($client);
  if ( not defined $clientId ) {
    Logging::error("Failed to get client id for '$client'");
    return 0;
  }

  if ( !$self->{only_mail_dump} and PleskVersion::atLeast( 6, 0, 0 ) ) {
    $size += $self->getClientSkeletonSize($client, $clientId);
  }

  if ( !$self->{only_mail_dump} and PleskVersion::atLeast( 7, 1, 0 ) ) {
    $size += $self->getClientCustomButtonsSize($client, $clientId);
  }

  Logging::info("Get backup size for client '$client' finished. Size is $size bytes.");
  return $size;
}

sub getClientSkeletonSize {
  my ($self, $client, $clientId ) = @_;
  my $size = 0;
  Logging::debug("Client '$client': ". (caller(0))[3]." started.");

  my $skeletonPath = AgentConfig::get('HTTPD_VHOSTS_D') . "/.skel/$clientId";
  if ( -e $skeletonPath ) {
    $size += $self->getCidSize($skeletonPath);
  }

  Logging::debug("Client '$client': ". (caller(0))[3]." finished. Size is $size bytes.");
  return $size;
}

sub getClientCustomButtonsSize {
  my ($self, $client, $clientId ) = @_;
  my $size = 0;
  Logging::debug("Client '$client': ". (caller(0))[3]." started.");

  my @buttonIds = $self->getCustomButtonIdsByOwner71('client', $clientId);
  foreach my $buttonId ( @buttonIds ) {
    $size += $self->getCustomButtonSize( $buttonId ) if defined $buttonId;
  }
 
  Logging::debug("Client '$client': ". (caller(0))[3]." finished. Size is $size bytes.");
  return $size;
}

sub getServerSettingsBackupSize {
  my ($self) = @_;
  my $size = 0;
  Logging::info("Get backup size for server settings");
  if ( $self->{configuration_dump} ) {
    Logging::info("Get backup size for server settings finished. Configuration-only mode detected. Size is $size bytes.");
    return 0;
  }

  my $skeletonPath = AgentConfig::get('HTTPD_VHOSTS_D') . "/.skel/0";
  $size += $self->getCidSize($skeletonPath);

  $size += $self->getServerSettingsKeysSize();

  $size += $self->getServerSettingsAppPackagesSize();

  $size += $self->getServerCustomButtonsSize();

  Logging::info("Get backup size of server settings finished. Size is $size bytes.");
  return $size;
}

sub getServerSettingsAppPackagesSize {
  my ($self) = @_;
  my $size = 0;
  Logging::debug("Server: ". (caller(0))[3]." started.");

  if ( PleskVersion::atLeast( 8, 3, 0 ) ) {
    my $distrib_path = AgentConfig::get("PRODUCT_ROOT_D") . "/var/apspkgarc";
    my %mapHash = $self->parseApsIndexFile( $distrib_path . "/archive-index.xml" );

    my $sql = "SELECT `name`, `version`, `release` FROM SiteAppPackages";
    if( $self->{dbh}->execute_rownum($sql) ) {
      while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
        my $distrib_name = $ptrRow->[0] . "-"
                         . $ptrRow->[1] . "-"
                         . $ptrRow->[2];
        my $file_name;
        foreach my $tfile_name ( keys %mapHash ) {
          if ( $distrib_name eq join( "-", @{ $mapHash{$tfile_name} } ) ) {
            $file_name = $tfile_name;
          }
        }
        $size += $self->getCidSize("$distrib_path/$file_name");
      }
    }
    $self->{dbh}->finish();
  }

  Logging::debug("Server: ". (caller(0))[3]." finished. Size is $size bytes.");
  return $size;
}

sub getServerSettingsKeysSize {
  my ($self) = @_;
  my $size = 0;
  Logging::debug("Server: ". (caller(0))[3]." started.");

  if ( PleskVersion::atLeast( 9, 0, 0 ) ) {
    my $phpini = AgentConfig::get('PRODUCT_ROOT_D') . "/admin/conf/php.ini";
    my $swkeyrepo = '/etc/sw/keys';
    if( -e $phpini ){
      open PHPINI, $phpini;
      while (<PHPINI>) {
        chomp;
        next if /^#/;
        next if /^$/;
        if ( $_ =~ /swkey\.repository_dir\s*=\s*[\"\']?(.+)[\"\']\s*$/) {
          $swkeyrepo = $1;
          Logging::debug( "getServerSettingsBackupSize: Found sw key repository: $swkeyrepo" );
          last;
        }
      }
      close PHPINI;
    }
    if( -d "$swkeyrepo/keys" ){
      my $keyDir = "$swkeyrepo/keys";
      Logging::debug( "getServerSettingsBackupSize: Load keys from '$keyDir'" );
      opendir DIR, "$keyDir";
      my @files = readdir( DIR );
      closedir DIR;
      foreach my $key(@files) {
        if( $key ne '.' and $key ne '..' and -f "$keyDir/$key" ){
          $size += $self->getCidSize("$keyDir/$key");
        }
      }
    }
    else{
      Logging::debug( "getServerSettingsBackupSize: Keys directory '$swkeyrepo/keys' is not found. The keys are not included to backup." );
    }
  }
  else{
    my $sql = "SELECT * FROM key_history WHERE filename IS NOT NULL";
    if($self->{dbh}->execute_rownum($sql)) {
      while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
        next if ( $ptrHash->{'filename'} =~ /\~$/ );
        my @keyPath = split( /\//, $ptrHash->{'filename'} );
        my ( $keyName, $keyDir, $additional );
        if ( $keyPath[3] eq 'key.d' ) {
          $keyName    = $keyPath[4];
          $keyDir     = "/etc/psa/key.d/";
          $additional = "true";
        }
        else {
          $keyName    = $keyPath[3];
          $keyDir     = "/etc/psa/";
          $additional = "false";
        }
        if ( -e $keyDir . "/" . $keyName ) {
          $size += $self->getCidSize("$keyDir/$keyName");
        }
      }
    }
    $self->{dbh}->finish();
  }

  Logging::debug("Server: ". (caller(0))[3]." finished. Size is $size bytes.");
  return $size;
}

sub getServerCustomButtonsSize {
  my ($self) = @_;
  my $size = 0;
  Logging::debug("Server: ". (caller(0))[3]." started.");

  my @buttonIds = $self->getCustomButtonIdsByOwner71('server', 0);
  foreach my $buttonId ( @buttonIds ) {
    $size += $self->getCustomButtonSize( $buttonId ) if defined $buttonId;
  }
 
  Logging::debug("Server: ". (caller(0))[3]." finished. Size is $size bytes.");
  return $size;
}

sub getCustomButtonSize {
  my ($self, $id) = @_;
  my $size = 0;

  my $customButtonsDir = AgentConfig::get("PRODUCT_ROOT_D") . '/admin/htdocs/images/custom_buttons';
  my $sql = "SELECT file FROM custom_buttons WHERE id=$id AND place!=''";
  if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
    my $file = $ptrRow->[0];
    if ( $file ) {
      $size += $self->getCidSize("$customButtonsDir/$file");
    }
  }
  $self->{dbh}->finish();

  return $size;
}

sub getCidSize {
  my ($self, $path, $no_recursion) = @_;
  my $cmd = "du -sb";
  if ( defined $no_recursion ) {
    $cmd .= " -S";
  }
  $cmd .= " $path";
  my $sizes = `$cmd`;
  
  my $unpacksize = 0;
  for(split /[\r\n]+/,$sizes) {
    my ($number, $file) = split /\t/,$_,2;
    $unpacksize += $number; 
  }
  Logging::debug("Retrieve size of '$path'" . ($no_recursion? "without recursion" : "" ) . ": $unpacksize bytes" );
  return $unpacksize;
}

sub writeStatistics {
  my $self = shift;

  my $logPath = $ENV{'COLLECT_BACKUP_STAT_LOG'};
  if (!defined($logPath) || length($logPath) == 0)
  {
    $logPath = "perfomance-statistics.log";
  }

  $logPath = ">>" . $logPath;

  open(LOGFILE, $logPath);

  print $logPath;

  print LOGFILE "Date: " . $self->getTime() . "\n";

  my $allTime = $self->{stopWatch}->getDiff("all");
  print LOGFILE "All time: " . $allTime . "\n";

  print LOGFILE "Sql time: " . $self->{dbh}->getStatistics()->{sqlTime} . "\n";

  my $xmlTarTime = $self->{packer}->getStatistics()->{totalTime};
  my $tarTime = $self->{packer}->getStatistics()->{tarTime};

  print LOGFILE "Xml time: " . ($xmlTarTime - $tarTime) . "\n";
  print LOGFILE "Files time: " . $tarTime . "\n";

  print LOGFILE "\n";

  close(LOGFILE);
}

sub getTime {
  my $self = shift;

  my ($second, $minute, $hour, $day, $month, $year) =  localtime(time);
  $year+=1900;
  $second = "0".$second if length($second) < 2;
  $minute = "0".$minute if length($minute) < 2;
  $hour = "0".$hour if length($hour) < 2;
  $day = "0".$day if length($day) < 2;
  $month++;
  $month = "0".$month if length($month) < 2;

  return "$year.$month.$day $hour:$minute:$second";
}

# --- Utility methods ---

sub getPostgresqlVersion {
  my $psql = AgentConfig::psqlBin();
  my @out  = `$psql --version | awk '{print \$3}'`;
  chomp $out[0];
  if ( $out[0] =~ /(\d+\.\d+\.\d+).*/ ) {
    return $1;
  }
}


sub getPleskMailnamesDir{
  return AgentConfig::get('PLESK_MAILNAMES_D') if PleskVersion::atLeast( 9, 0, 0 );
  return AgentConfig::get('QMAIL_MAILNAMES_D');
}

# Returns the elements that present in first array, but not in the second.
# Arrays must be sorted.
# Linear complexity.

sub arrayDifference {
  my ( $a1, $a2 ) = @_;
  my @ret;

  my $i1 = 0;
  my $i2 = 0;
  while ( $i1 < scalar(@$a1) && $i2 < scalar(@$a2) ) {
    if ( $a1->[$i1] eq $a2->[$i2] ) { $i1++; $i2++; next; }
    if ( $a1->[$i1] lt $a2->[$i2] ) { push @ret, $a1->[$i1]; $i1++; next; }
    if ( $a1->[$i1] gt $a2->[$i2] ) { $i2++; next; }
  }

  while ( $i1 < scalar(@$a1) ) {
    push @ret, $a1->[$i1];
    $i1++;
  }
  return @ret;
}

# Returns the elements that present in both arrays
# Arrays must be storted
# Linear complexity

sub arrayIntersection {
  my ( $a1, $a2 ) = @_;
  my @ret;

  my $i1 = 0;
  my $i2 = 0;
  while ( $i1 < scalar(@$a1) && $i2 < scalar(@$a2) ) {
    if ( $a1->[$i1] eq $a2->[$i2] ) {
      push @ret, $a1->[$i1];
      $i1++;
      $i2++;
      next;
    }
    if ( $a1->[$i1] lt $a2->[$i2] ) { $i1++; next; }
    if ( $a1->[$i1] gt $a2->[$i2] ) { $i2++; next; }
  }

  return @ret;
}

# --- Private instance methods ---

#
# maps to translate xmlName => fieldName
#

my %xmlPermissions = (
  'create_domains'          => 1,
  'manage_phosting'         => 1,
  'manage_php_safe_mode'    => 1,
  'manage_quota'            => 1,
  'manage_subdomains'       => 1,
  'manage_dns'              => 1,
  'manage_log'              => 1,
  'manage_crontab'          => 1,
  'manage_anonftp'          => 1,
  'manage_webapps'          => 1,
  'manage_sh_access'        => 1,
  'manage_maillists'        => 1,
  'make_dumps'              => 1,
  'allow_local_backups'     => 1,
  'allow_ftp_backups'       => 1,
  'make_phosting'           => 1,
  'manage_quota'            => 1,
  'manage_not_chroot_shell' => 1,
  'manage_ftp_password'     => 1,
  'cp_access'               => 1,
  'remote_access_interface' => 1,
  'manage_domain_aliases'   => 1,
  'manage_virusfilter'      => 1,
  'manage_spamfilter'       => 1,
  'manage_webstat'          => 1,
  'manage_performance'      => 1,

  'allow_oversell'      => 1,
  'create_clients'      => 1,
  'select_db_server'        => 1,
  'manage_subftp'           => 1,
  'access_appcatalog'       => 1,
  'allow_insecure_sites'    => 1,
  'site_builder'            => 1,
);

my %commonLimits = (
  'max_wu'      => 1,
  'max_db'      => 1,
  'max_pbox'    => 1,
  'mbox_quota'  => 1,
  'disk_space'  => 1,
  'max_traffic' => 1,
  'max_subftp_users' => 1,
  'max_unity_mobile_sites' => 1,
);

my %domainLimits25 = %commonLimits;
$domainLimits25{'max_box'} = 1;

my %domainLimits = %commonLimits;
$domainLimits{'max_box'}         = 1;
$domainLimits{'expiration'}      = 1;
$domainLimits{'max_subdom'}      = 1;
$domainLimits{'max_maillists'}   = 1;
$domainLimits{'max_webapps'}     = 1;
$domainLimits{'max_dom_aliases'} = 1;

$domainLimits{'disk_space_soft'} = 1;
$domainLimits{'max_traffic_soft'} = 1;
$domainLimits{'max_site'}         = 1;
$domainLimits{'max_site_builder'} = 1;


my %clientLimits25 = %domainLimits25;
$clientLimits25{'max_dom'} = 1;

my %clientLimits = %domainLimits;
$clientLimits{'max_dom'} = 1;
delete $clientLimits{'max_site'};
#PSA 9.0

my %resellerLimits = %clientLimits;
$resellerLimits{'max_cl'} = 1;


my %typeOfField = ( 'expiration' => 'timestamp' );


my %hostingScripting = (
  'ssi'           => 'ssi',
  'php'           => 'php',
  'php_safe_mode' => 'php_safe_mode',
  'cgi'           => 'cgi',
  'perl'          => 'perl',
  'asp'           => 'asp',
  'python'        => 'python',
  'coldfusion'    => 'coldfusion',
  'asp_dot_net'   => 'asp_dot_net',
  'fastcgi'       => 'fastcgi',
  'miva'          => 'miva',
  'php_handler_type' => 'php_handler_type'
);

my %subDomainScripting = (
  'ssi'         => 'ssi',
  'php'         => 'php',
  'cgi'         => 'cgi',
  'perl'        => 'perl',
  'asp'         => 'asp',
  'python'      => 'python',
  'coldfusion'  => 'coldfusion',
  'asp_dot_net' => 'asp_dot_net',
  'fastcgi'     => 'fastcgi',
  'miva'        => 'miva',
  'php_handler_type' => 'php_handler_type'
);

my %emptyableBoolElementInTemplate = (
  'asp'             => 1,
  'cgi'             => 1,
  'coldfusion'      => 1,
  'pdir_plesk_stat' => 1,
  'perl'            => 1,
  'php'             => 1,
  'python'          => 1,
  'ssi'             => 1
);

my %custom_button_owner_types = (
  "admin"        => 1,     #IS_ADMIN
  "server"       => 1,     #IS_ADMIN
  "reseller"     => 2,     #IS_RESELLER
  "client"       => 4,     #IS_CLIENT
  "domain-admin" => 8,     #IS_DOMAIN_OWNER
  "mailuser"     => 16,    #IS_MAIL_USER
);

my %locale_map = (
  'bg' => 'bg',
  'hk' => 'zh-HK',
  'ru' => 'ru-RU',
  'en' => 'en-US',
  'nl' => 'nl-NL',
  'br' => 'pt-BR',
  'it' => 'it-IT',
  'tr' => 'tr-TR',
  'es' => 'es-ES',
  'pl' => 'pl-PL',
  'ca' => 'ca-ES',
  'jp' => 'ja-JP',
  'tw' => 'zh-TW',
  'fi' => 'fi-FI',
  'cn' => 'zh-CN',
  'ko' => 'ko-KR',
  'fr' => 'fr-FR',
  'pt' => 'pt-PT',
  'de' => 'de-DE',
  'lt' => 'lt-LT'
);
my %rlocale_map = reverse %locale_map;

# list of attributes for template-item node in dump.xml
# needs to synchronize with template-item's ATTLIST of plesk.dtd
my %template_items = (
  'webmail'         => 1,
  'disk_space'      => 1,
  'disk_space_soft' => 1,
  'stat_ttl'        => 1,
  'maillists'       => 1,
  'wu_script'       => 1,
  'max_traffic'     => 1,
  'max_traffic_soft' => 1,
  'max_box'         => 1,
  'mbox_quota'      => 1,
  'max_wu'          => 1,
  'max_db'          => 1,
  'max_maillists'   => 1,
  'max_webapps'     => 1,
  'max_subdom'      => 1,
  'max_site'        => 1,
  'max_dom_aliases' => 1,
  'expiration'      => 1,
  'max_subftp_users'=> 1,
  'vh_type'         => 1,
  'quota'           => 1,
  'ssl'             => 1,
  'same_ssl'        => 1,
  'fp'              => 1,
  'fp_ssl'          => 1,
  'fp_auth'         => 1,
  'asp'             => 1,
  'ssi'             => 1,
  'php'             => 1,
  'cgi'             => 1,
  'perl'            => 1,
  'python'          => 1,
  'fastcgi'	        => 1,
  'coldfusion'      => 1,
  'webstat'         => 1,
  'errdocs'         => 1,
  'shell'           => 1,
  'pdir_plesk_stat' => 1,
  'dns_type'        => 1,
  'php_handler_type'=> 1,
  'php_safe_mode'   => 1,
  'max_connections' => 1,
  'bandwidth'       => 1,
  'miva'            => 1,
  'sb_publish'      => 1,
  'nonexist_mail'   => 1,
  'catch_addr'      => 1,
  'bounce_mess'     => 1,
  'max_site_builder' => 1,
  'max_unity_mobile_sites' => 1,
  'upsell_site_builder'		=> 1,

  # client template
  'cp_access'               => 1,
  'create_domains'          => 1,
  'manage_phosting'         => 1,
  'manage_php_safe_mode'    => 1,
  'manage_quota'            => 1,
  'manage_dns'              => 1,
  'manage_log'              => 1,
  'manage_crontab'          => 1,
  'manage_anonftp'          => 1,
  'manage_webapps'          => 1,
  'manage_maillists'        => 1,
  'manage_sh_access'        => 1,
  'manage_not_chroot_shell' => 1,
  'manage_subdomains'       => 1,
  'manage_virusfilter'      => 1,
  'manage_drweb'            => 1,
  'allow_local_backups'     => 1,
  'allow_ftp_backups'       => 1,
  'remote_access_interface' => 1,
  'manage_domain_aliases'   => 1,
  'manage_spamfilter'       => 1,
  'manage_webstat'          => 1,
  'manage_performance'      => 1,
  'select_db_server'        => 1,
  'manage_subftp'           => 1,
  'access_appcatalog'       => 1,
  'allow_insecure_sites'	=> 1,

  'excl_ip_num'             => 1,
  'max_dom'                 => 1,

  #reseller
  'create_clients'          => 1,
  'allow_oversell'          => 1,
  'oversell'                => 1, #overselling
  'overuse'                 => 1,
  'max_cl'                  => 1,

   #server
  'shared'                  => 1
);

my %template_items_ignore_addon = (
  'cp_access'               => 1,
  'remote_access_interface' => 1,
  'disk_space_soft'         => 1,
  'max_traffic_soft'        => 1,
  'stat_ttl'                => 1,
  'maillists'               => 1,
  'overuse'                 => 1,
  'dns_type'                => 1,
  'nonexist_mail'           => 1
);

my %defaultClientPermissions = (
  "allow_ftp_backups"       => "false"
  , "allow_local_backups"     => "false"
  , "change_limits"           => "false"
  , "cp_access"               => "true"
  , "create_domains"          => "false"
  , "dashboard"               => "true"
  , "manage_anonftp"          => "false"
  , "manage_crontab"          => "false"
  , "manage_dashboard"        => "true"
  , "manage_dns"              => "false"
  , "manage_domain_aliases"   => "false"
  , "manage_log"              => "false"
  , "manage_maillists"        => "false"
  , "manage_not_chroot_shell" => "false"
  , "manage_performance"      => "false"
  , "manage_phosting"         => "false"
  , "manage_php_safe_mode"    => "false"
  , "manage_quota"            => "false"
  , "manage_sh_access"        => "false"
  , "manage_spamfilter"       => "false"
  , "manage_subdomains"       => "false"
  , "manage_virusfilter"      => "false"
  , "manage_webapps"          => "false"
  , "manage_webstat"          => "true"
  , "remote_access_interface" => "false"
  , "select_db_server"        => "false"
  , "stdgui"                  => "true"
);

my %defaultDomainPermissions = (
  "allow_ftp_backups"       => "false"
  , "allow_local_backups"     => "false"
  , "cp_access"               => "true"
  , "create_domains"          => "false"
  , "manage_anonftp"          => "false"
  , "manage_crontab"          => "false"
  , "manage_dns"              => "false"
  , "manage_domain_aliases"   => "false"
  , "manage_log"              => "false"
  , "manage_maillists"        => "false"
  , "manage_not_chroot_shell" => "false"
  , "manage_performance"      => "false"
  , "manage_phosting"         => "false"
  , "manage_php_safe_mode"    => "false"
  , "manage_quota"            => "false"
  , "manage_sh_access"        => "false"
  , "manage_spamfilter"       => "false"
  , "manage_subdomains"       => "false"
  , "manage_virusfilter"      => "false"
  , "manage_webapps"          => "false"
  , "manage_webstat"          => "true"
  , "remote_access_interface" => "false"
  , "select_db_server"        => "false"
);
#
# end maps
#

sub filterSelectedResellers{
  my( $self, $input ) = @_;
  my @ret = @{$input};
  @ret = arrayIntersection( \@ret, $self->{resellers} ) if exists $self->{resellers};
  @ret = arrayDifference( \@ret, $self->{exclude_resellers} ) if exists $self->{exclude_resellers};
  return @ret;
}

sub filterSelectedClients{
  my( $self, $input ) = @_;
  my @ret = @{$input};
  @ret = arrayIntersection( \@ret, $self->{clients} ) if exists $self->{clients};
  @ret = arrayDifference( \@ret, $self->{exclude_clients} ) if exists $self->{exclude_clients};
  return @ret;
}

sub filterSelectedDomains{
  my( $self, $input ) = @_;
  my @ret = @{$input};
  @ret = arrayIntersection( \@ret, $self->{domains} ) if exists $self->{domains};
  @ret = arrayDifference( \@ret, $self->{exclude_domains} ) if exists $self->{exclude_domains};
  return @ret;
}

sub getAdminRootPath{
 my ($self) = @_;
 $self->{packer}->regAdminObjectBackupPath( '' );
}

sub createFullDump {
  my ($self) = @_;

  $self->getAdminRootPath();

  my $adminId = PleskStructure::getAdminId();
  $self->{packer}->addRootAdmin( $adminId, PleskStructure::getAdminGuid() );

  $self->{dump_status}->start(PleskStructure::getClientsCount(''), PleskStructure::getDomainsCount( '' ) );

  my @resellers = sort { $a cmp $b } PleskStructure::getResellers();
  @resellers = $self->filterSelectedResellers( \@resellers );

  foreach my $reseller (@resellers) {
        my @myclients = sort { $a cmp $b } PleskStructure::getMyClients($reseller);
        @myclients = $self->filterSelectedClients( \@myclients );
        my @domains = sort { $a cmp $b } PleskStructure::getDomainsForClient($reseller);
        @domains = $self->filterSelectedDomains( \@domains );
        $self->makeClientNode($reseller, \@domains, \@myclients, 0 );
  }

  my @clients = sort { $a cmp $b } PleskStructure::getAdminClients();

  foreach my $client (@clients) {
        if( PleskStructure::getClientType( $client ) eq 'client' ){
          my @myclients = sort { $a cmp $b } PleskStructure::getMyClients($client);
          @myclients = $self->filterSelectedClients( \@myclients );
          my @domains = sort { $a cmp $b } PleskStructure::getDomainsForClient($client);
          @domains = $self->filterSelectedDomains( \@domains );
          $self->makeClientNode($client, \@domains, \@myclients, 0 );
        }
  }

  my $adminName = PleskStructure::getAdminName();
  $self->{dump_status}->startClient($adminName);

  my @adminDomains = PleskStructure::getAdminDomains();
  @adminDomains = $self->filterSelectedDomains( \@adminDomains );
  foreach my $domainName(@adminDomains) {
       $self->makeDomainNode($domainName, 0 );
  }

  for my $userLogin (PleskStructure::getUserLogins($adminName) ) {
    my $userHash = $self->getUserHash($userLogin);
    $self->{packer}->addAdminUser($userLogin, $userHash);
  }

  for my $roleId (PleskStructure::getRoleIds($adminName) ) {
    my %permissions = $self->getRolePermissions($roleId);
    my $sql = "SELECT name FROM smb_roles WHERE id = '$roleId' and isBuiltIn = 0";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      if ( my $ptrHash = $self->{dbh}->fetchhash() ) {
        $self->{packer}->addAdminRole($ptrHash->{'name'}, 0, \%permissions);
      }
    }
    $self->{dbh}->finish();
  }

  $self->{dump_status}->endClient($adminName);

  $self->{dump_status}->finishObjects();
}

sub getClientRootPath{
 my ($self, $clientLogin ) = @_;
 my $clientId = PleskStructure::getClientId($clientLogin);
 my $clientType = PleskStructure::getClientType($clientLogin);
 if( $clientType eq 'reseller' ){
   return $self->{packer}->regResellersObjectBackupPath( $self->getAdminRootPath(), $clientId, $clientLogin );
 }
 elsif( $clientType eq 'client' ){
   if ( PleskVersion::atLeast( 9, 0, 0 ) ) {
	my $parentId = PleskStructure::getClientParentId( $clientLogin );
	my $parentLogin = PleskStructure::getClientNameFromId( $parentId );
	return $self->{packer}->regClientObjectBackupPath( $self->getClientRootPath($parentLogin), $clientId, $clientLogin  );
   }
   else{
     return $self->{packer}->regClientObjectBackupPath( $self->getAdminRootPath(), $clientId, $clientLogin  );
   }
 }
 else{
    return $self->getAdminRootPath();
 }
}

sub beginWriteStatus{
  my ($self) = @_;
  my %accounts;
  my %domains;
  if( exists $self->{resellers} ){
      foreach my $reseller ( @{ $self->{resellers} } ) {
        $accounts{$reseller} = 1;
	my @myclients = sort { $a cmp $b } PleskStructure::getMyClients($reseller);
	@myclients = $self->filterSelectedClients( \@myclients );
        for my $client( @myclients ){
          $accounts{$client} = 1;
        }
        foreach my $client( @myclients ){
  	   my @mydomains = sort { $a cmp $b } PleskStructure::getDomainsForClient($client);
   	   @mydomains = $self->filterSelectedDomains( \@mydomains );
           for my $domain( @mydomains ){
    	     $domains{$domain} = 1;
           }
        }
      }
  }
  if( exists $self->{clients} ){
    foreach my $client ( @{ $self->{clients} } ) {
      $accounts{$client} = 1;
      my @mydomains = sort { $a cmp $b } PleskStructure::getDomainsForClient($client);
      @mydomains = $self->filterSelectedDomains( \@mydomains );
      for my $domain( @mydomains ){
         $domains{$domain} = 1;
      }
    }
  }
 if( exists $self->{domains} ){
   for my $domain( @{$self->{domains}} ){
      $domains{$domain} = 1;
   }
 }

 $self->{dump_status}->start( scalar( keys(%accounts) ), scalar( keys( %domains ) ) );
}

sub createResellersDump{
  my ($self) = @_;

  my( %clients, %domains );

  my $client;
  foreach my $reseller ( @{ $self->{resellers} } ) {
    my @myclients = sort { $a cmp $b } PleskStructure::getMyClients($reseller);
    @myclients = $self->filterSelectedClients( \@myclients );
    $clients{$reseller} = \@myclients;

    my @mydomains = sort { $a cmp $b } PleskStructure::getDomainsForClient($reseller);
    @mydomains = $self->filterSelectedDomains( \@mydomains );
    $domains{$reseller} = \@mydomains;
  }

  foreach my $reseller ( @{ $self->{resellers} } ) {
        $self->getClientRootPath( $reseller );
        $self->makeClientNode($reseller, $domains{$reseller}, $clients{$reseller}, 1 );
  }
}

# $clients - ref to the hash clientNames to ref of array of domainNames

sub createClientsDump {
  my ( $self ) = @_;

  my $client;
  my (%clients);
  foreach my $client ( @{ $self->{clients} } ) {
    my @mydomains = sort { $a cmp $b } PleskStructure::getDomainsForClient($client);
    @mydomains = $self->filterSelectedDomains( \@mydomains );
    $clients{$client} = \@mydomains;
  }

  foreach $client (keys %clients) {
       $self->getClientRootPath( $client );
       $self->makeClientNode($client,$clients{$client}, undef, 1 );
  }
}

sub getDomainRootPath{
  my ($self, $domainName ) = @_;
  my $domainNameColumn = 'name';
  if ( PleskVersion::atLeast( 7, 1, 0 ) ) {
    $domainNameColumn = 'displayName';
  }
  my $sql = "SELECT id, cl_id, name FROM domains WHERE $domainNameColumn = '$domainName'";
  my $ptrHash;
  my $domainId;
  my $clientId;
  my $domainAscName;
  if( $self->{dbh}->execute($sql) ) {
    if ( $ptrHash = $self->{dbh}->fetchhash() ) {
      $domainId = $ptrHash->{'id'};
      $clientId = $ptrHash->{'cl_id'};
      $domainAscName = $ptrHash->{'name'};
    }
  }
  $self->{dbh}->finish();

  if ( defined $domainId and defined $clientId and defined $domainAscName) {
    return $self->{packer}->regDomainObjectBackupPath( $self->getClientRootPath( PleskStructure::getClientNameFromId( $clientId ) ), 
                                                       $domainId, $domainAscName );
  }
}

# $domains - ref to the array of domain names
sub createDomainsDump {
  my ( $self ) = @_;

  my $domain;
  foreach $domain ( @{$self->{domains}} ) {
    if ( $self->getDomainRootPath( $domain ) ) {
      $self->makeDomainNode( $domain, 1 );
    }
    else {
      my $msg = "Unable to backup domain because of unappropriate database content";
      print STDERR "$msg\n";
      Packer::printToError( $msg );
    }
  }
}

# Condition is not necessary if getting admin parameters
sub getMaxButtonLength {
  my ( $self, $parent, $parentType, $table, $condition, $tableValues ) = @_;

  my $value;
  if( ref($tableValues) eq 'HASH' ){
    $value = $tableValues->{'max_button_length'} if exists $tableValues->{'max_button_length'};
  }
  else{
     my $sql = "SELECT val FROM $table WHERE param='max_button_length'";
     $sql .= " AND $condition" if $condition;
     if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ){
       $value = $ptrRow->[0];
     }
     $self->{dbh}->finish();
  }

  if ( $value ){
      my $proc = undef;
      if ( $parentType eq 'client' ) {
        $proc = $self->getSubroutineRef("setClientMaxButtonLength");
      }
      else {
        Packer::printToError(
          "Error: getMaxButtonLength: Unexpected type of parent \"$parentType\""
        );
        return;
      }

      if ($proc) {
        $self->{packer}->$proc( $parent, $value );
      }
  }
}

#Condition is not necessary if getting admin parameters
sub getSkin {
  my ( $self, $parent, $parentType, $table, $condition, $tableValues ) = @_;

  return unless PleskVersion::atLeast( 5, 0, 0 );

  my $skinId = "skin_id";

  my $value;
  if( ref($tableValues) eq 'HASH' ){
    $value = $tableValues->{$skinId} if exists $tableValues->{$skinId};
    if( $value ){
      my $sql = "SELECT s.name FROM Skins as s WHERE s.id=$value";
      if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ){
        $value = $ptrRow->[0];
      }
      $self->{dbh}->finish();
    }
  }
  else{
    my $sql = "SELECT s.name FROM Skins as s, $table as t "
      . "WHERE t.param='$skinId' AND t.val = s.id";
    $sql .= " AND $condition" if $condition;
    if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ){
      $value = $ptrRow->[0];
    }
    $self->{dbh}->finish();
  }


  if ( $value ){
    my $proc = undef;
    if ( $parentType eq 'client' ) {
      $proc = $self->getSubroutineRef("setClientSkin");
    }
    else {
      Packer::printToError(
        "Error: getSkin: Unexpected type of parent \"$parentType\"");
      return;
    }

    if ($proc) {
      $self->{packer}->$proc( $parent, $value );
    }
  }
}

sub getLockScreen {
  my ( $self, $parent, $parentType, $table, $condition, $tableValues ) = @_;

  return unless PleskVersion::atLeast( 7, 5, 2 );

  my $value;
  if( ref($tableValues) eq 'HASH' ){
    $value = $tableValues->{'disable_lock_screen'} if exists $tableValues->{'disable_lock_screen'};
  }
  else{
    my $sql = "SELECT val FROM $table WHERE param='disable_lock_screen'";
    $sql .= " AND $condition" if $condition;

    my $ptrRow;
    if ( $self->{dbh}->execute_rownum($sql) and $ptrRow = $self->{dbh}->fetchrow() ) {
      $value = $ptrRow->[0];
    }
    $self->{dbh}->finish();
  }


  if( $value and $value eq 'true' )
  {
    my $proc = undef;
    if ( $parentType eq 'client' ) {
      $proc = $self->getSubroutineRef("setClientLockScreen");
    }
    else {
      Packer::printToError(
        "Error: getLockSkreen: Unexpected type of parent \"$parentType\"");
      return;
    }

    if ($proc) {
      $self->{packer}->$proc($parent);
    }
  }
}

sub dumpDomainPersonalPermissions {
  my ( $self, $domainId, $permId ) = @_;

  $self->addPermissions( $domainId, 'domain-personal', $permId );

  if ( !PleskVersion::atLeast( 7, 5, 4 ) ) {
    $self->{packer}->addDomainPersonalPermission( $domainId, 'manage_not_chroot_shell', 'true' );
  }
}

#
# sub makeClientNode
#
#  arguments:
#         $clientId - ID of client
#         $full - dump all domains
#         $ptrDomainHash
#

sub makeClientNode {
  my ( $self, $clientName, $domains, $childClients, $isroot ) = @_;

  my ($parent, $clientType );
  $clientType = PleskStructure::getClientType( $clientName );

  if( $clientType eq 'client' and $self->clientExcluded( $clientName ) ) {
     Packer::printToLog("Client '$clientName' is excluded from dump");
     return;
  }
  if( $clientType eq 'reseller' and $self->resellerExcluded( $clientName ) ) {
     Packer::printToLog("Reseller '$clientName' is excluded from dump");
     return;
  }

  foreach my $dumpedClient( @{$self->{dumped_clients}} ){
     if( $dumpedClient eq $clientName ){
       Packer::printToLog("Client '$dumpedClient' already dumped");
       return;
     }
  }
  push @{$self->{dumped_clients}}, $clientName;

  my ( $item, $sql, $ptrHash, $value, %client, $ptrRow, $id, %clientParams );

  Packer::printToLog("Client '$clientName' is started") if $clientType eq 'client';
  Packer::printToLog("Reseller '$clientName' is started") if $clientType eq 'reseller';
  $self->{dump_status}->startClient($clientName);

  $sql = "SELECT * FROM clients WHERE login = '$clientName'";

  if ( $self->{dbh}->execute($sql) ) {
    if (my $ptrHash = $self->{dbh}->fetchhash() ) {
      %client = %{ $ptrHash };
    }
  }
  $self->{dbh}->finish();

  my $clientId = $client{'id'};
  $parent = PleskStructure::getClientNameFromId( $client{'parent_id'} ) if exists $client{'parent_id'};
  if ( exists $client{'vendor_id'} ){
    my $vendorGuid = PleskStructure::getClientGuidFromId( $client{'vendor_id'} );
    $client{'vendor-guid'} = $vendorGuid;
  }
  $client{'country'} = $self->{packer}->normalizeCountry( $client{'country'} );

  #FIX ME $root->setAttribute( 'parent-id', $parentName );


  my %passwd;
  if ( PleskVersion::atLeast( 7, 1, 5 ) ) {
    $sql = "SELECT password, type FROM accounts WHERE id = '" . $client{'account_id'} . "'";

    if ( $self->{dbh}->execute($sql) ) {
      if ( $ptrHash = $self->{dbh}->fetchhash() ) {
        %passwd  = (
          'password' => $ptrHash->{'password'},
          'type'     => $self->{packer}->normalizePasswordType( $ptrHash->{'type'} )
        );
      }
      else {
        %passwd = ( 'password' => '', 'type' => 'plain' );
        my $msg =  "Broken referencial integrity: Account password is not found for client " . $client{'account_id'};
        print STDERR "$msg\n";
        Packer::printToError( $msg );
      }
    }
    $self->{dbh}->finish();
  }
  else {
    %passwd = ( 'password' => $client{'passwd'}, 'type' => 'plain' );
  }

  my $status = 0;
  if ( PleskVersion::atLeast( 5, 1, 0 ) ) {
    $status = $client{'status'};
  }
  else {
    $status |= $Status::ADMIN if $client{'status'} eq 'false';
  }

  my $doNotDumpDomainTemplates = 0;
  my $parentType;
  if( $parent ) { $parentType = PleskStructure::getClientType( $parent ); }
  else{ $parentType = 'admin'; }

  if( $clientType eq 'client' ){

     if( $isroot ){        
        $self->{packer}->addRootClient( $clientId, \%client, \%passwd, $status, PleskStructure::getClientGuid( $parent ) );
     }
     else{

       if( $parentType eq 'admin' ) {           
           $self->{packer}->addAdminClient( $clientId, \%client, \%passwd, $status );
       }
       elsif( $parentType eq 'reseller' ) {
         $doNotDumpDomainTemplates = 1;
         $self->{packer}->addResellerClient($client{'parent_id'}, $clientId, \%client, \%passwd, $status );
       }
       else{
            die "Cannot dump client '$clientName' of type '$clientType', parent '$parent' type '$parentType' not supported!";
       }
     }
  }

  elsif( $clientType eq 'reseller' ){     

     if( $isroot ){
       $self->{packer}->addRootReseller( $clientId, \%client, \%passwd, $status )
     }
     else{

       if( $parentType eq 'admin' ) {
         $self->{packer}->addAdminReseller( $clientId, \%client, \%passwd, $status );
       }
       else{
            die "Cannot dump client '$clientName' of type '$clientType', parent '$parent' type '$parentType' not supported!";
       }

     }
  }

  else{
     die "Cannot dump client '$clientName'. unknown type '$clientType'!";
  }

  unless ( $self->{shallow_dump} ) {

    $self->{packer}->setClientPinfo( $clientId, \%client );

    # Locale changed format since 7.5.0, and we
    # don't bother converting older formats
    if ( PleskVersion::atLeast( 7, 5, 0 ) ) {
      if ( exists( $client{'locale'} ) && $client{'locale'} ) {
        $self->{packer}->setClientLocale( $clientId, $client{'locale'} );
      }
    }

    $sql = "SELECT param,val FROM cl_param WHERE cl_id = $clientId";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( $ptrRow = $self->{dbh}->fetchrow() ) {
        $clientParams{ $ptrRow->[0] } = $ptrRow->[1];
      }
    }
    $self->{dbh}->finish();

    # button length
    $self->getMaxButtonLength( $clientId, 'client', "cl_param", "cl_id=$clientId", \%clientParams );
    $self->getSkin( $clientId, 'client', "cl_param", "cl_id=$clientId", \%clientParams );
    $self->getLockScreen( $clientId, 'client', "cl_param", "cl_id=$clientId", \%clientParams );

    if (PleskVersion::atLeast( 10, 0, 0) and not PleskVersion::isSmb()) {
      $self->{packer}->addClientGappsAccount($clientId, \%clientParams);
    }

    #
    # limits
    #
    if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
      if( $clientType eq 'reseller' ) {
        $self->addResellerLimits( $clientId, $client{'limits_id'} ) ;
        $self->addOverusePolicy( 'client', $clientId, \%clientParams );
      }
      else{
        if (!PleskVersion::atLeast( 10, 0, 0)) {
          $self->addClientLimits( $clientId, $client{'limits_id'} ) ;
          $self->addOverusePolicy( 'client', $clientId, \%clientParams );
        }
      }
    }
    elsif ( PleskVersion::atLeast( 5, 0, 0 ) ) {
      $self->addClientLimits( $clientId, $clientParams{'lim_id'} );
    }
    else {
      $self->addClientLimits25( $clientId, $clientParams{'lim_id'} );
    }
    $self->{packer}->fixDefaultLimits($clientType,$clientId);

    #
    # end limits
    #

    if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
      $self->addPermissions( $clientId, 'client', $client{'perm_id'} );
    }
    elsif ( PleskVersion::atLeast( 5, 0, 0 ) ) {
      $self->addPermissions( $clientId, 'client', $clientParams{'perm_id'} );
    }
    else {
      $self->addPermissions25($clientId);
    }

    #fix bug 88139
    if ( ( PleskVersion::atLeast( 7, 5, 1 ) and !$client{'perm_id'} )
      or !PleskVersion::atLeast( 7, 5, 1 ) )
    {
      $self->{packer}->addClientPermission( $clientId, 'cp_access', 'true' );
    }

    #fix bug 88544
    if ( !PleskVersion::atLeast( 7, 5, 4 ) ) {
      $self->{packer}->addClientPermission( $clientId, 'manage_not_chroot_shell', 'true' );
    }

    $self->addClientMulitpleLoginPermission($clientId, $clientParams{'multiply_login'} ) if exists $clientParams{'multiply_login'};

    #
    # Domain skeleton
    #
    if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
      my $skeletonPath =
        AgentConfig::get('HTTPD_VHOSTS_D') . "/.skel/$clientId";

      if ( !$self->{configuration_dump} ) {
        $self->{packer}->addClientDomainSkeleton( $clientId, $skeletonPath, "skel" );
      }
    }
  }

  my %ips = PleskStructure::getClientIps($clientName);

  $self->{packer}->addClientIps( $clientId, \%ips );

  # Site Applications pool
  $self->dumpSiteAppPool($clientId);

  $self->addClientTraffic($clientId);

  unless ($doNotDumpDomainTemplates) {
    $self->dumpDomainTemplates( $clientId, $clientType );
  }

  if( $childClients and @{$childClients} and @{$domains} ) {
    # we are in reseller node and should add nodes <domains> and <clients> before processing of domains, as this could lead to creating users and roles
    $self->{packer}->addResellerDomainsClientsNodes( $clientId );
  }

  for my $domainName ( @{$domains} ) {
    $self->makeDomainNode( $domainName, 0 );
  }

  # Make childs dump
  if( $childClients ){
    foreach my $myClientName(@{$childClients}){
      #TO DO check excluded clients. Not worked now
      my @clientdomains = PleskStructure::getDomainsForClient( $myClientName );
      @clientdomains = sort { $a cmp $b } @clientdomains;
      @clientdomains = $self->filterSelectedDomains( \@clientdomains );
      $self->makeClientNode($myClientName, \@clientdomains, undef, 0 );
    }
  }

  for my $userLogin (PleskStructure::getUserLogins($clientName) ) {
    my $userHash = $self->getUserHash($userLogin);
    $self->{packer}->addClientUser($clientId, $userLogin, $userHash);
  }

  for my $roleId (PleskStructure::getRoleIds($clientName) ) {
    my %permissions = $self->getRolePermissions($roleId);
    my $sql = "SELECT name FROM smb_roles WHERE id = '$roleId' AND isBuiltIn = 0";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      if ( my $ptrHash = $self->{dbh}->fetchhash() ) {
        $self->{packer}->addClientRole($clientId, $ptrHash->{'name'}, 0, \%permissions);
      }
    }
    $self->{dbh}->finish();
  }

  $self->getCustomButtonsByOwner( $clientType, $clientId );

  if ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
    $self->getSubscription('client', $clientId);
  }

  if ( PleskVersion::atLeast( 8, 3, 0 ) ) {
    unless ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
      $self->makeClientSiteBuilderNode($clientId);
    }
  }
  
  if ( PleskVersion::atLeast( 10, 1, 0 ) and not PleskVersion::isSmb() ) {
    if ( $clientType eq 'reseller' ) {
      $self->{packer}->makeBrandingThemeNode($clientName, $clientId);
    }
  }

  if ( PleskVersion::atLeast( 10, 1, 0 ) and not PleskVersion::isSmb() ) {
    $self->dumpApsBundle($clientId, 'client');
  }

  $self->{packer}->finishClient($clientId);
  $self->{dump_status}->endClient($clientName);
}

sub makeClientSiteBuilderNode () {
  my ( $self, $clientId ) = @_;

  my $sql = "SELECT sb_client_login, sb_reseller_id FROM SBResellers WHERE client_id='$clientId'";

  my $enabled = 'false';
  my @sbClients;
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      push @sbClients, $ptrHash;
      $enabled = 'true';
    }
  }
  $self->{packer}->setClientSiteBuilder( $clientId, \@sbClients, $enabled );

  $self->{dbh}->finish();

}

#-----------------------------------------------------------------
#    Dumps information about client's site applications pool
#-----------------------------------------------------------------
sub dumpSiteAppPool( $ $ ) {
  my ( $self, $cl_id ) = @_;

  # There were no commercial site applications prior to Plesk 7.1.0
  if ( !PleskVersion::atLeast( 7, 1, 0 ) ) {
    return;
  }

  my $sql;

  my $aishared = "ai.shared";
  if ( PleskVersion::atLeast( 9, 0, 0 ) ) {
    $aishared = "ai.disabled";
  }

  if ( PleskVersion::atLeast( 8, 3, 0 ) ) {
    $sql = "SELECT sap.name AS sapp_name, sap.version AS sapp_version, sap.release AS sapp_release, lt.license_type_hash AS license_type_id, $aishared As SHARED, cai.instances_limit AS instances_limit "
      . "FROM APSClientApplicationItems AS cai "
      . "INNER JOIN APSApplicationItems AS ai ON (ai.id = cai.app_item_id AND cai.client_id = '$cl_id') "
      . "INNER JOIN SiteAppPackages AS sap ON (sap.id=ai.pkg_id) "
      . "LEFT JOIN APSLicenseTypes AS lt ON (ai.license_type_id = lt.id) AND $aishared = 'false'";
  }
  elsif ( PleskVersion::atLeast( 7, 1, 0 ) ) {
    $sql ="SELECT sp.name AS sapp_name, sp.version AS sapp_version, sp.release AS sapp_release "
      . "FROM clients c INNER JOIN Repository r ON c.sapp_pool_id=r.rep_id "
      . "LEFT JOIN SiteAppPackages sp ON r.component_id=sp.id "
      . "WHERE c.id='$cl_id' AND (sp.access_level & 1) <> 0";
  }

  if ( $self->{dbh}->execute_rownum($sql) ) {

    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      $self->{packer}->addClientSapp( $cl_id, $ptrHash );
    }
  }
  $self->{dbh}->finish();
}

sub addPermissions25 {
  my ( $self, $parent, $parentType, $id ) = @_;

  # [42098] migration from 7.14 to 7.50 fails
  return unless ($id);

  my $proc = undef;
  if ( $parentType eq 'client' ) {
    $proc = $self->getSubroutineRef("addClientPermission");
  }
  else {
    Packer::printToError(
      "Error: addPermissions25: Unexpected type of parent \"$parentType\"");
    return;
  }

  my ( $perm_name, $sql, $ptrRow, $value, $item );
  my @perm_names = ( 'create_domains', 'manage_dns', 'manage_log' );

  foreach $perm_name (@perm_names) {
    $sql = "SELECT DISTINCT val FROM cl_param WHERE cl_id = '$id' AND param = '$perm_name'";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      if ( $ptrRow = $self->{dbh}->fetchrow() ) {
        my $value = @{ $ptrRow }[0];
        $self->{packer}->$proc( $parent, $perm_name, $value );
      }
    }
    $self->{dbh}->finish();
  }

  $sql = "SELECT DISTINCT val FROM cl_param WHERE cl_id='$id' AND (param='ip_based_allow' OR param='nb_allow')";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    if ( $ptrRow = $self->{dbh}->fetchrow() ) {
      my $value = @{ $ptrRow }[0];
      $self->{packer}->$proc( $parent, 'manage_phosting', $value );
    }
  }
  $self->{dbh}->finish();
}

sub addOverusePolicy{
 my( $self, $parentType, $parentId, $params ) = @_;
  my $proc;
  if ( $parentType eq 'client' ) {
    $proc = $self->getSubroutineRef( "addClientLimit" );
  }
  elsif ( $parentType eq 'domain' ) {
    $proc = $self->getSubroutineRef( "addDomainLimit" );
  }
  else {
    Packer::printToError(
      "Error: addOverusePolicy: Unexpected type of parent \"$parentType\"");
    return;
  }

  if( exists $params->{'DecompositionRule'} ){
    if( $params->{'DecompositionRule'} eq 'oversell' ){
      $self->{packer}->$proc( $parentId, 'oversell', 'true' );
    }
    else{
      $self->{packer}->$proc( $parentId, 'oversell', 'false' );
    }
  }


  if( exists $params->{'OveruseBlock'} ){
    if( $params->{'OveruseBlock'} eq 'true' ){
      if ( exists $params->{'OveruseSuspend'}) {
        if ( $params->{'OveruseSuspend'} eq 'true') {
          $self->{packer}->$proc( $parentId, 'overuse', 'block' );
        } else {
          $self->{packer}->$proc( $parentId, 'overuse', 'not_suspend' );
    }
      } else {
        $self->{packer}->$proc( $parentId, 'overuse', 'not_suspend' );
      }
    }
    elsif( $params->{'OveruseNotify'} eq 'true' ){
       $self->{packer}->$proc( $parentId, 'overuse', 'notify' );
    }
    else {
       $self->{packer}->$proc( $parentId, 'overuse', 'normal' );
    }
  } else {
    $self->{packer}->$proc( $parentId, 'overuse', 'normal' );
  }
}


sub addPermissions {
  my ( $self, $parent, $parentType, $id ) = @_;

  my $permsHash = $self->getPermsHash($id); 

  unless ($permsHash) {
    if ( $parentType eq 'client' ) {
      $permsHash = $self->fillDefaultClientPermissions();
    }
    else {
      $permsHash = $self->fillDefaultDomainPermissions();
    }
  }

  return unless $permsHash;
  
  my $proc;
  if ( $parentType eq 'client' ) {
    $proc = $self->getSubroutineRef("addClientPermission");
  }
  elsif ( $parentType eq 'domain-personal' ) {
    $proc = $self->getSubroutineRef("addDomainPersonalPermission");
  }
  else {
    Packer::printToError(
      "Error: addPermissions: Unexpected type of parent \"$parentType\"");
    return;
  }

  foreach my $key ( keys %{$permsHash} ) {
    $self->{packer}->$proc( $parent, $key, $permsHash->{$key} );
  }
}

sub getPermsHash {
  my ( $self, $permsId ) = @_;

  return unless $permsId;

  my ( $sql, $ptrRow, $item, $name, $value );

  $sql = "SELECT permission,value FROM Permissions WHERE id=$permsId";

  my %permsHash;
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( $ptrRow = $self->{dbh}->fetchrow() ) {
      $name  = $ptrRow->[0];
      $value = $ptrRow->[1];
      $name  = "manage_virusfilter" if $name eq "manage_drweb";

      next unless exists $xmlPermissions{$name};

      if ( $name eq 'make_dumps' ) {
        $permsHash{'allow_local_backups'} = $value;
        $permsHash{'allow_ftp_backups'} = $value;
        next;
      }
      $permsHash{$name} = $value;
    }
  }
  $self->{dbh}->finish();

  $sql = "SELECT value FROM Permissions WHERE id = $permsId and ("
        . "permission = 'ipb_allow' or permission = 'nb_allow')";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    if ( $ptrRow = $self->{dbh}->fetchrow() ) {
      my $value = @{ $ptrRow }[0];
      $permsHash{'manage_phosting'} = $value;
    }
  }
  $self->{dbh}->finish();

  return \%permsHash;
}

sub addClientMulitpleLoginPermission {
  my ( $self, $id, $multiple_sessions ) = @_;
  $self->{packer}->addClientPermission( $id, 'multiple-sessions', $multiple_sessions );
}

sub insertLimit {
  my ( $self, $parent, $parentType, $name, $value ) = @_;

  my $proc;
  if ( $parentType eq 'client' || $parentType eq 'reseller' ) {
    $proc = $self->getSubroutineRef("addClientLimit");
  }
  elsif ( $parentType eq 'domain' ) {
    $proc = $self->getSubroutineRef("addDomainLimit");
  }
  else {
    Packer::printToError(
      "Error: insertLimits: Unexpected type of parent \"$parentType\"");
    return;
  }

  if ( $value eq '' || $value eq '-1' ) {
    $self->{packer}->$proc( $parent, $name, '-1' );
    return;
  }

  if ( $name eq 'expiration' ) {
    my ( $mday, $mon, $year ) = ( localtime($value) )[ 3 .. 5 ];
    $mon++;
    $year += 1900;
    $value = sprintf( '%04d-%02d-%02d', $year, $mon, $mday );
  }

  if ( !PleskVersion::atLeast( 7, 0, 0 ) && $name eq 'mbox_quota' ) {
    $value = $value * 1024;
  }

  $self->{packer}->$proc( $parent, $name, $value );
}

sub addResellerLimits {
  my ( $self, $resellerId, $limitsId ) = @_;
  return $self->addLimits( $resellerId, 'reseller', $limitsId, \%resellerLimits );
}

sub addClientLimits {
  my ( $self, $clientId, $limitsId ) = @_;
  return $self->addLimits( $clientId, 'client', $limitsId, \%clientLimits );
}


sub addClientLimits25 {
  my ( $self, $clientId, $limitsId ) = @_;
  return $self->addLimits25( $clientId, 'client', $limitsId, 'cl_id', 'cl_param' );
}

sub addDomainLimits {
  my ( $self, $domainId, $limitsId ) = @_;
  return $self->addLimits( $domainId, 'domain', $limitsId, \%domainLimits );
}

sub addDomainLimits25 {
  my ( $self, $domainId, $limitsId ) = @_;
  $self->addLimits25( $domainId, 'domain', $limitsId, 'dom_id', 'dom_param' );

  #fix bug 50361
  my $ptrRow;
  my $sql = "SELECT traffic FROM hosting WHERE dom_id='$domainId'";
  if ( $self->{dbh}->execute_rownum($sql)
    && ( $ptrRow = $self->{dbh}->fetchrow() ) )
  {
    $self->insertLimit( $domainId, 'domain', 'max_traffic', $ptrRow->[0] );
  }
  $self->{dbh}->finish();
  $sql = "SELECT size FROM domains WHERE id='$domainId'";
  if ( $self->{dbh}->execute_rownum($sql)
    && ( $ptrRow = $self->{dbh}->fetchrow() ) )
  {

    $self->insertLimit( $domainId, 'domain', 'disk_space', $ptrRow->[0] );

  }
  $self->{dbh}->finish();
}

sub addLimits25 {
  my ( $self, $parent, $parentType, $limitId, $limitNames, $idName, $paramName )
    = @_;

  if ( !$limitId ) {
    return;
  }

  my ( $value, $name, $count, $sql, $ptrRow );

  $sql = "SELECT val, param FROM $paramName WHERE $idName='$limitId'";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( $ptrRow = $self->{dbh}->fetchrow() ) {
      ( $name, $value ) = @{$ptrRow};
    }
  }

  if ($limitNames) {
    foreach $name ( %{$limitNames} ) {
      $sql = "SELECT val FROM $paramName WHERE $idName = '$limitId' AND param = '$name'";

      if ( ( $self->{dbh}->execute_rownum($sql) )
        && ( $ptrRow = $self->{dbh}->fetchrow() ) )
      {
        ($value) = @{$ptrRow};
        if ( $name eq 'max_pbox' ) {
          $name = 'max_box';
        }
        $self->insertLimit( $parent, $parentType, $name, $value );
      }
      $self->{dbh}->finish();
    }
  }
}

#
# sub addLimits
#
# arguments:
#           $root    - XML node to add 'limit' node
#           $limitID - limit id
#
# return:
#           $count - number of the added nodes
#
sub addLimits {
  my ( $self, $parent, $parentType, $limitId, $limitNames ) = @_;

  my $count = 0;

  if ( !$limitId ) {
    if ( $limitNames ) {
      foreach my $key ( keys %{$limitNames} ) {
        next if $key eq 'max_pbox';
        $self->insertLimit( $parent, $parentType, $key, "-1" );
        $count++;
      }
    }
    return $count;
  }

  my ( $sql, $ptrRow, $value, $name );
  $sql = "SELECT limit_name,value FROM Limits WHERE id=$limitId";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    $count = 0;
    while ( $ptrRow = $self->{dbh}->fetchrow() ) {
      ( $name, $value ) = @{$ptrRow};
      next unless exists $limitNames->{$name};
      $self->insertLimit( $parent, $parentType, $name, $value );
      $count++;
    }
  }
  $self->{dbh}->finish();
  return $count;
}

#
# Gather domain templates information
#
sub dumpDomainTemplates {
  my ( $self, $clientId, $clientType ) = @_;
  return unless PleskVersion::atLeast( 5, 0, 0 );

  if (PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb()) {
    $self->dumpTemplates( $clientId, $clientType, 'domain' );
    $self->dumpTemplates( $clientId, $clientType, 'domain_addon' );
  }
  else {
    my $sql;
    my $tmpl_id;

    if ($clientId) {
      if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
        $sql = "SELECT tmpl_id FROM clients WHERE id=$clientId";
      }
      else {
        $sql = "SELECT val FROM cl_param WHERE cl_id=$clientId AND "
          . "param = 'dom_tmpl_list_id'";
      }

      if ( $self->{dbh}->execute_rownum($sql) ) {
        if( my $rowPtr = $self->{dbh}->fetchrow() ) {
          $tmpl_id = @{ $rowPtr }[0];
        }
      }
      $self->{dbh}->finish();
      $self->dumpTemplates9x( $clientId, $clientType, $tmpl_id, 'domain' ) if $tmpl_id;
    }
  }
}

sub dumpTemplates {
  my ( $self, $parent, $parentType, $templateType ) = @_;
  return $self->dumpTemplates9x($parent, $parentType, undef, $templateType);
}

sub dumpTemplates9x {
  my ( $self, $parent, $parentType, $tmpl_id, $templateType ) = @_;

  my $proc = undef;
  if ( $parentType eq 'server' ) {
    $proc = $self->getSubroutineRef("addTemplateToServer");
  }
  elsif ( $parentType eq 'reseller' ) {
    if ( 'domain' eq $templateType or 'domain_addon' eq $templateType) {
      $proc = $self->getSubroutineRef("addResellerDomainTemplate");
    }
    else {
      Packer::printToError(
        "Error: dumpTemplates: unexpected template type \"$templateType\" for parent \"$parentType\"");
      return;
    }
  }
  elsif ( $parentType eq 'client' ) {
    if ( 'domain' eq $templateType or 'domain_addon' eq $templateType) {
      $proc = $self->getSubroutineRef("addClientDomainTemplate");
    }
    else {
      Packer::printToError(
        "Error: dumpTemplates: unexpected template type \"$templateType\" for parent \"$parentType\"");
      return;
    }
  }
  else {
    Packer::printToError(
      "Error: dumpTemplates: Unexpected type of parent \"$parentType\"");
    return;
  }

  my $sql;

  my $ownerGuid;

  my %templateNames;
  my %templateNotes;
  my %templateUuids;
  my %templateExtIds;

  if (PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb()) {
    $ownerGuid = PleskStructure::getClientGuidFromId($parent);
    
    $sql = "SELECT * FROM Templates WHERE type='$templateType' AND owner_id = $parent";
    if ( $self->{dbh}->execute($sql) ) {
      while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
        my $id = $ptrHash->{'id'};
        $templateNames{ $id } = $ptrHash->{'name'};
        $templateUuids{ $id } = $ptrHash->{'uuid'};
        $templateExtIds{ $id } = $ptrHash->{'external_id'} if defined $ptrHash->{'external_id'} and $ptrHash->{'external_id'} ne '';
      }
    }
  }
  else {
    $sql = "SELECT t.id, t.name, t.note_id FROM Templates t, Repository r WHERE r.rep_id=$tmpl_id AND r.component_id = t.id";
    if ( $self->{dbh}->execute($sql) ) {
      while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
        $templateNames{ $ptrRow->[0] } = $ptrRow->[1];
        $templateNotes{ $ptrRow->[0] } = $ptrRow->[2];
      }
    }
  }
  $self->{dbh}->finish();

  my %planItems;
  my @planItemNames = @{$self->getPlanItemNames()};

  while ( my ( $tmpl_id, $tmpl_name ) = each %templateNames ) {

    my @template;

    my $logrotation_id;
    my $ip_pool_id;
    my $overuse_block;
    my $overuse_notify;
    my $overuse_suspend;
    my $aps_bundle_filter_id = undef;
    my $sql = "SELECT element, value FROM TmplData WHERE tmpl_id=$tmpl_id";
    if ( $self->{dbh}->execute($sql) ) {
      while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
        my ( $element, $value ) = @{$ptrRow};

        if ( !defined($value) ) {
          $value = '';
        }

        if ( $element eq 'stat_ttl' and ( $value == 0 or $value eq '' ) ) {
          $value = -1;
        }

#        if ( ($element eq 'disk_space' or $element eq 'max_traffic' or $element eq 'mbox_quota')
#             and $value == 0 and 'domain_addon' ne $templateType ) {
#          $value = -1;
#        }

        if ( $element eq 'vh_type'
            and $value ne 'none'
            and !PleskVersion::atLeast( 6, 0, 0 ) ) {
          $value = 'physical';
        }

        if ( $element eq 'logrotation_id' ) {
          $logrotation_id = $value;
          next;
        }

        if ($element eq 'tmpl_pool_id') {
          $ip_pool_id = $value;
          next;
        }

        if( $element eq 'overuse_block' ){
          $overuse_block = $value;
          next;
        }

        if( $element eq 'overuse_notify' ){
          $overuse_notify = $value;
          next;
        }

        if ( $element eq 'overuse_suspend' ){
          $overuse_suspend = $value;
          next;
        }

        if ( not exists $template_items{$element} ) {
          if ( PleskVersion::atLeast( 10, 1, 0 ) and not PleskVersion::isSmb() ) {
            if ( grep $_ eq $element, @planItemNames) {
              $planItems{ $element } = $value;
            }
            if ($element eq 'aps_bundle_filter_id') {
              $aps_bundle_filter_id = $value;
            }
          }
          next;
        }

        if ( 'domain_addon' eq $templateType ) {
          # skip items which are not allowed for domain_addon template
          next if exists $template_items_ignore_addon{$element};
        }
        if ( 'domain' eq $templateType ) {
          # skip items which are not allowed for domain template
          next if ($element eq 'cp_access' or $element eq 'remote_access_interface');
        }

        if ( $element eq 'quota' and ( $value eq 'false' or $value == 0 ) ) {
          $value = -1;
        }

        if ($element eq 'mbox_quota'
        and $value != -1
        and !PleskVersion::atLeast( 7, 0, 0 ) ) { $value *= 1024; }

        if ($element eq 'quota'
        and $value != -1
        and !PleskVersion::atLeast( 7, 0, 0 ) ) { $value *= 1024 * 1024; }

        if ( $value eq '' and exists $emptyableBoolElementInTemplate{$element} ) {
          $value = 'false';
        }

        my @data = ( $element, $value );
        push @template, \@data;
      }
    }
    $self->{dbh}->finish();

    my %logRotation;
    if ( $logrotation_id and ( $templateType eq 'domain' ) ) {
      %logRotation = $self->makeLogrotationNode($logrotation_id);
    }

    my %ipPool;
    if ( $ip_pool_id and ( $templateType eq 'reseller' or $templateType eq 'client' ) ) {
      %ipPool = $self->makeTemplateIpPool($ip_pool_id);
    }

    unless ( PleskVersion::atLeast( 6, 0, 0 ) ) {
      my @data = ( 'dns_type', 'master' );
      push @template, \@data;
    }

    if( $overuse_block and ('domain_addon' ne $templateType )){
        my @data = ( 'overuse' );       
        if( $overuse_block eq 'true' ) { 
           if ( $overuse_suspend ) {
             if ($overuse_suspend eq 'true') {
               push @data, 'block'; 
             } else {
               push @data, 'not_suspend';
             }
           } else {
             push @data, 'block'; 
           }
        }
        elsif( $overuse_notify and $overuse_notify eq 'true' ) { push @data, 'notify'; }
        else{ push @data, 'normal'; }
        push @template, \@data;
    }

    my @templateAttrs;
    if ( defined $templateNotes{$tmpl_id} ) {
      my @noteAttr = ( 'note-id', $templateNotes{$tmpl_id} );
      push @templateAttrs, \@noteAttr;
    }
    
    if ( defined $templateUuids{$tmpl_id} ) {
      my @guidAttr = ( 'guid', $templateUuids{$tmpl_id});
      push @templateAttrs, \@guidAttr;
    }
    
    if ( defined $templateExtIds{$tmpl_id} ) {
      my @extIdAttr = ( 'external-id', $templateExtIds{$tmpl_id});
      push @templateAttrs, \@extIdAttr;
    }
    
    if ( defined $ownerGuid ) {
      my @ownerGuidAttr = ( 'owner-guid', $ownerGuid);
      push @templateAttrs, \@ownerGuidAttr;
    }
    
    if ( $templateType eq 'domain_addon') {
      my @isAddonAttr = ( 'is-addon', 'true');
      push @templateAttrs, \@isAddonAttr;
    }

    my @apsFilterItems;
    my $filterType;
    if (defined($aps_bundle_filter_id)) {
      $sql = "SELECT propertyName, propertyValue FROM smb_apsBundleFilterItems WHERE filterId = '$aps_bundle_filter_id'";

      if ($self->{dbh}->execute_rownum($sql)) {
        while(my $ptrHash = $self->{dbh}->fetchhash()) {
          push @apsFilterItems, {$ptrHash->{'propertyName'} => $ptrHash->{'propertyValue'}};
        }
      }

      $self->{dbh}->finish();

      $sql = "SELECT type FROM smb_apsBundleFilters WHERE id = '$aps_bundle_filter_id'";

      if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow()) {
        $filterType = $ptrRow->[0];
       }

      $self->{dbh}->finish();  

    }
    
    if ( 'server' eq $parentType ) {
      $self->{packer}->$proc($templateType, $tmpl_name, \@templateAttrs, \@template, \%planItems, \%logRotation, \%ipPool, \@apsFilterItems, $filterType);
    }
    else {
      $self->{packer}->$proc( $parent, $tmpl_name, \@templateAttrs, \@template, \%planItems, \%logRotation, \%ipPool, \@apsFilterItems , $filterType);
    }
  }
}

sub makeTemplateIpPool {
  my ( $self, $ip_pool_id ) = @_;

  my $sql = "SELECT IP_Addresses.`ip_address`, ip_pool.`type`" .
			  " FROM ip_pool".
			  " LEFT JOIN IP_Addresses ON ip_pool.`ip_address_id` = IP_Addresses.`id`" .
			  " WHERE ip_pool.`id` = $ip_pool_id";

  my %ipPool = ();

  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
      $ipPool{ $ptrRow->[0] } = $ptrRow->[1];
    }
  }
  $self->{dbh}->finish();

  return %ipPool;
}

#
# Returns the XML node if domain service is present and undef
# otherwise
#
sub getDomainServiceStatus {
  my ( $self, $domId, $service ) = @_;

  my $status = 0;
  my $sql    = "SELECT status FROM DomainServices "
    . " WHERE dom_id=$domId AND type='$service'";

  if ( $self->{dbh}->execute_rownum($sql)
    and my $ptrRow = $self->{dbh}->fetchrow() )
  {
    $status = $ptrRow->[0];
  }
  else {
    $status = undef;
  }
  $self->{dbh}->finish();

  return $status;
}

#
# makeDomainNode
#
# arguments:
#           $domainId - domain id
#
# return:
#           $root - pointer to XML node
#
sub makeDomainNode {
  my ( $self, $domainName, $isroot) = @_;

  if( $self->domainExcluded( $domainName ) ) {
    Packer::printToLog("Domain '$domainName' is excluded from dump");
    return;
  }

  foreach my $dumpedDomain( @{$self->{dumped_domains}} ){
    if( $dumpedDomain eq $domainName ){
      Packer::printToLog("Domain '$domainName' already dumped");
      return;
    }
  }
  push @{$self->{dumped_domains}}, $domainName;

  Packer::printToLog("Domain '$domainName' is started");

  $self->{dump_status}->startDomain($domainName);

  my ( $sql, %domain, %domParams, $ptrRow, $ptrHash, $id );

  #
  # get domain's info
  #
  my $domainNameColumn = 'name';
  if ( PleskVersion::atLeast( 7, 1, 0 ) ) {
    $domainNameColumn = 'displayName';
  }

  Packer::printToLog("Getting domain info");
  $sql = "SELECT * FROM domains WHERE $domainNameColumn = '$domainName'";

  unless ( $self->{dbh}->execute_rownum($sql) ) {
    $self->{dbh}->finish();
    return;
  }
  unless ( $ptrHash = $self->{dbh}->fetchhash() ) {
    $self->{dbh}->finish();
    return;
  }
  %domain = %{$ptrHash};
  $self->{dbh}->finish();

  my $domainId = $domain{'id'};
  my $clientId = $domain{'cl_id'};
  
  if ( exists $domain{'vendor_id'} ){
    my $vendorGuid = PleskStructure::getClientGuidFromId( $domain{'vendor_id'} );
    $domain{'vendor-guid'} = $vendorGuid;
  }

  my $clientPermsId;
  if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
    $sql = "SELECT perm_id FROM clients WHERE id = $clientId";
  }
  else {
    $sql = "SELECT val FROM cl_param WHERE cl_id = $clientId AND param = 'perm_id'";
  }
  if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
    $clientPermsId = $ptrRow->[0];
  }
  $self->{dbh}->finish();

  $sql = "SELECT param,val FROM dom_param WHERE dom_id=$domainId";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( $ptrRow = $self->{dbh}->fetchrow() ) {
      $domParams{ $ptrRow->[0] } = $ptrRow->[1];
    }
  }
  $self->{dbh}->finish();

  #
  # end get domain's info
  #

  my $domainAsciiName;
  if ( PleskVersion::atLeast( 7, 1, 0 ) ) {
    $domainName = $domain{'displayName'};
    $domainAsciiName = $domain{'name'};
  }
  else {
    $domainName = $domain{'name'};
  }

  my $domainOwner = PleskStructure::getClientNameFromId( $domain{'cl_id'} );
  my $parentType = PleskStructure::getClientType( $domainOwner );
  if( $isroot ){
    $self->{packer}->addRootDomain( $domainId, $domainName, $domainAsciiName, \%domain, PleskStructure::getClientGuid($domainOwner) );
  }
  else{
    if( $parentType eq 'admin' ){
    	$self->{packer}->addAdminDomain( $domainId, $domainName, $domainAsciiName, \%domain );
    }
    elsif( $parentType eq 'reseller' ){
    	$self->{packer}->addResellerDomain( $domain{'cl_id'}, $domainId, $domainName, $domainAsciiName, \%domain );
    }
    elsif( $parentType eq 'client' ){
    	$self->{packer}->addClientDomain( $domain{'cl_id'}, $domainId, $domainName, $domainAsciiName, \%domain );
    }
    else{
       die "Cannot dump domain '$domainName'. Domain owner '$domainOwner' type '$parentType' is not supported!";
    }
  }

  $self->suspendDomain( $domainName );

  my $domainAsciiName = $domain{'name'};
  my $domainType      = $domain{'htype'};

  $self->addWwwStatus( $domainId, $domainAsciiName );

  # Domain's IP address
  Packer::printToLog("Getting domain IP");
  my $ip = PleskStructure::getDomainIp($domainName);
  if ($ip) {
    $self->{packer}->setDomainIP( $domainId, $ip, PleskStructure::getClientIpType( $domainOwner, $ip ) );
  }

  #
  # Status
  #
  Packer::printToLog("Dumping domain status");

  my $status = 0;
  my $siteStatus = undef;
  if ( PleskVersion::atLeast( 5, 1, 0 ) ) {
    
	$status = $domain{'status'};
	
    if ($domain{'status'} != $Status::ENABLED) {    
	   $siteStatus = $Status::WITH_PARENT;
    }
	
    if ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
      $status += 0;
      $status = $domain{'webspace_status'};
      $siteStatus = $domain{'status'};
    }
  }
  else {
    $status |= $Status::ADMIN  if $domain{'admin_status'} eq 'false';
    $status |= $Status::CLIENT if $domain{'status'}       eq 'false';
  }
  $self->{packer}->setWebspaceStatus( $domainId, $status, $siteStatus );

  #
  # No further info required if shallow dump specified
  #
  if ( $self->{shallow_dump} && !$self->{only_mail_dump} ) {

    # Need to dump information about all databases for DbServers mapping

    Packer::printToLog("Dumping domain databases");

    $sql = "SELECT id FROM data_bases WHERE dom_id=$domainId";
    if ( $self->{dbh}->execute_rownum($sql) ) {

      my (@databases);
      while ( $ptrRow = $self->{dbh}->fetchrow() ) {
        push @databases, $ptrRow->[0];
      }

      foreach my $dbid (@databases) {
        $self->makeDatabaseNode( $dbid, $domainId, 'domain', undef, undef );
      }
    }

    # Domain's DNS settings
    $self->makeDomainDnsZone( \%domain );

    if ((!defined $ip) && ( $domainType ne 'vrt_hst' ) ) {
      my $preferredIp = $self->{packer}->getDomainARecordIp($domainId);
      my $type;
      $ip = PleskStructure::getIp4DomainWoHosting($domainId, $preferredIp, \$type);
      if ( $ip ) {
        $self->{packer}->setDomainIP( $domainId, $ip, $type );
      }
    }

    $self->{packer}->removeDomainDnsZone($domainId);

    $self->{dbh}->finish();
    $self->unSuspendDomain();
    $self->{packer}->finishDomain($domainId);
    $self->{dump_status}->endDomain($domainName);
    return;
  }

  # Check whether this domain is default on IP
  if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
    $sql = "SELECT COUNT(*) FROM IP_Addresses WHERE default_domain_id=$domainId";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      if ( @{ $self->{dbh}->fetchrow() }[0] ne "0" ) {
        $self->{packer}->setDomainDefault($domainId);
      }
    }
    $self->{dbh}->finish();
  }
  else {
    if ( PleskStructure::isExclusiveIp($ip) ) {
      $self->{packer}->setDomainDefault($domainId);
    }
  }

  # Domain's DNS settings
  $self->makeDomainDnsZone( \%domain );

  if ( !$self->{only_mail_dump} ) {
    # Domain's aliases
    $self->dumpDomainAliases($domainId);
  }

  if ((!defined $ip) && ( $domainType ne 'vrt_hst' ) ) {
    my $preferredIp = $self->{packer}->getDomainARecordIp($domainId);
    my $type;
    $ip = PleskStructure::getIp4DomainWoHosting($domainId, $preferredIp, \$type);
    if ( $ip ) {
      $self->{packer}->setDomainIP( $domainId, $ip, $type );
    }
  }

  Packer::printToLog("Getting domain limits");

  #
  # limits
  #
  if ( !$self->{only_mail_dump} ) {
    if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
      $self->addDomainLimits( $domainId, $domain{'limits_id'} );
      $self->addOverusePolicy( 'domain', $domainId, \%domParams );
    }
    elsif ( PleskVersion::atLeast( 5, 0, 0 ) ) {
      $self->addDomainLimits( $domainId, $domParams{'lim_id'} );
    }
    else {
      $self->addDomainLimits25( $domainId, $domParams{'lim_id'} );
    }
    $self->{packer}->fixDefaultLimits('domain',$domainId);
  }

  #
  # end limits
  #

  #
  # permissions
  #
  if ( !$self->{only_mail_dump} )  {
    if ( PleskVersion::atLeast( 10, 0, 0) and not PleskVersion::isSmb() ) {
      $self->dumpDomainPersonalPermissions( $domainId, $domain{'permissions_id'} );
    }
    else {
      $self->dumpDomainPersonalPermissions( $domainId, $clientPermsId );
    }
  }

  #
  # end permissions
  #

  if ( !$self->{only_hosting_dump} ) {
      Packer::printToLog("Dumping domain mailsystem");

      my $mailServiceStatus;
      if (PleskVersion::atLeast(6, 0, 0)) {
        $mailServiceStatus = $self->getDomainServiceStatus($domainId, 'mail');
      } else {
        $mailServiceStatus = Status::make($Status::ENABLED);
      }

      if ( defined($mailServiceStatus) ) {
        $self->{packer}->setDomainMailService( $domainId, $mailServiceStatus );

        if (PleskVersion::atLeast(6, 0, 0)) {
          $sql = "SELECT m.id, a.password, a.type FROM mail m".
            " LEFT JOIN accounts a ON m.account_id=a.id ".
            " WHERE m.dom_id=$domainId ORDER BY m.mail_name";
        } else {
          $sql = "SELECT id, password, 'plain' FROM mail ".
            " WHERE dom_id=$domainId ORDER BY mail_name";
        }

        if ( $self->{dbh}->execute_rownum($sql) ) {
          my (@mails);
          while ( $ptrRow = $self->{dbh}->fetchrow() ) {
            push @mails, [ @{$ptrRow} ];
          }
          $self->{packer}->setDomainMailNamesContent( $domainId, $domainName, getPleskMailnamesDir() . "/$domainAsciiName/" ) if $self->{dump_full_mail};

          foreach $ptrRow (@mails) {
            $ptrRow->[2] = $self->{packer}->normalizePasswordType( $ptrRow->[2] );
            if ($isroot and not PleskVersion::atLeast(10, 0, 0))  {
                $parentType = 'root';
            }
            $self->makeMailUserNode( $domainId, @{$ptrRow}, $domainName, $domainAsciiName, $parentType, $domain{'cl_id'} );
          }
        }
        $self->{dbh}->finish();
        $self->getCatchAllAddress($domainId);

        $self->getDomainKeysDomainSupport( $domainId, $domainName,  $domain{'dns_zone_id'} );

        my $oldWebMailFlag = undef;
        if (!PleskVersion::atLeast(9, 0, 0)) {
          $oldWebMailFlag = $self->getOldWebMailStatus(\%domain);
        }

        $self->{packer}->setDomainWebMail( $domainId, ( exists $domParams{'webmail'} ?  $domParams{'webmail'} : defined($oldWebMailFlag) ? 'horde' : 'none' ) );

        $self->{packer}->setDomainGLSupport( $domainId, (exists $domParams{'gl_filter'} ) ? $domParams{'gl_filter'} : 'on' );
      }
  }
  else { Packer::printToLog("Skip domain mailsystem dump due to settings"); }

  my @SiteApplications;

  if ( !$self->{only_mail_dump} ) {
    #-----------------------------------------------------------------
    # Information about domain's site applications
    # should be extracted prior to database/hosting/custom-buttons
    # dump, as site applications' linked resources should be dumped
    # separately.
    #-----------------------------------------------------------------
    @SiteApplications = $self->getDomainSiteapps($domainId, $self->{dbh});

    #-----------------------------------------------------------------
    # Domain's databases
    #-----------------------------------------------------------------
    Packer::printToLog("Dumping domain databases");

    # Site applications' databases should not be dumped here
    my $exclude_list = '';
    {
      my @excluded_dbs;
      foreach my $sapp (@SiteApplications) {
        foreach my $row ( $sapp->getDatabases() ) {
          push @excluded_dbs, $row->{'id'};
        }
      }
      if (@excluded_dbs) {
        $exclude_list = " AND id NOT IN(" . Packer::getSqlList(@excluded_dbs) . ")";
      }
    }

    # Databases without those used in site apps
    $sql = "SELECT id FROM data_bases WHERE dom_id=$domainId" . $exclude_list;
    if ( $self->{dbh}->execute_rownum($sql) ) {

      my (@databases);
      while ( $ptrRow = $self->{dbh}->fetchrow() ) {
        push @databases, $ptrRow->[0];
      }
      foreach my $dbid (@databases) {
        $self->makeDatabaseNode( $dbid, $domainId, 'domain', undef, undef );
      }
    }
    $self->{dbh}->finish();
  }
  else { Packer::printToLog("Skip dumping domain databases due to settings"); }

  #
  # maillist
  #
  if ( !$self->{only_hosting_dump} ) {
    Packer::printToLog("Dumping domain maillists");
    if ( PleskVersion::atLeast( 6, 0, 0 ) ) {

      my $maillistsStatus = $self->getDomainServiceStatus( $domainId, 'maillists' );
      if (defined $maillistsStatus) {
        $self->{packer}->setDomainMailLists( $domainId, $maillistsStatus );

        my $archiveDir = AgentConfig::get("MAILMAN_VAR_D") . "/archives/private";
        my @archiveDirs;

        $sql = "SELECT id,name,status FROM MailLists WHERE dom_id=$domainId";
        if ( $self->{dbh}->execute_rownum($sql) ) {
          my (@mlists);
          while ( $ptrRow = $self->{dbh}->fetchrow() ) {
            push @mlists, [ @{$ptrRow} ];
            my $datafile = $ptrRow->[1] . ".mbox/" . $ptrRow->[1] . ".mbox";
            push @archiveDirs, $datafile  if -f "$archiveDir/$datafile";
          }
          $self->{packer}->setDomainMailListContent( $domainId, $domainAsciiName, $archiveDir, { 'include' => \@archiveDirs, 'follow_symlinks' => 1 } ) if $self->{dump_full_mail};
          foreach $ptrRow (@mlists) {
            $self->makeMailListNode( $domainId, @{$ptrRow} );
          }
        }
        $self->{dbh}->finish();
      }
    }
  }

  else { Packer::printToLog("Skip dumping domain maillists due to settings"); }

  #
  # end maillist
  #

  if ( !$self->{only_mail_dump} ) {
    Packer::printToLog("Dumping domain statistics");

    $self->addDomainTraffic($domainId);

    if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
      my $cert_id = $domain{'cert_rep_id'};
      $cert_id = '' unless ( defined($cert_id) );
      $sql = "SELECT c.id FROM certificates c, Repository r "
           . "WHERE c.id=r.component_id AND r.rep_id='$cert_id' ORDER BY c.id";
    }
    else {
      $sql = "SELECT id FROM certificates WHERE dom_id=$domainId";
    }
    my @ids;
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( $ptrRow = $self->{dbh}->fetchrow() ) {
        push @ids, $ptrRow->[0];
      }
    }
    $self->{dbh}->finish();

    my $defaultCert = -1;
    $sql = "SELECT c.id FROM domains d, hosting h, IP_Addresses ip, certificates c "
         . "WHERE c.id=ip.ssl_certificate_id AND ip.id=h.ip_address_id AND h.dom_id=d.id AND d.id=$domainId";
    if ( $self->{dbh}->execute_rownum($sql) and $ptrRow = $self->{dbh}->fetchrow() ) {
      $defaultCert = $ptrRow->[0];
    }
    $self->{dbh}->finish();

    foreach $id (@ids) {
      $self->makeCertificateNode( $id, $domainId, 'domain', $id == $defaultCert );
    }

    my $tomcatServiceStatus;
    if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
      $tomcatServiceStatus = $self->getDomainServiceStatus( $domainId, 'tomcat' );
    }

    if (defined $tomcatServiceStatus) {
      $self->{packer}->setDomainTomcat( $domainId, $tomcatServiceStatus );

      $sql = "SELECT wa.name,wa.status FROM WebApps wa LEFT JOIN DomainServices ds ON wa.domain_service_id=ds.id WHERE ds.dom_id = $domainId";

      my ( %webapps, $webapp );

      if ( $self->{dbh}->execute_rownum($sql) ) {
        while ( $ptrRow = $self->{dbh}->fetchrow() ) {
          $webapps{ $ptrRow->[0] } = $ptrRow->[1];
        }
      }
      $self->{dbh}->finish();

      foreach $webapp ( keys %webapps ) {
        $self->makeWebAppNode( $domainId, $webapp, $webapps{$webapp}, $domainAsciiName );
      }
    }

    if( not PleskVersion::atLeast(10, 0, 0)) {
      if ( $isroot ) {
        $self->makeDomAdminUserRole('root', $domain{'cl_id'}, $domainId, \%domParams);
      }
      else {
        $self->makeDomAdminUserRole(PleskStructure::getClientType( $domainOwner ), $domain{'cl_id'}, $domainId, \%domParams);
      }
    }

  }

  if ( $domainType eq "vrt_hst" ) {
    $self->makePhostingNode( \%domain, \%domParams, \@SiteApplications );
  }
  elsif ( $domainType eq "std_fwd" ) {
    $self->makeShostingNode( \%domain );
  }
  elsif ( $domainType eq "frm_fwd" ) {
    $self->makeFhostingNode( \%domain );
  }

  if ( !$self->{only_mail_dump} ) {
    $self->getCustomButtonsByOwner( 'domain-admin', $domainId );

    if ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
      $self->getSubscription('domain', $domainId);
      if ( PleskVersion::atLeast( 10, 1, 0 ) ) {
        $self->getSubscriptionProperties('domain', $domainId);
        $self->dumpApsBundle($domainId, 'domain');
      }
      $self->{packer}->dumpUnityMobileIntegration($domainId, \%domParams);
    }
  }

  $self->unSuspendDomain();
  $self->{dump_status}->endDomain($domainName);
  $self->{packer}->finishDomain($domainId);
}

#
# makeSiteNode
#
sub makeSiteNode {
  my ( $self, $domainAsciiName, $domainName) = @_;

  my ( $sql, %domain, %domParams, $ptrRow, $ptrHash, $id );

  #
  # get domain's info
  #

  Packer::printToLog("Getting site info");
  $sql = "SELECT * FROM domains WHERE name = '$domainAsciiName'";

  unless ( $self->{dbh}->execute_rownum($sql) ) {
    $self->{dbh}->finish();
    return;
  }
  unless ( $ptrHash = $self->{dbh}->fetchhash() ) {
    $self->{dbh}->finish();
    return;
  }
  %domain = %{$ptrHash};
  $self->{dbh}->finish();

  my $domainId = $domain{'id'}; 
  my $webspaceId = $domain{'webspace_id'}; 

  $sql = "SELECT param,val FROM dom_param WHERE dom_id=$domainId";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( $ptrRow = $self->{dbh}->fetchrow() ) {
      $domParams{ $ptrRow->[0] } = $ptrRow->[1];
    }
  }
  $self->{dbh}->finish();

  #
  # end get domain's info
  #

  my $domainType      = $domain{'htype'};

  my $domainOwner = PleskStructure::getClientNameFromId( $domain{'cl_id'} );
  my $parentType = PleskStructure::getClientType( $domainOwner );


  $self->{packer}->addDomainSite( $webspaceId, $domainId, $domainAsciiName, $domainName, \%domain );

  $self->suspendDomain( $domainName );

  $self->addWwwStatus( $domainId, $domainAsciiName );

  #
  # Status
  #
  Packer::printToLog("Dumping site status");

  my $status = $domain{'status'};
  $self->{packer}->setDomainStatus( $domainId, $status );

  # Domain's DNS settings
  $self->makeDomainDnsZone( \%domain );

  if ( !$self->{only_mail_dump} ) {
    # Domain's aliases
    $self->dumpDomainAliases($domainId);
  }

  #
  # No further info required if shallow dump specified
  #
  if ( $self->{shallow_dump} && !$self->{only_mail_dump} ) {

    # Need to dump information about all databases for DbServers mapping

    Packer::printToLog("Dumping domain databases");

    $sql = "SELECT id FROM data_bases WHERE dom_id=$domainId";
    if ( $self->{dbh}->execute_rownum($sql) ) {

      my (@databases);
      while ( $ptrRow = $self->{dbh}->fetchrow() ) {
        push @databases, $ptrRow->[0];
      }
      foreach my $dbid (@databases) {
        $self->makeDatabaseNode( $dbid, $domainId, 'domain', undef, undef );
      }
    }
    $self->{dbh}->finish();
    $self->unSuspendDomain();
    $self->{packer}->finishDomain($domainId);
    return;
  }

  if ( !$self->{only_hosting_dump} ) {
      Packer::printToLog("Dumping site mailsystem");

      my $mailServiceStatus;
      $mailServiceStatus = $self->getDomainServiceStatus($domainId, 'mail');

      if ( defined($mailServiceStatus) ) {
        $self->{packer}->setDomainMailService( $domainId, $mailServiceStatus );

        $sql = "SELECT m.id, a.password, a.type FROM mail m".
          " LEFT JOIN accounts a ON m.account_id=a.id ".
          " WHERE m.dom_id=$domainId ORDER BY m.mail_name";

        if ( $self->{dbh}->execute_rownum($sql) ) {
          my (@mails);
          while ( $ptrRow = $self->{dbh}->fetchrow() ) {
            push @mails, [ @{$ptrRow} ];
          }
          $self->{packer}->setDomainMailNamesContent( $domainId, $domainName, getPleskMailnamesDir() . "/$domainAsciiName/" ) if $self->{dump_full_mail};

          foreach $ptrRow (@mails) {
            $ptrRow->[2] = $self->{packer}->normalizePasswordType( $ptrRow->[2] );
            $self->makeMailUserNode( $domainId, @{$ptrRow}, $domainName, $domainAsciiName );
          }
        }
        $self->{dbh}->finish();

        $self->getCatchAllAddress($domainId);

        $self->getDomainKeysDomainSupport( $domainId, $domainName, $domain{'dns_zone_id'} );

        my $oldWebMailFlag = $self->getOldWebMailStatus(\%domain);

        $self->{packer}->setDomainWebMail( $domainId, ( exists $domParams{'webmail'} ?  $domParams{'webmail'} : defined($oldWebMailFlag) ? 'horde' : 'none' ) );

        $self->{packer}->setDomainGLSupport( $domainId, (exists $domParams{'gl_filter'} ) ? $domParams{'gl_filter'} : 'on' );
      }
  }
  else { Packer::printToLog("Skip site mailsystem dump due to settings"); }

  my @SiteApplications;

  if ( !$self->{only_mail_dump} ) {
    #-----------------------------------------------------------------
    # Information about domain's site applications
    # should be extracted prior to database/hosting/custom-buttons
    # dump, as site applications' linked resources should be dumped
    # separately.
    #-----------------------------------------------------------------
    @SiteApplications = $self->getDomainSiteapps($domainId, $self->{dbh});

    #-----------------------------------------------------------------
    # Domain's databases
    #-----------------------------------------------------------------
    Packer::printToLog("Dumping site databases");

    # Site applications' databases should not be dumped here
    my $exclude_list = '';
    {
      my @excluded_dbs;
      foreach my $sapp (@SiteApplications) {
        foreach my $row ( $sapp->getDatabases() ) {
          push @excluded_dbs, $row->{'id'};
        }
      }
      if (@excluded_dbs) {
        $exclude_list = " AND id NOT IN(" . Packer::getSqlList(@excluded_dbs) . ")";
      }
    }

    # Databases without those used in site apps
    $sql = "SELECT id FROM data_bases WHERE dom_id=$domainId" . $exclude_list;
    if ( $self->{dbh}->execute_rownum($sql) ) {

      my (@databases);
      while ( $ptrRow = $self->{dbh}->fetchrow() ) {
        push @databases, $ptrRow->[0];
      }
      $self->{dbh}->finish();

      foreach my $dbid (@databases) {
        $self->makeDatabaseNode( $dbid, $domainId, 'domain', undef, undef );
      }
    }
    else{ $self->{dbh}->finish(); }
  }
  else { Packer::printToLog("Skip dumping domain databases due to settings"); }

  #
  # maillist
  #
  if ( !$self->{only_hosting_dump} ) {
    Packer::printToLog("Dumping site maillists");
      my $maillistsStatus = $self->getDomainServiceStatus( $domainId, 'maillists' );
      if (defined $maillistsStatus) {
        $self->{packer}->setDomainMailLists( $domainId, $maillistsStatus );

        my $archiveDir = AgentConfig::get("MAILMAN_VAR_D") . "/archives/private";
        my @archiveDirs;

        $sql = "SELECT id,name,status FROM MailLists WHERE dom_id=$domainId";
        if ( $self->{dbh}->execute_rownum($sql) ) {
          my (@mlists);
          while ( $ptrRow = $self->{dbh}->fetchrow() ) {
            push @mlists, [ @{$ptrRow} ];
            my $datafile = $ptrRow->[1] . ".mbox/" . $ptrRow->[1] . ".mbox";
            push @archiveDirs, $datafile  if -f "$archiveDir/$datafile";
          }
          $self->{packer}->setDomainMailListContent( $domainId, $domainAsciiName, $archiveDir, { 'include' => \@archiveDirs, 'follow_symlinks' => 1 } ) if $self->{dump_full_mail};
          foreach $ptrRow (@mlists) {
            $self->makeMailListNode( $domainId, @{$ptrRow} );
          }
        }
        $self->{dbh}->finish();
      }
  }
  else { Packer::printToLog("Skip site domain maillists due to settings"); }

  #
  # end maillist
  #

  if ( !$self->{only_mail_dump} ) {
    Packer::printToLog("Dumping site statistics");

    $self->addDomainTraffic($domainId);

    my $cert_id = $domain{'cert_rep_id'};

    $cert_id = '' unless ( defined($cert_id) );
    $sql = "SELECT c.id FROM certificates c, Repository r "
         . "WHERE c.id=r.component_id AND r.rep_id='$cert_id' ORDER BY c.id";

    my @ids;
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( $ptrRow = $self->{dbh}->fetchrow() ) {
        push @ids, $ptrRow->[0];
      }
    }
    $self->{dbh}->finish();

    foreach $id (@ids) {
      $self->makeCertificateNode( $id, $domainId, 'domain' );
    }

    my $tomcatServiceStatus = $self->getDomainServiceStatus( $domainId, 'tomcat' );

    if (defined $tomcatServiceStatus) {
      $self->{packer}->setDomainTomcat( $domainId, $tomcatServiceStatus );

      $sql = "SELECT wa.name,wa.status FROM WebApps wa LEFT JOIN DomainServices ds ON wa.domain_service_id=ds.id WHERE ds.dom_id = $domainId";

      my ( %webapps, $webapp );

      if ( $self->{dbh}->execute_rownum($sql) ) {
        while ( $ptrRow = $self->{dbh}->fetchrow() ) {
          $webapps{ $ptrRow->[0] } = $ptrRow->[1];
        }
      }
      $self->{dbh}->finish();

      foreach $webapp ( keys %webapps ) {
        $self->makeWebAppNode( $domainId, $webapp, $webapps{$webapp}, $domainAsciiName );
      }
    }

    if ( $domainType eq "vrt_hst" ) {
      $self->makePhostingNode( \%domain, \%domParams, \@SiteApplications, 1 );
    }
    elsif ( $domainType eq "std_fwd" ) {
      $self->makeShostingNode( \%domain );
    }
    elsif ( $domainType eq "frm_fwd" ) {
      $self->makeFhostingNode( \%domain );
    }

    $self->getCustomButtonsByOwner( 'domain-admin', $domainId );

    $self->getSubscription('domain', $domainId);
    $self->{packer}->dumpUnityMobileIntegration($domainId, \%domParams);
  }

  $self->unSuspendDomain();
  $self->{packer}->finishDomain($domainId);
}

#-----------------------------------------------------------------
#    Returns <dns> or <dns-zone> xml elements
#-----------------------------------------------------------------
sub makeDomainDnsZone( $ ) {
  my ( $self, $domainHashPtr ) = @_;

  Packer::printToLog("Dumping domain DNS");

  if ( PleskVersion::atLeast( 8, 0, 0 ) ) {
    return $self->makeDnsZone( $domainHashPtr->{'dns_zone_id'},
      $domainHashPtr->{'id'}, 'domain' );
  }
  else {
    return $self->makeDnsZoneOld( $domainHashPtr->{'id'} );
  }
}

#-----------------------------------------------------------------
#    Returns <dnsrec> xml node or false if given record should not be dumped
#-----------------------------------------------------------------
sub makeDnsRecord( $ ) {
  my ( $self, $ptrHash ) = @_;

  return if $ptrHash->{'type'}        =~ /none/;
  return if $ptrHash->{'displayHost'} =~ /.*_domainkey.*/;

  if ( $ptrHash->{'type'} eq 'TXT' ) {
    $ptrHash->{'val'} =~ s/"(.*)"/$1/;
  }

  # Fix broken CNAME mail records (bug #110731)
  if (  $ptrHash->{'type'} eq 'CNAME'
    and $ptrHash->{'host'} eq "mail." . $ptrHash->{'val'} )
  {
    $ptrHash->{'type'} = 'A';
    $ptrHash->{'val'} =~ s/\.$//;
    if (defined($ptrHash->{'displayVal'})) {
      delete($ptrHash->{'displayVal'});
    }
    $ptrHash->{'val'} = PleskStructure::getDomainIp( $ptrHash->{'val'} );
  }

  return %{$ptrHash};
}

sub geteDnsZone {
  my ( $self, $dnsZoneId, $paramsPtr, $recordsPtr ) = @_;

  my $sql = "SELECT * FROM dns_zone WHERE id=$dnsZoneId";

  if ( !$self->{dbh}->execute_rownum($sql) ) {
    $self->{dbh}->finish();
    my $msg = "Broken referencial integrity: DNS zone id $dnsZoneId is not found in dns_zone";
    print STDERR "$msg\n";
    Packer::printToError( $msg );
    return;
  }

  if ( my $hashPtr = $self->{dbh}->fetchhash($sql) ) {
    %{$paramsPtr} = %{ $hashPtr };
  }

  $self->{dbh}->finish();

  # dns records
  $sql = "SELECT * FROM dns_recs WHERE dns_zone_id=$dnsZoneId";
  if ( $self->{dbh}->execute_rownum($sql) ) {

    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      my %dnsrec = $self->makeDnsRecord($ptrHash);
      if (%dnsrec) {
        push @{$recordsPtr}, \%dnsrec;
      }
    }
  }
  $self->{dbh}->finish();
}

sub makeDnsZone{
  my ( $self, $dnsZoneId, $parent, $parentType ) = @_;

  my $proc;
  if ( 'domain' eq $parentType ) {
    $proc = $self->getSubroutineRef("setDomainDnsZone");
  }
  else {
    Packer::printToError('Error: makeDnsZone: Unexpected type of parent \"$parentType\"');
    return;
  }

  my %params;
  my @records;
  $self->geteDnsZone( $dnsZoneId, \%params, \@records );

  $self->{packer}->$proc( $parent, \%params, \@records );
}

sub makeDnsZoneOld( $ ) {
  my ( $self, $domainId ) = @_;

  my $sql = "SELECT dns_zone";
  if ( PleskVersion::atLeast( 5, 0, 0 ) ) {
    $sql .= ",dns_type";
  }
  $sql .= " FROM domains WHERE id='$domainId'";

  my ( $enabled, $type );
  if ( $self->{dbh}->execute_rownum($sql)
    and my $ptrRow = $self->{dbh}->fetchrow() )
  {
    $enabled = $ptrRow->[0] eq 'false' ? 'false' : 'true';
    $type =
      ( !$ptrRow->[1] or ( $ptrRow->[1] eq 'master' ) ) ? 'master' : 'slave';
  }
  $self->{dbh}->finish();

  my @records;
  $sql = "SELECT * FROM dns_recs WHERE dom_id=$domainId";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      push @records, $ptrHash;
    }
  }
  $self->{dbh}->finish();

  # Add old master record

  my $masterIp;

  $sql = "SELECT * FROM dns_masters WHERE dom_id = '$domainId'";

  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      $masterIp = $ptrHash->{'ip_address'};
    }
  }
  $self->{dbh}->finish();

  $self->{packer}->setDomainDnsOld( $domainId, $enabled, $type, $masterIp, \@records );
}

#-----------------------------------------------------------------
#    Dumps domain aliases for given domain ID
#-----------------------------------------------------------------
sub dumpDomainAliases( $ $ ) {
  my ( $self, $domainId ) = @_;

  # Domain aliases are introduced since Plesk 8.0.0
  if ( !PleskVersion::atLeast( 8, 0, 0 ) ) {
    return;
  }

  Packer::printToLog( 'Dumping domain aliases... ', 1 );

  my @aliases;

  my $sql = "SELECT * FROM domainaliases WHERE dom_id='$domainId'";
  if ( $self->{dbh}->execute_rownum($sql) ) {

    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      push @aliases, $ptrHash;
    }
  }
  $self->{dbh}->finish();

  foreach my $alias (@aliases) {

    my %params;
    my @records;
    if ( $alias->{'dns_zone_id'} != 0 ) {
      $self->geteDnsZone( $alias->{'dns_zone_id'}, \%params, \@records );
    }

    $self->{packer}->addDomainAlias( $domainId, $alias, \%params, \@records );
  }

  Packer::printToLog('OK');
}

sub getDomainSiteapps( $ $ ) {
  my ($self, $dom_id, $dbh) = @_;
  # Site applications migration is supported since Plesk 7.0.0
  if ( !PleskVersion::atLeast( 7, 0, 0 ) ) {
    return;
  }

  if ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
    return SiteApp10::getDomainSiteapps($dom_id);
  }
  else {
    return SiteApp::getDomainSiteapps($dom_id, $dbh);
  }
}

sub addWwwStatus {
  my ( $self, $domainId, $domainName ) = @_;

  my $sql;

  if ( PleskVersion::atLeast( 8, 0, 0 ) ) {
    $sql = "SELECT r.* FROM dns_recs r, domains d WHERE d.id=$domainId "
	  . "AND d.dns_zone_id=r.dns_zone_id AND ( r.type = 'CNAME' or r.type = 'A' ) AND r.host = 'www.$domainName.'";
  }
  else {
    $sql = "SELECT * FROM dns_recs WHERE dom_id = $domainId AND ( type = 'CNAME' OR  type = 'A' ) "
	  . "AND host = 'www.$domainName.'";
  }

  if ( $self->{dbh}->execute_rownum($sql) ) {
    $self->{packer}->setDomainWwwStatus( $domainId, 'true' );
  }
  else {
    $self->{packer}->setDomainWwwStatus( $domainId, 'false' );
  }
  $self->{dbh}->finish();
}

my %domAdminPermsToRolePerms = (
  'manage_log'         => 'logRotationManagement',
  'manage_anonftp'     => 'anonymousFtpManagement',
  'manage_crontab'     => 'scheduledTasksManagement',
  'manage_spamfilter'  => 'spamfilterManagement',
  'allow_local_backups'=> 'backupRestoreManagement', 
  'allow_ftp_backups'  => 'backupRestoreManagement', 
  'manage_virusfilter' => 'antivirusManagement',
  'manage_dns'         => 'dnsManagement',
  'manage_webapps'     => 'javaApplicationsManagement',
  'manage_maillists'   => 'mailListsManagement',
  'site_builder'       => 'sitebuilderManagement'
);

my %domAdminCardToUser = (
  'companyName' => 'companyName',
  'personalName'=> 'contactName',
  'phone'       => 'phone',
  'fax'         => 'fax',
  'email'       => 'email', 
  'address'     => 'address', 
  'city'        => 'city',
  'state'       => 'state',
  'zip'         => 'zip',
  'country'     => 'country'
);

sub makeDomAdminUserRole {
  my ( $self, $parentType, $clientId, $domainId, $domParams ) = @_;

  my ( $sql, $item, $ptrHash, $xmlName, $fieldName );

  $sql = "SELECT * FROM dom_level_usrs WHERE dom_id=$domainId";
  unless ( $self->{dbh}->execute_rownum($sql) ) {
    $self->{dbh}->finish();
    return;
  }
  my %domainUser;
  if ( $ptrHash = $self->{dbh}->fetchhash() ) {
    %domainUser = %{ $ptrHash };
  }
  $self->{dbh}->finish();

  my $state;
  if ( $domainUser{'state'} ) {
    $state = $domainUser{'state'};
  }

  my $passwd;
  my $passwdType;
  if ( PleskVersion::atLeast( 7, 1, 5 ) ) {
    if ( defined $domainUser{'account_id'}
      and $domainUser{'account_id'} != 0 )
    {
      ( $passwd, $passwdType ) = $self->makeAccountPasswordNode( $domainUser{'account_id'} );
    }
  }
  else {
    $passwd     = $domainUser{'passwd'};
    $passwdType = 'plain';
  }

  my %cardHash;
  if ( PleskVersion::atLeast( 5, 0, 0 ) && defined( $domainUser{'card_id'} ) ) {
    $sql = "SELECT * from Cards WHERE id=$domainUser{'card_id'}";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      if ( $ptrHash = $self->{dbh}->fetchhash() ) {
        %cardHash = %{$ptrHash};
      }
      else {
        my $msg =  "Broken referencial integrity: Card info is not found for domain user";
        print STDERR "$msg\n";
        Packer::printToError( $msg );
      }
    }
    $self->{dbh}->finish();
  }

  my $domainName = PleskStructure::getDomainNameFromId($domainId);
  my $roleName = "Domain Administrator (" . $domainName . ")";

  my %userHash;
  foreach my $item ( keys %cardHash ) {
    if ( exists $domAdminCardToUser{$item} ) {
      $userHash{ $domAdminCardToUser{$item} } = $cardHash{ $item };
    }
  }

  if ( (!defined ($userHash{'contactName'}) ) || ($userHash{'contactName'} eq '') ) {
    $userHash{'contactName'} = "Domain Administrator (" . $domainName . ")";
  }
  $userHash{'password'} = $passwd;
  $userHash{'SmbRoleName'} = $roleName;
  $userHash{'isDomainAdmin'} = '1';
  $userHash{'isBuiltIn'} = 0;
  $userHash{'uuid'} = '';
  $userHash{'isLocked'} = 1;
  $userHash{'isBase64'} = '0';


  my $permsHash = $self->getPermsHash( $domainUser{'perm_id'} );
  my %rolePermsHash = ();
  foreach my $perm ( keys %{$permsHash} ) {
    if ( $permsHash->{$perm} eq 'true' && exists $domAdminPermsToRolePerms{$perm} ) { 
      $rolePermsHash{ $domAdminPermsToRolePerms{$perm} } = 1;
    }
  }
  
  $rolePermsHash{'browseStats'} = 1;

  if( $parentType eq 'root' ){
    $self->{packer}->addRootUser( $domainName, \%userHash);
    $self->{packer}->addRootRole( $roleName, 0, \%rolePermsHash);
  }
  elsif( $parentType eq 'admin' ){
    $self->{packer}->addAdminUser( $domainName, \%userHash);
    $self->{packer}->addAdminRole( $roleName, 0, \%rolePermsHash);
  }
  elsif( $parentType eq 'reseller' || $parentType eq 'client'){
    $self->{packer}->addClientUser( $clientId, $domainName, \%userHash);
    $self->{packer}->addClientRole( $clientId, $roleName, 0, \%rolePermsHash);
  }

}

sub makeMailAccessUserRole {
  my ( $self, $mailname, $domainName, $password, $passwordType, $parentType, $clientId, $antispam, $antivirus ) = @_;

  return if PleskVersion::atLeast(10, 0, 0);

  my $perms;
  $perms = (defined $antispam)?  ( "antispam" . ((defined $antivirus)? "; antivirus" : '' ) ): ( (defined $antivirus)? "antivirus" : undef );
  my $roleName = "Mail User" . ((defined $perms)? " ($perms)" : '');

  my %userHash;
  $userHash{'SmbRoleName'} = $roleName;
  $userHash{'isDomainAdmin'} = '0';
  $userHash{'isBuiltIn'} = 0;
  $userHash{'uuid'} = '';
  $userHash{'isLocked'} = 0;
  $userHash{'contactName'} = $mailname;
  $userHash{'email'} = $mailname.'@'.$domainName;
  $userHash{'password'} = $password;
  $userHash{'passwordType'} = $passwordType;
  $userHash{'isBase64'} = '0';

  my %rolePermsHash = ();
  $rolePermsHash{ 'spamfilterManagement' } = 1 if defined $antispam;
  $rolePermsHash{ 'antivirusManagement' } = 1 if defined $antivirus;
  
  if( $parentType eq 'admin' ) {
    $self->{packer}->addAdminUser( $mailname.'@'.$domainName, \%userHash);
    $self->{packer}->addAdminRole( $roleName, 0, \%rolePermsHash);
  }
  elsif( $parentType eq 'reseller' || $parentType eq 'client') {
    $self->{packer}->addClientUser( $clientId, $mailname.'@'.$domainName, \%userHash);
    $self->{packer}->addClientRole( $clientId, $roleName, 0, \%rolePermsHash);
  }
  elsif( $parentType eq 'root' ) {
    $self->{packer}->addRootUser( $mailname.'@'.$domainName, \%userHash);
    $self->{packer}->addRootRole( $roleName, 0, \%rolePermsHash);
  }
}

sub getTrafficValue {
  my ( $self, $tableName, $whereCond ) = @_;

  my $trafficValue = '';

  my $sql = "SELECT http_in, http_out, ftp_in, ftp_out, smtp_in, smtp_out, "
      . "pop3_imap_in, pop3_imap_out, date FROM $tableName WHERE $whereCond";

  if ( $self->{dbh}->execute_rownum($sql) ) {
     my @keys = ( 'http', 'ftp', 'smtp', 'pop3-imap' );
     my ( $key, $i, $ptrRow );
     while ( $ptrRow = $self->{dbh}->fetchrow() ) {
       for ( $i = 0 ; $i < @keys * 2 ; ++$i ) {
         if ( $ptrRow->[$i] ) {
            $trafficValue .= $ptrRow->[8];
            $trafficValue .= ' ';
            $trafficValue .= $keys[ $i / 2 ];
            $trafficValue .= ' ';
            $trafficValue .= ( $i % 2 ? 'out' : 'in' );
            $trafficValue .= ' ';
            $trafficValue .= $ptrRow->[$i];
            $trafficValue .= "\n";
        }
      }
    }
  }
  $self->{dbh}->finish();
  return $trafficValue;
}


#
# addDomainTraffic - add current traffic
#
#     arguments:
#                 $root - XML node to add traffic's nodes
#                 $domainId - ID of domain
#
sub addDomainTraffic {
  my ( $self, $domainId ) = @_;

  my $trafficValue = '';

  if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
    $trafficValue = $self->getTrafficValue( 'DomainsTraffic', "dom_id=$domainId" );
  }
  else {
    my $sql = "SELECT transfer, date FROM stat WHERE dom_id=$domainId";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      my $ptrRow;
      while ( $ptrRow = $self->{dbh}->fetchrow() ) {
        if ( $ptrRow->[0] ) {
          $trafficValue .= $ptrRow->[1];
          $trafficValue .= ' http in ';
          $trafficValue .= $ptrRow->[0];
          $trafficValue .= "\n";
        }
      }
    }
    $self->{dbh}->finish();
  }

  $self->{packer}->setDomainTraffic( $domainId, $trafficValue ) if $trafficValue;
}

sub addClientTraffic {
  my ( $self, $clientId ) = @_;

  my $trafficValue = '';

  if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
    $trafficValue = $self->getTrafficValue( 'ClientsTraffic', "cl_id=$clientId" );
    $self->{packer}->setClientTraffic( $clientId, $trafficValue ) if $trafficValue;
  }
}

# There are 3 types of nonexistent user mail handling:
# Bounce with message (bounce:message text)
# Catch to address (email@address)
# SMTP reject (reject)
# Returns bounce|catch|reject or empty string
sub getNonexistentMode {
  my ( $self, $domainId ) = @_;

  my $sql = "SELECT p.value FROM Parameters p, DomainServices ds "
    . "WHERE ds.parameters_id = p.id AND ds.dom_id = $domainId AND p.parameter = 'nonexist_mail'";
  my $mode;
  if ( $self->{dbh}->execute_rownum($sql) ) {
    if (my $rowPtr = $self->{dbh}->fetchrow()) {
      $mode = @{ $rowPtr }[0];
    }
  }
  else {
    $mode = "bounce:This address no longer accepts mail.";
  }
  $self->{dbh}->finish();

  return $mode;
}

sub getCatchAllAddress {
  my ( $self, $domainId ) = @_;

  my $sql;
  if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
    my $mode = $self->getNonexistentMode($domainId);

    if ( $mode =~ /^catch$/ ) {
      $sql = "SELECT p.value FROM Parameters p, DomainServices ds "
        . "WHERE ds.parameters_id = p.id AND ds.dom_id = $domainId AND p.parameter = 'catch_addr'";

    }
    elsif ( $mode =~ /^bounce$/ ) {
      $sql = "SELECT CONCAT('bounce:', p.value) FROM Parameters p, DomainServices ds "
        . "WHERE ds.parameters_id = p.id AND ds.dom_id = $domainId AND p.parameter = 'bounce_mess'";

    }
    elsif ( $mode =~ /^reject$/ ) {
      $sql = "SELECT 'reject'";
    }

  }
  elsif ( PleskVersion::atLeast( 5, 0, 0 ) ) {
    $sql = "SELECT p.value FROM Parameters p, dom_param dp WHERE "
      . "dp.dom_id = $domainId AND dp.param='param_id' AND dp.val = p.id AND p.parameter = 'catch_addr'";

  }
  else {
    $sql = "SELECT dp.val FROM dom_param dp WHERE dp.dom_id = $domainId AND dp.param = 'catch_addr'";
  }

  # some not supported mode or default parameter [bug 43901]
  return unless ($sql);

  if ( $self->{dbh}->execute_rownum($sql) ) {
    if ( my $rowPtr = $self->{dbh}->fetchrow() ) {
      my $catchAllAddr = @{ $rowPtr }[0];
      if ($catchAllAddr) {
        $self->{packer}->setDomainCatchMail( $domainId, $catchAllAddr );
      }
    }
  }
  $self->{dbh}->finish();
}

#
# makeMailListNode
#
#   arguments:
#              $mlistId - ID of the mail list
#              $mlistName - name
#              $mllistState - state of maillist
#
#   return:
#              $root - XML node
#
sub makeMailListNode {
  my ( $self, $domainId, $mlistId, $mlistName, $mlistState ) = @_;

  unless ( defined Mailman::version() ) {
    Packer::printToLog("Unable to found Mailman installation");
    return;
  }

  my @owners = Mailman::getListOwners($mlistName);
  if ( !@owners ) {
    Packer::printToLog("Bad maillist $mlistName, skipped");
    return;
  }

  my %listMembers = Mailman::getListMembers($mlistName);

  my $archiveDir = AgentConfig::get("MAILMAN_VAR_D") . "/archives/private/$mlistName.mbox";

  $self->{packer}->addDomainMailList( $domainId, $mlistName,
    Mailman::getListPassword($mlistName),
    $mlistState, \@owners, \%listMembers, $archiveDir, "$mlistName.mbox" );
}

sub makeCertificateNode {
  my ( $self, $certId, $parent, $parentType, $default ) = @_;

  my $proc;
  if ( 'server' eq $parentType ) {
    $proc = $self->getSubroutineRef("addServerCertificate");
  }
  elsif ( 'domain' eq $parentType ) {
    $proc = $self->getSubroutineRef("addDomainCertificate");
  }
  else {
    Packer::printToError('Error: makeCertificateNode: unexpected parent type');
    return;
  }

  my ( $sql, %cert, $item );

  $sql = "SELECT * FROM certificates WHERE id=$certId";
  unless ( $self->{dbh}->execute_rownum($sql) ) {
    $self->{dbh}->finish();
    &Packer::printToError(
      "Error: makeCertificateNode: certificate ID '$certId' is not found");
    return undef;
  }

  if ( my $hashPtr = $self->{dbh}->fetchhash() ) {
    %cert = %{ $hashPtr };
  }
  $self->{dbh}->finish();

  my $cert;
  my $csr;
  my $ca_cert;
  my $name;
  if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
    $cert    = $cert{'cert'};
    $csr     = $cert{'csr'};
    $ca_cert = $cert{'ca_cert'};
  }
  elsif ( PleskVersion::atLeast( 5, 0, 0 ) ) {
    $cert = $cert{'pub_key'};
  }
  if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
    $name = $cert{'name'};
  }
  else {

    # certificate names were not used in Plesk prior to 6.0
    $name = $certId;
  }
  my $pvt_key = $cert{'pvt_key'};

  if ( 'server' eq $parentType ) {
    $self->{packer}->$proc( $name, $cert, $csr, $ca_cert, $pvt_key, $default );
  }
  else {
    $self->{packer}->$proc( $parent, $name, $cert, $csr, $ca_cert, $pvt_key, $default );
  }
}

sub makeDatabaseNode {
  my ( $self, $dbId, $parent, $parentType, $sappId, $sappResource ) = @_;

  my ( $ptrHash, $ptrRow, $item, $charset, $dbServerId, $dbServerHost, $dbServerPort );

  my $proc;
  if( $sappId ){
        if ( 'domain' eq $parentType ) {
          $proc = $self->getSubroutineRef("addDomainSappDatabase");;
        }
        elsif ( 'subdomain' eq $parentType ) {
          $proc = $self->getSubroutineRef("addSubDomainSappDatabase");
        }
        else {
          Packer::printToError( "Error: makeDatabaseNode: Unexpected type of parent \"$parentType\"");
          return;
        }
  }
  else{
        if ( 'domain' eq $parentType ) {
          $proc = $self->getSubroutineRef("addDomainDatabase");
        }
        else {
          Packer::printToError( "Error: makeDatabaseNode: Unexpected type of parent \"$parentType\"");
          return;
        }
  }

  my $sql = "SELECT name, type FROM data_bases WHERE id=$dbId";
  unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $ptrRow = $self->{dbh}->fetchrow() ) ) {
    $self->{dbh}->finish();
    my $msg = "Broken referencial integrity: Database id $dbId is not found in data_bases";
    print STDERR "$msg\n";
    Packer::printToError( $msg );
    return;
  }
  my ( $dbName, $dbType ) = @{ $ptrRow };
  $self->{dbh}->finish();

  if ( $dbType eq "postgres" or $dbType eq "postgresql" ) {
    $dbType = "postgresql";
  }
  elsif ( $dbType ne "mysql" ) {
    Packer::printToError("Unknown database type: $dbType");
    return;
  }

  my %dbServer;

  # Remote databases hosting was introduced in 8.0.0
  if ( PleskVersion::atLeast( 8, 0, 0 ) ) {
    my $sql = "SELECT host,port,ds.type FROM DatabaseServers as ds, data_bases as db WHERE "
       . "ds.id = db.db_server_id AND db.id = $dbId";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      if( $ptrRow = $self->{dbh}->fetchrow() ) {
        $dbServerHost = ( $ptrRow->[0] ne 'localhost' ) ? $ptrRow->[0] : 'localhost';
        $dbServerPort = $ptrRow->[1];

        $dbServer{'type'} = $ptrRow->[2];
        $dbServer{'host'} = "$ptrRow->[0]";
        $dbServer{'port'} = "$ptrRow->[1]";
      }
    }
    $self->{dbh}->finish();
  }

  my @dbUsers;
  my %optional;
  $optional{'sapp-param'} = $sappResource if ($sappResource);
  my %contentDescription;

  if ( !$self->{shallow_dump} ) {
    Packer::printToLog("Database $dbName");

    if ( PleskVersion::atLeast( 7, 5, 0 ) ) {
      $sql = "SELECT db_users.id, login, password, type FROM db_users, accounts WHERE "
        . "db_users.db_id = $dbId AND db_users.account_id = accounts.id";

      if ( $self->{dbh}->execute_rownum($sql) ) {
        while ( $ptrHash = $self->{dbh}->fetchhash() ) {
          $ptrHash->{'password'} ||= '';    # NULL -> ''
          my %item;
          $item{'login'}    = $ptrHash->{'login'};
          $item{'password'} = $ptrHash->{'password'};
          $item{'type'}     = $self->{packer}->normalizePasswordType( $ptrHash->{'type'} );
          $item{'id'}	  = $ptrHash->{'id'};

          push @dbUsers, \%item;
        }
      }
    }
    else {
      if ( $self->{dbh}->execute_rownum( "SELECT login, passwd FROM db_users WHERE db_id = $dbId") ) {

        while ( $ptrHash = $self->{dbh}->fetchhash() ) {
          my $dbPasswdType;

          # In Plesk 7.1.5 DB password type wasn't described in the DB. We
          # can distinguish the type only using length of password.
          if ( length( $ptrHash->{'passwd'} ) > 14 ) {
            $dbPasswdType = 'encrypted';
          }
          else {
            $dbPasswdType = 'plain';
          }

          if ( !defined $ptrHash->{'passwd'} ) { $ptrHash->{'passwd'} = ''; }

          my %item;
          $item{'login'}    = $ptrHash->{'login'};
          $item{'password'} = $ptrHash->{'passwd'};
          $item{'type'}     = $dbPasswdType;

          push @dbUsers, \%item;
        }
      }
    }
    $self->{dbh}->finish();

    my ( $dbUser, $dbPasswd, $plesk_7 );

# remote database hosting regularizes the database connectivity even for local hosts.
# thanks goes to xman.
    if ( PleskVersion::atLeast( 8, 0, 0 ) ) {

      my $sql = "SELECT admin_login, admin_password, ds.id FROM DatabaseServers as ds, data_bases as db "
        . "WHERE ds.id = db.db_server_id AND db.id = $dbId";
      unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $ptrRow = $self->{dbh}->fetchrow() ) ) {
        $self->{dbh}->finish();
        my $msg = "Broken referencial integrity: DatabaseServers vs data_bases for db id $dbId";
        print STDERR "$msg\n";
        Packer::printToError( $msg );
        return;
      }
      ( $dbUser, $dbPasswd, $dbServerId ) = @{ $ptrRow };
      $self->{dbh}->finish();

      if ( $dbType eq "mysql" and $dbServerHost eq 'localhost' ) {
        $dbPasswd = AgentConfig::get('password');
      }

      if ( $dbType eq "postgresql" ) {
        $optional{'version'} = getPostgresqlVersion();
      }
    }
    else {
      $dbServerId = $dbType;
      if ( $dbType eq "mysql" ) {
        $dbUser   = 'admin';
        $dbPasswd = AgentConfig::get('password');
      }
      else {
        $sql = "SELECT param, val FROM misc WHERE param RLIKE '^postgresql_admin_'";
        if ( $self->{dbh}->execute_rownum($sql) ) {
          while ( $ptrRow = $self->{dbh}->fetchrow() ) {
            my ( $name, $value ) = @{$ptrRow};
            $dbUser   = $value if $name =~ /login$/;
            $dbPasswd = $value if $name =~ /passwd$/;
          }
        }
        $self->{dbh}->finish();

        $plesk_7 = 1;
      }
    }

    if ( $dbType eq "mysql" ) {
      $optional{'version'} = Db::MysqlUtils::getVersion();
    }

    %contentDescription = (
      "name"     => $dbName,
      "type"     => $dbType,
      "user"     => $dbUser,
      "password" => $dbPasswd,
      "host"     => $dbServerHost,
      "port"     => $dbServerPort,
      "plesk_7"  => $plesk_7
    );

    if ( $dbType eq "postgresql" ) {
      my $psql = AgentConfig::psqlBin();

#[Bug 119082]
#$charset = `PGUSER=$dbUser PGPASSWORD='$dbPasswd' $psql -l template1 | grep '^[ \t]*$dbName ' | awk '{print \$5}'`;
      my $wrapPgsql = Packer::getDbConnect(
        $dbType,       $dbUser, $dbPasswd, $dbName,
        $dbServerHost, undef,   undef,     undef,
        undef,         $dbServerPort
      );
      if ( ref($wrapPgsql) eq 'HASH' ) {
        if (
          $wrapPgsql->{'EXECUTE'}->( "select pg_catalog.pg_encoding_to_char(d.encoding) FROM pg_catalog.pg_database d where d.datname='$dbName'" )
           )
        {
          my $ptrRow;
          if ( ( $ptrRow = $wrapPgsql->{'FETCHROW'}->() ) ) {
            $charset = $ptrRow->[0];
            if ( $charset ne '' ) {
              $optional{'charset'} = $charset;
            }
          }
          $wrapPgsql->{'FINISH'}->();
        }
      }
      else {
        Packer::printToLog( "Cannot connect to postgresql $dbServerHost:$dbServerPort (database '$dbName')" );
      }
    }

    if ( $dbType eq "mysql" ) {
      my $wrapMysql = Packer::getDbConnect(
        $dbType,       $dbUser, $dbPasswd, $dbName,
        $dbServerHost, undef,   undef,     undef,
        undef,         $dbServerPort
      );
      if ( ref($wrapMysql) eq 'HASH' ) {
        if ( $wrapMysql->{'EXECUTE'}->("SHOW VARIABLES LIKE \"character_set_database\"") )
        {
          my $ptrRow = $wrapMysql->{'FETCHROW'}->();
          my $charset = $ptrRow->[1] if $ptrRow;
          $optional{'charset'} = $charset if $charset;
          $wrapMysql->{'FINISH'}->();
        }
      }
    }
  }

  if( $sappId ){
       $self->{packer}->$proc( $dbId, $dbServerId, $parent, $sappId, $dbName, $dbType, \%optional, \%dbServer, \@dbUsers, \%contentDescription );
  }
  else{
       $self->{packer}->$proc( $dbId, $dbServerId, $parent, $dbName, $dbType, \%optional, \%dbServer, \@dbUsers, \%contentDescription );
  }
}

#
# makeMailUserNode
#
# argumets:
#           $mailId - mail ID
#           $passwd - password
#           $typePasswd - type of password
#           $domainName - name of domain
#
# return:
#           $root = XML node 'mailuser'
#
sub makeMailUserNode {
  my ( $self, $domainId, $mailId, $passwd, $typePasswd, $domainName, $domainAsciiName, $parentType, $clientId ) = @_;

  my ( $sql, %mail, $item, $ptrRow, $ptrHash, $dir, $id, $mbox_quota, %mailParams );

  $sql = "SELECT * FROM mail WHERE id = $mailId";
  unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $ptrHash = $self->{dbh}->fetchhash() ) ) {
    $self->{dbh}->finish();
    my $msg = "Broken referencial integrity: Mail id $mailId is not found in mail";
    print STDERR "$msg\n";
    Packer::printToError( $msg );
    return;
  }
  %mail = %{ $ptrHash };
  $self->{dbh}->finish();
  my $mailName = $mail{'mail_name'};
  my $userUid = undef;
  if (exists $mail{'userId'} and $mail{'userId'} != 0) {    
    $sql = "SELECT uuid FROM smb_users WHERE id = $mail{'userId'}";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( $ptrRow = $self->{dbh}->fetchrow() ) {
        $userUid = $ptrRow->[0];
      }
    }
    $self->{dbh}->finish();  
  }

  $self->{packer}->addMail( $domainId, $mailId, $mailName, $passwd, $typePasswd, $userUid );

  if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
    $sql = "SELECT param,val FROM mn_param WHERE mn_id=$mailId";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( $ptrRow = $self->{dbh}->fetchrow() ) {
        $mailParams{ $ptrRow->[0] } = $ptrRow->[1];
      }
    }
    $self->{dbh}->finish();
  }

  unless ( PleskVersion::atLeast(10, 0, 0) ) {
    if( defined $parentType && defined $clientId ) {
      my ( $access, $antispam, $antivirus );
      if ( defined $mail{'perm_id'} ) {
        $sql = "SELECT permission FROM Permissions WHERE id = $mail{'perm_id'} AND value='true'";
        if ( $self->{dbh}->execute_rownum($sql) ) {
          while ( $ptrRow = $self->{dbh}->fetchrow() ) {
            if ( $ptrRow->[0] eq 'cp_access') {
              $access = 1;
            }
            elsif ( $ptrRow->[0] eq 'manage_spamfilter' ) {
              $antispam = 1;
            }
            elsif ( $ptrRow->[0] eq 'manage_virusfilter' ) {
              $antivirus = 1;
            }
          }
          if ( $access ) {
            $self->makeMailAccessUserRole($mailName, $domainName, $passwd, $typePasswd, $parentType, $clientId, $antispam, $antivirus);
          }
        }
        $self->{dbh}->finish();
      }
      elsif ( defined( $mail{'cp_access'} ) ) {
        $self->makeMailAccessUserRole($mailName, $domainName, $passwd, $typePasswd, $parentType, $clientId );
      }
    }
  }

  if ( $mail{'mbox_quota'} ) {
    $mbox_quota = $mail{'mbox_quota'};

    if ( !PleskVersion::atLeast( 6, 0, 0 ) and $mbox_quota ne "-1" ) {
      $mbox_quota = $mbox_quota * 1024;
    }

    $self->{packer}->setMailBoxQuota( $mailId, $mbox_quota );
  }

  my $enable_mailbox = $mail{'postbox'} =~ /true/;

  # Check whether there autoresponder with attach
  # On 'Olde Pleskes' there was bug allowing attaches
  # when mailbox in turned off, so we have to explicitly
  # turn mailbox on if there is attach.
  $sql = "SELECT COUNT(ra.filename) FROM mail_resp AS mr, resp_attach as ra "
       . "WHERE ra.rn_id = mr.id AND mr.mn_id = $mailId";
  if (  $self->{dbh}->execute_rownum($sql)
    and ( $ptrRow = $self->{dbh}->fetchrow() )
    and $ptrRow->[0] != 0 )
  {
    $enable_mailbox = 1;
  }
  $self->{dbh}->finish();

  if ($enable_mailbox) {
    $dir = getPleskMailnamesDir() . "/$domainAsciiName/$mailName/Maildir";

    $self->{packer}->setMailBox( $mailId, $mailName, $domainAsciiName,
      ( $mail{'postbox'} =~ /true/ ? 'true' : 'false' ), $dir );
  }

  #
  # aliases
  #
  if ( PleskVersion::atLeast( 5, 0, 0 ) ) {
    $sql = "SELECT alias FROM mail_aliases WHERE mn_id=$mailId";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( $ptrRow = $self->{dbh}->fetchrow() ) {
        $self->{packer}->addMailAliase( $mailId, $ptrRow->[0] );
      }
    }
    $self->{dbh}->finish();
  }

  #
  # end aliases
  #

  #
  # mail forwarding
  #

  my @members;
  $sql = "SELECT address FROM mail_redir WHERE mn_id=$mailId";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( $ptrRow = $self->{dbh}->fetchrow() ) {
      if ( $ptrRow->[0] ne $mailName . "@" . $domainName ) {
        push @members, $ptrRow->[0];
      }
    }
  }
  $self->{dbh}->finish();

  my $forwarding_enabled = ($mail{'mail_group'} eq 'true');

  unless ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
    if ( $mail{'redir_addr'} ) {
      push @members, $mail{'redir_addr'};
    }
    $forwarding_enabled = $forwarding_enabled || ($mail{'redirect'} eq 'true');
  }

  $self->{packer}->setMailForwarding( $mailId, $forwarding_enabled, \@members );

  #
  # end mail forwarding
  #

  #
  # autoresponders
  #
  $dir = getPleskMailnamesDir() . "/$domainAsciiName/$mailName/\@attachments";

  my (@autos);
  $sql = "SELECT id FROM mail_resp WHERE mn_id=$mailId ORDER BY id";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( $ptrRow = $self->{dbh}->fetchrow() ) {
      push @autos, $ptrRow->[0];
    }
  }
  $self->{dbh}->finish();

  my %attaches;
  my @autoresponders;

  foreach $id (@autos) {
    my %item =
      makeAutoresponderNode( $self, $id, $mailName . "@" . $domainName );

    push @autoresponders, \%item;
  }

  my @filelist;
  my $util = AgentConfig::get("PRODUCT_ROOT_D") . "/admin/sbin/";
  if ( PleskVersion::atLeast( 6, 5, 0 ) ) {
    my $mn = $mailName;
    $mn =~ s/\&/\\&/g;
    $util .= "mailmng --list-attachments --domain-name=$domainName --mailname=$mn";
  }
  else {
    $util .= "list_files -1A '" . getPleskMailnamesDir()  . "/$domainAsciiName/$mailName/\@attachments/'";
  }

  my @filelist;
  open( LIST, "$util |" );
  while (<LIST>) {
    chomp;
    my ($file) = split /\t/;
    push @filelist, $file;
  }

  #clear list if utility failed
  unless ( close(LIST) ) {
    @filelist = ();
  }

  # Old Plesk utilities printed return code on stdout (#66905)
  unless ( PleskVersion::atLeast( 7, 1, 0 ) ) {
    if ( $#filelist != -1 && $filelist[$#filelist] =~ /^\d$/ ) {
      $#filelist -= 1;
    }
  }

  $self->{packer}->setMailAutoresponders( $mailId, $mailName, $domainAsciiName, $dir,
					  $mail{'autoresponder'}, \@autoresponders, \@filelist );

  #
  # end autoresponders
  #

  #
  # Addressbook
  #
  if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
    $self->makeAddressbookNode( $mailId, $mailName . '@' . $domainAsciiName );
  }

  #
  # Spamassassin
  #
  if ( PleskVersion::atLeast( 7, 0, 0 ) ) {

    $dir = getPleskMailnamesDir() . "/$domainAsciiName/$mailName/.spamassassin";
    $self->makeSpamassassinNode( $mailId, $mail{'mail_name'}, $domainAsciiName, $mail{'spamfilter'}, $dir );
  }

  #
  # End Spamassassin
  #

  if ( PleskVersion::atLeast( 8, 1, 1 ) ) {
    if ( $mail{'virusfilter'} ne 'none' ) {
      my %states_map = ( 'incoming' => 'in', 'outgoing' => 'out', 'any' => 'inout' );
      if ( defined( $states_map{ $mail{'virusfilter'} } ) ) {
        $self->{packer}->setMailVirusSettings( $mailId, $states_map{ $mail{'virusfilter'} } );
      }
    }
  }
  # FIXME: why so crude check here?
  elsif ( -e "/etc/drweb/users.conf" ) {
    my $state = $self->makeDrwebNode( $mailName . '@' . $domainName );
     $self->{packer}->setMailVirusSettings( $mailId, $state )  if (defined($state) and ($state ne 'none'));
  }

}

#
# makeAutoresponderNode
#
#  arguments:
#             $id - ID of autoreponder
#
#  return:
#             $root - XML node
#select * from clients;

sub makeAutoresponderNode {
  my ( $self, $autoId, $mailName ) = @_;

  my ( $name, $value, $sql, %auto, $ptrRow, $item );

  $sql = "SELECT * FROM mail_resp WHERE id=$autoId";
  unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $ptrRow = $self->{dbh}->fetchhash() ) ) {
    $self->{dbh}->finish();
    my $msg = "Broken referencial integrity: autoresponder id $autoId is not found in mail_resp";
    print STDERR "$msg\n";
    Packer::printToError( $msg );
    return;
  }

  %auto = %{ $ptrRow };
  $self->{dbh}->finish();

  #
  # forward
  #
  $sql = "SELECT address FROM resp_forward WHERE rn_id=$autoId";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    my (@list);
    while ( $ptrRow = $self->{dbh}->fetchrow() ) {

      # skip empty entries - found somewhere @ the wild Net
      push @list, $ptrRow->[0] if $ptrRow->[0];
    }
    if (@list) {
      $auto{'redirect'} = join( ',', @list );
    }
  }
  $self->{dbh}->finish();

  my @attach;
  $sql = "SELECT filename FROM resp_attach WHERE rn_id=$autoId";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( $ptrRow = $self->{dbh}->fetchrow() ) {
      push @attach, $ptrRow->[0];
    }
  }
  $self->{dbh}->finish();
  $auto{'attach'} = \@attach;
  return %auto;
}

sub makeAddressbookNode {
  my ( $self, $mailId, $mailName ) = @_;

  my @params;

  my $sql = "SELECT Db FROM mysql.db WHERE db='horde'";
  unless( $self->{dbh}->execute_rownum($sql) ){
    $self->{dbh}->finish();
    return;
  }
  $self->{dbh}->finish();

  $sql = "SHOW TABLES FROM horde LIKE 'turba_objects'";
  unless( $self->{dbh}->execute_rownum($sql) ) {
    $self->{dbh}->finish();
    return;
  }
  $self->{dbh}->finish();

  my $ptrHash;
  $sql = "SELECT * FROM horde.turba_objects WHERE owner_id = '$mailName'";
  if ($self->{dbh}->execute_rownum($sql) ) {
    while ( $ptrHash = $self->{dbh}->fetchhash() ) {
      push @params, $ptrHash;
    }
  }
  $self->{dbh}->finish();

  $self->{packer}->setMailAddressbook( $mailId, \@params );
}

sub getWebspaceId {
  my ( $self, $domainId) = @_;
 
  my $use_webspaces = PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb();

  return 0 unless $use_webspaces;

  my $www_root;
  my $webspace_id;
  my $sql = "SELECT webspace_id FROM domains WHERE id = $domainId";
  if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
    $webspace_id = $ptrRow->[0];
  }
  $self->{dbh}->finish();
  return $webspace_id;
}

sub getDomainWwwRoot {
  my ( $self, $domainId, $absolute) = @_;

  my $use_webspaces = PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb();
  if($use_webspaces){
    my $www_root;
    my $sql = "SELECT www_root FROM hosting WHERE dom_id = $domainId";
    if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
      $www_root = $ptrRow->[0];
    }
    $self->{dbh}->finish();
    if($absolute) {
      return $www_root;
    }
    my $parentDomainName;
    my $webspace_id = $self->getWebspaceId($domainId);
    if($webspace_id) {
      $parentDomainName = PleskStructure::getDomainAsciiNameFromId($webspace_id);
    }
    else {
      $parentDomainName = PleskStructure::getDomainAsciiNameFromId($domainId);
    }
    my $parentDomainRoot = $self->getWebspaceRoot($parentDomainName);
    $www_root = substr($www_root,length($parentDomainRoot));
    substr($www_root,0,1) = '' if substr($www_root,0,1) eq '/';
    return $www_root;
  }
  else {
    return ($absolute? $self->getWebspaceRoot( PleskStructure::getDomainAsciiNameFromId($domainId) ) . '/' : '' ) .'httpdocs';
  }
}

sub getSubDomainWwwRoot {
  my ( $self, $subdomainId, $absolute) = @_;

  my $domainId;
  my $subdomainName;
  my $sql = "SELECT dom_id, name FROM subdomains WHERE id = $subdomainId";
  if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
    $domainId = $ptrRow->[0];
    $subdomainName = $ptrRow->[1];
  }
  $self->{dbh}->finish();
  
  my $use_webspaces = PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb();
  if($use_webspaces){
    my $www_root;
    $sql = "SELECT www_root FROM subdomains WHERE id = $subdomainId";
    if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
      $www_root = $ptrRow->[0];
    }
    $self->{dbh}->finish();
    if($absolute) {
      return $www_root;
    }

    my $webspaceId = $self->getWebspaceId($domainId);
    $domainId = $webspaceId if $webspaceId;
    my $parentDomainRoot = $self->getWebspaceRoot( PleskStructure::getDomainAsciiNameFromId($domainId) );
    $www_root = substr($www_root,length($parentDomainRoot));
    substr($www_root,0,1) = '' if substr($www_root,0,1) eq '/';
    return $www_root;
  }
  else {
    return ($absolute? $self->getWebspaceRoot( PleskStructure::getDomainAsciiNameFromId($domainId) ) . '/' : '' ) .'subdomains/' . $subdomainName . '/httpdocs';
  }
}

sub getDomainCgiRoot {
  my ( $self, $domainId, $absolute) = @_;

  my $use_webspaces = PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb();
  if($use_webspaces){
    my $parentDomainName;
    my $webspace_id = $self->getWebspaceId($domainId);
    if($webspace_id) {
      $parentDomainName = PleskStructure::getDomainAsciiNameFromId($webspace_id);
    }
    else {
      $parentDomainName = PleskStructure::getDomainAsciiNameFromId($domainId);
    }
    my $parentDomainRoot = $self->getWebspaceRoot($parentDomainName);

    my $cgi_bin_mode;
    my $sql = "SELECT val FROM dom_param WHERE dom_id = $domainId AND param = 'cgi_bin_mode'";
    if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
      $cgi_bin_mode = $ptrRow->[0];
    }
    $self->{dbh}->finish();

    my $www_root;
    my $cgi_root;
    if($cgi_bin_mode eq 'www-root') {
      $sql = "SELECT www_root FROM hosting WHERE dom_id = $domainId";
      if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
        $www_root = $ptrRow->[0];
      }
      $self->{dbh}->finish();
      $cgi_root = $www_root . '/cgi-bin';
    }
    else {
      $cgi_root = $parentDomainRoot . '/cgi-bin';
    }
    if($absolute) {
      return $cgi_root;
    }
    $cgi_root = substr($cgi_root,length($parentDomainRoot));
    substr($cgi_root,0,1) = '' if substr($cgi_root,0,1) eq '/';
    return $cgi_root;
  }
  else {
    return ($absolute? $self->getWebspaceRoot( PleskStructure::getDomainAsciiNameFromId($domainId) ) . '/' : '' ) .'cgi-bin';
  }
}

sub getSubDomainCgiRoot {
  my ( $self, $subdomainId, $absolute) = @_;

  my $domainId;
  my $subdomainName;
  my $sql = "SELECT dom_id, name FROM subdomains WHERE id = $subdomainId";
  if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
    $domainId = $ptrRow->[0];
    $subdomainName = $ptrRow->[1];
  }
  $self->{dbh}->finish();
  
  my $use_webspaces = PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb();
  if($use_webspaces){
    my $webspaceId = $self->getWebspaceId($domainId);
    my $parentDomainRoot = $self->getWebspaceRoot( PleskStructure::getDomainAsciiNameFromId($webspaceId? $webspaceId : $domainId) );

    my $cgi_bin_mode;
    $sql = "SELECT val FROM dom_param WHERE dom_id = $domainId AND param = 'cgi_bin_mode'";
    if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
      $cgi_bin_mode = $ptrRow->[0];
    }
    $self->{dbh}->finish();

    my $www_root;
    my $cgi_root;
    if($cgi_bin_mode eq 'www-root') {
      $sql = "SELECT www_root FROM subdomains WHERE id = $subdomainId";
      if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ) {
        $www_root = $ptrRow->[0];
      }
      $self->{dbh}->finish();
      $cgi_root = $www_root . '/cgi-bin';
    }
    else {
      if($webspaceId) {
        $cgi_root = $parentDomainRoot . '/cgi-bin';
      }
      else {
        $cgi_root = $parentDomainRoot . '/subdomains/' . $subdomainName . '/cgi-bin';
      }
    }
    if($absolute) {
      return $cgi_root;
    }
    $cgi_root = substr($cgi_root,length($parentDomainRoot));
    substr($cgi_root,0,1) = '' if substr($cgi_root,0,1) eq '/';
    return $cgi_root;
  }
  else {
    return ($absolute? $self->getWebspaceRoot( PleskStructure::getDomainAsciiNameFromId($domainId) ) . '/' : '' ) .'subdomains/' . $subdomainName . '/cgi-bin';
  }
}

sub makePhostingNode {
  my ( $self, $ptrDomain, $ptrDomParams, $ptrSiteApplications, $is_site ) = @_;

  my @SiteApplications = @{$ptrSiteApplications};

  unless ( ref($ptrDomain) =~ /HASH/ ) {
    &Packer::printToError("Error: makePhostNode: bad arguments");
    return undef;
  }

  my (
    $domainName, $domainRoot, $path,    $sql,
    %hosting,    $domainId,   $xmlName, $fieldName,
    $id,         $item,       $ptrRow,  $ptrHash
  );
  $domainName = $ptrDomain->{'name'};
  $domainId   = $ptrDomain->{'id'};

  my %hostingParams;

  $sql = "SELECT * FROM hosting WHERE dom_id=$domainId";
  if ( ( $self->{dbh}->execute_rownum($sql) ) && ( $ptrHash = $self->{dbh}->fetchhash() ) ) {
    %hosting = %{ $ptrHash };
  }
  $self->{dbh}->finish();

  if ( defined( $hosting{'webstat'} ) and $hosting{'webstat'} ) {
    $hostingParams{'webstat'} = $hosting{'webstat'};
  }

  if ( defined( $hosting{'ssl'} ) and $hosting{'ssl'} ) {
    $hostingParams{'https'} = $hosting{'ssl'};
  }
  
  if ( PleskVersion::atLeast( 7, 5, 3 ) ) {
    if ( defined( $hosting{'same_ssl'} ) and ( $hosting{'same_ssl'} eq 'true' ) ) {
      $hostingParams{'shared-content'} = 'true';
    }
  }

  $hostingParams{'www-root'} = $self->getDomainWwwRoot($domainId);

  $sql = "SELECT param, val FROM dom_param WHERE dom_id='$domainId' "
         . "AND ( param='apacheErrorDocs' OR param='cgi_bin_mode')";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( $ptrRow = $self->{dbh}->fetchrow() ) {
      if ($ptrRow->[0] eq 'apacheErrorDocs') {
        $hostingParams{'errdocs'} = 'true' if ($ptrRow->[1] eq 'true');
      }
      elsif ($ptrRow->[0] eq 'cgi_bin_mode') {
        $hostingParams{'cgi_bin_mode'} = 'www-root' if ($ptrRow->[1] eq 'www-root');
      }
    }
  }
  $self->{dbh}->finish();
  
  if (defined( $hosting{'maintenance_mode'} )) {
    $hostingParams{'maintenance_mode'} = $hosting{'maintenance_mode'};
  }

  my %sysuser;

  #
  # sysuser
  #
  Logging::info("Dumping system user");

  if ( PleskVersion::atLeast( 5, 0, 0 ) ) {
    if ( $id = $hosting{'sys_user_id'} ) {
      %sysuser = $self->makePleskSysUserNode($id);
      $sysuser{'relative_path'} = $self->getDefaultWebspaceRoot( $domainName );
    }
  }
  else {
    %sysuser = $self->makeSyntheticSysUserNode( $hosting{'login'}, $hosting{'passwd'}, 'plain' );
  }
  #
  # end sysuser
  #

  my $domUser = lc( $sysuser{'login'} );
  my %scripting;

  #
  # scripting
  #
  while ( ( $xmlName, $fieldName ) = each(%hostingScripting) ) {
    next if $fieldName =~ /asp_dot_net/;
    if ( defined( $hosting{$fieldName} ) ) {
      if ( $hosting{$fieldName} eq 'true' or $hosting{$fieldName} eq 'false' ) {
          $scripting{$xmlName} = $hosting{$fieldName};
      }
      elsif ( $fieldName eq 'php_handler_type') {
        $scripting{$xmlName} = $hosting{$fieldName} if $hosting{$fieldName};
        $scripting{$xmlName} = 'isapi' if $scripting{$xmlName} eq 'module';
      }
    }
  }
  delete $scripting{'php_handler_type'} if exists $scripting{'php_handler_type'} and ( not exists $scripting{'php'} or $scripting{'php'} eq 'false');

  #
  # end scripting
  #

  $hostingParams{'wu_script'} = 'true' if exists $ptrDomParams->{'wu_script'} && $ptrDomParams->{'wu_script'} eq 'true';

  $hostingParams{'sitebuilder-site-id'} = $ptrDomParams->{'site_builder_site_id'} if exists $ptrDomParams->{'site_builder_site_id'};

  if ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
    my $published = $self->{packer}->getSb5SitePublished($domainName);
    $hostingParams{'sitebuilder-site-published'} = $published if defined $published;
  }


  my @sites;
  if (PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb()) {
    $sql = "SELECT name, displayName, id FROM domains WHERE webspace_id=$domainId";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
        push @sites, { %{$ptrHash} };
      }
    }
    $self->{dbh}->finish();
  }

  if ( !$self->{only_mail_dump} ) {
    $self->{packer}->setDomainPhosting( $domainId,
                                        \%hostingParams,
                                        $is_site? undef: \%sysuser,
                                        \%scripting
                                      );
  }
  else {
    if( @sites ) {
      $self->{packer}->setDomainPhostingEmpty( $domainId, $is_site? undef: \%sysuser );
    }
  }

  if ( !$self->{only_mail_dump} ) {
    #-----------------------------------------------------------------
    # Site applications content should not be included into dump
    # together with other website content.
    #-----------------------------------------------------------------
    my @exclude_httpdocs_files;
    push @exclude_httpdocs_files, 'plesk-stat';
    my @exclude_httpsdocs_files;
    push @exclude_httpsdocs_files, 'plesk-stat';
    my @exclude_cgi_bin_files;
    foreach my $sapp (@SiteApplications) {
      next if !$sapp->isDomainSiteapp($domainName);

      if ( $sapp->isSsl() ) {
        push @exclude_httpsdocs_files, $sapp->getHtdocsFiles();
      }
      else {
        push @exclude_httpdocs_files, $sapp->getHtdocsFiles();
      }
      push @exclude_cgi_bin_files, $sapp->getCgibinFiles();
    }

    # Hosting content dump
    my $webspaceId = $self->getWebspaceId($domainId); # used for Site content-related subs

    $domainRoot = $self->getDomainRoot($domainName);
    if ( -d $domainRoot and !$self->{configuration_dump} ) {
      my @exclude_vhost_files;
      my $httpdocsDir = $self->getDomainWwwRoot($domainId);
      push @exclude_vhost_files, "$httpdocsDir/plesk-stat";
      if ( -d $domainRoot . "/httpsdocs") {
        push @exclude_vhost_files, "httpsdocs/plesk-stat";
      }
      if(@sites) {
        foreach my $ptrHash (@sites) {
          my $siteHttpdocsDir = $self->getDomainWwwRoot($ptrHash->{'id'});
          push @exclude_vhost_files, "$siteHttpdocsDir/plesk-stat";
        }
      }

      $self->{packer}->setDomainPhostingFullContent( $domainId, $domainName, $domainRoot, { 'sysuser' => $domUser, 'exclude' => \@exclude_vhost_files } ) if $self->{dump_vhost};
    }
    my $domainWwwRoot = $self->getDomainWwwRoot($domainId, 'absolute');
    if ( -d $domainWwwRoot and !$self->{configuration_dump} ) {
      $self->{packer}->setDomainPhostingHttpdocsContent( $domainId, $domainName, $webspaceId? $webspaceId:$domainId, $domainWwwRoot, { 'sysuser' => $domUser, 'exclude' => \@exclude_httpdocs_files }  );
    }
    
    if ( -d $domainRoot . "/httpsdocs" and !$self->{configuration_dump} ) {
      $self->{packer}->setDomainPhostingHttpsdocsContent( $domainId, $domainName, "$domainRoot/httpsdocs", { 'sysuser' => $domUser, 'exclude' => \@exclude_httpsdocs_files } );
    }
    
    my $domainCgiRoot = $self->getDomainCgiRoot($domainId, 'absolute');
    if ( -d $domainCgiRoot and !$self->{configuration_dump} ) {
      $self->{packer}->setDomainPhostingCgibinContent( $domainId, $domainName, $webspaceId? $webspaceId:$domainId, $domainCgiRoot, { 'sysuser' => $domUser, 'exclude' => \@exclude_cgi_bin_files } );
    }
    if ( -d $domainRoot and !$self->{configuration_dump} ) {

      $self->{packer}->setDomainPhostingStatisticsContent( $domainId, $domainName, "$domainRoot/statistics" ) if $self->{dump_vhost};

      my $webstatRoot = ( PleskVersion::atLeast( 5, 0, 0 ) ) ?
                        $domainRoot . "/statistics/webstat"
                      : $domainRoot . "/httpdocs/webstat";

      $self->{packer}->setDomainPhostingWebstatContent( $domainId, $domainName, $webstatRoot );

      $self->{packer}->setDomainPhostingWebstatSslContent( $domainId, $domainName,"$domainRoot/statistics/webstat-ssl" );

      $self->{packer}->setDomainPhostingFtpstatContent( $domainId, $domainName, "$domainRoot/statistics/ftpstat" );

      $self->{packer}->setDomainPhostingLogsContent( $domainId, $domainName, "$domainRoot/statistics/logs" ) unless $self->{skip_logs};

      $self->{packer}->setDomainPhostingAnonFtpstatContent( $domainId, $domainName, "$domainRoot/statistics/anon_ftpstat" );

      $self->{packer}->setDomainPhostingErrdocsContent( $domainId, $domainName, "$domainRoot/error_docs", { 'sysuser' => $domUser }  );

      $self->{packer}->setDomainPhostingPrivateContent( $domainId, $domainName, "$domainRoot/private", { 'sysuser' => $domUser }  );

      if (PleskVersion::isSmb()) {
        $self->{packer}->setSappScriptsContentToPhosting($domainId, "$domainRoot/vault_scripts", "$domainRoot/vault_https_scripts");
      }
    }

    if ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
      if ( !$self->{configuration_dump} and !$self->{only_mail_dump} ) {
        $self->addSb5DomainContent( $domainId, $domainName, $ptrDomParams->{'site_builder_site_id'});
      }
      else {
        Packer::printToLog("Skip domain sitebuilder content dumping due to settings");
      }
    }

    #-----------------------------------------------------------------
    # Dump of installed site applications
    #-----------------------------------------------------------------
    foreach my $sapp (@SiteApplications) {
      if ( !$sapp->isDomainSiteapp($domainName) ) {
        next;
      }
      $self->dumpSiteApplication( $sapp, $domainRoot, $domainWwwRoot, $domainCgiRoot, $domainId, 'domain', $webspaceId);
    }

    unless ($is_site) {
      #
      # frontpage user
      #
      if ( exists $hosting{'fp_adm'} and $hosting{'fp_adm'} ) {
        $self->{packer}->setDomainFrontPageAdmin( $domainId, $hosting{'fp_adm'}, $hosting{'fp_pass'}, 'plain' );
      }
      elsif ( exists $hosting{'fp'} and $hosting{'fp'} eq 'true' and !$hosting{'fp_adm'} and !$hosting{'fp_pass'} ) {
        # Workaround for older Plesks: during upgrade 1.3 -> 2.0 (or something)
        # frontpage administrator records were lost
        my ( $fp_adm, $fp_pass, $fp_pass_type );
        if ( PleskVersion::atLeast( 5, 0, 0 ) ) {
          my %sysuser = $self->getSysUserInfo( $hosting{'sys_user_id'} );
          $fp_adm = lc( $sysuser{'login'} );
          if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
            ($fp_pass, $fp_pass_type ) = $self->makeAccountPasswordNode( $sysuser{'account_id'} );
          }
          else {
            $fp_pass      = $sysuser{'passwd'};
            $fp_pass_type = 'plain';
          }
        }
        else {
          $fp_adm       = $hosting{'login'};
          $fp_pass      = $hosting{'login'};
          $fp_pass_type = 'plain';
        }
        $self->{packer}->setDomainFrontPageAdmin( $domainId, $fp_adm, $fp_pass, $fp_pass_type );
      }
      #
      # end frontpage user
      #
    }

    $self->makeDomainLogrotationNode($domainId);
    $self->addAnonFtp( $domainId, $domainName, { 'sysuser' => $domUser } );

    #
    # protected dirs
    #
    if ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
      $sql = "SELECT id, path, realm, non_ssl, cgi_bin, `ssl` FROM protected_dirs WHERE dom_id=$domainId ORDER BY id";
      if ( $self->{dbh}->execute_rownum($sql) ) {
        my (@dirs);
        while ( $ptrRow = $self->{dbh}->fetchrow() ) {
          push @dirs, [ @{$ptrRow} ];
        }
        foreach $ptrRow (@dirs) {
          my $pdirId = $ptrRow->[0];
          my $pdirPath = $ptrRow->[1];
          my $pdirTitle = $ptrRow->[2];
          my $pdirNonSSL = $ptrRow->[3];
          my $pdirCGI  = $ptrRow->[4];
		  my $pdirSSL = $ptrRow->[5];
          $item = $self->makeProtDirNode( $domainId, $pdirId, $pdirPath, $pdirTitle, $pdirNonSSL, $pdirSSL, $pdirCGI);
        }
      }
      $self->{dbh}->finish();
    }
    else {
      if ( PleskVersion::atLeast( 8, 0, 0 ) ) {
        $sql = "SELECT id,path,realm,non_ssl,`ssl`,cgi_bin FROM protected_dirs WHERE dom_id=$domainId ORDER BY id";
      }
      elsif ( PleskVersion::atLeast( 6, 0, 0 ) ) {
        $sql = "SELECT id,path,realm,non_ssl,`ssl` FROM protected_dirs WHERE dom_id=$domainId ORDER BY id";
      }
      else {
        $sql = "SELECT id,path,realm FROM protected_dirs WHERE dom_id=$domainId ORDER BY id";
      }
      if ( $self->{dbh}->execute_rownum($sql) ) {
        my (@dirs);
        while ( $ptrRow = $self->{dbh}->fetchrow() ) {
          push @dirs, [ @{$ptrRow} ];
        }
        foreach $ptrRow (@dirs) {
          unless ( defined( $hosting{'ssl'} ) and $hosting{'ssl'} =~ /true/ ) {
            $ptrRow->[4] = 'false';    # bug 97159
          }
          $item = $self->makeProtDirNode( $domainId, @{$ptrRow} );
        }
      }
      $self->{dbh}->finish();
    }
    #
    # end protected dirs
    #

    unless ($is_site) {
      #
      # web users
      #
      $sql = "SELECT * FROM web_users WHERE dom_id=$domainId ORDER BY id";
      if ( $self->{dbh}->execute_rownum($sql) ) {
        my (@webs);
        while ( $ptrHash = $self->{dbh}->fetchhash() ) {
          push @webs, { %{$ptrHash} };
        }
        $self->{dbh}->finish();
        foreach $ptrHash (@webs) {
          $self->makeWebUserNode( $ptrHash, $domainId, $domainName );
        }
      }
      else {
        $self->{dbh}->finish();
      }
      #
      # end web users
      #
    }

    unless ($is_site) {
      #
      # ftpusers
      #
      if ( PleskVersion::atLeast(10, 0, 0) ) {
        $sql = "SELECT * FROM ftp_users WHERE dom_id=$domainId";
        if ( $self->{dbh}->execute_rownum($sql) ) {
          my (@ftps);
          while ( $ptrHash = $self->{dbh}->fetchhash() ) {
            push @ftps, { %{$ptrHash} };
          }
          $self->{dbh}->finish();
          foreach $ptrHash (@ftps) {
            $self->makeSubFtpUserNode( $ptrHash, $domainId, $domainName );
          }
        }
        else {
          $self->{dbh}->finish();
        }
      }
      #
      # end ftpusers
      #
    }

    #
    # subdomains
    #
    if ( PleskVersion::atLeast( 7, 0, 0 ) ) {
      $sql = "SELECT * FROM subdomains WHERE dom_id=$domainId ORDER BY id";

      my (@subdoms);
      if ( $self->{dbh}->execute_rownum($sql) ) {
        while ( $ptrHash = $self->{dbh}->fetchhash() ) {
          push @subdoms, { %{$ptrHash} };
        }
      }
      $self->{dbh}->finish();
      foreach $ptrHash (@subdoms) {
        $ptrHash->{'www-root'} = $self->getSubDomainWwwRoot($ptrHash->{'id'});
        $self->dumpSubDomain( $domainId, $ptrHash, $domainName, \@SiteApplications );
      }
    }
    #
    # end subdomains
    #
  }

  if(@sites) {
    foreach my $ptrHash (@sites) {
      $self->makeSiteNode($ptrHash->{'name'},$ptrHash->{'displayName'} );
    }
  }

  if ( !$self->{only_mail_dump} ) {
    #
    # configuration files (conf/vhost[_ssl].conf)
    #
    $domainRoot = $self->getDomainRoot($domainName);
    my $confDir      = $domainRoot . '/conf/';
    my $vhostConf    = 'vhost.conf';
    my $vhostSslConf = 'vhost_ssl.conf';
    my @confFiles;
    push @confFiles, $vhostConf    if ( -f $confDir . $vhostConf );
    push @confFiles, $vhostSslConf if ( -f $confDir . $vhostSslConf );

    $self->{packer}->setDomainPhostingConfContent( $domainId, $domainName, $confDir, { 'include' =>  \@confFiles } );
    #
    # end configuration files (conf/vhost[_ssl].conf)
    #

    #
    # Webalizer configuration
    #
    my ( $directRef, @hiddenRefs, @groupRefs );
    if ( PleskVersion::atLeast( 7, 5, 3 ) ) {
      my $sql = "SELECT referrer FROM webalizer_hidden_referrer "
              . " WHERE dom_id=$domainId";
      if ( $self->{dbh}->execute_rownum($sql) ) {
        while ( $ptrRow = $self->{dbh}->fetchrow() ) {
          if ( $ptrRow->[0] eq "Direct Request" ) {
            $directRef = 'true';
          }
          else {
            push @hiddenRefs, $ptrRow->[0];
          }
        }
      }
      $self->{dbh}->finish();

      $sql = "SELECT referrer, group_name FROM webalizer_group_referrer "
           . " WHERE dom_id=$domainId";
      if ( $self->{dbh}->execute_rownum($sql) ) {
        while ( $ptrRow = $self->{dbh}->fetchrow() ) {
          push @groupRefs, { 'ref' => $ptrRow->[0], 'name' => $ptrRow->[1] };
        }
      }
      $self->{dbh}->finish();

      $self->{packer}->setDomainWebalizer( $domainId, $directRef, \@hiddenRefs, \@groupRefs );
    }

    #
    # SiteBuilder connfiguration
    #

    if ( PleskVersion::atLeast( 8, 3, 0 ) ) {
      unless ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
        $self->makeDomainSiteBuilderNode($domainId);
      }
    }
    #
    # Perfomance
    #
    if (    ( defined( $hosting{'max_connection'} ) and $hosting{'max_connection'} )
         or ( defined( $hosting{'traffic_bandwidth'} ) and $hosting{'traffic_bandwidth'} )
       ) {
      $self->{packer}->setDomainPerfomance( $domainId,
                                            $hosting{'max_connection'},
                                            $hosting{'traffic_bandwidth'} );
    }

    if( exists $ptrDomParams->{'stat_ttl'} ){
      $self->{packer}->setDomainWebStat( $domainId, $ptrDomParams->{'stat_ttl'} );
    }
  }
}

sub makeDomainSiteBuilderNode () {
  my ( $self, $domainId ) = @_;

  my $sql = "SELECT sb_site_id, sb_siteowner_id, sb_siteowner_login FROM SBSites WHERE virtualHost_id = '$domainId'";

  my $enabled = 'false';
  my @sbSites;
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      push @sbSites, $ptrHash;
      $enabled = 'true';
    }
  }
  $self->{packer}->setDomainSitebuilder( $domainId, \@sbSites, $enabled );
  $self->{dbh}->finish();
}

#-----------------------------------------------------------------
#    Returns XmlNode of sapp-installed type or false
#-----------------------------------------------------------------
sub dumpSiteApplication( $ $ $ $ ) {
  my ( $self, $sapp, $vhost_path, $httpdocs_path, $cgibin_path, $parent, $parentType, $webspaceId ) = @_;
  my $proc = undef;
  if ( 'domain' eq $parentType ) {
    $proc = $self->getSubroutineRef("addDomainSapp");
  }
  elsif ( 'subdomain' eq $parentType ) {
    $proc = $self->getSubroutineRef("addSubDomainSapp");
  }
  else {
    Packer::printToError(
      "Error: dumpSiteApplication: Unexpected type of parent \"$parentType\"");
  }

  my $sapp_id = "$parentType:$parent:" . $sapp->getInstallPrefix();

  my $prefix = $sapp->getInstallPrefix();

  my $licenseType;
  if ( PleskVersion::atLeast( 8, 3, 0 ) ) {
    $licenseType = $sapp->getAPSClientItemLicenseType();
  }

  $self->{packer}->$proc(
    $parent,               $sapp_id,
    $sapp->getName(),      $sapp->getVersion(),
    $sapp->getRelease(),   $sapp->getDescription(),
    $sapp->isCommercial(), $sapp->isIntegrated(),
    $prefix,               $sapp->isSsl(),
    $licenseType
  );

  $self->{packer}->setSappParams( $sapp_id, $sapp );

  #-----------------------------------------------------------------
  # Linked resources
  #-----------------------------------------------------------------

  # Databases
  foreach my $row ( $sapp->getDatabases() ) {
    $self->makeDatabaseNode( $row->{'id'}, $parent, $parentType, $sapp_id, $row->{'param'} );
  }

  # Custom buttons
  foreach my $row ( $sapp->getCustomButtons() ) {
    $self->getCustomButtonById71( $row->{'id'}, $parent, $parentType, $sapp_id );

    #if ( ref($cb) =~ /XmlNode/ ) {
    #  $sapp_installed->{'ADDCHILD'}->($cb);
    #  push @{ $self->{skip_custom_buttons} }, $row->{'id'};
    #}
  }

  $self->{packer}->makeSiteAppInstalled( $sapp_id, $prefix, $sapp->isSsl() );

  if (PleskVersion::atLeast(10, 0, 0) and not PleskVersion::isSmb()) {
    $self->{packer}->setSappEntryPoints( $sapp_id, $sapp);
  }

  #-----------------------------------------------------------------
  # Site application content
  #-----------------------------------------------------------------
  # sapp-scripts
  {
    if ( 'domain' eq $parentType ) {
      $proc = $self->getSubroutineRef("setDomainSappScriptsContent");
    }
    elsif ( 'subdomain' eq $parentType ) {
      $proc = $self->getSubroutineRef("setSubDomainSappScriptsContent");
    }
    $self->{packer}->$proc(
      $parent,
      $sapp_id,
      "$vhost_path/vault_scripts/$prefix",
      "$vhost_path/vault_https_scripts/$prefix"
    );
  }

  # cgi-bin
  {
    if ( 'domain' eq $parentType ) {
      $proc = $self->getSubroutineRef("setDomainSappCgibinContent");
    }
    elsif ( 'subdomain' eq $parentType ) {
      $proc = $self->getSubroutineRef("setSubDomainSappCgibinContent");
    }
    $self->{packer}->$proc( $parent, $sapp_id, $cgibin_path . '/' . $prefix, $sapp->{cgi_bin}, $webspaceId );
  }

  # htdocs
  my $htdocs_dir;
  my $use_webspaces = PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb();

  if($use_webspaces) {
    $htdocs_dir = $httpdocs_path . '/' . $prefix;
  }
  else {
    $htdocs_dir = $vhost_path . ( $sapp->isSsl() ? "/httpsdocs/" : "/httpdocs/" ) . $prefix;
  }
  

  # add '.htpasswd' files to dump, bug 93109
  my ( $file, %files );
  if (defined $self->{htdocs} ) {
    @files{ @{ $sapp->{htdocs} } } = ();
    foreach $file ( @{ $sapp->getHtdocsFiles() } ) {
      if ( $file =~ /\.htaccess$/ ) {
        my $_file = $file;
        $_file =~ s/\.htaccess$/.htpasswd/;
        if ( !exists $files{$_file} && -e "$htdocs_dir/$_file" ) {
          push @{ $sapp->{htdocs} }, $_file;
        }
      }
    }
  }
  if ( 'domain' eq $parentType ) {
    $proc = $self->getSubroutineRef("setDomainSappHttpContent");
  }
  elsif ( 'subdomain' eq $parentType ) {
    $proc = $self->getSubroutineRef("setSubDomainSappHttpContent");
  }

  $self->{packer}->$proc( $parent, $sapp_id, $htdocs_dir, $sapp->{htdocs}, $webspaceId );
  
  if ($self->{include_app_distrib}) {
    my $distrib_path = AgentConfig::get("PRODUCT_ROOT_D") . "/var/apspkgarc";
    my $file_name = $self->getApsArchiveFileName($distrib_path, $sapp->getName()."-".$sapp->getVersion()."-".$sapp->getRelease()); # what about absent release???
    $self->{packer}->setServerSettings() unless defined $self->{packer}->{serverNode};
    $self->{packer}->addServerAppPackage($sapp->getName(), $sapp->getVersion(), $sapp->getRelease(), $distrib_path, $file_name);
  }
}

sub makeDomainLogrotationNode {
  my ( $self, $domainId ) = @_;

  if ( PleskVersion::atLeast( 5, 0, 0 ) ) {
    my $sql = "SELECT val FROM dom_param WHERE param = 'logrotation_id' "
             . "AND dom_id=$domainId";
    my $ptrRow;
    if ( $self->{dbh}->execute_rownum($sql) ) {
      $ptrRow = $self->{dbh}->fetchrow();
    }
    $self->{dbh}->finish();
    
    if ( defined $ptrRow ) {
      my $logrotation_id = $ptrRow->[0];
      my %logrot = $self->makeLogrotationNode($logrotation_id);
      $self->{packer}->setDomainLogrotation( $domainId, \%logrot );
    }
  }
}

sub makeLogrotationNode {
  my ( $self, $logrotation_id ) = @_;

  my $sql = "SELECT * FROM log_rotation WHERE id=$logrotation_id";

  my %logRotation = ();

  if ( $self->{dbh}->execute_rownum($sql) and my $hashPtr = $self->{dbh}->fetchhash() )
  {
    %logRotation = %{$hashPtr};
  }
  $self->{dbh}->finish();

  return %logRotation;
}

#-----------------------------------------------------------------
#    $ptrSubDomain - pointer to hash with subdomain's info (row of table 'subdomains')
#    $domainName - name of domain
#    $ptrSiteApplications - pointer to list of domain's site applications
#-----------------------------------------------------------------
sub dumpSubDomain( $ $ $ ) {
  my ( $self, $domainId, $ptrSubDomain, $domainName, $ptrSiteApplications ) =  @_;
  unless ( ref($ptrSubDomain) =~ /HASH/ ) {
    Packer::printToError("Error: dumpSubDomain: bad argumets");
    return undef;
  }

  my $subDomainName;
  my $subAsciiDomainName;
  if ( PleskVersion::atLeast( 7, 1, 0 ) ) {
    $subDomainName = $ptrSubDomain->{'displayName'};
    $subAsciiDomainName = $ptrSubDomain->{'name'};
  }
  else {
    $subDomainName = $ptrSubDomain->{'name'};
  }

  # Subdomains may be SSL-enabled since 8.0
  my $https = '';
  my $shared_content = '';
  
  if ( PleskVersion::atLeast( 8, 0, 0 ) ) {
    if ( 'true' eq $ptrSubDomain->{'ssl'} ) {
      $https = 'true';
    }
    $shared_content = $ptrSubDomain->{'same_ssl'};
  }

  # sysuser
  my %sysuser;
  if ( $ptrSubDomain->{'sys_user_type'} =~ /native/ ) {
    my %additionalSysuser;
    $additionalSysuser{'sys_user_id'} = $ptrSubDomain->{'sys_user_id'};
    $self->makeSubFtpUserNode(\%additionalSysuser, $domainId, $domainName);    
  }

  # scripting
  my %scripting;
  while ( my ( $xmlName, $fieldName ) = each(%subDomainScripting) ) {
    next if $fieldName =~ /asp_dot_net/;
    if ( $ptrSubDomain->{$fieldName} eq 'true' or $ptrSubDomain->{$fieldName} eq 'false' ) {
      $scripting{$xmlName} = $ptrSubDomain->{$fieldName};
    }
    elsif( $xmlName eq 'php_handler_type' ){
      $scripting{$xmlName} = $ptrSubDomain->{$fieldName} if $ptrSubDomain->{$fieldName};
      $scripting{$xmlName} = 'isapi' if $scripting{$xmlName} eq 'module';
    }
  }
  delete $scripting{'php_handler_type'} if exists $scripting{'php_handler_type'} and ( not exists $scripting{'php'} or $scripting{'php'} eq 'false');

  $self->{packer}->addSubDomain( $domainId, $ptrSubDomain, $subDomainName, $subAsciiDomainName, $https, $shared_content, \%sysuser, \%scripting );

  #-----------------------------------------------------------------
  # Site applications content should not be included into dump
  # together with other subdomain's content.
  #-----------------------------------------------------------------
  my @SiteApplications = @{$ptrSiteApplications};
  my @exclude_httpdocs_files;
  my @exclude_httpsdocs_files;
  my @exclude_cgi_bin_files;
  foreach my $sapp (@SiteApplications) {
    next if !$sapp->isSubdomainSiteapp( $ptrSubDomain->{'id'} );

    if ( $sapp->isSsl() ) {
      push @exclude_httpsdocs_files, $sapp->getHtdocsFiles();
    }
    else {
      push @exclude_httpdocs_files, $sapp->getHtdocsFiles();
    }
    push @exclude_cgi_bin_files, $sapp->getCgibinFiles();
  }

  # content
  
  my $webspaceId = $self->getWebspaceId($domainId); #used for Site content-related subs 
  
  my $subDomainRoot = $self->getWebspaceRoot($domainName) . "/subdomains/$subDomainName";

  my $subDomainWwwRoot = $self->getSubDomainWwwRoot($ptrSubDomain->{'id'}, 'absolute');
  if ( -d $subDomainWwwRoot and not $self->{configuration_dump} ) {
    $self->{packer}->setSubDomainHttpdocsContent( $ptrSubDomain->{'id'}, $subDomainName, $domainName, $subDomainWwwRoot, \@exclude_httpdocs_files, $webspaceId );
  }

  if ( -d $subDomainRoot."/httpsdocs" and not $self->{configuration_dump} ) {
    $self->{packer}->setSubDomainHttpsdocsContent( $ptrSubDomain->{'id'}, $subDomainName, $domainName, "$subDomainRoot/httpsdocs", \@exclude_httpsdocs_files );
  }
  

  my $subDomainCgiRoot = $self->getSubDomainCgiRoot($ptrSubDomain->{'id'}, 'absolute');
  if ( -d $subDomainCgiRoot and not $self->{configuration_dump} ) {
    $self->{packer}->setSubDomainCgibinContent( $ptrSubDomain->{'id'}, $subDomainName, $domainName, $subDomainCgiRoot, \@exclude_cgi_bin_files, $webspaceId );
  }
  if ( -d $subDomainRoot and not $self->{configuration_dump} ) {
    $self->{packer}->setSubDomainConfContent( $ptrSubDomain->{'id'}, $subDomainName, $domainName, "$subDomainRoot/conf" );

    if (PleskVersion::isSmb()) {
    	$self->{packer}->setSMBSubDomainSappScriptsContent($ptrSubDomain->{'id'}, "$subDomainRoot/vault_scripts", "$subDomainRoot/vault_https_scripts");
    }
  }

  # Dump of installed site applications
  foreach my $sapp (@SiteApplications) {
    if ( !$sapp->isSubdomainSiteapp( $ptrSubDomain->{'id'} ) ) {
      next;
    }
    $self->dumpSiteApplication( $sapp, $subDomainRoot, $subDomainWwwRoot, $subDomainCgiRoot, $ptrSubDomain->{'id'}, 'subdomain', $webspaceId);
  }
}

#
# makeWebUserNode
#
#  argumenets:
#       $ptrWebUser - pointer to hash with web user's info (row of table 'web_users')
#       $domainName - name of domain
#
#  return:
#       $root = XML node
#
sub makeWebUserNode {
  my ( $self, $ptrWebUser, $domainId, $domainName ) = @_;
  unless ( ref($ptrWebUser) =~ /HASH/ ) {
    &Packer::printToError("Error: makeWebUserNode: bad argumets");
    return undef;
  }
  my ( $home, $fileName, $userName, $item, $xmlName, $fieldName );

  my %sysuser;
  if ( PleskVersion::atLeast( 5, 0, 0 ) ) {
    %sysuser = $self->makePleskSysUserNode( $ptrWebUser->{'sys_user_id'} );
    $userName = $sysuser{'login'};
  }
  else {
    $userName = $ptrWebUser->{'username'};
    my @pwnam    = getpwnam($userName);
    my $password = $pwnam[1];
    %sysuser =
      $self->makeSyntheticSysUserNode( $userName, $password, 'encrypted' );
  }

  my $webUserHome = $self->getDomainRoot($domainName) . "/web_users/$userName";

  my $privateData = $self->getDomainRoot($domainName) . "/private/$userName";

  $self->{packer}->addDomainWebUser(
    $domainId,   $domainName,    \%sysuser,
    $ptrWebUser, $webUserHome, $privateData
  );
}

#
# makeSubFtpUserNode - make node for additionals ftp accounts
#

sub makeSubFtpUserNode {
  my ( $self, $ptrFtpUser, $domainId, $domainName ) = @_;

  my %sysuser = $self->makePleskSysUserNode($ptrFtpUser->{'sys_user_id'});

  $sysuser{'relative_path'} = $self->getDefaultWebspaceRoot($domainName);

  $self->{packer}->addDomainSubFtpUser($domainId, $domainName, \%sysuser);
}

#
# makeProtDirNode - make protected directory node
#
# arguments:
#        $pdirId - ID
#        $pdirPath - path of protected directory
#        $pdirTitle - header text
#        $pdirNonSSL - whether is directory in http
#        $pdirSSL - or in https
#
# return:
#        $root - XML node
#
sub makeProtDirNode {
  my ( $self, $domainId, $pdirId, $pdirPath, $pdirTitle, $pdirNonSSL, $pdirSSL, $pdirCGI ) = @_;

  my ( $sql, $userNode, $item, $ptrRow );

  # workaround of CLI inabliity to create '' directory.
  if ( $pdirPath eq '' ) {
    $pdirPath = '/';
  }

  if ( PleskVersion::atLeast( 7, 1, 0 ) ) {
    $sql = "SELECT p.login, a.password, a.type FROM pd_users p "
        . " LEFT JOIN accounts a ON p.account_id = a.id "
        . " WHERE p.pd_id=$pdirId ORDER BY p.id";
  }
  else {
    $sql = "SELECT p.login, p.passwd, 'plain' FROM pd_users p "
      . " WHERE p.pd_id=$pdirId ORDER BY p.id";
  }

  my @users;
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( $ptrRow = $self->{dbh}->fetchrow() ) {
      push @users,
        {
        'login'      => $ptrRow->[0],
        'passwd'     => $ptrRow->[1],
        'passwdType' => $ptrRow->[2]
        };
    }
  }
  $self->{dbh}->finish();
  $self->{packer}->addDomainProtectedDir( $domainId, $pdirPath, $pdirTitle, $pdirNonSSL, $pdirSSL, $pdirCGI, \@users );
}

sub addAnonFtp {
  my ( $self, $domainId, $domainName, $optPtr ) = @_;

  my ( $anonRoot, $ptrHash, $count, $domainRoot, $sql );
  $count = 0;
  $sql   = "SELECT * FROM anon_ftp WHERE dom_id=$domainId";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( $ptrHash = $self->{dbh}->fetchhash() ) {
      $domainRoot = $self->getDomainRoot($domainName);
      my ( $pub_path, $incoming_path );
      if ( -d $domainRoot ) {
        $pub_path      = "$domainRoot/anon_ftp/pub";
        $incoming_path = "$domainRoot/anon_ftp/incoming";
      }

      $self->{packer}->setDomainAnonFtp( $domainId, $domainName, $ptrHash, $pub_path, $incoming_path, $optPtr );

      $count++;
    }
  }
  $self->{dbh}->finish();
  return $count;
}

sub addSb5DomainContent {
  my ( $self, $domainId, $domainName, $uuid ) = @_;
  unless ( defined $uuid) {
    Logging::info( "addSb5DomainContent: domain $domainName site_builder_site_id is not found. skip");
    return;
  }
  $self->{packer}->setSb5DomainContent( $domainId, $domainName, $uuid);
}

sub getSysUserInfo {
  my ( $self, $sysUserId ) = @_;

  my $sql = "SELECT * FROM sys_users WHERE id=$sysUserId";
  my $hashPtr;
  unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $hashPtr = $self->{dbh}->fetchhash() ) ) {
    $self->{dbh}->finish();
    my $msg = "Broken referencial integrity: Sys user id $sysUserId is not found in sys_users";
    print STDERR "$msg\n";
    Packer::printToError( $msg );
    return;
  }
  my %sysuser = %{ $hashPtr };
  $self->{dbh}->finish();

  return %sysuser;
}

sub makePleskSysUserNode {
  my ( $self, $sysUserId ) = @_;
  my %sysuser = $self->getSysUserInfo($sysUserId);

  Logging::trace("Making system user node: $sysuser{'login'}");

  #
  # attributes
  #

  my $quota;
  if ( $sysuser{'quota'} ) {
    if ( PleskVersion::atLeast( 7, 0, 0 ) ) {
      $quota = $sysuser{'quota'};
    }
    else {
      if ( $sysuser{'quota'} ne "-1" ) {
        $quota = $sysuser{'quota'} * 1024 * 1024;
      }
    }
    $sysuser{'quota'} = $quota if $quota;
  }

  #
  # end attributes
  #

  #
  # password node
  #
  if ( PleskVersion::atLeast( 6, 0, 0 ) ) {
    if ( $sysuser{'account_id'} != 0 ) {
      ( $sysuser{'passwd'}, $sysuser{'passwdType'} ) = $self->makeAccountPasswordNode( $sysuser{'account_id'} );
    }
  }
  else {
    $sysuser{'passwd'}     = $sysuser{'passwd'};
    $sysuser{'passwdType'} = 'plain';
  }

  #
  # end password node
  #

  #
  # crontab node
  #
  if( ! $self->{configuration_dump} ) {
    my $crontabmng = AgentConfig::get("PRODUCT_ROOT_D") . "/admin/sbin/crontabmng";
    if ( -x $crontabmng ) {
      open( CRONTAB, "$crontabmng get $sysuser{'login'} |" );
      my $crontab = "";
      while (<CRONTAB>) {
        last if $_ eq "0\n";
        $crontab .= $_;
      }
      close(CRONTAB);
      if ( $crontab ne "\n" ) {
        $sysuser{'cron'} = $crontab;
      }
    }
  }

  #
  # end crontab node
  #

  return %sysuser;
}

sub makeSyntheticSysUserNode {
  my ( $self, $name, $password, $passtype ) = @_;

  my %sysuser;
  $sysuser{'login'}      = $name;
  $sysuser{'passwd'}     = $password;
  $sysuser{'passwdType'} = $passtype;
  return %sysuser;
}

sub makeFhostingNode {
  my ( $self, $ptrDomain ) = @_;

  return if ( $self->{only_mail_dump} );

  unless ( ref($ptrDomain) =~ /HASH/ ) {
    &Packer::printToError("Error: makeFhostingNode: bag arguments");
    return undef;
  }
  my ( $sql, $domainId, $forward, $rowPtr );
  $domainId = $ptrDomain->{'id'};
  $sql      = "SELECT redirect FROM forwarding WHERE dom_id=$domainId";
  unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $rowPtr = $self->{dbh}->fetchrow() ) ) {
    $self->{dbh}->finish();
    my $msg = "Broken referencial integrity: forward for domain " . $ptrDomain->{'name'} . " is not found in forwarding";
    print STDERR "$msg\n";
    Packer::printToError( $msg );
    return;
  }

  ($forward) = @{ $rowPtr };
  $self->{packer}->setDomainFhosting( $domainId, $forward);
  $self->{dbh}->finish();
}

sub makeShostingNode {
  my ( $self, $ptrDomain ) = @_;

  return if ( $self->{only_mail_dump} );

  unless ( ref($ptrDomain) =~ /HASH/ ) {
    &Packer::printToError("Error: makeShostingNode: bag arguments");
    return undef;
  }

  my ( $sql, $domainId, $forward, $rowPtr );
  $domainId = $ptrDomain->{'id'};

  $sql      = "SELECT redirect FROM forwarding WHERE dom_id=$domainId";
  unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $rowPtr = $self->{dbh}->fetchrow() ) ) {
    $self->{dbh}->finish();
    my $msg = "Broken referencial integrity: forward for domain " . $ptrDomain->{'name'} . " is not found in forwarding";
    print STDERR "$msg\n";
    Packer::printToError( $msg );
    return;
  }

  ($forward) = @{ $rowPtr };
  $self->{packer}->setDomainShosting( $domainId, $forward );
  $self->{dbh}->finish();
}

# sub getClientIps {
#   my ($clientId) = @_;

#   if (PleskVersion::atLeast(6, 0, 0)) {
#   } elsif (PleskVersion::atLeast(5, 0, 0)) {
#     my $sql = "SELECT d.name, d.id, h.ip_address, a.status, d.cl_id FROM domains d "
#       . "LEFT JOIN hosting h ON h.dom_id = d.id "
#       . "LEFT JOIN anon_ftp a ON a.dom_id = d.id"
#       . "WHERE cl_id = $clientId";
#   } else {
#   }
# }

#
# Returns password node for given account ID
#

sub _makePasswordData{
  my ($password, $type) = @_;

  if (!defined $password) {
        Packer::printToError( "'undef' password passed to _makePasswordData. Change to empty!" );
        $password = '';
  }

  if (!$password) {
	$type = 'plain';
  } else {
	if ($type ne 'plain') {
	  $type = 'encrypted';
	}
  }
  return ($password, $type);
}

my $tableAccounts = undef;

sub makeAccountPasswordNode {
  my ( $self, $accountId ) = @_;

  my ( $passwd, $type );
  my %values;

  if( not defined $tableAccounts ){
    if ( $self->{dbh}->execute( "SELECT id, password, type FROM accounts" ) ) {
       while( my $ptrRow = $self->{dbh}->fetchrow() ){
         $values{$ptrRow->[0]} = ();
         push @{$values{$ptrRow->[0]}}, $ptrRow->[1];
         push @{$values{$ptrRow->[0]}}, $ptrRow->[2];
      }
    }
    $self->{dbh}->finish();
    $tableAccounts = \%values;
  }

  if ( exists $tableAccounts->{$accountId} )
  {
    ( $passwd, $type ) = @{$tableAccounts->{$accountId}};
    ( $passwd, $type ) = _makePasswordData( $passwd, $self->{packer}->normalizePasswordType($type) );
  }
  else {

    # generates a stub node
    ( $passwd, $type ) = _makePasswordData();
  }
  $self->{dbh}->finish();

  return ( $passwd, $type );
}

my @_planItemNames;

sub getPlanItemNames {
  my ( $self ) = @_;

  if ( @_planItemNames ) {
    return \@_planItemNames;
  }

  my $planItemsPtr = $self->getPlanItems();
  foreach my $planItemPtr ( @{$planItemsPtr} ) {
    push @_planItemNames, $planItemPtr->{'name'};
  }

  return \@_planItemNames;
}

sub getSubscriptionProperties {
  my ( $self, $type, $id ) = @_;

  my @planItemNames = @{$self->getPlanItemNames()};
  
  my $subscriptionProperties = $self->getSubscriptionPropertiesHash($id, $type);

  foreach my $key (keys %{$subscriptionProperties}) {
      if ( grep $_ eq $key, @planItemNames) {
        my $templatePlanItemNode = $self->{packer}->makeTemplatePlanItem( $key,  $subscriptionProperties->{$key} );
        $self->{packer}->addToPreferences('domain', $id, $templatePlanItemNode) if defined $templatePlanItemNode;
      }
  }
}

sub getSubscriptionPropertiesHash {
  my ( $self, $id, $type ) = @_;

  my %subscriptionProperties;

  my $sql = "SELECT name, value FROM SubscriptionProperties INNER JOIN Subscriptions ON id=subscription_id  WHERE object_id='$id' AND object_type='$type'";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      $subscriptionProperties{$ptrHash->{'name'}} = $ptrHash->{'value'};
    }
  }
  $self->{dbh}->finish();

  return \%subscriptionProperties;
}

sub getSubscription
{
    my ( $self, $type, $id) = @_;
    my $subscriptionNode = $self->makeSubscriptionNode($type, $id);
    $self->{packer}->addToPreferences($type, $id, $subscriptionNode) if defined $subscriptionNode;
}

sub makeSubscriptionNode {
    my ( $self, $type, $id ) = @_;
    
    my $rowPtr;
    my $sql = "SELECT id, locked FROM Subscriptions WHERE object_id = '$id' AND object_type = '$type'";
	
	if (PleskVersion::atLeast( 10, 1, 0) and not PleskVersion::isSmb()) {
	  $sql .= " AND custom = 'false' ";
	}
	
    unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $rowPtr = $self->{dbh}->fetchrow() ) ){
      $self->{dbh}->finish();
      return;
    }
    my $subscription_id = $rowPtr->[0];
    my $locked = $rowPtr->[1];
    
    $self->{dbh}->finish();
    
    
    my %planQuantity;
    my $subscriptionNode = XmlNode->new( 'subscription', 'attributes' => {
                                         'locked' => $locked
    } );
    
    $sql = "SELECT plan_id, quantity FROM PlansSubscriptions WHERE subscription_id = '$subscription_id'";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( my $row = $self->{dbh}->fetchrow() ) {
        $planQuantity{$row->[0]} = $row->[1];
      }
    }
    $self->{dbh}->finish();

    while ( my ($planId, $quantity) = each(%planQuantity) ) {
      $sql = "SELECT uuid, type FROM Templates WHERE id = '$planId'";
      unless ( $self->{dbh}->execute_rownum($sql) ) {
        Logging::warning( "Error during getting uuid from plan '$planId'");
        $self->{dbh}->finish();
        next;
      }
      if ( my $row = $self->{dbh}->fetchrow() ) {
        my $planGuid = $row->[0];
        my $type = $row->[1];
        $self->{dbh}->finish();
        my $planNode = XmlNode->new( 'plan', 'attributes' => {
                                     'quantity' => $quantity,
                                     'plan-guid' => $planGuid
        } );
        if ($type eq 'domain_addon') {
          $planNode->setAttribute('is-addon', 'true');
        }
        $subscriptionNode->addChild($planNode);
      }
      $self->{dbh}->finish();
    }

    return $subscriptionNode;
}

sub makeSpamassassinNode {
  if ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
    makeSpamassassinNode100(@_);
  }
  elsif ( PleskVersion::atLeast( 8, 1, 0 ) ) {
    makeSpamassassinNode81(@_);
  }
  elsif ( PleskVersion::atLeast( 7, 0, 0 ) ) {
    my ( $self, $mailId, $mailNm, $domainAsciiname, $status, $dir ) = @_;
    $self->makeSpamassassinNode70( $mailId, $mailNm, $domainAsciiname, $dir );
  }
}

sub makeSpamassassinNode100 {
  my ( $self, $mailId, $mailNm, $domainAsciiname, $status, $dir ) = @_;

  my $mailname = $mailNm . '@' . $domainAsciiname;

  my $rowPtr;

  my $sql = "SELECT id, preferences FROM spamfilter WHERE username = '$mailname'";
  unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $rowPtr = $self->{dbh}->fetchrow() ) ) {
    $self->{dbh}->finish();
    Logging::warning(  "Error during getting information about spam filter for $mailname");
    return;
  }

  my $filter_id   = $rowPtr->[0];
  my $server      = 0;

  my $rownum;
  do {
    $self->{dbh}->finish();
    if ($server) {
      $sql = "SELECT id FROM spamfilter WHERE username = '*@*'";
      if ( ( $self->{dbh}->execute_rownum($sql) ) && ( my $row = $self->{dbh}->fetchrow() ) ) {
        $filter_id = $row->[0];
      }
      $self->{dbh}->finish();
    }

    $sql = "SELECT preference, value FROM spamfilter_preferences WHERE spamfilter_id = '$filter_id' ORDER BY length(preference), preference";
    $rownum = $self->{dbh}->execute_rownum($sql);
  } while ( !$rownum && !$server++ );

  my ( @blacckList, @whiteList, @unblackList, @unwhiteList );
  my ( $action, $requiredScore, $subj_text );

  while ( my $row = $self->{dbh}->fetchrow() ) {
    push @blacckList,  $row->[1] if $row->[0] eq "blacklist_from";
    push @whiteList,   $row->[1] if $row->[0] eq "whitelist_from";
    push @unblackList, $row->[1] if $row->[0] eq "unblacklist_from";
    push @unwhiteList, $row->[1] if $row->[0] eq "unwhitelist_from";
    $requiredScore = $row->[1] if ( $row->[0] eq "required_score" );
    $action = $row->[1] if ( $row->[0] eq "action" );

    if ( $row->[0] eq "rewrite_header" ) {
      $row->[1] =~ s/^subject //;
      $subj_text = $row->[1];
    }
  }
  $self->{dbh}->finish();

  if( $action ne 'delete' && $action ne 'move' ) {
    $action = 'mark';
  }
  
  my %spamServerSettings = undef;

  if ($mailname eq "*@*") {
    $sql = "SELECT * FROM misc WHERE param RLIKE '^spamfilter_'";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while (my $row = $self->{dbh}->fetchrow() ) {
        $spamServerSettings{$row->[0]} = $row->[1];
      }
    }
    $status = $spamServerSettings{'spamfilter_enabled'};
    $self->{dbh}->finish();
  }

  $self->{packer}->setMailSpamSettings(
    $mailId, $mailNm, $domainAsciiname,
    ( $status eq 'true' ? 'on' : 'off' ),
    undef, $action,
    $requiredScore, undef, $subj_text,
    \@blacckList,    \@whiteList,
    \@unblackList,   \@unwhiteList,
    $dir, (defined(%spamServerSettings) ? \%spamServerSettings : undef) 
  );
}

sub makeSpamassassinNode81 {
  my ( $self, $mailId, $mailNm, $domainAsciiname, $status, $dir ) = @_;

  my $mailname = $mailNm . '@' . $domainAsciiname;

  my $rowPtr;

  my $sql = "SELECT id, preferences, reject_spam FROM spamfilter WHERE username = '$mailname'";
  unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $rowPtr = $self->{dbh}->fetchrow() ) ) {
    $self->{dbh}->finish();
    Logging::warning(  "Error during getting information about spam filter for $mailname");
    return;
  }

  my $filter_id   = $rowPtr->[0];
  my $reject_spam = $rowPtr->[2];
  my $server      = 0;

  my $rownum;
  do {
    $self->{dbh}->finish();
    if ($server) {
      $sql = "SELECT id FROM spamfilter WHERE username = '*@*'";
      if ( ( $self->{dbh}->execute_rownum($sql) ) && ( my $row = $self->{dbh}->fetchrow() ) ) {
        $filter_id = $row->[0];
      }
      $self->{dbh}->finish();
    }

    $sql = "SELECT preference, value FROM spamfilter_preferences WHERE spamfilter_id = '$filter_id' ORDER BY length(preference), preference";
    $rownum = $self->{dbh}->execute_rownum($sql);
  } while ( !$rownum && !$server++ );

  my ( @blacckList, @whiteList, @unblackList, @unwhiteList );
  my ( $requiredScore, $subj_text );

  while ( my $row = $self->{dbh}->fetchrow() ) {
    push @blacckList,  $row->[1] if $row->[0] eq "blacklist_from";
    push @whiteList,   $row->[1] if $row->[0] eq "whitelist_from";
    push @unblackList, $row->[1] if $row->[0] eq "unblacklist_from";
    push @unwhiteList, $row->[1] if $row->[0] eq "unwhitelist_from";
    $requiredScore = $row->[1] if ( $row->[0] eq "required_score" );

    if ( $row->[0] eq "rewrite_header" ) {
      $row->[1] =~ s/^subject //;
      $subj_text = $row->[1];
    }
  }
  $self->{dbh}->finish();

  my %spamServerSettings = undef;

  if ($mailname eq "*@*") {
    $sql = "SELECT * FROM misc WHERE param RLIKE '^spamfilter_'";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while (my $row = $self->{dbh}->fetchrow() ) {
        $spamServerSettings{$row->[0]} = $row->[1];
      }
    }
    $status = $spamServerSettings{'spamfilter_enabled'};
    $self->{dbh}->finish();
  }

  $self->{packer}->setMailSpamSettings(
    $mailId, $mailNm, $domainAsciiname,
    ( $status eq 'true' ? 'on' : 'off' ),
    undef, ( $reject_spam eq 'true' ? 'delete' : 'mark' ),
    $requiredScore, undef, $subj_text,
    \@blacckList,    \@whiteList,
    \@unblackList,   \@unwhiteList,
    $dir, (defined(%spamServerSettings) ? \%spamServerSettings : undef) 
  );
}

sub makeSpamassassinNode70 {
  my ( $self, $mailId,, $mailNm, $domainAsciiname, $dir ) = @_;

  my $mailName = $mailNm . '@' . $domainAsciiname;

  my $rowPtr;

  my $sql = "SELECT count(*) FROM sa_conf WHERE mailname='$mailName'";
  unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $rowPtr = $self->{dbh}->fetchrow() ) ) {
    $self->{dbh}->finish();
    my $msg = "Broken referencial integrity: mailname $mailName is not found in sa_conf";
    print STDERR "$msg\n";
    Packer::printToError( $msg );
    return;
  }

  my @row = @{ $rowPtr };
  $self->{dbh}->finish();
  if ( $row[0] == 0 ) {
    return;
  }

  my $status        = undef;
  my $action        = undef;
  my $server_conf   = undef;
  my $requiredScore = undef;
  my $subj_text     = undef;
  my $hashPtr;

  $sql = "SELECT * FROM sa_conf WHERE mailname='$mailName'";
  unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $hashPtr = $self->{dbh}->fetchhash() ) ) {
    $self->{dbh}->finish();
    my $msg = "Broken referencial integrity: mailname $mailName is not found in sa_conf";
    print STDERR "$msg\n";
    Packer::printToError( $msg );
    return;
  }
  my %spam = %{ $hashPtr };
  $self->{dbh}->finish();

  my $item;
  if ( $spam{'flt_enabled'} eq 'both' ) {
    $status      = 'on';
    $server_conf = 'true';
  }
  elsif ( $spam{'flt_enabled'} eq 'user' ) {
    $status      = 'on';
    $server_conf = 'false';
  }
  else {

    #skip creation
    return;
  }
  $requiredScore = $spam{'hits_required'};
  if ( $spam{'spam_action'} eq 'save' ) {
    $action = 'mark';
  }
  else {
    $action = 'delete';
  }

  my ( $modify_subj );
  if ( $spam{'rw_subject_tag'} ) {
    $modify_subj = 'true';
    $subj_text   = $spam{'rw_subject_tag'};
  }
  else {
    $modify_subj = 'false';
  }

  my @blacckList = $self->addSpamassassinPatterns( $spam{'mailname'}, 'black' );
  my @whiteList =
    $self->addSpamassassinPatterns( $item, $spam{'mailname'}, 'white' );

  my @empty;
  $self->{packer}->setMailSpamSettings(
    $mailId,  $mailNm,  $domainAsciiname,
    $status, $server_conf, $action,
    $requiredScore, $modify_subj, $subj_text,
    \@blacckList, \@whiteList, \@empty, \@empty,
    $dir, undef
  );
}

sub addSpamassassinPatterns {
  my ( $self, $mailname, $color ) = @_;

  my @ret;

  my $rowPtr;

  my $sql = "SELECT pattern FROM sa_list WHERE mailname='$mailname' AND color = '$color'";
  if ($self->{dbh}->execute($sql)) {
    while ($rowPtr = $self->{dbh}->fetchrow()) {
      push @ret, $rowPtr->[0];
    }
    $self->{dbh}->finish();
    return @ret;
  } else {
    $self->{dbh}->finish();
    my $msg = "Broken referencial integrity: ${color}list patterns for $mailname is not found in sa_list";
    print STDERR "$msg\n";
    Packer::printToError( $msg );
    return @ret;  
  }

}

sub makeDrwebNode {
  my ( $self, $mailname ) = @_;
  my $drwebFile = "/etc/drweb/users.conf";
  unless ( open( DRWEBFILE, "<$drwebFile" ) ) {
    Packer::printToLog("Error: unable to open $drwebFile");
    return;
  }
  while (<DRWEBFILE>) {
    chomp;
    next if /^#/;      #skip comments
    next if /^\[/;
    next if /^\s*$/;
    my @parts = split /\s+/, $_, 4;
    if ( $parts[3] =~ /$mailname/ ) {
      if ( $parts[1] =~ /to/ ) {
        return 'in';
      }
      elsif ( $parts[1] =~ /from/ ) {
        return 'out';
      }
      else {
        return 'inout';
      }
      last;
    }
  }
  close(DRWEBFILE);
  return 'none';
}

sub makeWebAppNode {
  my ( $self, $domainId, $webAppName, $webAppStatus, $dom_name ) = @_;

  # webapp is useless without cid
  $webAppStatus ||= 0;
  my $warDir = AgentConfig::get("CATALINA_HOME") . "/psa-wars/$dom_name";

  $self->{packer}->addDomainTomcatWebApp( $domainId, $dom_name, $webAppName, $webAppStatus,
    $warDir );
}

sub getCustomButtonById71( $ ) {
  my ( $self, $id, $parent, $parentType, $sappId ) = @_;

  my $proc;
  if( $sappId ){
    if ( 'domain' eq $parentType ) {
      $proc = $self->getSubroutineRef("addDomainSappCustomButton");
    }
    elsif ( 'subdomain' eq $parentType ) {
      $proc = $self->getSubroutineRef("addSubDomainSappCustomButton");
    }
    else {
      Packer::printToError( "Error: getCustomButtonById71: Unexpected type of siteapp parent '$parentType'");
      return;
    }
  }
  else{
    if ( 'server' eq $parentType ) {
      $proc = $self->getSubroutineRef("addServerCustomButton");
    }
    elsif ( 'client' eq $parentType || 'reseller' eq $parentType ) {
      $proc = $self->getSubroutineRef("addClientCustomButton");
    }
    elsif ( 'domain-admin' eq $parentType ) {
      $proc = $self->getSubroutineRef("addDomainCustomButton");
    }
    elsif ( 'mailuser' eq $parentType ) {
      $proc = $self->getSubroutineRef("addMailCustomButton");
    }
    elsif ( 'sapp' eq $parentType ) {
      $proc = $self->getSubroutineRef("addSappCustomButton");
    }
    else {
      Packer::printToError("Error: getCustomButtonById71: Unexpected type of parent '$parentType'");
      return;
    }
  }

  my $options;

  # several times there was empty button place in database, which is incorrect
  my $sql = "SELECT * FROM custom_buttons WHERE id=$id AND place!=''";
  unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $options = $self->{dbh}->fetchhash() ) ) {
    $self->{dbh}->finish();
    return;
  }
  $self->{dbh}->finish();


  my $customButtonsDir = AgentConfig::get("PRODUCT_ROOT_D") . '/admin/htdocs/images/custom_buttons';

  my $icon = $options->{'file'};

  if( $sappId ){
    $self->{packer}->$proc( $parent, $sappId, $id, $options, $customButtonsDir, $icon );
  }
  else{
    if ( 'server' eq $parentType ) {
      $self->{packer}->$proc( $id, $options, $customButtonsDir, $icon );
    }
    else {
      $self->{packer}->$proc( $parent, $id, $options, $customButtonsDir, $icon );
    }
  }

}

sub getCustomButtonIdsByOwner71 {
  my ( $self, $owner_type, $owner_id ) = @_;

  my $level = $custom_button_owner_types{$owner_type};
  my $sql = "SELECT id FROM custom_buttons WHERE level='$level' AND level_id='$owner_id'";
  if ( !$self->{dbh}->execute_rownum($sql) ) {
    $self->{dbh}->finish();
    return;
  }

  my @ids;
  while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
    my $id = $ptrRow->[0];
    if ( grep /^$id$/, @{ $self->{skip_custom_buttons} } ) {
      next;
    }
    push @ids, $id;
  }
  $self->{dbh}->finish();

  return  @ids;
}

sub getCustomButtonsByOwner71 {
  my ( $self, $owner_type, $owner_id ) = @_;
  my @ids = $self->getCustomButtonIdsByOwner71($owner_type, $owner_id);
  return unless @ids;
  return  map { $self->getCustomButtonById71( $_, $owner_id, $owner_type ) } @ids;
}

sub getCustomButtonsByOwner65 {
  my ( $self, $owner_type, $owner_id ) = @_;

  return if ( $owner_type ne "client" );

  my $sql = "SELECT text, url, conhelp, options FROM custom_buttons WHERE "
    . "cl_id=$owner_id";

  my @nodes;

  if ( $self->{dbh}->execute_rownum($sql) ) {

    my $ptrHash;
    while ( $ptrHash = $self->{dbh}->fetchhash() ) {

      my $node = XmlNode->new( 'custom-button',
        'attributes' =>
          { 'sort-priority' => '100', 'interface-place' => 'domain' } );
      my %attributes = (
        'url'     => 'url',
        'text'    => 'text',
        'conhelp' => 'conhelp'
      );

      while ( my ( $dbfield, $nodefield ) = each %attributes ) {
        $node->{'ATTRIBUTE'}->( $nodefield, $ptrHash->{$dbfield} );
      }

      ## 128 == CUSTOM_BUTTON_PUBLIC
      parseCustomButtonOptions( $node, $ptrHash->{'options'} | 128 );

      push @nodes, $node;
    }
  }
  $self->{dbh}->finish();
  return @nodes;
}

sub getCustomButtonsByOwner {
  my $self = shift;
  return $self->getCustomButtonsByOwner71(@_)
    if ( PleskVersion::atLeast( 7, 1, 0 ) );
  return $self->getCustomButtonsByOwner65(@_)
    if ( PleskVersion::atLeast( 6, 5, 0 ) );
  return ();
}

sub makeDefaultIpNode {
  my ( $self, $parent ) = @_;
  my $sql = "SELECT ip_address FROM IP_Addresses ip, misc m WHERE ip.id = m.val AND m.param = 'def_ip_id'";
  if ( ( $self->{dbh}->execute_rownum($sql) ) && ( my $ptrHash = $self->{dbh}->fetchhash() ) ) {
    $self->{packer}->setServerDefaultIp( $ptrHash->{'ip_address'} );
  }
  $self->{dbh}->finish();
}

sub makeAdminInfoNode {
  my ( $self, $ptrAdmin ) = @_;
  my ( $name, $value );
  my $passwd = AgentConfig::get { 'password' };
  my ( $skin, $max_btn_len, $send_announce, $external_id );

  if( exists $ptrAdmin->{'admin_skin_id'} ){
     my $sql = "SELECT `name` FROM `Skins` where `id`=" . $ptrAdmin->{'admin_skin_id'};
     if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ){
       $skin = $ptrRow->[0];
     }
     $self->{dbh}->finish();
  }
  if( exists $ptrAdmin->{'max_button_length'} ){
    $max_btn_len = $ptrAdmin->{'max_button_length'};
         delete $ptrAdmin->{'max_button_length'}
  }
  if( exists $ptrAdmin->{'send_announce'} ){
    $send_announce = $ptrAdmin->{'send_announce'};
    delete $ptrAdmin->{'send_announce'};
  }
  if( exists $ptrAdmin->{'external_id'} ){
    $external_id = $ptrAdmin->{'external_id'};
    delete $ptrAdmin->{'external_id'};
  }

  my %gappsInfo;
  if (PleskVersion::atLeast( 10, 0, 0) and not PleskVersion::isSmb()) {
    my $adminId = PleskStructure::getAdminId();  
    my $sql = "SELECT param, val FROM cl_param WHERE cl_id = " . $adminId;
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
        $gappsInfo{ $ptrHash->{'param'} } = $ptrHash->{'val'};
      }
    }
	$self->{dbh}->finish();
  }
   
  $self->{packer}->setServerAdminInfo( $ptrAdmin, $passwd, $skin, $max_btn_len, $send_announce, $external_id, \%gappsInfo );
}

sub blockToNum {
  my ( $self, $mask ) = @_;
  my $longMask = unpack( "N", pack( "C4", split( /\./, $mask ) ) );
  my $block;
  for ( $block = 0 ; $block < 32 ; $block = $block + 1 ) {
    my $tmp = 2**( 32 - $block - 1 );
    last if !( $longMask & $tmp );
  }
  return $block;
}

my @_planItems;

sub getPlanItems {
  my ( $self) = @_;

  if ( @_planItems ) {
    return \@_planItems;
  }

  my $sql = "SELECT * from PlanItems";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( my $planItemPtr = $self->{dbh}->fetchhash() ) {
      push @_planItems, $planItemPtr;
    }
  }
  $self->{dbh}->finish();
  return \@_planItems;
}

sub dumpPlanItems {
  my ( $self) = @_;

  my @planItems = @{$self->getPlanItems()};
  if ( @planItems ) {
    my $customButtonsDir = AgentConfig::get("PRODUCT_ROOT_D") . '/admin/htdocs/images/custom_buttons';
    foreach my $planItemPtr ( @planItems ) {
      my $sql = "SELECT name, value from PlanItemProperties WHERE plan_item_id = '" . $planItemPtr->{'id'} . "'";
      if ( $self->{dbh}->execute_rownum($sql) ) {
        my %planItemProps;
        while ( my $planItemPropertyPtr = $self->{dbh}->fetchrow() ) {
          $planItemProps{$planItemPropertyPtr->[0]} = $planItemPropertyPtr->[1];
        }
        $self->{packer}->addPlanItemToServer($planItemPtr, \%planItemProps, $customButtonsDir);
      }
      $self->{dbh}->finish();
    }
  }
}

sub makeServerNode {
  my ( $self ) = @_;

  $self->{packer}->setServerSettings();

  #Dump server skeleton

  my $skeletonPath =
    AgentConfig::get('HTTPD_VHOSTS_D') . "/.skel/0";

  if ( !$self->{configuration_dump} ) {
    $self->{packer}->addServerSkeleton( $skeletonPath, "skel" );

    my $customTemplatesPath =  AgentConfig::get('PRODUCT_ROOT_D')."/admin/conf/templates/custom";
    if (-d $customTemplatesPath) {
      $self->{packer}->addServerCustomApacheTemplates($customTemplatesPath);
    }

    if ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
      if (!$self->{only_mail_dump} ) {
        $self->{packer}->setSb5ServerContent();

        $self->{packer}->addServerCustomHealthConfig();        
      }
    }

    if (PleskVersion::isSmb()) {
      my $sql = "SHOW TABLES LIKE 'smb_%'";
      $self->{dbh}->execute_rownum($sql);
      my @smb_tables;
      while (my $row = $self->{dbh}->fetchrow() ) {
        push @smb_tables, $row->[0];
      }
      $self->{dbh}->finish();

      my %options;
      $options{name} = $self->{dbh}->getName();
      $options{user} = $self->{dbh}->getUser();
      $options{type} = $self->{dbh}->getType();
      $options{password} = $self->{dbh}->getPassword();
      $options{tables} = \@smb_tables;
      $options{replace_and_no_create_info} = 1;
      $self->{packer}->addSmbDbDump(\%options);

      if (AgentConfig::sbRoot()) {
        $self->{packer}->addSbDump();
      }

      if (-d AgentConfig::get('HTTPD_VHOSTS_D')."/fs") {
        $self->{packer}->addFileSharingContent(AgentConfig::get('HTTPD_VHOSTS_D')."/fs");
      }

      if (-d AgentConfig::get('HTTPD_VHOSTS_D')."/fs-passwd") {
        $self->{packer}->addFileSharingPasswd(AgentConfig::get('HTTPD_VHOSTS_D')."/fs-passwd");
      }
    }
   
  }

  #Dump system and default ip
  my $sql = "SELECT ip_address, mask, iface, c.pvt_key pvtkey FROM IP_Addresses i INNER JOIN certificates c ON c.id = i.ssl_certificate_id";
  my %ips;
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      $ptrHash->{'type' } = PleskStructure::isExclusiveIp( $ptrHash->{'ip_address'} ) ? 'exclusive' : 'shared';
      $self->{packer}->addServerIp($ptrHash);
    }
  }
  $self->{dbh}->finish();

  $self->makeDefaultIpNode();

  #Dump hostname
  $sql = "SELECT val FROM misc WHERE param = 'FullHostName'";
  if ( ( $self->{dbh}->execute_rownum($sql) ) && ( my $ptrHash = $self->{dbh}->fetchhash() ) ) {
    $self->{packer}->setServerHostname("$ptrHash->{'val'}");
  }
  $self->{dbh}->finish();
  
  #Dump connection information to Billing
  $sql = "SELECT cp.param, cp.val FROM cl_param cp, clients c WHERE cp.cl_id = c.id AND c.login = 'admin'";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    my %connInfo;
    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      $connInfo{ $ptrHash->{'param'} } = $ptrHash->{'val'};
    }
    $self->{packer}->setPpbConnection(\%connInfo);
  }
  $self->{dbh}->finish();  

  #Dump admin info
  $sql = "SELECT param, val FROM misc WHERE param RLIKE '^admin' or param='send_announce' or param='max_button_length'"
    ;    # do not use LIKE, bug #106566
  my %admin;
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      $admin{ $ptrHash->{'param'} } = $ptrHash->{'val'};
    }
  }
  $self->{dbh}->finish();

  my $adminId = PleskStructure::getAdminId();
  $sql = "SELECT external_id FROM clients WHERE id = $adminId";

  if ( $self->{dbh}->execute($sql) ) {
    if (my $ptrHash = $self->{dbh}->fetchhash() ) {
      $admin{'external_id'} = $ptrHash->{'external_id'};
    }
  }
  $self->{dbh}->finish();

  $self->makeAdminInfoNode( \%admin );

  # Dump database servers
  if ( PleskVersion::atLeast( 8, 0, 0 ) ) {
    $sql = "SELECT param, val FROM misc WHERE param RLIKE '^default_server_'";
    my %def;

    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
        $def{ $ptrHash->{'param'} } = $ptrHash->{'val'};
      }
    }
    $self->{dbh}->finish();

    $sql = "SELECT id, host, port, type, admin_login, admin_password FROM DatabaseServers";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
        my $param = 'default_server_' . $ptrHash->{'type'};
        my $default = exists $def{$param} && $def{$param} == $ptrHash->{'id'};

        my $passwd;
        if ( $ptrHash->{'type'} eq 'mysql'
          && $ptrHash->{'host'} eq 'localhost' )
        {
          $passwd = AgentConfig::get('password');
        }

        $self->{packer}->adddServerDb( $ptrHash, $passwd, $default );
      }
    }
    $self->{dbh}->finish();
  }

  my %params;

  if ( !$self->{shallow_dump} ) {

    if( !$self->{configuration_dump} ){
      # Dump key info
      if ( PleskVersion::atLeast( 9, 0, 0 ) ) {
         my $phpini = AgentConfig::get('PRODUCT_ROOT_D') . "/admin/conf/php.ini";
         my $swkeyrepo = '/etc/sw/keys';
         if( -e $phpini ){
                open PHPINI, $phpini;
                while (<PHPINI>) {
                  chomp;
                  next if /^#/;
                  next if /^$/;
                  if ( $_ =~ /swkey\.repository_dir\s*=\s*[\"\']?(.+)[\"\']\s*$/) {
                    $swkeyrepo = $1;
                    Packer::printToLog( "Found sw key repository: $swkeyrepo" );
                    last;
                  }
               }
               close PHPINI;
         }
         if( -d "$swkeyrepo/keys" ){
            my $keyDir = "$swkeyrepo/keys";
            Packer::printToLog( "Load keys from '$keyDir'" );
            opendir DIR, "$keyDir";
            my @files = readdir( DIR );
            closedir DIR;
            foreach my $key(@files){
               if( $key ne '.' and $key ne '..' and -f "$keyDir/$key" ){
	         my $cmd = getLicenseCommand() . " --get-instance-id $keyDir/$key";
		 my $exec = `$cmd`;
		 chomp($exec);
                 $self->{packer}->addServerKey( $key, $key, $keyDir, 'false', $exec);
              }
           }
         }
         else{
           Packer::printToError( "Keys directory '$swkeyrepo/keys' is not found. The keys are not included to backup." );
         }


      }
      else{
        $sql = "SELECT * FROM key_history WHERE filename IS NOT NULL";
        if ( $self->{dbh}->execute_rownum($sql) ) {
          while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
            next if ( $ptrHash->{'filename'} =~ /\~$/ );
            my @keyPath = split( /\//, $ptrHash->{'filename'} );
            my ( $keyName, $keyDir, $additional );
            if ( $keyPath[3] eq 'key.d' ) {
              $keyName    = $keyPath[4];
              $keyDir     = "/etc/psa/key.d/";
              $additional = "true";
            }
            else {
              $keyName    = $keyPath[3];
              $keyDir     = "/etc/psa/";
              $additional = "false";
            }

            if ( -e $keyDir . "/" . $keyName ) {
              $self->{packer}->addServerKey( $ptrHash->{'plesk_key_id'}, $keyName, $keyDir, $additional );
            }
          }
        }
        $self->{dbh}->finish();
      }
    }

    my $ptrRow;
    if ( $self->{dbh}->execute_rownum('SELECT param, val FROM misc') ) {
      while ( $ptrRow = $self->{dbh}->fetchrow() ) {
        $params{ $ptrRow->[0] } = $ptrRow->[1];
      }
    }
    $self->{dbh}->finish();

    my @unessential_params = (
      'spf_rules',         'spf-guess',
      'spf-exp',           'rbl_server',
      'spf_behavior',      'disable_pop_auth',
      'disable_smtp_auth', 'spf_enabled',
      'use_vocabulary',    'allow_short_pop3_names',
      'dns_zone_status'
    );

    foreach my $param (@unessential_params) {
      if ( !exists $params{$param} ) {
        $params{$param} = '';
      }
    }

    # TODO: implement server settings backups for earlier Plesks
    if ( PleskVersion::atLeast( 8, 1, 0 ) ) {

      # Dump server settings

      $params{'rbl_server'} =~ s/;/,/g;

      # Dump mail settings
      my $mailmng = AgentConfig::get('PRODUCT_ROOT_D') . '/admin/sbin/mailmng';
      my $letter_size = `$mailmng --get-max-letter-size`;
      chomp($letter_size);

      my @blackList;
      my %whiteList;

      # black list
      if ( $self->{dbh}->execute_rownum('SELECT domain FROM badmailfrom') ) {
        while ( $ptrRow = $self->{dbh}->fetchrow() ) {
          push @blackList, $ptrRow->[0];
        }
      }
      $self->{dbh}->finish();

      # white list
      if ( $self->{dbh}->execute_rownum('SELECT ip_address, ip_mask FROM smtp_poplocks') ) {
        while ( $ptrRow = $self->{dbh}->fetchrow() ) {
          $whiteList{$ptrRow->[0]} = $ptrRow->[1];
        }
      }
      $self->{dbh}->finish();

      $self->{packer}->setServerMail( $letter_size, \%params, \@blackList, \%whiteList );

      # dump External Webmails

      my @externalWebmails;

      if (PleskVersion::atLeast(10, 1, 0) and not PleskVersion::isSmb()) {
        if ( $self->{dbh}->execute_rownum('SELECT name, url, enabled FROM externalWebmails ')) {
          while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
            push @externalWebmails, $ptrHash;
          }
        }

        $self->{dbh}->finish();

        if (scalar(@externalWebmails) > 0) {
          $self->{packer}->setExternalWebmails(\@externalWebmails);
        }

      }

      # dump dns
      my @records;
      if ( $self->{dbh}->execute_rownum('SELECT * FROM dns_recs_t') ) {
        while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
          push @records, $ptrHash;
        }
      }
      $self->{dbh}->finish();

      $self->{packer}->setServerDNS( \%params, \@records );

      # dump certificates
      my @ids;
      my $cert_id = $params{'cert_rep_id'};
      $sql = "SELECT c.id FROM certificates c, Repository r "
        . "WHERE c.id=r.component_id AND r.rep_id='$cert_id' ORDER BY c.id";

      if ( $self->{dbh}->execute_rownum($sql) ) {
        while ( $ptrRow = $self->{dbh}->fetchrow() ) {
          push @ids, $ptrRow->[0];
        }
      }
      $self->{dbh}->finish();

      my $defaultCert;
      foreach my $id (@ids) {
        if ( $params{'default_certificate_id'} == $id ) {
          $defaultCert = 1;
        }
        else {
          $defaultCert = 0;
        }
        $self->makeCertificateNode( $id, 0, 'server', $defaultCert );
      }

      $self->{packer}->addPanelCertificate();

      # dump custom buttons
      $self->getCustomButtonsByOwner( 'server', 0 );

      $self->{packer}->setControlsVisibility( \%params );

      if (PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb()) {
        my $adminId = PleskStructure::getAdminId();
        $self->dumpTemplates( $adminId, 'server', 'domain' );
        $self->dumpTemplates( $adminId, 'server', 'domain_addon' );
        $self->dumpTemplates( $adminId, 'server', 'reseller' );
        if (PleskVersion::atLeast( 10, 1, 0 )) {
          $self->dumpPlanItems();
        }
      }
      else {
        # dump domain templates
        $self->dumpTemplates9x( 0, 'server', $params{'dom_tmpl_list_id'}, 'domain' );

        # dump reseller templates
        $self->dumpTemplates9x( 0, 'server', $params{'res_tmpl_list_id'}, 'reseller' );
      }

    }

    if ( PleskVersion::atLeast( 8, 2, 0 ) ) {

      # dump SSO

      if (defined $params{'sso_server'}) {
        my $cert;
        if ( defined $params{'sso_certificate'}
          && -r $params{'sso_certificate'} )
        {
          open( CERT, $params{'sso_certificate'} );
          read( CERT, $cert, -s CERT );
          close(CERT);
        }

        my $idpCert;
        if ( defined $params{'sso_idp_cert'} && -r $params{'sso_idp_cert'} ) {
          open( IDPCERT, $params{'sso_idp_cert'} );
          read( IDPCERT, $idpCert, -s IDPCERT );
          close(IDPCERT);
        }

        $self->{packer}->setServerSSO( \%params, $cert, $idpCert );

        if ( PleskVersion::atLeast( 8, 3, 0 ) ) {

          # SSO branding
          my @branding;
          $sql = "SELECT * FROM SSOBranding";
          if ( $self->{dbh}->execute_rownum($sql) ) {
            while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
              push @branding, $ptrRow;
            }
          }
          $self->{dbh}->finish();
          $self->{packer}->setServerSSOBranding( \@branding );
        }
      }
      # Dump misc parameters
    }
  }

  if ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
    $self->{packer}->setServerAppVault();
    $self->{packer}->setServerPackagesPool();

    $sql = "SELECT `name`, `version`, `release`, `cacheId`, `isUploaded`, `isVisible` FROM smb_apsPackages";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
        my $distrib_path = AgentConfig::get("PRODUCT_ROOT_D") . "/var/apspackages/" . $ptrRow->[3] ;
        my $file_name = $ptrRow->[3] . ".app.zip";
        $self->{packer}->addServerAppPackage( $ptrRow->[0], $ptrRow->[1], $ptrRow->[2], $distrib_path, $file_name, $ptrRow->[4], $ptrRow->[5] );
      }
    }
    $self->{dbh}->finish();
  }
  elsif ( PleskVersion::atLeast( 8, 3, 0 ) ) {

    if (!PleskVersion::isSmb()) {
      $self->{packer}->setServerAppVault();

      my $distrib_path = AgentConfig::get("PRODUCT_ROOT_D") . "/var/apspkgarc";

      $self->{packer}->setServerPackagesPool();

      $sql = "SELECT `name`, `version`, `release` FROM SiteAppPackages";
      if ( $self->{dbh}->execute_rownum($sql) ) {

        while ( my $ptrRow = $self->{dbh}->fetchrow() ) {

          my $distrib_name =
              $ptrRow->[0] . "-"
            . $ptrRow->[1] . "-"
            . $ptrRow->[2];  # What if 1 and 2 does not exist, or they exist always?

          my $file_name = $self->getApsArchiveFileName($distrib_path, $distrib_name);
          $self->{packer}->addServerAppPackage( $ptrRow->[0], $ptrRow->[1], $ptrRow->[2], $distrib_path, $file_name );
        }
      }
      $self->{dbh}->finish();

      #Application Item

      $self->{packer}->setServerAppItemsPool();

      my $aishared = "ai.shared";
      if ( PleskVersion::atLeast( 9, 0, 0 ) ) {
        $aishared = "ai.disabled";
      }
      $sql = "SELECT lt.license_type_hash AS license_type_id, sap.name AS sapp_name, sap.version AS sapp_version, sap.release AS sapp_release, $aishared AS shared, ai.disabled AS disabled "
        . "FROM APSApplicationItems AS ai INNER JOIN SiteAppPackages sap ON (sap.id = ai.pkg_id) "
        . "LEFT JOIN APSLicenseTypes AS lt ON (lt.id = ai.license_type_id)";
      if ( $self->{dbh}->execute_rownum($sql) ) {

        while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
          $self->{packer}->addServerAppItem($ptrHash);
        }
      }
      $self->{dbh}->finish();

      #Application License

      $self->{packer}->setServerAppLicensesPool();

      $sql = "SELECT l.key_number, l.source, lt.license_type_hash FROM APSLicenses l, APSLicenseTypes lt WHERE l.license_type_id = lt.id";
      if ( $self->{dbh}->execute_rownum($sql) ) {

        while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
          $self->{packer}->addServerAppLicense( $ptrRow->[0], $ptrRow->[2], $ptrRow->[1] );
        }
      }
      $self->{dbh}->finish();

    }
    else{
      my $distrib_path = AgentConfig::get("PRODUCT_ROOT_D") . "/admin/smb/application/apspackages/";
      $self->{packer}->addApsCache($distrib_path) if !$self->{configuration_dump} ;
    }
  }
  
  if ( PleskVersion::atLeast( 8, 3, 0 ) ) {
      unless ( PleskVersion::atLeast( 10, 0, 0 ) and not PleskVersion::isSmb() ) {
      # SiteBuilder Config
      $sql = "SELECT * FROM SBConfig";
      if ( $self->{dbh}->execute_rownum($sql) ) {
        my @config;
        while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
          push @config, $ptrHash;
        }
        $self->{packer}->setServerSBConfig( \@config );
      }
      $self->{dbh}->finish();
    }
  }

  $self->{packer}->setServerBackupSettings( \%params );

  $self->dumpServerPreferences( \%params );

  $self->makeSpamassassinNode(undef, "*", "*", undef, undef);

  # Server wide grey-listing preferences
  my (%glParams, $row);
  if ( $self->{dbh}->execute_rownum('SELECT param, value FROM GL_settings') ) {
    while ( $row = $self->{dbh}->fetchrow() ) {
      $glParams{ $row->[0] } = $row->[1];
    }
  }
  $self->{dbh}->finish();
  $self->{dbh}->execute_rownum('SELECT * FROM GL_remote_domains');
  while ( $row = $self->{dbh}->fetchrow() ) {
   push @{$glParams{ 'white_domains' }}, $row->[0] if $row->[1] eq 'white' ;
   push @{$glParams{ 'black_domains' }}, $row->[0] if $row->[1] eq 'black';
  }
  $self->{dbh}->finish();

  $glParams{'personal-conf'} = defined($params{'spamfilter_use_mailuser_prefs'}) ? $params{'spamfilter_use_mailuser_prefs'} : "false";

  $self->{packer}->setGLServerSettings(\%glParams);

  my $virusfilterNode = $self->{packer}->addServerVirusfilter($params{'virusfilter'});

  if ($params{'virusfilter'} eq 'kav') {
    $self->setServerVirusfilterKav($virusfilterNode) if defined $virusfilterNode;
    my $kavSettingsNode = $self->{packer}->addServerKavSettings();
    $self->setServerKavSettings($kavSettingsNode) if defined $kavSettingsNode;
  }

  if (PleskVersion::atLeast(10, 0, 0) and not PleskVersion::isSmb()) {
    $self->{packer}->setServerColdFusion(\%params);
  }

  if (PleskVersion::atLeast(10, 0, 0) and not PleskVersion::isSmb()) {
    $self->{packer}->setServerGappsInfo(\%params);
  }

  if (PleskVersion::atLeast(10, 0, 0) and not PleskVersion::isSmb()) {
    #Dump event handler
    $self->{dbh}->execute_rownum('SELECT * FROM actions');
    my @actions;
    while (my $ptrHash = $self->{dbh}->fetchhash()) {
      push @actions, $ptrHash;
    }
    $self->{dbh}->finish();

    $self->{dbh}->execute_rownum('SELECT a.name, eh.priority, eh.user, eh.command, eh.action_id FROM event_handlers eh, actions a WHERE eh.action_id = a.id');
    my %events;
    while (my $ptrHash = $self->{dbh}->fetchhash()) {
      push @{$events{$ptrHash->{'action_id'}}}, $ptrHash;
    }
    $self->{dbh}->finish();
    $self->{packer}->setServerEventHandler(\%events, \@actions, \%params);
  }

  if (PleskVersion::atLeast(10, 0, 0) and not PleskVersion::isSmb()) {
    $self->{packer}->addServerSiteIsolationConfig();
    $self->dumpServerNotifications();
    $self->{packer}->addServerMailmanConfiguration();
  }
  
  if (PleskVersion::atLeast(10, 1, 0) and not PleskVersion::isSmb()) {
    $self->{packer}->makeBrandingThemeNode('admin', undef);
  }
}

sub dumpServerNotifications {
  my ( $self ) = @_;

  my $expirationWarnDays = 1;

  my $sql = "SELECT val FROM misc WHERE param = 'exp_warn_time'";
  if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow() ){
    $expirationWarnDays = $ptrRow->[0];
  }
  $self->{dbh}->finish();  

  my @notifications;
  $sql = "SELECT * FROM Notifications";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while (my $ptrHash = $self->{dbh}->fetchhash()) {
      push @notifications, $ptrHash;
    }
  }
  $self->{dbh}->finish();

  my %notes;
  $sql = "SELECT id, text FROM Notes";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while (my $ptrRow = $self->{dbh}->fetchrow()) {
      $notes{ $ptrRow->[0] } = $ptrRow->[1];
    }
  }
  $self->{dbh}->finish();

  $self->{packer}->addServerNotifications( $expirationWarnDays, \@notifications, \%notes );
}

sub dumpServerPreferences {
  my ( $self, $params ) = @_;

  $self->{packer}->addServerPreferences( $params );

  if (PleskVersion::atLeast(9, 0, 0) and not PleskVersion::isSmb()) {
    my $sql = "SELECT type, netaddr, netmask FROM cp_access";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
        $self->{packer}->addRestrictionItem( $ptrRow->[0], $ptrRow->[1], $ptrRow->[2] );
      }
    }
    $self->{dbh}->finish();
  }
  
  if ( PleskVersion::atLeast(10, 1, 0) and not PleskVersion::isSmb()) {
    if (defined($params->{'disable_mail_ui'})) {
      $self->{packer}->addDisableMailUiOption($params->{'disable_mail_ui'});
    }
    if (defined($params->{'crontab_secure_shell'}) || defined($params->{'crontab_secure_shell_compatibility_mode'})) {
      $self->{packer}->addCrontabSecureSettings($params);
    }
  }
}

sub setServerVirusfilterKav() {
  my ( $self, $virusfilterNode ) = @_;
  
  my $table;
  my $sql = "SHOW TABLES LIKE 'module_kav_parameters'";
  if ( $self->{dbh}->execute_rownum( $sql ) and $self->{dbh}->fetchrow() ) {
    $table = 1;
  }
  $self->{dbh}->finish();
  
  if ( $table ) {
    my %parameters;
    my %db2xmlAttr = ( 'server_scan_direction' => 'state', 
                       'allow_custom_settings' => 'allow-personal'
                     );

    my %db2xmlValue = ( 'server_scan_direction' => {
                          'any' => 'inout',
                          'incoming' => 'in',
                          'outgoing' => 'out',
                          'none' => 'none'
                        },
                        'allow_custom_settings' => {
                          'yes' => 'both',
                          'no' => 'none',
                        }
                      );
    $sql = "SELECT param, val FROM module_kav_parameters WHERE param='allow_custom_settings' OR param='server_scan_direction'";
    if ( $self->{dbh}->execute_rownum( $sql ) ) {
      while ( my $row = $self->{dbh}->fetchrow() ) {
        $parameters{$row->[0]} = $row->[1];
      }
    }
    $self->{dbh}->finish();
    foreach my $key (keys %parameters) {
      if ( exists $db2xmlAttr{$key} && exists $db2xmlValue{$key} && exists ($db2xmlValue{$key}{$parameters{$key}}) ) {
        $virusfilterNode->setAttribute($db2xmlAttr{$key}, $db2xmlValue{$key}{$parameters{$key}} );
      }
    }
  }

  return $virusfilterNode;
}

sub setServerKavSettings() {
  my ( $self, $kavSettingsNode ) = @_;

  my $table;
  my $sql = "SHOW TABLES LIKE 'module_kav_groups'";
  if ( $self->{dbh}->execute_rownum( $sql ) and $self->{dbh}->fetchrow() ) {
    $table = 1;
  }
  $self->{dbh}->finish();

  if ( $table ) {
    my $groupSettings;
    $sql = "SELECT settings FROM module_kav_groups WHERE name='server_default'";
    if ( $self->{dbh}->execute_rownum( $sql ) and my $row = $self->{dbh}->fetchrow() ) {
      $groupSettings = $row->[0];
    }
    $self->{dbh}->finish();

    my %allowedParams = (
      'Check' => 1,
      'AddXHeaders' => 1,
      'AdminAddress' => 1,
      'QuarantinePath' => 1,
      'FilterByName' => 1,
      'FilterByMIME' => 1,
      'SkipByName' => 1,
      'SkipByMIME' => 1,
      'Quarantine' => 1,
      'AdminNotify' => 1,
      'AdminAction' => 1,
      'SenderNotify' => 1,
      'RecipientNotify' => 1,
      'RecipientAttachReport' => 1,
      'RecipientAction' => 1,
      'CuredAdminAction' => 1,
      'CuredRecipientAction' => 1,
      'InfectedAdminAction' => 1,
      'SuspiciousAdminAction' => 1,
      'InfectedRecipientAction' => 1,
      'SuspiciousRecipientAction' => 1,
      'FilteredRecipientAction' => 1,
      'FilteredQuarantine' => 1
    );

    my @groupSettingsArray = split(/\n+/, $groupSettings);
    if ( @groupSettingsArray ) {
      foreach my $setting ( @groupSettingsArray ) {
        if ($setting =~ /(\S*)=(.*)/) {
          my $param = $1;
          my $val = $2;
          if ( $allowedParams{$param} ) {
            $kavSettingsNode->addChild( XmlNode->new( 'param', 'content' => $val, 'attributes' => { 'name' => $param } ) );
          }
        }
      }
    }
  }

  return $kavSettingsNode;
}

sub parseApsIndexFile() {
  my ( $self, $file ) = @_;

  my %xmlHash;
  my $name;
  if ( open( FILE, $file ) ) {
    while (<FILE>) {
      chomp;
      if ( $_ =~ /<([A-Za-z\-]*)\s+name=\"(.*)\">/ ) {
        $name = $2;
      }
      elsif ( $_ =~ /<([A-Za-z\-]*)>(.*)<\/([A-Za-z\-]*)>/ ) {
        push @{ $xmlHash{$name} }, $2 unless $1 eq "valid";
      }
    }
    close FILE;
  }
  else {
    Logging::info( "Aps index file " . $file . " is not exists. Skipping.");
  }
  return %xmlHash;
}

sub getApsArchiveFileName() {
  my ( $self, $distrib_path, $package_info ) = @_;

  my %mapHash = $self->parseApsIndexFile( $distrib_path . "/archive-index.xml" );
  my $file_name;

  foreach my $tfile_name ( keys %mapHash ) {
    if ( $package_info eq join( "-", @{ $mapHash{$tfile_name} } ) ) {
      $file_name = $tfile_name;
    }
  }

  return $file_name;
}

sub getDomainKeysDomainSupport() {
  my ( $self, $domainId, $domainName, $dnsZoneId ) = @_;
  my $sql = "SELECT p.value FROM Parameters p, DomainServices ds "
    . "WHERE ds.parameters_id = p.id AND ds.dom_id = $domainId AND p.parameter = 'domain_keys_sign'";

  my ($state, $rowPtr);
  unless ( ( $self->{dbh}->execute_rownum($sql) ) && ( $rowPtr = $self->{dbh}->fetchrow() ) ) {
    $self->{dbh}->finish();
    return;
  }
  $state = @{ $rowPtr }[0];
  $self->{dbh}->finish();

  my $publickKey;
  $sql = "SELECT * FROM dns_recs WHERE dns_zone_id=$dnsZoneId AND displayHost='default._domainkey.$domainName.'";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      chop $ptrHash->{'val'};
      $publickKey = substr( $ptrHash->{'val'}, 2 );
    }
  }
  $self->{dbh}->finish();

  $self->{packer}->setDomainKeysDomainSupport( $domainId, $domainName, $state,
    '/etc/domainkeys/' . $domainName, $publickKey );
}

sub getOldWebMailStatus() {
  my ($self, $ptrDomain) = @_;
  my $sql;
  my $retCode = undef;
  if (PleskVersion::atLeast( 8, 0, 0 )) {
    my $dnsZoneId = $ptrDomain->{'dns_zone_id'};
    $sql = "SELECT * FROM dns_recs WHERE dns_zone_id=$dnsZoneId";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
        if ($ptrHash->{'displayHost'} =~ /.*webmail.*/) {
          $retCode = 1;
        }
      }
    }
    $self->{dbh}->finish();
    return $retCode;
  }else{
    my $domainId = $ptrDomain->{'id'};
    $sql = "SELECT * FROM dns_recs WHERE dom_id=$domainId";
    if ( $self->{dbh}->execute_rownum($sql) ) {
      while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
        if ($ptrHash->{'host'} =~ /.*webmail.*/) {
          $retCode = 1;
        }
      }
    }
    $self->{dbh}->finish();
    return $retCode;
  }
}

sub addAllRoles {
  my ($self) = @_;
  my $sql = "SELECT id, name, isBuiltIn FROM smb_roles";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      my $roleId = $ptrHash->{'id'};
      my %permissions = $self->getRolePermissions($roleId);
      $self->{packer}->addRootRole( $ptrHash->{'name'}, $ptrHash->{'isBuiltIn'}, \%permissions);
    }
  }
  $self->{dbh}->finish();
}

sub getRolePermissions {
  my ($self, $roleId) = @_;

  my %permissions;
  my $sql = "SELECT smb_generalPermissions.code, smb_roleGeneralPermissions.isAllowed FROM smb_roles "
            . "INNER JOIN smb_roleGeneralPermissions ON smb_roles.id = smb_roleGeneralPermissions.roleId "
            . "INNER JOIN smb_generalPermissions ON smb_generalPermissions.id = smb_roleGeneralPermissions.generalPermissionId "
            . "WHERE smb_roles.id = '$roleId' ";

  if ( $self->{dbh}->execute_rownum($sql) )  {
    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
       $permissions{$ptrHash->{'code'}} = $ptrHash->{'isAllowed'};
    }
  }
  $self->{dbh}->finish();

  return %permissions;
}

sub addAllUsers {
  my ($self) = @_;
  my $sql = "SELECT login FROM smb_users";
  if ( $self->{dbh}->execute_rownum($sql) ) {
    while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
      my $userLogin = $ptrHash->{'login'};
      my $userHash = $self->getUserHash($userLogin);
      $self->{packer}->addRootUser($userLogin, $userHash) if $userHash;
    }
  }
  $self->{dbh}->finish();
}

sub getUserHash {
  my ($self, $user) = @_;

  my $hashPtr;
  my $sql = "SELECT smb_users.*, smb_roles.name AS SmbRoleName "
            . "FROM smb_users INNER JOIN smb_roles ON smb_users.roleId = smb_roles.id "
            . "WHERE smb_users.login = '$user'";

  if ( $self->{dbh}->execute_rownum($sql) ) {
    $hashPtr = $self->{dbh}->fetchhash();
  }

  $self->{dbh}->finish();

  return $hashPtr;
}

sub getPanelMode {
  my ($self) = @_;
  my $mode;
  my $sql = "SELECT val FROM misc WHERE param = 'power_user_panel'";
  if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow()) {
    $mode = $ptrRow->[0] eq 'true' ? 'PowerUser' : undef;
  }
  $self->{dbh}->finish();
  return $mode;
}

sub dumpApsBundle {
  my ($self, $parentId, $parentType) = @_;

  my $filterType;

  my $properties = $self->getSubscriptionPropertiesHash($parentId, $parentType);

  my $filterId = $properties->{'aps_bundle_filter_id'};

  if ($filterId eq '') {
    return;
  }

  my $sql = "SELECT type FROM smb_apsBundleFilters WHERE id = '$filterId'";
  if ( $self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow()) {
    $filterType = $ptrRow->[0];
  }
  $self->{dbh}->finish();

  $sql = "SELECT propertyName, propertyValue FROM smb_apsBundleFilterItems WHERE filterId = '$filterId'";

  my @items;
  if ($self->{dbh}->execute_rownum($sql)) {
    while(my $ptrHash = $self->{dbh}->fetchhash()) {
       push @items, {$ptrHash->{'propertyName'} => $ptrHash->{'propertyValue'}} ;
    }
  }
  $self->{dbh}->finish();

  $self->{packer}->makeApsBundleFilterNode($parentId, $parentType, $filterType, \@items, undef);
}

sub fillDefaultClientPermissions {
  my $self = @_;
  my %permissions;
  unless (PleskVersion::atLeast(10, 0, 0)) {
    %permissions = %defaultClientPermissions;
  }
  return \%permissions;
}

sub fillDefaultDomainPermissions {
  my $self = @_;
  my %permissions;
  unless (PleskVersion::atLeast(10, 0, 0)) {
    %permissions = %defaultDomainPermissions;
  }
  return \%permissions;
}


1;

# Local Variables:
# mode: cperl
# cperl-indent-level: 2
# indent-tabs-mode: nil
# tab-width: 4
# End:
