# Copyright 1999-2015. Parallels IP Holdings GmbH. All Rights Reserved.
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 Mailman;
use AgentConfig;
use DAL;
use CommonPacker;
use StopWatch;
use Logging;
use HelpFuncs;
use Db::DbConnect;
use Db::Connection;
use Db::MysqlUtils;
use ExtensionPackage;
use CapabilityInfoDumper;
use ObjectsFilter;
use MiscConfigParser;
use Encode;
use Suspend;

use XmlLogger;

use vars qw|@ISA|;
use vars qw|$FULL $SHALLOW $CONFIGURATION $ONLY_MAIL $ONLY_HOSTING $INCLUDE_APP_DISTRIB $NO_LICENSE $CAPABILITY %listOfInstalledApplicationsOnServer|;
$FULL          = 0;
$SHALLOW       = 1;
$CONFIGURATION = 2;
$ONLY_MAIL     = 4;
$ONLY_HOSTING  = 8;
$INCLUDE_APP_DISTRIB = 16;
$NO_LICENSE = 32;
$CAPABILITY = 64;

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( 8, 6, 0 ) ) {
                    push @problems,
                    "Migration from version >= "
                    . PleskMaxVersion::get()
                    . " is not supported";
                }
                else {
                    push @problems, "Migration from version < 8.6.0 is not supported";
                }
            }
        }

        if ( !defined AgentConfig::iconvBin() ) {
            push @problems, "No 'iconv' binary found on the host";
        }
    }

    return @problems;
}

# --- Public instance methods ---
sub _init {
    my ( $self, $storagePolicy, $dumpStatusPolicy, $agentsSharedDir, $skipLogs, $backupParameters ) = @_;

    $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->{capability_dump}    = undef;
    $self->{shallow_dump}       = undef;
    $self->{only_mail_dump}     = undef;
    $self->{only_hosting_dump}  = undef;
    $self->{description}        = undef;
    $self->{dump_vhost}         = 0;
    $self->{dump_full_mail}     = 0;
    $self->{admin_info}         = 0;
    $self->{server_settings}    = 0;
    $self->{dumped_domains}     = [];
    $self->{dumped_clients}     = [];
    $self->{existing_remote_db_servers} = [];
    $self->{suspender} = Suspend->new();
    $self->{backupParameters} = $backupParameters;

    # domain name => remote web node id
    # domain name => undef if local
    $self->{remoteWebNodes} = ();

    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') );

    if (defined $self->{backupParameters} && defined $self->{backupParameters}->{db}) {
        $self->{dbh} = Db::Connection::getConnection(#host port dbType dbName login password
            'type'      => 'mysql',
            'user'      => $self->{backupParameters}->{db}->{username},
            'password'  => $self->{backupParameters}->{db}->{password},
            'name'      => $self->{backupParameters}->{db}->{dbname},
            'host'      => $self->{backupParameters}->{db}->{host},
            'port'      => (defined($self->{backupParameters}->{db}->{port}) ? $self->{backupParameters}->{db}->{port} : "3306"),
            'utf8names' => 1,
            'connRetries' => AgentConfig::getMaxDbConnectionsRetries()
        );
    } else {
        $self->{dbh} = Db::Connection::getConnection(
            'type'      => 'mysql',
            'user'      => 'admin',
            'password'  => AgentConfig::get('password'),
            'name'      => 'psa',
            'host'      => 'localhost',
            'utf8names' => 1,
            'connRetries' => AgentConfig::getMaxDbConnectionsRetries()
        );
    }

    die "Can not connect to the 'psa' database. Please, check database server is running and connection settings are valid" if (!$self->{dbh});

    if ($self->{collectStatistics})
    {
        $self->{dbh}->startCollectStatistics();
    }

    DAL::init( $self->{dbh} );

    PleskStructure::init( $self->{dbh} );

    Encoding::setDefaultEncoding("UTF-8");

    if ( PleskVersion::atLeast( 11, 1, 13 ) ) {
        ExtensionPackage::init( $self );
    }

    $self->{capability} = CapabilityInfoDumper->new( $self );

    $self->{filter} = ObjectsFilter->new( $self );

    $self->{has_remote_db_server} = 0;

    foreach my $ptrHash (@{DAL::getDatabaseServers()}) {
        if (HelpFuncs::isRemoteHost($ptrHash->{'host'})) {
            $self->{has_remote_db_server} = 1;
            last;
        }
    }
}

sub getCollectStatistics {
    my $envVal = $ENV{'COLLECT_BACKUP_STAT'};
    if ($envVal)
    {
        return $envVal;
    }
    return 0;
}

sub turnOnMigrationMode {
    my ($self) = @_;
    $self->{packer}->turnOnMigrationMode();
}

sub turnOnListingOnlyMode {
    my ($self) = @_;
    $self->{packer}->turnOnListingOnlyMode();
}

sub turnOnCapabilityOnlyMode {
    my ($self) = @_;
    $self->{packer}->turnOnCapabilityOnlyMode();
}

sub setDecryptFullDump {
    my ($self) = @_;
    $self->{packer}->setDecryptFullDump();
}

sub setDumpWholeVHost{
    my ($self) = @_;
    $self->{dump_vhost} = 1;
}

sub setDumpWholeMail{
    my ($self) = @_;
    $self->{dump_full_mail} = 1;
}

sub setContentTransport{
    my $self = shift;
    my $contentTransportType = shift;
    $self->{packer}->setContentTransport($contentTransportType, @_)
}

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 setIncrementalCreationDate{
    my ($self, $incrementalCreationDate) = @_;
    $self->{packer}->setIncrementalCreationDate($incrementalCreationDate);
}

sub getBackupPrefix {
    my ($self) = @_;
    return $self->{packer}->{backupPrefix};
}

sub setLastIndexPath{
    my ($self, $lastIndexPath) = @_;
    $self->{packer}->setLastIndexPath($lastIndexPath);
}

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 getClientGuid {
    my ($self, $name) = @_;
    return PleskStructure::getClientGuid($name);
}

sub getDomainGuid {
    my ($self, $name) = @_;
    my $domainPtr = DAL::getDomainPtr($name);
    return ($domainPtr->{'guid'}) ? $domainPtr->{'guid'} : '';
}

sub getDomainAsciiName {
    my ($self, $name) = @_;
    my $domainPtr = DAL::getDomainPtr($name);
    return $domainPtr->{'name'};
}

sub setDumpType {
    my ( $self, $type ) = @_;

    if ( $type & $SHALLOW )       { $self->{shallow_dump}       = 1; }
    if ( $type & $CONFIGURATION ) { $self->{configuration_dump} = 1; }
    if ( $type & $CAPABILITY )    { $self->{capability_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;
    }

    if ( $type & $NO_LICENSE ) {
        $self->{no_license} = 1;
    }
}

sub setDescription {
    my ( $self, $description ) = @_;
    $self->{description} = $description;
}

sub setExistingRemoteDbServers {
    my ( $self, $dbServers ) = @_;
    $self->{existing_remote_db_servers} = $dbServers;
}

sub selectDomains {
    my ( $self, @inputDomains ) = @_;
    @inputDomains = sort { $a cmp $b } @inputDomains;

    my @pleskDomains = sort { $a cmp $b } PleskStructure::getDomains();
    my @missingDomains = HelpFuncs::arrayDifference( \@inputDomains, \@pleskDomains );

    if (@missingDomains) {
        my $msg;
        my $sitesHash = PleskStructure::getSites();
        my @sites = keys %{$sitesHash};
        @sites = sort { $a cmp $b } @sites;

        my @missingDomainsAreSites = HelpFuncs::arrayIntersection(\@missingDomains, \@sites);

        if (@missingDomainsAreSites) {
            $msg = "The following domains are additional domains: " . ( join ",", @missingDomainsAreSites ) . ".\n";
            $msg .= "Backups of specified domains will be included in backups of their subscriptions." . "\n";
            foreach my $site (@missingDomainsAreSites) {
                $msg .= "Domain '" . $site ."' will be backed up with subscription '" . $sitesHash->{$site} . "'.\n";
            }
        } else {
            $msg = "The following domains were not found on the host: "  . ( join ",", @missingDomains );
        }
        print STDERR "$msg\n";
        Logging::warning( $msg, 'MissingDomains' );
        @inputDomains = HelpFuncs::arrayIntersection( \@inputDomains, \@pleskDomains );
    }
    $self->{domains} = \@inputDomains if @inputDomains;
}

sub selectDomainsById {
    my ( $self, @inputDomains ) = @_;

    my @pleskDomains = PleskStructure::getDomainsFromId( \@inputDomains );
    if( @inputDomains ){
        my $msg = "The following domain id's were not found on the host: " . ( join ",", @inputDomains );
        print STDERR "$msg\n";
        Logging::warning( $msg, 'MissingDomains' );
    }
    $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 = HelpFuncs::arrayDifference( \@inputClients, \@pleskClients );

    if (@missingClients) {
        my $msg = "The following clients were not found on the host: " . ( join ",", @missingClients );
        print STDERR "$msg\n";
        Logging::warning( $msg, 'MissingClients' );
        @inputClients = HelpFuncs::arrayIntersection( \@inputClients, \@pleskClients );
    }
    $self->_selectClients( \@inputClients ) if @inputClients;
}

sub selectClientsById {
    my ( $self, @inputClients ) = @_;

    my @pleskClients = PleskStructure::getClientsFromId( \@inputClients);
    if( @inputClients ){
        my $msg = "The following clients id's were not found on the host: " . ( join ",", @inputClients );
        print STDERR "$msg\n";
        Logging::warning( $msg, 'MissingClients' );
    }
    $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 = HelpFuncs::arrayDifference( \@inputResellers, \@pleskResellers );

    if (@missingResellers) {
        my $msg = "The following resellers were not found on the host: " . ( join ",", @missingResellers );
        print STDERR "$msg\n";
        Logging::warning( $msg, 'MissingResellers' );
        @inputResellers = HelpFuncs::arrayIntersection( \@inputResellers, \@pleskResellers );
    }
    $self->_selectClients( \@inputResellers ) if @inputResellers;
}

sub selectResellersById {
    my ( $self, @inputResellers ) = @_;

    my @pleskResellers = PleskStructure::getResellersFromId( \@inputResellers);
    if( @inputResellers ){
        my $msg =  "The following reseller id's were not found on the host: " . ( join ",", @inputResellers );
        print STDERR "$msg\n";
        Logging::warning( $msg, 'MissingResellers' );
    }
    $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 (@resellers);
    $self->{clients} = \@clients if (@clients);
    $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} = [HelpFuncs::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} = [HelpFuncs::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} = [HelpFuncs::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) = @_;
    $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;
    Logging::debug( "Select to backup server settings" );
}

sub selectAdminInfo {
    my ($self) = @_;
    $self->{admin_info} = 1;
    Logging::debug( "Select to backup Administrator info" );
}

sub setSuspend{
    my ( $self, $suspend, $suspendSid ) = @_;
    $self->{suspender}->setSuspend($suspend, $suspendSid);
}

sub Cleanup{
    my $self = shift;
    $self->{suspender}->unSuspendDomain();
}

sub getListOfInstalledApplicationsOnServer {
    my ($self) = @_;

    if (scalar keys %listOfInstalledApplicationsOnServer == 0) {
        if (PleskVersion::atLeast( 10, 3, 0 )) {
            my @serverApplications = SiteApp::getServerSettingsForApplications();
            if (@serverApplications) {
                foreach my $ptrRow ( @{DAL::getApsPackages103()} ) {
                    my $application = $self->getConcreteApplicationInfo(\@serverApplications, $ptrRow->[6]);
                    if (!$application) {
                        Logging::debug("Can't get '$ptrRow->[0]-$ptrRow->[1]-$ptrRow->[2]' application info by '$ptrRow->[6]'", 'SiteappError');
                        next;
                    }

                    my $packageArchive = $application->getPackageArchive();
                    my $packageArchiveFileName = undef;
                    my $packageArchiveDir = undef;

                    my $idx = rindex( $packageArchive, '/' );

                    if ( $idx > 0 ) {
                        $packageArchiveDir = substr( $packageArchive, 0, $idx);
                        $packageArchiveFileName = substr( $packageArchive, $idx+1);
                    }

                    $listOfInstalledApplicationsOnServer{$application->getRegistryUid()} = [$ptrRow->[0], $ptrRow->[1], $ptrRow->[2], $packageArchiveDir, $packageArchiveFileName, $ptrRow->[4], $ptrRow->[5], $application];
                }
            }
        }
    }
    return \%listOfInstalledApplicationsOnServer;
}

sub dump {
    my ($self) = @_;

    Logging::beginObject('server', 'server');

    $self->{packer}->turnOffContent() if $self->{configuration_dump};

    my $panelMode = DAL::getPanelMode();

    $self->{packer}->setRoot( $self->{description}, $self->{configuration_dump} ? 0: 1, defined($panelMode) ? $panelMode : 'panel', PleskStructure::getAdminGuid() );

    my $embeddedInfo = undef;
    if (PleskVersion::atLeast(12, 0, 11) && ($self->{server_settings} || $self->{admin_info})) {
        my $cmd = AgentConfig::getBackupRestoreHelperUtil() . " --backup-server";
        Logging::debug("Exec: $cmd");
        my $ret = `$cmd`;
        my $retCode = $? >> 8;
        if ($retCode == 0) {
            eval {require XML::Simple; 1;};
            my $xs = XML::Simple->new(ForceArray => 1, SuppressEmpty => '');
            $embeddedInfo = $xs->XMLin($ret, KeyAttr => []);
        } else {
            Logging::warning("Cannot execute backup helper: " . $cmd . ", STDOUT:" . $ret, 'UtilityError');
        }
    }

    if( exists $self->{dump_all} ) {
        $self->createFullDump();

        $self->makeAdminInfoNode($embeddedInfo) if $self->{admin_info};
        eval {
            $self->makeServerNode($embeddedInfo) if $self->{server_settings};
            $self->{packer}->addServerNodeToDump();
            1;
        } or do {
            $self->excludeProblemServerFromBackup( $@ );
        }
    }
    else{
        my $done;
        $self->beginWriteStatus();
        if( $self->{admin_info} or $self->{server_settings} ) {
            $self->{packer}->addRootAdmin( PleskStructure::getAdminId(), PleskStructure::getAdminGuid() );
            $done = 1;
        }
        if (exists $self->{resellers}){
            $self->createResellersDump();
            $done = 1;
        }
        if (exists $self->{clients}) {
            $self->createClientsDump();
            $done = 1;
        }
        if ( exists $self->{domains} ) {
            $self->createDomainsDump();
            $done = 1;
        }

        # Commented due to different behaviour for service plans and reseller plans until making desicion about correct case
        # Commented due to different behaviour for service plans and reseller plans until making desicion about correct case
        # $self->dumpResellersTemplates(\@{$self->{resellers}});

        $self->{packer}->addRootRoles();
        $self->{packer}->addRootUsers();
        $self->{packer}->addRootDomains();
        if( $self->{server_settings} ){
            $self->makeAdminUsersAndRoles();
            eval {
                $self->makeServerNode($embeddedInfo);
                $done = 1;
                1;
            } or do {
                $self->excludeProblemServerFromBackup( $@ );
            }
        }
        if( $self->{admin_info} ) {
            $self->makeAdminInfoNode($embeddedInfo);
            $done = 1;
        }
        if ($self->{capability_dump}) {
            $self->{capability}->dumpServer();
            $done = 1;
        }
        $self->{packer}->addServerNodeToDump();

        $self->{dump_status}->finishObjects();
        if ( not $done ) {
            Logging::error("No objects to dump found");
            Logging::endObject();
            return 1;
        }
    }

    $self->{suspender}->unSuspendDomain();
    my $ret = $self->{packer}->finish(PleskVersion::atLeast( 11, 0, 0 ));

    if ($self->{collectStatistics})
    {
        $self->writeStatistics();
    }

    Logging::endObject();

    # XXX won't work on 10.4.4: $self->_reportDomainStatistics();

    return $ret;
}

###### Size mode functions ######
sub getSize {
    my ($self) = @_;

    use BackupSizeCalculator;

    my $backupSizeCalculator = BackupSizeCalculator->new( $self );

    return $backupSizeCalculator->getSize();
}
###### End Size mode functions ######

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: " . HelpFuncs::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);
}

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 %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;

#windows specific limits
$domainLimits{'max_cf_dsn'} = 1;
$domainLimits{'max_mssql_db'} = 1;
$domainLimits{'max_odbc'} = 1;
$domainLimits{'max_shared_ssl_links'} = 1;
$domainLimits{'mssql_dbase_space'} = 1;
$domainLimits{'mysql_dbase_space'} = 1;
$domainLimits{'max_iis_app_pools'} = 1;

my %clientLimits = %domainLimits;
$clientLimits{'max_dom'} = 1;
delete $clientLimits{'max_site'};

my %resellerLimits = %clientLimits;
$resellerLimits{'max_cl'} = 1;

sub getAdminRootPath{
    my ($self) = @_;
    $self->{packer}->regAdminObjectBackupPath( '' );
}

my %generalRolePermissions = (
    'userManagement' => 1,
    'webSitesAndDomainsManagement' => 1,
    'logRotationManagement' => 1,
    'anonymousFtpManagement' => 1,
    'scheduledTasksManagement' => 1,
    'spamfilterManagement' => 1,
    'antivirusManagement' => 1,
    'databasesManagement' => 1,
    'backupRestoreManagement' => 1,
    'browseStats' => 1,
    'applicationsManagement' => 1,
    'sitebuilderManagement' => 1,
    'filesManagement' => 1,
    'ftpAccountsManagement' => 1,
    'dnsManagement' => 1,
    'javaApplicationsManagement' => 1,
    'mailManagement' => 1,
    'mailListsManagement' => 1,
    'publishFilesOnTheWeb' => 1,
);

sub getRolePermissions {
    my ($self, $roleId) = @_;

    my %permissions = %{DAL::getRolePermissions($roleId)};
    %permissions = map { $_ => $permissions{$_} } grep { $generalRolePermissions{$_} } keys %permissions;

    return \%permissions;
}

sub createFullDump {
    my ($self) = @_;

    $self->getAdminRootPath();

    $self->{packer}->addRootAdmin( PleskStructure::getAdminId(), PleskStructure::getAdminGuid() );

    $self->{dump_status}->start(PleskStructure::getClientsCount(''), PleskStructure::getDomainsCount( '' ) );

    my @resellers = sort { $a cmp $b } PleskStructure::getResellers();
    @resellers = $self->{filter}->filterSelectedResellers( \@resellers );

    foreach my $reseller (@resellers) {
        my @myclients = sort { $a cmp $b } PleskStructure::getMyClients($reseller);
        @myclients = $self->{filter}->filterSelectedClients( \@myclients );
        my @domains = sort { $a cmp $b } PleskStructure::getDomainsForClient($reseller);
        @domains = $self->{filter}->filterSelectedDomains( \@domains );
        eval {
            $self->makeClientNode($reseller, \@domains, \@myclients, 0 );
            1;
        } or do {
            $self->excludeProblemClientFromBackup( $reseller, $@ );
        }
    }

    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->{filter}->filterSelectedClients( \@myclients );
            my @domains = sort { $a cmp $b } PleskStructure::getDomainsForClient($client);
            @domains = $self->{filter}->filterSelectedDomains( \@domains );
            eval {
                $self->makeClientNode($client, \@domains, \@myclients, 0 );
                1;
            } or do {
                $self->excludeProblemClientFromBackup( $client, $@ );
            }
        }
    }

    $self->dumpResellersTemplates(\@resellers);

    my $adminName = PleskStructure::getAdminName();
    $self->{dump_status}->startClient($adminName);

    my @adminDomains = PleskStructure::getAdminDomains();
    @adminDomains = $self->{filter}->filterSelectedDomains( \@adminDomains );
    foreach my $domainName(@adminDomains) {
        eval {
            $self->makeDomainNode($domainName, 0 );
            1;
        } or do {
            $self->excludeProblemDomainFromBackup($domainName, $@);
        };
    }

    $self->makeAdminUsersAndRoles();

    $self->{dump_status}->endClient($adminName);

    $self->{capability}->dumpServer();

    $self->{dump_status}->finishObjects();
}

sub excludeProblemClientFromBackup {
    my ( $self, $clientName, $reason ) = @_;

    my $clientType = PleskStructure::getClientType( $clientName );
    my $clientId = PleskStructure::getClientId( $clientName );

    Logging::warning( "$clientType $clientName is skipped from backup due to error: $reason" );
    Logging::debug( "$clientType $clientName is skipped from backup due to error: $reason", 'fatal' );

    if ( $clientType eq 'client' ) {
        delete $self->{packer}->{clientNodes}->{$clientId};
    } elsif ( $clientType eq 'reseller' ) {
        delete $self->{packer}->{resellerNodes}->{$clientId};
    }
}

sub excludeProblemDomainFromBackup {
    my ( $self, $domainName, $reason ) = @_;

    Logging::warning( "Domain $domainName is skipped from backup due to error: $reason" );
    Logging::debug( "Domain $domainName is skipped from backup due to error: $reason", 'fatal' );

    my $domainId = PleskStructure::getDomainId( $domainName );
    delete $self->{packer}->{domainNodes}->{$domainId};
    delete $self->{packer}->{domainShortNodes}->{$domainId};

    $self->{suspender}->unSuspendDomain();
}

sub excludeProblemSiteFromBackup {
    my ( $self, $domainId, $ptrSite, $reason ) = @_;

    Logging::warning( "Domain $ptrSite->{'name'} is skipped from backup due to error: $reason" );
    Logging::debug( "Domain $ptrSite->{'name'} is skipped from backup due to error: $reason", 'fatal' );

    $self->{packer}->removeSiteNode( $domainId, $ptrSite->{'name'} );

    $self->{suspender}->unSuspendDomain();
}

sub excludeProblemSubdomainFromBackup {
    my ( $self, $domainId, $ptrSubdomain, $reason ) = @_;

    Logging::warning( "Subdomain $ptrSubdomain->{'name'} is skipped from backup due to error: $reason" );
    Logging::debug( "Subdomain $ptrSubdomain->{'name'} is skipped from backup due to error: $reason", 'fatal' );

    $self->{packer}->removeSubdomainNode( $domainId, $ptrSubdomain->{'name'} );

    delete $self->{packer}->{subDomainNodes}->{$ptrSubdomain->{'id'}};
}

sub excludeProblemDatabaseFromBackup {
    my ( $self, $domainId, $db, $reason ) = @_;

    Logging::warning( "Database $db->{'name'}, type $db->{'type'} is skipped from backup due to error: $reason" );
    Logging::debug( "Database $db->{'name'}, type $db->{'type'} is skipped from backup due to error: $reason", 'fatal' );

    $self->{packer}->removeDatabaseNode( $domainId, $db );
}

sub excludeProblemServerFromBackup {
    my ( $self, $reason ) = @_;

    Logging::warning( "Server settings are skipped from backup due to error: $reason" );
    Logging::debug( "Server settings are skipped from backup due to error: $reason", 'fatal' );

    $self->{packer}->removeServerNode();
}

sub excludeProblemMailUserFromBackup {
    my ( $self, $domainId, $mail, $reason ) = @_;

    my $domainName = PleskStructure::getDomainNameFromId( $domainId );
    Logging::warning( "Mail user $mail->{'mail_name'} on domain $domainName is skipped from backup due to error: $reason" );
    Logging::debug( "Mail user $mail->{'mail_name'} on domain $domainName is skipped from backup due to error: $reason", 'fatal' );

    $self->{packer}->removeMailuserNode( $domainId, $mail );
}

sub excludeProblemMailSystemFromBackup {
    my ( $self, $domainId, $reason ) = @_;

    my $domainName = PleskStructure::getDomainNameFromId( $domainId );

    Logging::warning( "Mail System on domain $domainName is skipped from backup due to error: $reason" );
    Logging::debug( "Mail System on domain $domainName is skipped from backup due to error: $reason", 'fatal' );

    $self->{packer}->removeMailSystemNode( $domainId );
}

sub excludeProblemHostingFromBackup {
    my ( $self, $domainId, $hostingType, $reason ) = @_;

    my $domainName = PleskStructure::getDomainNameFromId( $domainId );

    Logging::warning( "Hosting on domain $domainName is skipped from backup due to error: $reason" );
    Logging::debug( "Hosting on domain $domainName is skipped from backup due to error: $reason", 'fatal' );

    $self->{packer}->removeHostingNode( $domainId, $hostingType );
}

sub excludeProblemDnsZoneFromBackup {
    my ( $self, $domainId, $reason ) = @_;

    my $domainName = PleskStructure::getDomainNameFromId( $domainId );

    Logging::warning( "DNS Service on domain $domainName is skipped from backup due to error: $reason" );
    Logging::debug( "DNS Service on domain $domainName is skipped from backup due to error: $reason", 'fatal' );

    $self->{packer}->removeDomainDnsZone($domainId);
}

sub excludeProblemUserFromBackup {
    my ( $self, $userLogin, $callback, $reason ) = @_;

    Logging::warning( "User $userLogin is skipped from backup due to error: $reason" );
    Logging::debug( "User $userLogin is skipped from backup due to error: $reason", 'fatal' );

    $callback->( $userLogin );
}

sub excludeProblemRoleFromBackup {
    my ( $self, $callback, $roleName, $reason ) = @_;

    Logging::warning( "Role $roleName is skipped from backup due to error: $reason" );
    Logging::debug( "Role $roleName is skipped from backup due to error: $reason", 'fatal' );

    $callback->( $roleName );
}

sub makeAdminLimitsAndPermissions {
    my ($self) = @_;
    if ( PleskVersion::atLeast( 10, 4, 0 ) ) {
        my $permsHash = $self->getPermsHash(PleskStructure::getAdminId());
        foreach my $key ( keys %{$permsHash} ) {
            $self->{packer}->addAdminPermission($key, $permsHash->{$key});
        }
    }
}

sub makeAdminUsersAndRoles {
    my ( $self ) = @_;

    my $adminName = PleskStructure::getAdminName();
    my @adminUsersLogins = PleskStructure::getUserLogins( $adminName );

    my $callback = sub {
        $self->{packer}->addAdminUser( @_ );
    };

    my $callbackFailed = sub {
        $self->{packer}->removeAdminUser( @_ );
    };

    $self->addUsers( \@adminUsersLogins, $callback, $callbackFailed );

    my $callbackRole = sub {
        $self->{packer}->removeAdminRole( @_ );
    };

    for my $roleId ( PleskStructure::getRoleIds( $adminName ) ) {
        my @role = grep {$_->{'id'}==$roleId and $_->{'isBuiltIn'}==0 } @{DAL::selectSmbRoles()};
        eval {
            if ( @role ) {
                $self->{packer}->addAdminRole( $role[0]->{'name'}, 0, $self->getRolePermissions($roleId), DAL::getRoleServicePermissions($roleId) ) ;
            }
            1;
        } or do {
            $self->excludeProblemRoleFromBackup( $callbackRole, $role[0]->{'name'}, $@ );
        }
    }
}

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->{filter}->filterSelectedClients( \@myclients );
            for my $client( @myclients ){
                $accounts{$client} = 1;
            }
            foreach my $client( @myclients ){
                my @mydomains = sort { $a cmp $b } PleskStructure::getDomainsForClient($client);
                @mydomains = $self->{filter}->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->{filter}->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->{filter}->filterSelectedClients( \@myclients );
        $clients{$reseller} = \@myclients;

        my @mydomains = sort { $a cmp $b } PleskStructure::getDomainsForClient($reseller);
        @mydomains = $self->{filter}->filterSelectedDomains( \@mydomains );
        $domains{$reseller} = \@mydomains;
    }

    foreach my $reseller ( @{ $self->{resellers} } ) {
        $self->getClientRootPath( $reseller );
        eval {
            $self->makeClientNode($reseller, $domains{$reseller}, $clients{$reseller}, 1 );
            1;
        } or do {
            $self->excludeProblemClientFromBackup( $reseller, $@ );
        }
    }
}

sub createClientsDump {
    my ( $self ) = @_;

    my (%clients);
    foreach my $client ( @{ $self->{clients} } ) {
        my @mydomains = sort { $a cmp $b } PleskStructure::getDomainsForClient($client);
        @mydomains = $self->{filter}->filterSelectedDomains( \@mydomains );
        $clients{$client} = \@mydomains;
    }

    foreach my $client (keys %clients) {
        $self->getClientRootPath( $client );
        eval {
            $self->makeClientNode( $client, $clients{$client}, undef, 1 );
            1;
        } or do {
            $self->excludeProblemClientFromBackup( $client, $@ );
        }
    }
}

sub getDomainRootPath{
    my ($self, $domainName ) = @_;

    my $sql = "SELECT id, cl_id, name FROM domains WHERE displayName = BINARY '?'";
    my $ptrHash;
    my $domainId;
    my $clientId;
    my $domainAscName;
    if( $self->{dbh}->execute($sql, $domainName) ) {
        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) {
        my $rootPath = PleskVersion::isShm() ?
        $self->getAdminRootPath() : $self->getClientRootPath(PleskStructure::getClientNameFromId($clientId));
        return $self->{packer}->regDomainObjectBackupPath($rootPath, $domainId, $domainAscName);
    }
}

sub createDomainsDump {
    my ( $self ) = @_;

    foreach my $domainName ( @{$self->{domains}} ) {
        if ( $self->getDomainRootPath( $domainName ) ) {
            eval {
                $self->makeDomainNode( $domainName, 1 );
                1;
            } or do {
                $self->excludeProblemDomainFromBackup($domainName, $@);
            };
        }
        else {
            my $msg = "Unable to backup domain $domainName because of unappropriate database content";
            print STDERR "$msg\n";
            Logging::warning( $msg, 'PleskDbError' );
        }
    }
}

sub dumpDomainPersonalPermissions {
    my ( $self, $domainId, $permId ) = @_;
    $self->addPermissions( $domainId, 'domain-personal', $permId );
}

sub makeClientNode {
    my ( $self, $clientName, $domains, $childClients, $isroot ) = @_;

    my ($parent, $clientType );
    $clientType = PleskStructure::getClientType( $clientName );

    if( $clientType eq 'client' and $self->clientExcluded( $clientName ) ) {
        Logging::debug("Client '$clientName' is excluded from dump");
        return;
    }
    if( $clientType eq 'reseller' and $self->resellerExcluded( $clientName ) ) {
        Logging::debug("Reseller '$clientName' is excluded from dump");
        return;
    }

    foreach my $dumpedClient( @{$self->{dumped_clients}} ){
        if( $dumpedClient eq $clientName ){
            Logging::debug("Client '$dumpedClient' already dumped");
            return;
        }
    }
    push @{$self->{dumped_clients}}, $clientName;

    my ( $item, $sql, $ptrHash, $value, %client, $ptrRow, $id, %clientParams );
    Logging::beginObject($clientType,$clientName, $self->generateObjectUuidForLogging($clientType,$clientName));
    Logging::debug("Client '$clientName' is started") if $clientType eq 'client';
    Logging::debug("Reseller '$clientName' is started") if $clientType eq 'reseller';
    $self->{dump_status}->startClient($clientName);

    $sql = "SELECT * FROM clients WHERE login = '?'";

    if ( $self->{dbh}->execute($sql, $clientName) ) {
        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'} = CommonPacker::normalizeCountry( $client{'country'} );

    my %passwd;
    unless ( $self->{shallow_dump} ) {
        $sql = "SELECT password, type FROM accounts WHERE id = ?";

        if ( $self->{dbh}->execute($sql, $client{'account_id'}) ) {
            if ( $ptrHash = $self->{dbh}->fetchhash() ) {
                %passwd  = (
                    'password' => $ptrHash->{'password'},
                    'type'     => CommonPacker::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";
                Logging::warning( $msg , 'BrokenDbIntegrity');
            }
        }
        $self->{dbh}->finish();
    }
    my $status = $client{'status'};

    my $doNotDumpDomainTemplates = 0;

    my $parentType;

    if( $parent ) {
        $parentType = PleskStructure::getClientType( $parent );
        $client{'owner-guid'} = PleskStructure::getClientGuid( $parent );
        $client{'owner-name'} = $parent;
    } else{
        $parentType = 'admin';
        $client{'owner-guid'} = PleskStructure::getAdminGuid();
        $client{'owner-name'} = 'admin';
    }

    if( $clientType eq 'client' ){

        if ( exists $client{'vendor_id'} ){
            my $vendorLogin = PleskStructure::getClientNameFromId( $client{'vendor_id'} );
            $client{'vendor-login'} = $vendorLogin;
        }

        if( $isroot ){
            if ($self->{admin_info} or $self->{server_settings}) {
                $self->{packer}->addAdminClient( $clientId, \%client, \%passwd, $status );
            } else {
                $self->{packer}->addRootClient( $clientId, \%client, \%passwd, $status );
            }
        }
        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 ){
            if ( $self->{admin_info}  or $self->{server_settings} ) {
                $self->{packer}->addAdminReseller( $clientId, \%client, \%passwd, $status );
            } else {
                $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'!";
    }

    $sql = "SELECT param,val FROM cl_param WHERE cl_id = ?";
    if ( $self->{dbh}->execute_rownum($sql, $clientId) ) {
        while ( $ptrRow = $self->{dbh}->fetchrow() ) {
            $clientParams{ $ptrRow->[0] } = $ptrRow->[1];
        }
    }
    $self->{dbh}->finish();

    unless ( $self->{shallow_dump} ) {

        $self->{packer}->setClientPinfo( $clientId, \%client );

        if ( exists( $client{'locale'} ) && $client{'locale'} ) {
            $self->{packer}->setClientLocale( $clientId, $client{'locale'} );
        }

        if ( ($clientType eq 'reseller')
                or !PleskVersion::atLeast(10, 0, 0)
                or (DAL::isBusinessModelUpgraded() and ($client{'limits_id'} or $client{'perm_id'})) )
        {
            #
            # limits
            #

            if( $clientType eq 'reseller' ) {
                $self->addResellerLimits( $clientId, $client{'limits_id'} );
            }
            else{
                $self->addClientLimits( $clientId, $client{'limits_id'} );
            }

            $self->addClientOverusePolicy( $clientId, \%clientParams );
            $self->{packer}->fixDefaultLimits($clientType,$clientId);

            #
            # end limits
            #

            $self->addPermissions( $clientId, 'client', $client{'perm_id'} );

            #fix bug 88139
            if ( !$client{'perm_id'} )
            {
                $self->{packer}->addClientPermission( $clientId, 'cp_access', 'true' );
            }

            $self->addClientMulitpleLoginPermission($clientId, $clientParams{'multiply_login'} ) if exists $clientParams{'multiply_login'};
        }

        #
        # Domain skeleton
        #

        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 );

    $self->addClientTraffic($clientId);

    unless ($doNotDumpDomainTemplates or $self->{capability_dump}) {
        $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 );
    }

    # 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->{filter}->filterSelectedDomains( \@clientdomains );
            eval {
                $self->makeClientNode($myClientName, \@clientdomains, undef, 0 );
                1;
            } or do {
                $self->excludeProblemClientFromBackup( $myClientName, $@ );
            }
        }
        $self->{packer}->removeResellerChildNodeIfEmpty( $clientId, 'clients' );
    }

    for my $domainName ( @{$domains} ) {
        eval {
            $self->makeDomainNode( $domainName, 0 );
            1;
        } or do {
            $self->excludeProblemDomainFromBackup( $domainName, $@ );
        };
    }
    $self->{packer}->removeResellerChildNodeIfEmpty( $clientId, 'domains' );

    unless ($self->{capability_dump}) {
        # dump users
        my @users = PleskStructure::getUserLogins($clientName);
        my $callback = sub {
            $self->{packer}->addClientUser($clientId, @_);
        };

        my $callbackFailed = sub {
            $self->{packer}->removeClientUser( $clientId, @_ );
        };

        my $callbackRole = sub {
            $self->{packer}->removeClientRole( $clientId, @_ );
        };

        $self->addUsers( \@users, $callback, $callbackFailed );

        # dump roles
        for my $roleId (PleskStructure::getRoleIds($clientName) ) {
            my @role = grep {$_->{'id'}==$roleId and $_->{'isBuiltIn'}==0} @{DAL::selectSmbRoles()};
            eval {
                if ( @role ) {
                    $self->{packer}->addClientRole($clientId, $role[0]->{'name'}, 0, $self->getRolePermissions($roleId), DAL::getRoleServicePermissions($roleId));
                }
                1;
            } or do {
                $self->excludeProblemRoleFromBackup( $callbackRole, $role[0]->{'name'}, $@ );
            }
        }
    }
    $self->getCustomButtonsByOwner( $clientType, $clientId );

    if ( PleskVersion::atLeast( 10, 0, 0 ) and not $self->{capability_dump} ) {
        $self->getSubscription('client', $clientId);
    }

    if ( PleskVersion::atLeast( 11, 1, 0 ) ) {
        if (exists $clientParams{'theme_skin'} and not $self->{configuration_dump}) {
            if ( $clientType eq 'reseller' ) {
                $self->{packer}->makeSkinNode($clientParams{'theme_skin'}, $clientName, $clientId);
            }
        }
    } elsif ( PleskVersion::atLeast( 10, 1, 0 ) ) {
        if ( $clientType eq 'reseller' ) {
            $self->{packer}->makeBrandingThemeNode($clientName, $clientId);
        }
    }

    if ( PleskVersion::atLeast( 10, 1, 0 ) ) {
        $self->dumpApsBundle($clientId, 'client');
    }

    $self->processExtensionPackage($clientType, $clientName, $clientId);

    if ( $clientType eq 'reseller' ) {
        my $clientsDescriptions = DAL::getClientsDescriptions( $clientId );
        my $subscriptionsDescriptions = DAL::getSubscriptionsResellerDescriptions( $clientId );
        my @allDescriptions = ( @{$clientsDescriptions}, @{$subscriptionsDescriptions} );
        $self->{packer}->makeResellerDescriptionsNode( $clientId, \@allDescriptions );
    }

    $self->{packer}->makeClParamNode( $clientId, $clientType, \%clientParams );

    $self->{packer}->finishClient($clientId);
    $self->{dump_status}->endClient($clientName);

    Logging::endObject();
}

sub addDomainOverusePolicy{
    my( $self, $parentId, $params ) = @_;

    if( exists $params->{'DecompositionRule'} ){
        my $oversell = ($params->{'DecompositionRule'} eq 'oversell')? 'true' : 'false';
        $self->{packer}->addDomainLimit( $parentId, 'oversell', $oversell );
    }

    # 1) Calculate (OveruseBlock, OveruseSuspend, OveruseNotify) 
    # according to domain params and Plesk version
    my $overuseParams = {};
    if ( PleskVersion::atLeast( 10, 0, 0 ) ) {
        if ( exists $params->{'OveruseBlock'} ) {
            $overuseParams->{'OveruseBlock'} = $params->{'OveruseBlock'};
        } else {
            $overuseParams->{'OveruseBlock'} = 'true';
        }
        if ( exists $params->{'OveruseSuspend'} ) {
            $overuseParams->{'OveruseSuspend'} = $params->{'OveruseSuspend'};
        } else {
            $overuseParams->{'OveruseSuspend'} = 'false';
        }
        if ( exists $params->{'OveruseNotify'} ) {
            $overuseParams->{'OveruseNotify'} = $params->{'OveruseNotify'};
        } else {
            $overuseParams->{'OveruseNotify'} = 'false';
        }
    } elsif ( PleskVersion::atLeast( 9, 0, 0 ) ) {
        if ( exists $params->{'OveruseBlock'} ) {
            $overuseParams->{'OveruseBlock'} = $params->{'OveruseBlock'};
        } else {
            $overuseParams->{'OveruseBlock'} = 'false';
        }
        $overuseParams->{'OveruseSuspend'} = $params->{'OveruseBlock'};
        if ( exists $params->{'OveruseNotify'} ) {
            $overuseParams->{'OveruseNotify'} = $params->{'OveruseNotify'};
        } else {
            $overuseParams->{'OveruseNotify'} = 'true';
        }
    } else { # Plesk 8.x
        $overuseParams->{'OveruseBlock'} = 'true';
        $overuseParams->{'OveruseSuspend'} = 'false';
        $overuseParams->{'OveruseNotify'} = 'false'
    }

    # 2) Convert database (OveruseBlock, OveruseSuspend, OveruseNotify) 
    # to API/CLI value: normal | block | notify | not_suspend | not_suspend_notify
    my $overuse = 'normal';

    if( $overuseParams->{'OveruseBlock'} eq 'true' ) {
        if ( $overuseParams->{'OveruseSuspend'} eq 'true') {
            $overuse = 'block';
        } else {
            if ( $overuseParams->{'OveruseNotify'} eq 'true') {
                $overuse = 'not_suspend_notify';
            } else {
                $overuse = 'not_suspend';
            }
        }
    } else {
        if( $overuseParams->{'OveruseNotify'} eq 'true' ){
            $overuse = 'notify';
        }
        else {
            $overuse = 'normal';
        }
    }

    # 3) Put value into domain limit
    $self->{packer}->addDomainLimit( $parentId, 'overuse', $overuse );
}

sub addClientOverusePolicy{
    my( $self, $parentId, $params ) = @_;

    if( exists $params->{'DecompositionRule'} ){
        my $oversell = ($params->{'DecompositionRule'} eq 'oversell')? 'true' : 'false';
        $self->{packer}->addClientLimit( $parentId, 'oversell', $oversell );
    }

    my $overuse;
    if( $params->{'OveruseBlock'} eq 'true' ){
        $overuse = 'block';
    }
    elsif( $params->{'OveruseNotify'} eq 'true' ){
        $overuse = 'notify';
    }
    else {
        $overuse = 'normal';
    }

    $self->{packer}->addClientLimit( $parentId, 'overuse', $overuse );
}

sub addPermissions {
    my ( $self, $parent, $parentType, $id ) = @_;

    unless($parentType eq 'client' || $parentType eq 'domain-personal') {
        Logging::warning( "Error: addPermissions: Unexpected type of parent \"$parentType\"",'assert');
        return;
    }

    my $permsHash = $self->getPermsHash($id);
    unless ($permsHash) {
        if ( $parentType eq 'client' ) {
            $permsHash = $self->fillDefaultClientPermissions();
        }
        else {
            $permsHash = $self->fillDefaultDomainPermissions();
        }
    }
    return unless $permsHash;

    foreach my $key ( keys %{$permsHash} ) {
        if ( $parentType eq 'client' ) {
            $self->{packer}->addClientPermission( $parent, $key, $permsHash->{$key} );
        }
        else {
            $self->{packer}->addDomainPersonalPermission( $parent, $key, $permsHash->{$key} );
        }
    }
}

sub getXmlPermissions {
    my ( $self ) = @_;
    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,
        'allow_account_local_backups' => 1,
        'allow_account_ftp_backups'   => 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,
        'manage_mail_settings'    => 1,
        'manage_protected_dirs'   => 1,
        'manage_website_maintenance' => 1,
        'manage_php_settings'     => 1,
        'manage_php_version'      => 1,
        'manage_server_actionlog'=> 1,
        'manage_server_admin_access' => 1,
        'manage_server_app_vault' => 1,
        'manage_server_backup' => 1,
        'manage_server_branding' => 1,
        'manage_server_coldfusion' => 1,
        'manage_server_components' => 1,
        'manage_server_crontab' => 1,
        'manage_server_custom_buttons' => 1,
        'manage_server_db' => 1,
        'manage_server_dns_template' => 1,
        'manage_server_domain_traffic_report' => 1,
        'manage_server_events' => 1,
        'manage_server_filesharing' => 1,
        'manage_server_firewall' => 1,
        'manage_server_google_services' => 1,
        'manage_server_info' => 1,
        'manage_server_ip_addresses' => 1,
        'manage_server_languages' => 1,
        'manage_server_license' => 1,
        'manage_server_mail' => 1,
        'manage_server_maillist' => 1,
        'manage_server_mass_email' => 1,
        'manage_server_migration' => 1,
        'manage_server_modules' => 1,
        'manage_server_notifications' => 1,
        'manage_server_optimization' => 1,
        'manage_server_preview_domain' => 1,
        'manage_server_reboot' => 1,
        'manage_server_services' => 1,
        'manage_server_sessions' => 1,
        'manage_server_settings' => 1,
        'manage_server_shutdown' => 1,
        'manage_server_skeleton' => 1,
        'manage_server_spam_filter' => 1,
        'manage_server_ssl_certificates' => 1,
        'manage_server_summary_report' => 1,
        'manage_server_support' => 1,
        'manage_server_time' => 1,
        'manage_server_ui' => 1,
        'manage_server_updates' => 1,
        'manage_server_virus_protection' => 1,
        'manage_server_vzpp' => 1,
        'manage_server_webmail' => 1,
        'manage_server_mail_black_white_lists' => 1,
        'manage_iis_app_pool' => 1,
        'manage_shared_ssl' => 1,
        'manage_additional_permissions' => 1,
        'manage_server_mailgate' => 1,
        'change_limits' => 1,
        'access_service_users' => 1,
        'remote_db_connection' => 1,
        'manage_wordpress_toolkit' => 1,
        'manage_security_wordpress_toolkit' => 1,
        'rename_domains' => 1,
    );
    return \%xmlPermissions;
}

sub getPermsHash {
    my ( $self, $permsId ) = @_;

    return unless $permsId;

    my $sql = "SELECT permission,value FROM Permissions WHERE id=?";

    my $xmlPermissions = $self->getXmlPermissions();

    my %permsHash;
    if ( $self->{dbh}->execute_rownum($sql, $permsId) ) {
        while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
            my $name  = $ptrRow->[0];
            my $value = HelpFuncs::convertToTrueFalseString($ptrRow->[1]);
            $name  = "manage_virusfilter" if $name eq "manage_drweb";
            $name  = "manage_php_settings" if $name eq "manage_php_safe_mode";

            next unless exists $xmlPermissions->{$name};

            if ( $name eq 'make_dumps' ) {
                $permsHash{'allow_local_backups'} = $value;
                $permsHash{'allow_ftp_backups'} = $value;
                $permsHash{'allow_account_local_backups'} = $value;
                $permsHash{'allow_account_ftp_backups'} = $value;
                next;
            }

            $permsHash{$name} = $value;

            if (!PleskVersion::atLeast(12, 1, 23) && $name eq 'manage_phosting') {
                $permsHash{'manage_php_version'} = $value;
            }
            if (!PleskVersion::atLeast(12, 1, 25)) {
                if ($name eq 'allow_local_backups') {
                    $permsHash{'allow_account_local_backups'} = $value;
                } elsif ($name eq 'allow_ftp_backups') {
                    $permsHash{'allow_account_ftp_backups'} = $value;
                }
            }
        }
    }
    $self->{dbh}->finish();

    $sql = "SELECT value FROM Permissions WHERE id = ? and ("
    . "permission = 'ipb_allow' or permission = 'nb_allow')";
    if ( $self->{dbh}->execute_rownum($sql, $permsId) ) {
        if ( my $ptrRow = $self->{dbh}->fetchrow() ) {
            my $value = HelpFuncs::convertToTrueFalseString(@{ $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 ) = @_;

    if ( (!defined $value) || ($value eq '') ) {
        $value = '-1';
    }
    if( $value ne '-1') {
        if ( $name eq 'expiration' ) {
            my ( $mday, $mon, $year ) = ( localtime($value) )[ 3 .. 5 ];
            $mon++;
            $year += 1900;
            $value = sprintf( '%04d-%02d-%02d', $year, $mon, $mday );
        }
    }

    if ( $parentType eq 'client' || $parentType eq 'reseller' ) {
        $self->{packer}->addClientLimit( $parent, $name, $value );
    }
    elsif ( $parentType eq 'domain' ) {
        $self->{packer}->addDomainLimit( $parent, $name, $value );
    }
    else {
        Logging::warning( "Error: insertLimits: Unexpected type of parent \"$parentType\"",'assert');
    }
}

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 addDomainLimits {
    my ( $self, $domainId, $limitsId ) = @_;
    return $self->addLimits( $domainId, 'domain', $limitsId, \%domainLimits );
}

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';
                my $val = ($key eq 'max_site' && !PleskVersion::atLeast( 10, 0, 0 )) ? "1" : "-1";
                $self->insertLimit( $parent, $parentType, $key, $val );
                $count++;
            }
        }
        return $count;
    }

    my ( $sql, $ptrRow, $value, $name );
    $sql = "SELECT limit_name,value FROM Limits WHERE id=?";
    if ( $self->{dbh}->execute_rownum($sql, $limitId) ) {
        $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;
}

sub dumpDomainTemplates {
    my ( $self, $clientId, $clientType ) = @_;

    if ( PleskVersion::atLeast( 10, 0, 0 ) ) {
        $self->dumpTemplates( $clientId, $clientType, 'domain' );
        $self->dumpTemplates( $clientId, $clientType, 'domain_addon' );
    }
    else {
        my $sql;
        my $tmpl_id;

        if ($clientId) {

            $sql = "SELECT tmpl_id FROM clients WHERE id=?";

            if ( $self->{dbh}->execute_rownum($sql, $clientId) ) {
                if( my $rowPtr = $self->{dbh}->fetchrow() ) {
                    $tmpl_id = @{ $rowPtr }[0];
                }
            }
            $self->{dbh}->finish();
            $self->dumpTemplates9x( $clientId, $clientType, $tmpl_id, 'domain' ) if $tmpl_id;
        }
    }
}

sub dumpResellersTemplates {
    my ( $self, $resellers ) = @_;

    return unless (ref($resellers) =~ /ARRAY/ && $self->{packer}->{migrationMode} && PleskVersion::atLeast( 10, 0, 0 ));

    foreach my $reseller (@{$resellers}) {
        $self->dumpResellerTemplate(PleskStructure::getClientId($reseller));
    }
}

sub dumpResellerTemplate {
    my ( $self, $resellerId ) = @_;

    return unless ($self->{packer}->{migrationMode} && PleskVersion::atLeast( 10, 0, 0 ));

    my $planId = PleskStructure::getResellerPlanId($resellerId);
    if (defined $planId) {
        my $adminId = PleskStructure::getAdminId();
        $self->dumpTemplate($adminId, 'server', $planId, 'reseller');
    }
}

sub dumpTemplates {
    my ( $self, $parent, $parentType, $templateType ) = @_;
    return $self->dumpTemplates9x($parent, $parentType, undef, $templateType);
}

# explicit function for dumping single plan to avoid legacy suffix 9x
sub dumpTemplate {
    my ( $self, $parent, $parentType, $template, $templateType ) = @_;
    return $self->dumpTemplates9x($parent, $parentType, $template, $templateType);
}

# list of attributes for template-item node in dump.xml
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,
    '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_handler_id'  => 1,
    'php_safe_mode'   => 1,
    'php_served_by_nginx'    => 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,
    'mailservice'     => 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,
    'allow_account_local_backups' => 1,
    'allow_account_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,
    'manage_website_maintenance' => 1,
    'manage_protected_dirs'   => 1,
    'manage_mail_settings'    => 1,
    'manage_php_settings'     => 1,
    'access_service_users'	=> 1,
    'manage_php_version'      => 1,
    'rename_domains'          => 1,

    'excl_ip_num'             => 1,
    'excl_ipv6_num'           => 1,
    'max_dom'                 => 1,

    #reseller
    'create_clients'          => 1,
    'allow_oversell'          => 1,
    'oversell'                => 1, #overselling
    'overuse'                 => 1,
    'max_cl'                  => 1,

    #server
    'shared'                  => 1,

    #windows specific permissions
    'manage_additional_permissions' => 1,
    'manage_iis_app_pool'     => 1,
    'manage_shared_ssl'       => 1,

    #windows specific limits
    'max_cf_dsn'              => 1,
    'max_mssql_db'            => 1,
    'max_odbc'                => 1,
    'max_shared_ssl_links'    => 1,
    'mssql_dbase_space'       => 1,
    'mysql_dbase_space'       => 1,
    'max_iis_app_pools'       => 1,

    'unpaid_website_status'   => 1,
    'remote_db_connection'    => 1,

    'outgoing_messages_domain_limit'          => 1,
    'outgoing_messages_mbox_limit'            => 1,
    'outgoing_messages_subscription_limit'    => 1,
    'outgoing_messages_enable_sendmail'       => 1,

    'manage_wordpress_toolkit'            => 1,
    'manage_security_wordpress_toolkit'   => 1,

    'access_service_users' => 1,
);

my %dumped_templates;
sub dumpTemplates9x {
    my ( $self, $parent, $parentType, $tmpl_id, $templateType ) = @_;

    my $sql;

    my $ownerGuid;

    my %templateNames;
    my %templateUuids;
    my %templateExtIds;

    if ( PleskVersion::atLeast( 10, 0, 0 ) ) {
        $ownerGuid = PleskStructure::getClientGuidFromId($parent);

        $sql = "SELECT * FROM Templates WHERE ";
        my @sqlParams = ();
        if ($tmpl_id) {
            $sql .= "id = ?";
            push(@sqlParams, $tmpl_id);
        } else {
            $sql .= "owner_id = ? AND type = '?'";
            push(@sqlParams, $parent, $templateType);
        }

        if ( $self->{dbh}->execute($sql, @sqlParams) ) {
            while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
                my $id = $ptrHash->{'id'};
                if (not exists $dumped_templates{$id}) {
                    $dumped_templates{$id} = 1;
                    $templateNames{ $id } = $ptrHash->{'name'};
                    $templateUuids{ $id } = $ptrHash->{'uuid'};
                    $templateExtIds{ $id } = $ptrHash->{'external_id'} if defined $ptrHash->{'external_id'} and $ptrHash->{'external_id'} ne '';
                }
            }
        }
    }
    elsif ($tmpl_id) {
        $sql = "SELECT t.id, t.name FROM Templates t, Repository r WHERE r.rep_id=? AND r.component_id = t.id";
        if ( $self->{dbh}->execute($sql, $tmpl_id) ) {
            while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
                $templateNames{ $ptrRow->[0] } = $ptrRow->[1];
            }
        }
    } else {
        Logging::debug("Warning: Empty template ID provided while dumping templates");
    }
    $self->{dbh}->finish();

    my %planItems;
    my @planItemNames = @{$self->getPlanItemNames()};

    while ( my ( $template_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 %phpSettings = undef;
        my %phpHandlerSettings = ('php_handler_id' => undef, 'php_handler_type' => undef, 'php_version' => undef);
        my @defaultDbServers;
        my %webServerSettings;

        if ( $self->{dbh}->execute("SELECT element, value FROM TmplData WHERE tmpl_id=?", $template_id) ) {
            my @tmplData;
            while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
                my @tmplElem = @{$ptrRow};
                push @tmplData, \@tmplElem;
            }

            foreach my $ptrRow (@tmplData) {
                my ( $element, $value ) = @{$ptrRow};

                if ( !defined($value) ) {
                    $value = '';
                }

                if ( $element eq 'stat_ttl' and ( $value == 0 or $value eq '' ) ) {
                    $value = -1;
                }

                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 ) ) {
                        if ( grep $_ eq $element, @planItemNames) {
                            $planItems{ $element } = $value;
                        }
                        if ($element eq 'aps_bundle_filter_id') {
                            $aps_bundle_filter_id = $value;
                        }
                    }

                    if ( PleskVersion::atLeast( 10, 4, 0) ) {
                        if ($element eq 'phpSettingsId') {
                            %phpSettings = %{DAL::getPhpSettings($value)};
                            my $phpSettingsNoticeText = DAL::getPhpSettingsNoticeText($value);
                            $phpSettings{'notice'} = $phpSettingsNoticeText if defined $phpSettingsNoticeText;
                        }
                    }

                    if ( PleskVersion::atLeast( 11, 0, 0) ) {
                        if ($element eq 'default_server_mysql'
                                or $element eq 'default_server_postgresql'
                        ) {
                            foreach my $ptrHash (@{DAL::getDatabaseServers()}) {
                                if ($value == $ptrHash->{'id'}) {
                                    push @defaultDbServers, $ptrHash;
                                }
                            }
                        }
                    }

                    if ( PleskVersion::atLeast( 11, 5, 21 ) ) {
                        if ($element eq 'webServerSettingsId') {
                            my $webServerSettings = DAL::getWebServerSettings($value);
                            while (my ($settingName, $settingValue) = each(%{$webServerSettings})) {
                                my @data = ( $settingName, $self->{packer}->{base64}->{'ENCODE'}->($settingValue) );
                                push @template, \@data;
                            };
                        }
                    }

                    next;
                }

                if ( 'domain_addon' eq $templateType ) {
                    # skip items which are not allowed for domain_addon template
                    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
                    );
                    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;
                }

                my %emptyableBoolElementInTemplate = (
                    'asp'             => 1,
                    'cgi'             => 1,
                    'coldfusion'      => 1,
                    'pdir_plesk_stat' => 1,
                    'perl'            => 1,
                    'php'             => 1,
                    'python'          => 1,
                    'ssi'             => 1
                );
                if ( $value eq '' and exists $emptyableBoolElementInTemplate{$element} ) {
                    $value = 'false';
                }

                if (exists($phpHandlerSettings{$element})) {
                    $phpHandlerSettings{$element} = $value;
                    next;
                } elsif (!PleskVersion::atLeast(12, 1, 23) && $element eq 'manage_phosting') {
                    my @data = ('manage_php_version', $value);
                    push @template, \@data;
                } elsif (!PleskVersion::atLeast(12, 1, 25)) {
                    my @data;
                    if ($element eq 'allow_local_backups') {
                        @data = ('allow_account_local_backups', $value);
                    } elsif ($element eq 'allow_ftp_backups') {
                        @data = ('allow_account_ftp_backups', $value);
                    }
                    if (@data) {
                        push @template, \@data;
                    }
                }

                my @data = ( $element, $value );
                push @template, \@data;
            }
        }
        $self->{dbh}->finish();

        my $logRotation = {};
        if ( $logrotation_id and ( $templateType eq 'domain' ) ) {
            $logRotation = DAL::getLogrotation($logrotation_id);
        }

        my %ipPool;
        if ( $ip_pool_id and ( $templateType eq 'reseller' or $templateType eq 'client' ) ) {
            %ipPool = $self->makeTemplateIpPool($ip_pool_id);
        }

        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 {
                        if ($overuse_notify eq 'true') {
                            push @data, 'not_suspend_notify';
                        } 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;
        }

        if ($phpHandlerSettings{'php_handler_id'}) {
            my $phpHandlerId = $phpHandlerSettings{'php_handler_id'};
            push(@template, ['php_handler_id', $phpHandlerId]);
            my $phpHandlers = HostingDumper::getPhpHandlers();
            if (exists($phpHandlers->{$phpHandlerId})) {
                push(@template, ['php_handler_type', $phpHandlers->{$phpHandlerId}{'type'}]);
                push(@template, ['php_version', $phpHandlers->{$phpHandlerId}{'version'}]);
            }
        } else {
            push(@template, ['php_handler_type', $phpHandlerSettings{'php_handler_type'}]) if $phpHandlerSettings{'php_handler_type'};
            push(@template, ['php_version', $phpHandlerSettings{'php_version'}]) if $phpHandlerSettings{'php_version'};
        }

        my @templateAttrs;

        if ( defined $templateUuids{$template_id} ) {
            my @guidAttr = ( 'guid', $templateUuids{$template_id});
            push @templateAttrs, \@guidAttr;
        }

        if ( defined $templateExtIds{$template_id} ) {
            my @extIdAttr = ( 'external-id', $templateExtIds{$template_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)) {
            @apsFilterItems = @{DAL::getApsBundleFilterItems($aps_bundle_filter_id)};
            $filterType = DAL::getApsBundleFilterType($aps_bundle_filter_id);
        }

        my %packerData;
        $packerData{'attributes'} = \@templateAttrs;
        $packerData{'data'} = \@template;
        $packerData{'items'} = \%planItems;
        $packerData{'log-rotation'} = $logRotation;
        $packerData{'ip-pool'} = \%ipPool;
        $packerData{'aps-filter-items'} = \@apsFilterItems;
        $packerData{'aps-filter-type'} = $filterType;
        $packerData{'php-settings'} = \%phpSettings;
        $packerData{'default-db-servers'} = \@defaultDbServers;

        if ( $parentType eq 'server' ) {
            $self->{packer}->addTemplateToServer($templateType, $tmpl_name, \%packerData);
        }
        elsif ( $parentType eq 'reseller' ) {
            if ( 'domain' eq $templateType or 'domain_addon' eq $templateType) {
                $self->{packer}->addResellerDomainTemplate( $parent, $tmpl_name, \%packerData);
            }
            else {
                Logging::warning("Error: dumpTemplates: unexpected template type \"$templateType\" for parent \"$parentType\"",'assert');
            }
        }
        elsif ( $parentType eq 'client' ) {
            if ( 'domain' eq $templateType or 'domain_addon' eq $templateType) {
                $self->{packer}->addClientDomainTemplate( $parent, $tmpl_name, \%packerData);
            }
            else {
                Logging::warning("Error: dumpTemplates: unexpected template type \"$templateType\" for parent \"$parentType\"",'assert');
            }
        }
        else {
            Logging::warning("Error: dumpTemplates: Unexpected type of parent \"$parentType\"",'assert');
        }


    }
}

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` = ?";

    my %ipPool = ();

    if ( $self->{dbh}->execute_rownum($sql, $ip_pool_id) ) {
        while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
            $ipPool{ $ptrRow->[0] } = $ptrRow->[1];
        }
    }
    $self->{dbh}->finish();

    return %ipPool;
}

sub makeDomainNode {
    my ( $self, $domainName, $isroot ) = @_;

    $self->{isAnyDbDumpedOnDomain} = 0;

    if( $self->domainExcluded( $domainName ) ) {
        Logging::debug("Domain '$domainName' is excluded from dump");
        return;
    }

    foreach my $dumpedDomain( @{$self->{dumped_domains}} ){
        if( $dumpedDomain eq $domainName ){
            Logging::debug("Domain '$domainName' already dumped");
            return;
        }
    }
    push @{$self->{dumped_domains}}, $domainName;

    Logging::debug("Domain '$domainName' is started");
    Logging::beginObject('domain',$domainName, $self->generateObjectUuidForLogging('domain', $domainName));

    $self->{dump_status}->startDomain($domainName);

    my ( $sql, $ptrHash );

    Logging::debug("Getting domain info");
    my $domainPtr;
    $domainPtr = DAL::getDomainPtr($domainName);

    unless( $domainPtr ) {
        Logging::error("Failed to get domain hash for '$domainName'",'PleskDbError');
        Logging::endObject();
        return;
    }
    my %domain = %{$domainPtr};

    # There are no guid field in 'domains' table in the old Plesk, but currently it is a required parameter, so we set empty guid here and it will be replaced with non-empty value by resolver
    $domain{'guid'} = '' if !defined $domain{'guid'};

    my $domainId = $domain{'id'};
    my $clientId = $domain{'cl_id'};

    $self->_initDomainContentStat($domainId);

    if ($self->{packer}->{migrationMode} && !$self->{admin_info} && !$self->{packer}->{listingOnlyMode} && $isroot && PleskVersion::atLeast( 10, 0, 0)) {
        my @roles = @{DAL::selectSmbRoles()};
        foreach my $role ( @roles ) {
            my $roleAdded = 0;
            my @users = @{DAL::getUsersLoginsByRoleId($role->{'id'})};
            foreach my $userLogin ( @users ) {
                my $userHash = DAL::getUserHash($userLogin);
                next if (! keys %$userHash);  # if hash is empty (error occurred)
                if ((defined $userHash->{'subscriptionDomainId'} && $userHash->{'subscriptionDomainId'} == $domainId) || (defined $userHash->{'mailDomId'} && $userHash->{'mailDomId'} == $domainId)) {
                    $userHash->{'subscriptionDomainId'} = $domainId; # restrict users, which is moved to admin, to block possible access of a customer to all admin's subscriptions
                    if (!$roleAdded) {
                        $self->{packer}->addRootRole( $role->{'name'}, $role->{'isBuiltIn'}, $self->getRolePermissions($role->{'id'}), DAL::getRoleServicePermissions($role->{'id'}) ) ;
                        $roleAdded = 1;
                    }
                    $self->{packer}->addRootUser($userLogin, $userHash);
                }
            }
        }
    }

    my $embeddedInfo = undef;
    if (PleskVersion::atLeast(11, 1, 0)) {
        my $cmd = AgentConfig::getBackupRestoreHelperUtil();
        $cmd .= " --backup-subscription " . $domain{'name'};
        Logging::debug( "Exec: $cmd" );
        my $ret = `$cmd`;
        my $retCode = $? >> 8;
        if ($retCode == 0) {
            eval {require XML::Simple; 1;};
            my $xs = XML::Simple->new(ForceArray => 1, SuppressEmpty => '');
            $embeddedInfo = $xs->XMLin($ret, KeyAttr => []);
        } else {
            Logging::warning( "Cannot execute backup helper: " . $cmd . ", STDOUT:" . $ret, 'UtilityError');
        }
    }

    if ( exists $domain{'vendor_id'} ){
        my $vendorGuid = PleskStructure::getClientGuidFromId( $domain{'vendor_id'} );
        $domain{'vendor-guid'} = $vendorGuid;
        my $vendorLogin = PleskStructure::getClientNameFromId( $domain{'vendor_id'} );
        $domain{'vendor-login'} = $vendorLogin;
    }

    if ( defined PleskStructure::getClientGuidFromId($clientId) ) {
        $domain{'owner-guid'} = PleskStructure::getClientGuidFromId($clientId);
        $domain{'owner-name'} = PleskStructure::getClientNameFromId($clientId);
    }

    my $clientPermsId;
    $sql = "SELECT perm_id FROM clients WHERE id = ?";

    if ( $self->{dbh}->execute_rownum($sql, $clientId) and my $ptrRow = $self->{dbh}->fetchrow() ) {
        $clientPermsId = $ptrRow->[0];
    }
    $self->{dbh}->finish();

    my $domainAsciiName;
    $domainName = $domain{'displayName'};
    $domainAsciiName = $domain{'name'};

    my $domainOwner = PleskStructure::getClientNameFromId( $domain{'cl_id'} );
    my $parentType = PleskVersion::isShm() ? 'admin' : PleskStructure::getClientType( $domainOwner );
    if( $isroot ){
        if ($self->{admin_info} or $self->{server_settings}) {
            $self->{packer}->addAdminDomain( $domainId, $domainName, $domainAsciiName, \%domain );
        } else {
            $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->{suspender}->suspendDomain( $domainName );

    $domainAsciiName = $domain{'name'};
    my $domainType      = $domain{'htype'};

    $self->addWwwStatus( $domainId, $domainAsciiName ) unless $self->{capability_dump};

    # Domain's IP address
    Logging::debug("Getting domain IP");
    my $ip = PleskStructure::getDomainIp($domainName);
    if ($ip) {
        foreach my $ipAddress (@{$ip}) {
            $self->{packer}->setDomainIP( $domainId, $ipAddress, PleskStructure::getClientIpType( $domainOwner, $ipAddress ) );
        }
    }

    #
    # Status
    #
    Logging::debug("Dumping domain status");

    my $status = 0;
    my $siteStatus = undef;

    $status = $domain{'status'};
    if ( PleskVersion::atLeast( 10, 0, 0 ) ) {
        $status += 0;
        $status = $domain{'webspace_status'};
        $siteStatus = $self->getSiteStatus($domain{'status'}, \%domain);
    } elsif ( $domain{'status'} != $Status::ENABLED ) {
        $siteStatus = $self->getSiteStatus($Status::WITH_PARENT, \%domain);
    } else {
        $siteStatus = $self->getSiteStatus($domain{'status'}, \%domain);
    }

    $self->{packer}->setWebspaceStatus( $domainId, $status, $siteStatus ) unless $self->{capability_dump};

    $self->_markDomainWithRemoteDbAsMigrated($domainId) unless $self->{only_mail_dump};

    my $subscriptionNode;

    unless ($self->{only_mail_dump} or $self->{capability_dump}) {
        if ( PleskVersion::atLeast( 10, 0, 0 ) ) {
            $subscriptionNode = $self->getSubscription('domain', $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

        Logging::debug("Dumping domain databases");
        foreach my $db (@{DAL::getDomainDatabases($domainId)}) {
            eval {
                $self->makeDatabaseNode( $db->{'id'}, $domainId, 'domain', undef, undef, $embeddedInfo );
                1;
            } or do {
                $self->excludeProblemDatabaseFromBackup( $domainId, $db, $@ );
            }
        }

        eval {
            # 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 ) {
                    foreach my $ipAddress (@{$ip}) {
                        $self->{packer}->setDomainIP( $domainId, $ipAddress, $type );
                    }
                } else {
                    my $serviceIps = $self->getServiceIps( $domainId, 'mail', $ip );
                    if ($serviceIps) {
                        foreach my $serviceIp (keys %{$serviceIps}) {
                            $self->{packer}->setDomainIP( $domainId, $serviceIp, $serviceIps->{$serviceIp} );
                            last;
                        }
                    }
                }
            }
            1;
        } or do {
            $self->excludeProblemDnsZoneFromBackup( $domainId, $@ );
        };

        $self->{packer}->removeDomainDnsZone($domainId);

        $self->{suspender}->unSuspendDomain();

        $self->{capability}->dumpDomain($domainId, $domainName, $domainAsciiName);

        $self->{packer}->finishDomain($domainId);
        $self->{dump_status}->endDomain($domainName);
        Logging::endObject();
        $self->{packer}->{stat}{dbDumpsCount}++ if ($self->{isAnyDbDumpedOnDomain} == 1);
        return;
    }

    # Check whether this domain is default on IP

    $sql = "SELECT COUNT(*) FROM IP_Addresses WHERE default_domain_id=?";
    if ( $self->{dbh}->execute_rownum($sql, $domainId) ) {
        if ( @{ $self->{dbh}->fetchrow() }[0] ne "0" ) {
            $self->{packer}->setDomainDefault($domainId);
        }
    }
    $self->{dbh}->finish();

    if ( !$self->{only_mail_dump} ) {
        # Domain's aliases
        $self->dumpDomainAliases($domainId);
    }

    my $domParams = $self->getDomainParams(\%domain);

    Logging::debug("Getting domain limits and permissions");

    if ( !$self->{only_mail_dump} ) {
        if ( PleskVersion::atLeast( 11, 1, 16 ) ) {
            #
            # for limits and permissions at least 11.1.16
            #
            my $subscriptionProperties = DAL::getDomainSuscriptionProperties($domainId);
            $self->addDomainLimits( $domainId, $subscriptionProperties->{'limitsId'} );
            $self->addDomainOverusePolicy( $domainId, $subscriptionProperties );
            # The following call of fixDefaultLimits should be before permissions dumping to avoid validation by schema fails
            $self->{packer}->fixDefaultLimits('domain',$domainId);
            $self->dumpDomainPersonalPermissions( $domainId, $subscriptionProperties->{'permissionsId'} );
        }
        else {
            #
            # limits
            #
            $self->addDomainLimits( $domainId, $domain{'limits_id'} );
            $self->addDomainOverusePolicy( $domainId, $domParams );
            #
            # end limits
            #
            # The following call of fixDefaultLimits should be before permissions dumping to avoid validation by schema fails
            $self->{packer}->fixDefaultLimits('domain',$domainId);
            #
            # permissions
            #
            if ( PleskVersion::atLeast( 10, 0, 0) ) {
                $self->dumpDomainPersonalPermissions( $domainId, $domain{'permissions_id'} );
            }
            else {
                $self->dumpDomainPersonalPermissions( $domainId, $clientPermsId );
            }
            #
            # end permissions
            #
        }
    }

    if ( !$self->{only_hosting_dump} ) {
        eval {
            $self->dumpMailSystem( $parentType, $domParams, \%domain, $isroot, $ip );
            1;
        } or do {
            $self->excludeProblemMailSystemFromBackup( $domainId, $@ );
        }
    } else {
        Logging::debug("Skip domain mailsystem dump due to settings");
    }

    my @SiteApplications;
    my @excluded_dbs;
    my @excluded_dns_records;
    my $applicationsInstalled = 1;

    if ($self->{configuration_dump} && $self->{packer}->{listingOnlyMode}) {
        $applicationsInstalled = DAL::isInstalledApplicationsWithDatabases($domainId);
    } else {
        $applicationsInstalled = DAL::isInstalledApplications($domainId);
    }

    #-----------------------------------------------------------------
    # Information about domain's site applications
    # should be extracted prior to database/hosting/custom-buttons/dns-records
    # dump, as site applications' linked resources should be dumped
    # separately.
    #-----------------------------------------------------------------

    if ($applicationsInstalled) {
        @SiteApplications = SiteApp::getDomainSiteapps($domainId, $self->{dbh});

        foreach my $sapp (@SiteApplications) {
            foreach my $row ( $sapp->getDatabases() ) {
                push @excluded_dbs, $row->{'id'};
            }

            foreach my $row ( $sapp->getDnsRecords() ) {
                push @excluded_dns_records, $row->{'id'};
            }
        }
    }

    if ( !$self->{only_mail_dump} ) {
        #-----------------------------------------------------------------
        # Domain's databases
        #-----------------------------------------------------------------
        Logging::debug("Dumping domain databases");

        # Databases without those used in site apps
        foreach my $db (@{DAL::getDomainDatabases($domainId, \@excluded_dbs)}) {
            eval {
                $self->makeDatabaseNode( $db->{'id'}, $domainId, 'domain', undef, undef, $embeddedInfo );
                1;
            } or do {
                $self->excludeProblemDatabaseFromBackup( $domainId, $db, $@ );
            }
        }

        if ( PleskVersion::atLeast( 11, 1, 0 ) ) {
            Logging::debug("Dumping database users belonging to any database");
            $self->{packer}->makeDbUsersToAnyDatabasesNode( $domainId, DAL::getCommonDatabaseUsers( $domainId, $embeddedInfo ) );
        }
    } else {
        Logging::debug("Skip dumping domain databases due to settings");
    }

    eval {
        # Domain's DNS settings
        $self->makeDomainDnsZone( \%domain, \@excluded_dns_records );
        if ((!defined $ip) && ( $domainType ne 'vrt_hst' ) ) {
            my $preferredIp = $self->{packer}->getDomainARecordIp($domainId);
            my $type;
            $ip = PleskStructure::getIp4DomainWoHosting($domainId, $preferredIp, \$type);
            if ( $ip ) {
                foreach my $ipAddress (@{$ip}) {
                    $self->{packer}->setDomainIP( $domainId, $ipAddress, $type );
                }
            } else {
                my $serviceIps = $self->getServiceIps( $domainId, 'mail', $ip );
                if ($serviceIps) {
                    foreach my $serviceIp (keys %{$serviceIps}) {
                        $self->{packer}->setDomainIP( $domainId, $serviceIp, $serviceIps->{$serviceIp} );
                        last;
                    }
                }
            }
        }
        1;
    } or do {
        $self->excludeProblemDnsZoneFromBackup( $domainId, $@ );
    };

    $self->{packer}->makeDomainParamsNode($domainId, $domParams);

    $self->makeMailListsNode($domainId, $domainAsciiName, $ip);

    if ( !$self->{only_mail_dump} ) {
        Logging::debug("Dumping domain statistics");

        $self->addDomainTraffic($domainId);

        $self->addDomainCertificates($domainId, $domainName, defined($domain{'cert_rep_id'}) ? $domain{'cert_rep_id'} : '');

        my $tomcatServiceStatus = DAL::getDomainTomcatStatus($domainId);
        if (defined $tomcatServiceStatus) {
            my $serviceIps = $self->getServiceIps( $domainId, 'tomcat', $ip);
            $self->{packer}->setDomainTomcat( $domainId, $tomcatServiceStatus, $serviceIps );
            my $webapps = DAL::getDomainWebApps($domainId);
            foreach my $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);
            }
        }

    }

    my $subscriptionProperties = undef;

    if ( PleskVersion::atLeast(10, 4, 0)) {

        $subscriptionProperties = $self->getSubscriptionPropertiesHash($domainId, 'domain');

        if (exists $subscriptionProperties->{'phpSettingsId'}) {

            my $phpSettingsId = $subscriptionProperties->{'phpSettingsId'};

            my %phpSettings = %{DAL::getPhpSettings($phpSettingsId)};

            my $phpSettingsNoticeText = DAL::getPhpSettingsNoticeText($phpSettingsId);

            $phpSettings{'notice'} = $phpSettingsNoticeText if defined $phpSettingsNoticeText;

            $self->{packer}->setDomainPhpSettingsNode($domainId, \%phpSettings);
        }

    }

    if ( not PleskVersion::atLeast(10, 0, 0) ) {
        use LegacyDomainUserDumper;
        my $ldud = LegacyDomainUserDumper->new($self);
        $ldud->makeDomainUserNode($domainId, $domParams);
    }

    eval {
        my $serviceIps = $self->getServiceIps( $domainId, 'web', $ip);

        if ( $domainType eq "vrt_hst" ) {
            $self->makePhostingNode( \%domain, $domParams, \@SiteApplications, 0, $serviceIps, $embeddedInfo);
        }
        elsif ( $domainType eq "std_fwd" ) {
            $self->makeShostingNode( \%domain, $serviceIps );
        }
        elsif ( $domainType eq "frm_fwd" ) {
            $self->makeFhostingNode( \%domain, $serviceIps );
        }
        1;
    } or do {
        $self->excludeProblemHostingFromBackup( $domainId, $domainType, $@ );
    };


    if ( !$self->{only_mail_dump} ) {
        $self->getCustomButtonsByOwner( 'domain-admin', $domainId );

        if ( PleskVersion::atLeast( 10, 0, 0 ) ) {
            $self->{packer}->dumpUnityMobileIntegration($domainId, $domParams);
            if ( PleskVersion::atLeast( 10, 1, 0 ) ) {
                $self->getSubscriptionProperties('domain', $domainId);
                $self->dumpApsBundle($domainId, 'domain');
            }

            if ( PleskVersion::atLeast( 11, 0, 0 ) ) {
                my @defaultDbServers;
                my @availableTypes = ('default_server_mysql','default_server_postgresql');
                foreach my $defaultDbServerType (@availableTypes) {
                    if ( exists $subscriptionProperties->{$defaultDbServerType}) {
                        foreach my $ptrHash (@{DAL::getDatabaseServers()}) {
                            if ($subscriptionProperties->{$defaultDbServerType} == $ptrHash->{'id'}) {
                                push @defaultDbServers, $ptrHash;
                            }
                        }
                    }
                }

                if (@defaultDbServers) {
                    $self->{packer}->makeDomainDefaultDbServersNode($domainId, \@defaultDbServers);
                }
            }

            if ( PleskVersion::atLeast( 11, 5, 23 ) ) {
                if (ref($subscriptionNode) =~ /XmlNode/) {
                    $self->{packer}->makeSubscriptionPropertiesNode($subscriptionNode, $subscriptionProperties);
                }

                if ($applicationsInstalled) {
                    foreach my $sapp (@SiteApplications) {
                        my $apsRegistryId = $sapp->getApplicationApsRegistryId();
                        foreach my $row ( $sapp->getMailUsers() ) {
                            $self->{packer}->assignApplicationInfoToMailUser($apsRegistryId, $row);
                        }
                    }
                }
            }
        }
    }

    $self->{suspender}->unSuspendDomain();

    $self->processExtensionPackage('domain', $domainName, $domainId);

    if ( exists $domainPtr->{'description'} ) {
        $self->{packer}->makeDomainDescriptionsNode( $domainId, $domainPtr->{'description'} );
    }

    if ( !$self->{only_mail_dump} && PleskVersion::atLeast(12, 0, 11)) {
        my @wpInstances = @{DAL::getSubscriptionWordPressNonApsInstances($domainId)};

        if ( @wpInstances ) {
            foreach my $instance (@wpInstances) {
                my @wpInstanceProperties = @{DAL::getWordPressInstanceProperties($instance->{'id'})};
                $self->{packer}->setNonApsWordpressInstance($domainId, $instance, \@wpInstanceProperties, 'false');
            }
        }
    }

    if (!$self->{only_mail_dump} && PleskVersion::atLeast(12, 5, 28)) {
        $self->{packer}->setComposerInstances($domainId, $embeddedInfo);
    }

    $self->{packer}->{stat}{dbDumpsCount}++ if ($self->{isAnyDbDumpedOnDomain} == 1);

    $self->{dump_status}->endDomain($domainName);
    $self->{packer}->finishDomain($domainId);
    Logging::endObject();
}

sub dumpMailSystem {
    my ( $self, $parentType, $domParams, $domain, $isroot, $ip ) = @_;

    Logging::debug("Dumping domain mailsystem");

    my $domainId = $domain->{'id'};
    my $domainName = $domain->{'displayName'};
    my $domainAsciiName = $domain->{'name'};
    my $isSite = exists $domain->{'webspace_id'} ? $domain->{'webspace_id'} ne '0' : 0;

    my $mailServiceStatus = $self->getDomainMailStatus($domainId);
    if ( defined($mailServiceStatus) ) {
        my $serviceIps = $self->getServiceIps( $domainId, 'mail', $ip );
        $self->{packer}->setDomainMailService( $domainId, $mailServiceStatus, $serviceIps );

        my @mails = @{DAL::getDomainMails($domainId)};
        if ( @mails ) {

            my $remoteMailIp = undef;
            while ( my ($ipAddress, $ipType) = each(%{$serviceIps})) {
                if (PleskStructure::isRemoteCommunicationIp($ipAddress)) {
                    Logging::debug("Mail for " . $domainName . " is placed on remote node with ip:" . $ipAddress);
                    $remoteMailIp = $ipAddress;
                    last;
                }
            }

            if ($self->{dump_full_mail}) {
                $self->{packer}->setDomainMailNamesContent( $domainId, $domainName,
                    AgentConfig::getPleskMailnamesDir($domainAsciiName, undef),
                    AgentConfig::mailContentUser(), $remoteMailIp );
            }

            foreach my $ptrRow (@mails) {
                $ptrRow->[2] = CommonPacker::normalizePasswordType( $ptrRow->[2] );
                if ($isroot and not PleskVersion::atLeast(10, 0, 0))  {
                    $parentType = 'root';
                }
                eval {
                    $self->makeMailUserNode( $domainId, $ptrRow->[0], $ptrRow->[1], $ptrRow->[2], $domainName, $domainAsciiName, $parentType, $domain->{'cl_id'} );
                    1;
                } or do {
                    $self->excludeProblemMailUserFromBackup( $domainId, $ptrRow, $@ );
                }
            }
            $self->{packer}->removeMailSystemChildNodeIfEmpty( $domainId, 'mailuser' );
        }

        $self->getCatchAllAddress($domainId);

        $self->getDomainKeysDomainSupport( $domainId, $domainName,  $domain->{'dns_zone_id'} );

        my $oldWebMailFlag = undef;
        if ( !PleskVersion::atLeast( 9, 0, 0 ) ) {
            $oldWebMailFlag = $self->getOldWebMailStatus( $domain->{'dns_zone_id'} );
        }

        my $webmail;

        if ( $isSite and !PleskVersion::atLeast( 12, 0, 0 ) ) {
            my $parentDomainParams = DAL::getDomainParams( $domain->{'webspace_id'} );
            $webmail = exists $parentDomainParams->{'webmail'} ? $parentDomainParams->{'webmail'} : 'none';
        } else {
            $webmail = exists $domParams->{'webmail'} ?  $domParams->{'webmail'} : defined($oldWebMailFlag) ? 'horde' : 'none';
        }

        $self->{packer}->setDomainWebMail( $domainId, $webmail );

        $self->{packer}->setDomainGLSupport( $domainId, (exists $domParams->{'gl_filter'} ) ? $domParams->{'gl_filter'} : 'on' );

        if (PleskVersion::atLeast(12, 0, 0)) {
            my $sql = "SELECT `parameter`, `value` FROM Parameters INNER JOIN DomainServices ON Parameters.id = DomainServices.parameters_id WHERE DomainServices.dom_id = ? AND Parameters.`parameter` LIKE 'outgoing_messages_%'";
            if ( $self->{dbh}->execute_rownum($sql, $domainId) ) {
                while (my $ptrRow = $self->{dbh}->fetchrow() ) {
                    $self->{packer}->makeOutgoingMessagesParameter('mailsystem', $domainId, $ptrRow->[0], $ptrRow->[1]);
                }
            }
            $self->{dbh}->finish();
        }

    }
}

sub _markDomainWithRemoteDbAsMigrated {
    my ($self, $domainPtr) = @_;

    return if !$self->{has_remote_db_server};

    # Remote databases hosting was introduced in 8.0.0, so we will process corresponding versions and in migration mode only
    return if ($self->{packer}->{listingOnlyMode} || !$self->{packer}->{migrationMode});

    my $domainId = $domainPtr->{'id'};
    my $domParams = $self->getDomainParams($domainPtr);
    if (!exists $domParams->{'disable_remote_db_remove'} || $domParams->{'disable_remote_db_remove'} ne 'true') {
        my $hasRemoteDatabases = 0;
        my $sql = "SELECT DISTINCT host FROM data_bases INNER JOIN DatabaseServers ON data_bases.db_server_id = DatabaseServers.id WHERE dom_id = ?";
        if ( $self->{dbh}->execute_rownum($sql, $domainId) ) {
            while (my $ptrRow = $self->{dbh}->fetchrow()) {
                if (HelpFuncs::isRemoteHost($ptrRow->[0])) {
                    $hasRemoteDatabases = 1;
                    last;
                }
            }
        }

        if ($hasRemoteDatabases) {
            Logging::debug("Mark domain with remote database as migrated");
            if (!exists $domParams->{'disable_remote_db_remove'}) {
                $self->{dbh}->execute("INSERT INTO dom_param (dom_id, param, val) VALUES (?, 'disable_remote_db_remove', 'true')", $domainId);
            } else {
                $self->{dbh}->execute("UPDATE dom_param SET val = 'true' WHERE dom_id = ? AND param = 'disable_remote_db_remove'", $domainId);
            }
        }
    }
}

sub getDomainMailStatus {
    my ($self, $domainId) = @_;
    return DAL::getDomainServiceStatus( $domainId, 'mail' );
}

sub getDomainMaillistStatus {
    my ($self, $domainId) = @_;
    return DAL::getDomainServiceStatus( $domainId, 'maillists' );
}

sub getDomainDefaultCert {
    my $self = shift;
    if ( PleskVersion::atLeast( 10, 2, 0 ) ) {
        return DAL::getDomainDefaultCert102(@_);
    }
    else {
        return DAL::getDomainDefaultCert101(@_);
    }
}

sub makeSiteNode {
    my ( $self, $domainAsciiName, $domainName, $ptrApplications, $embeddedInfo ) = @_;

    my ( $sql, $ptrHash );

    Logging::beginObject( 'domain', $domainName, $self->generateObjectUuidForLogging( 'domain', $domainName ) );

    my $domainPtr = DAL::getDomainPtrByAsciiName($domainAsciiName);
    unless( $domainPtr ) {
        Logging::error("Failed to get domain hash for '$domainName'",'PleskDbError');
        Logging::endObject();
        return;
    }
    my %domain = %{$domainPtr};

    my $domainId = $domain{'id'};
    my $webspaceId = $domain{'webspace_id'};

    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->{suspender}->suspendDomain( $domainName );

    $self->addWwwStatus( $domainId, $domainAsciiName );

    #
    # Status
    #
    Logging::debug("Dumping site status");

    my $status = $self->getSiteStatus($domain{'status'}, \%domain);
    $self->{packer}->setDomainStatus( $domainId, $status );

    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} ) {
        $self->{suspender}->unSuspendDomain();
        $self->{packer}->finishDomain($domainId);
        Logging::endObject();
        return;
    }

    my $domParams = $self->getDomainParams(\%domain);

    if ( !$self->{only_hosting_dump} ) {
        eval {
            $self->dumpMailSystem($parentType, $domParams, \%domain, 0, undef);
            1;
        } or do {
            $self->excludeProblemMailSystemFromBackup( $domainId, $@ );
        }
    }
    else { Logging::debug("Skip site mailsystem dump due to settings"); }

    my @SiteApplications;
    my @excluded_dbs;
    my @excluded_dns_records;
    my $applicationsInstalled = 1;

    if ($self->{configuration_dump} && $self->{packer}->{listingOnlyMode}) {
        $applicationsInstalled = DAL::isInstalledApplicationsWithDatabases($webspaceId);
    } else {
        $applicationsInstalled = DAL::isInstalledApplications($webspaceId);
    }

    #-----------------------------------------------------------------
    # Information about domain's site applications
    # should be extracted prior to database/hosting/custom-buttons/dns-records
    # dump, as site applications' linked resources should be dumped
    # separately.
    #-----------------------------------------------------------------

    if ($applicationsInstalled) {

        @SiteApplications = @{$ptrApplications};

        foreach my $sapp (@SiteApplications) {
            foreach my $row ( $sapp->getDatabases() ) {
                push @excluded_dbs, $row->{'id'};
            }

            foreach my $row ( $sapp->getDnsRecords() ) {
                push @excluded_dns_records, $row->{'id'};
            }
        }
    }

    eval {
        # Domain's DNS settings
        $self->makeDomainDnsZone( \%domain, \@excluded_dns_records );
        1;
    } or do {
        $self->excludeProblemDnsZoneFromBackup( $domainId, $@ );
    };

    $self->{packer}->makeDomainParamsNode($domainId, $domParams);

    $self->makeMailListsNode($domainId, $domainAsciiName);

    if ( !$self->{only_mail_dump} ) {
        Logging::debug("Dumping site statistics");

        $self->addDomainTraffic($domainId);

        $self->addDomainCertificates($domainId, $domainName, defined($domain{'cert_rep_id'}) ? $domain{'cert_rep_id'} : '');

        my $tomcatServiceStatus= DAL::getDomainTomcatStatus($domainId);
        if (defined $tomcatServiceStatus) {
            $self->{packer}->setDomainTomcat( $domainId, $tomcatServiceStatus );
            my $webapps = DAL::getDomainWebApps($domainId);
            foreach my $webapp ( keys %{$webapps} ) {
                $self->makeWebAppNode( $domainId, $webapp, $webapps->{$webapp}, $domainAsciiName );
            }
        }

        if ( $domainType eq "vrt_hst" ) {
            $self->makePhostingNode( \%domain, $domParams, \@SiteApplications, 1, undef, $embeddedInfo );
        }
        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);
    }

    if ( exists $domainPtr->{'description'} ) {
        $self->{packer}->makeDomainDescriptionsNode( $domainId, $domainPtr->{'description'} );
    }

    $self->{suspender}->unSuspendDomain();
    $self->{packer}->finishDomain($domainId);
    Logging::endObject();
}

sub makeDomainDnsZone {
    my ( $self, $domainHashPtr, $excludedRecordsPtr) = @_;

    Logging::debug("Dumping domain DNS");

    # dns_zone_id could be null for wildcard subdomains
    if ( $domainHashPtr->{'dns_zone_id'} != 0) {
        return $self->makeDnsZone( $domainHashPtr->{'dns_zone_id'}, $domainHashPtr->{'id'}, 'domain', $excludedRecordsPtr );
    }
}

sub makeDnsRecord {
    my ( $self, $ptrHash) = @_;

    return unless $ptrHash->{'type'}    =~ /^A|AAAA|NS|MX|CNAME|PTR|TXT|master|SRV|AXFR$/;
    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'});
        }
        my $ips = PleskStructure::getDomainIp( $ptrHash->{'val'} ) ;
        foreach my $ip (@{$ips}) {
            if ($ip =~/^\d+\.\d+\.\d+\.\d+$/) {
                $ptrHash->{'val'} = $ip;
            }
        }
    }

    return %{$ptrHash};
}

sub geteDnsZone {
    my ( $self, $dnsZoneId, $paramsPtr, $recordsPtr, $excludedRecordsPtr ) = @_;

    my $sql = "SELECT * FROM dns_zone WHERE id=?";

    if ( !$self->{dbh}->execute_rownum($sql, $dnsZoneId) ) {
        $self->{dbh}->finish();
        my $msg = "Broken referencial integrity: DNS zone id $dnsZoneId is not found in dns_zone";
        print STDERR "$msg\n";
        Logging::warning($msg,'BrokenDbIntegrity');
        return;
    }

    if ( my $hashPtr = $self->{dbh}->fetchhash($sql) ) {
        %{$paramsPtr} = %{ $hashPtr };
    }

    $self->{dbh}->finish();

    # dns records
    if (PleskVersion::atLeast(10, 4)) {
        $sql = "SELECT dns_recs.*, dns_refs.status FROM dns_recs LEFT OUTER JOIN dns_refs ON dns_refs.zoneRecordId = dns_recs.id WHERE dns_zone_id = ?";
    } else {
        $sql = "SELECT * FROM dns_recs WHERE dns_zone_id = ?";
    }
    my @sqlParams = ($dnsZoneId);
    if( ref($excludedRecordsPtr) =~ /ARRAY/ ) {
        my @excludeIds = @{$excludedRecordsPtr};
        if ( @excludeIds ) {
            $sql .= " AND id NOT IN (" . join(',', ("?")x@excludeIds) . ")";
            @sqlParams = (@sqlParams, @excludeIds);
        }
    }

    if ( $self->{dbh}->execute_rownum($sql, @sqlParams) ) {

        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, $excludedRecordsPtr ) = @_;

    my %params;
    my @records;
    $self->geteDnsZone( $dnsZoneId, \%params, \@records, $excludedRecordsPtr );

    if ( 'domain' eq $parentType ) {
        $self->{packer}->setDomainDnsZone( $parent, \%params, \@records );
    }
    else {
        Logging::warning('Error: makeDnsZone: Unexpected type of parent \"$parentType\"','assert');
    }
}

sub dumpDomainAliases {
    my ( $self, $domainId ) = @_;

    Logging::debug( 'Dumping domain aliases... ', 1 );

    my $aliases = DAL::getDomainAliases($domainId);

    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 );
    }

    Logging::debug('OK');
}

sub addWwwStatus {
    my ( $self, $domainId, $domainName ) = @_;

    my $sql;

    # Domain is considered to have 'www' prefix when there is 'CNAME', 'A' or 'AAAA' record with 'www' name.
    # While domain has CNAME record Plesk prohibits creation of 'www' alias or subdomain.
    # If user remove CNAME records then it may create 'www' alias and 'www' A-records will be added to zone.
    # When deployer will restore backup of such domain then it will first create CNAME record (due to 'www' domain flag)
    # and then it will try to restore 'www' alias and will fail due to conflict.
    # So 'www' flag for domain is set only if CNAME records is found, 'A' and 'AAAA' are not checked.
    $sql = "SELECT r.* FROM dns_recs r, domains d WHERE d.id=? "
    . "AND d.dns_zone_id=r.dns_zone_id AND r.type = 'CNAME' AND r.host = '?'";

    if ( $self->{dbh}->execute_rownum($sql, $domainId, "www.$domainName.") ) {
        $self->{packer}->setDomainWwwStatus( $domainId, 'true' );
    }
    else {
        $self->{packer}->setDomainWwwStatus( $domainId, 'false' );
    }
    $self->{dbh}->finish();
}

sub makeDomAdminUserRole {
    my ( $self, $parentType, $clientId, $domainId, $domParams ) = @_;

    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'
    );
    my ( $sql, $item, $ptrHash, $xmlName, $fieldName );

    $sql = "SELECT * FROM dom_level_usrs WHERE dom_id=?";
    unless ( $self->{dbh}->execute_rownum($sql, $domainId) ) {
        $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 ( defined $domainUser{'account_id'}
            and $domainUser{'account_id'} != 0 )
    {
        ( $passwd, $passwdType ) = $self->makeAccountPasswordNode( $domainUser{'account_id'} );
    }

    my %cardHash;
    if ( defined( $domainUser{'card_id'} ) ) {
        $sql = "SELECT * from Cards WHERE id=?";
        if ( $self->{dbh}->execute_rownum($sql, $domainUser{'card_id'}) ) {
            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";
                Logging::warning($msg ,'BrokenDbIntegrity');
            }
        }
        $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'} = $state eq "true" ? 0 : 1;
    $userHash{'isBase64'} = '0';
    $userHash{'subscriptionDomainId'} = $domainId;
    $userHash{'passwordType'} = $passwdType;


    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;

    $self->addSpecificUserAndRole( $parentType, $domainName, $roleName, \%userHash, \%rolePermsHash, $clientId );
}

sub makeMailAccessUserRole {
    my ( $self, $mailname, $domainName, $password, $passwordType, $parentType, $clientId, $antispam, $antivirus, $userUid ) = @_;

    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'} = $userUid;
    $userHash{'isLocked'} = 0;
    $userHash{'contactName'} = $mailname;
    $userHash{'email'} = $mailname.'@'.$domainName;
    $userHash{'password'} = $password;
    $userHash{'passwordType'} = $passwordType;
    $userHash{'isBase64'} = '0';
    $userHash{'subscriptionDomainId'} = PleskStructure::getDomainId($domainName);
    $userHash{'mailId'} = 1; # Needed to make user assigned to existing mail

    my %rolePermsHash = ();
    $rolePermsHash{ 'spamfilterManagement' } = 1 if defined $antispam;
    $rolePermsHash{ 'antivirusManagement' } = 1 if defined $antivirus;

    $self->addSpecificUserAndRole( $parentType, $mailname.'@'.$domainName, $roleName, \%userHash, \%rolePermsHash, $clientId );
}

sub addSpecificUserAndRole {
    my ( $self, $parentType, $userName, $roleName, $userHash, $rolePermsHash, $ownerId ) = @_;

    my $callback;
    eval {
        if( $parentType eq 'root' ) {
            $callback = sub {
                $self->{packer}->removeRootUser( @_ );
            };
            $self->{packer}->addRootUser( $userName, $userHash);
        }
        elsif( $parentType eq 'admin' ) {
            $callback = sub {
                $self->{packer}->removeAdminUser( @_ );
            };
            $self->{packer}->addAdminUser( $userName, $userHash);
        }
        elsif( $parentType eq 'reseller' || $parentType eq 'client') {
            $callback = sub {
                $self->{packer}->removeClientUser( $ownerId, @_ );
            };
            $self->{packer}->addClientUser( $ownerId, $userName, $userHash);
        }
        1;
    } or do {
        $self->excludeProblemUserFromBackup( $userName, $callback, $@ );
    };

    eval {
        if( $parentType eq 'root' ) {
            $callback = sub {
                $self->{packer}->removeRootRole( @_ );
            };
            $self->{packer}->addRootRole( $roleName, 0, $rolePermsHash, []);
        }
        elsif( $parentType eq 'admin' ) {
            $callback = sub {
                $self->{packer}->removeAdminRole( @_ );
            };
            $self->{packer}->addAdminRole( $roleName, 0, $rolePermsHash, []);
        }
        elsif( $parentType eq 'reseller' || $parentType eq 'client') {
            $callback = sub {
                $self->{packer}->removeClientRole( $ownerId, @_ );
            };
            $self->{packer}->addClientRole( $ownerId, $roleName, 0, $rolePermsHash, []);
        }
        1;
    } or do {
        $self->excludeProblemRoleFromBackup( $callback, $roleName, $@ );
    };
}

sub getTrafficValue {
    my ( $self, $tableName, $idName, $idValue ) = @_;

    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 $idName = ?";

    if ( $self->{dbh}->execute_rownum($sql, $idValue) ) {
        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:
#                 $domainId - ID of domain
#
sub addDomainTraffic {
    my ( $self, $domainId ) = @_;

    my $trafficValue = '';

    $trafficValue = $self->getTrafficValue( 'DomainsTraffic', 'dom_id', $domainId );

    $self->{packer}->setDomainTraffic( $domainId, $trafficValue ) if $trafficValue;
}

sub addDomainCertificates {
    my ($self, $domainId, $domainName, $domainCertRepId) = @_;

    my @defaultCerts = @{$self->getDomainDefaultCert($domainId)};
    my @domainCerts = @{DAL::getCertificateIds($domainCertRepId)};
    foreach my $id (@domainCerts) {
        $self->makeCertificateNode($id, $domainId, 'domain', grep {$_ eq $id} @defaultCerts ? 1 : 0);
    }

    if ($self->{include_app_distrib}) {
        my %hostingParams = %{DAL::getHostingParams($domainId)};
        if (exists($hostingParams{'certificate_id'}) and $hostingParams{'certificate_id'} != 0) {
            my $id = $hostingParams{'certificate_id'};
            if (!(grep {$_ eq $id} @domainCerts)) {
                $self->{packer}->setServerSettings() unless defined $self->{packer}->{serverNode};
                $self->makeCertificateNode($id, 0, 'server', 0);
            }
        } else {
            foreach my $id (@defaultCerts) {
                if (!(grep {$_ eq $id} @domainCerts)) {
                    $self->{packer}->setServerSettings() unless defined $self->{packer}->{serverNode};
                    $self->makeCertificateNode($id, 0, 'server', 0);
                }
            }
        }
    }
}

sub addClientTraffic {
    my ( $self, $clientId ) = @_;

    my $trafficValue = '';

    $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 = ? AND p.parameter = 'nonexist_mail'";
    my $mode;
    if ( $self->{dbh}->execute_rownum($sql, $domainId) ) {
        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;
    my @sqlParams = ();

    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 = ? AND p.parameter = 'catch_addr'";
        push(@sqlParams, $domainId);
    }
    elsif ( $mode =~ /^bounce$/ ) {
        $sql = "SELECT CONCAT('bounce:', p.value) FROM Parameters p, DomainServices ds "
        . "WHERE ds.parameters_id = p.id AND ds.dom_id = ? AND p.parameter = 'bounce_mess'";
        push(@sqlParams, $domainId);
    }
    elsif ( $mode =~ /^reject$/ ) {
        $sql = "SELECT 'reject'";
    }

    # some not supported mode or default parameter [bug 43901]
    return unless ($sql);

    if ( $self->{dbh}->execute_rownum($sql, @sqlParams) ) {
        if ( my $rowPtr = $self->{dbh}->fetchrow() ) {
            my $catchAllAddr = @{ $rowPtr }[0];
            if ($catchAllAddr) {
                $self->{packer}->setDomainCatchMail( $domainId, $catchAllAddr );
            }
        }
    }
    $self->{dbh}->finish();
}

sub makeMailmanMailListNode {
    my ( $self, $domainId, $mlistId, $mlistName, $mlistState ) = @_;

    unless ( defined Mailman::version() ) {
        Logging::debug("Unable to found Mailman installation");
        return;
    }

    my @owners = Mailman::getListOwners($mlistName);
    if ( !@owners ) {
        Logging::debug("Bad maillist $mlistName, skipped");
        return;
    }

    my %listMembers = Mailman::getListMembers($mlistName);

    $self->{packer}->addDomainMailList( $domainId, $mlistName,
        Mailman::getListPassword($mlistName),
        $mlistState, \@owners, \%listMembers );
}

my %dumpedCerts;

sub makeCertificateNode {
    my ( $self, $certId, $parent, $parentType, $default ) = @_;

    return if $dumpedCerts{$certId};

    my ( $sql, %cert, $item );

    $sql = "SELECT * FROM certificates WHERE id=?";
    unless ( $self->{dbh}->execute_rownum($sql, $certId) ) {
        $self->{dbh}->finish();
        my $msg = "Broken referencial integrity: certificate ID '$certId' is not found";
        print STDERR "$msg\n";
        Logging::warning($msg,'BrokenDbIntegrity');
        return;
    }

    if ( my $hashPtr = $self->{dbh}->fetchhash() ) {
        %cert = %{ $hashPtr };
    }
    $self->{dbh}->finish();

    my $cert;
    my $csr;
    my $ca_cert;
    my $name;

    $cert    = $cert{'cert'};
    $csr     = $cert{'csr'};
    $ca_cert = $cert{'ca_cert'};

    $name = $cert{'name'};

    my $pvt_key = $cert{'pvt_key'};

    if ( 'server' eq $parentType ) {
        $self->{packer}->addServerCertificate( $name, $cert, $csr, $ca_cert, $pvt_key, $default );
    }
    elsif ( 'domain' eq $parentType ) {
        $self->{packer}->addDomainCertificate( $parent, $name, $cert, $csr, $ca_cert, $pvt_key, $default );
    }
    else {
        Logging::warning('Error: makeCertificateNode: unexpected parent type','assert');
    }
    $dumpedCerts{$certId} = 1;
}

sub makeDatabaseNode {
    my ( $self, $dbId, $parent, $parentType, $sappId, $sappDb, $embeddedInfo ) = @_;

    my ( $ptrHash, $item, $charset, $dbServerId, $dbServerHost, $dbServerPort, $skipContent, $dbServerVersion );

    $skipContent = 0;

    my $dbRow;
    my $sql;
    if (PleskVersion::atLeast(12, 1, 0)) {
        $sql = "SELECT name, type, external_id FROM data_bases WHERE id = ?"
    } else {
        $sql = "SELECT name, type FROM data_bases WHERE id = ?"
    }
    unless ( ( $self->{dbh}->execute_rownum($sql, $dbId) ) && ( $dbRow = $self->{dbh}->fetchrow() ) ) {
        $self->{dbh}->finish();
        my $msg = "Broken referencial integrity: Database id $dbId is not found in data_bases";
        print STDERR "$msg\n";
        Logging::warning($msg ,'BrokenDbIntegrity');
        return;
    }
    my ( $dbName, $dbType ) = @{ $dbRow };
    my %optional;
    if (PleskVersion::atLeast(12, 1, 0)) {
        $optional{'external_id'} = $dbRow->[2];
    }
    $self->{dbh}->finish();

    $dbName = HelpFuncs::trim($dbName);

    Logging::beginObject($dbType,$dbName, undef);

    if ( $dbType eq "postgres" or $dbType eq "postgresql" ) {
        $dbType = "postgresql";
    }

    my %dbServer;

    my $sql = "SELECT host, port, ds.type, ds.server_version FROM DatabaseServers as ds, data_bases as db WHERE "
    . "ds.id = db.db_server_id AND db.id = ?";
    if ( $self->{dbh}->execute_rownum($sql, $dbId) ) {
        if( my $ptrRow = $self->{dbh}->fetchrow() ) {
            $dbServerHost = ( $ptrRow->[0] ne 'localhost' ) ? $ptrRow->[0] : 'localhost';
            $dbServerPort = $ptrRow->[1];
            $dbServerVersion = $ptrRow->[3];

            $dbServer{'type'} = $ptrRow->[2];
            $dbServer{'host'} = $ptrRow->[0];
            $dbServer{'port'} = $ptrRow->[1];
        }
    }
    $self->{dbh}->finish();

    my @dbUsers;
    if ( defined $sappDb ) {
        $optional{'sapp-param'} = $sappDb->{'param'} if ($sappDb->{'param'});
        $optional{'aps-registry-id'} = $sappDb->{'apsRegistryId'} if exists $sappDb->{'apsRegistryId'};
        $optional{'db-existent'} = $sappDb->{'db-existent'} if exists $sappDb->{'db-existent'};
        $optional{'prefix'} = $sappDb->{'prefix'} if exists $sappDb->{'prefix'};
    }
    my %contentDescription;

    if ( !$self->{shallow_dump} ) {
        Logging::debug("Database $dbName");

        foreach my $dbUserHash ( @{DAL::getDatabaseUsers( $dbId, $embeddedInfo )} ) {
            $dbUserHash->{'password'} ||= '';    # NULL -> ''
            my %item;
            $item{'login'}    = $dbUserHash->{'login'};
            $item{'password'} = $dbUserHash->{'password'};
            $item{'type'}     = CommonPacker::normalizePasswordType( $dbUserHash->{'type'} );
            $item{'id'}       = $dbUserHash->{'id'};
            $item{'external_id'} = $dbUserHash->{'external_id'} if $dbUserHash->{'external_id'};
            if ( defined $sappDb->{'apsCreatedUser'} and $sappDb->{'apsCreatedUser'} eq $dbUserHash->{'login'} ) {
                $item{'aps-registry-id'} = $sappDb->{'apsRegistryId'};
            }

            if (exists $dbUserHash->{'default_user_id'} && $dbUserHash->{'default_user_id'} eq $dbUserHash->{'id'}) {
                $item{'default'} = '';
            }

            if (defined $sappDb && exists $sappDb->{'db-user-existent'}) {
                $item{'db-user-existent'} = $sappDb->{'db-user-existent'};
            }

            if (exists $dbUserHash->{'acl'}) {
                $item{'acl'} = $dbUserHash->{'acl'};
            }
            if (exists($dbUserHash->{'privileges'})) {
                $item{'privileges'} = $dbUserHash->{'privileges'};
            }

            push @dbUsers, \%item;
        }

        my ( $dbUser, $dbPasswd );

        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 = ?";
        my $ptrRow;
        unless ( ( $self->{dbh}->execute_rownum($sql, $dbId) ) && ( $ptrRow = $self->{dbh}->fetchrow() ) ) {
            $self->{dbh}->finish();
            my $msg = "Broken referencial integrity: DatabaseServers vs data_bases for db id $dbId";
            print STDERR "$msg\n";
            Logging::warning($msg ,'BrokenDbIntegrity');
            return;
        }
        ( $dbUser, $dbPasswd, $dbServerId ) = @{ $ptrRow };
        $self->{dbh}->finish();

        my %dbServerInfo;
        $dbServerInfo{'id'} = $dbServerId;
        $dbServerInfo{'type'} = $dbType;
        $dbServerInfo{'host'} = $dbServerHost;
        $dbServerInfo{'admin_password'} = $dbPasswd;

        $dbPasswd = PleskStructure::getDbServerAdminPassword(\%dbServerInfo);
        if (not defined $dbPasswd) {
            Logging::error("Unable to define superuser password for ". $dbType ." server on ". $dbServerHost);
            return;
        }

        if ( $dbType eq "postgresql" ) {
            $optional{'version'} = $dbServerVersion ne '' ? $dbServerVersion : AgentConfig::getPostgresqlVersion();
        }

        if ( $dbType eq "mysql" ) {
            $optional{'version'} = $dbServerVersion ne '' ? $dbServerVersion : Db::MysqlUtils::getVersion();
        }

        %contentDescription = (
            "name"     => $dbName,
            "type"     => $dbType,
            "user"     => $dbUser,
            "password" => $dbPasswd,
            "host"     => $dbServerHost,
            "port"     => $dbServerPort,
            "plesk_7"  => 0
        );

        if ( $dbType eq "postgresql" ) {
            my $psql = AgentConfig::psqlBin();
            if ( -e $psql ) {
#[Bug 119082]
#$charset = `PGUSER=$dbUser PGPASSWORD='$dbPasswd' $psql -l template1 | grep '^[ \t]*$dbName ' | awk '{print \$5}'`;
                my $wrapPgsql = Db::DbConnect::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 {
                    Logging::error( "Cannot connect to PostgreSQL $dbServerHost:$dbServerPort (database '$dbName')" );
                    $skipContent = 1;
                }
            } else {
                Logging::error("The psql command is absent on a server, so the dump of PostgreSQL databases can not be created.");
                $skipContent = 1;
            }
        }

        if ( $dbType eq "mysql" ) {
            my $wrapMysql = Db::DbConnect::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;
                    $charset = $ptrRow->[1] if $ptrRow;
                    $optional{'charset'} = $charset if $charset;
                    $wrapMysql->{'FINISH'}->();
                }

                # We can't always use UTF8 for dump content because it may lead to corruption of latin1-only-databases.
                # See http://bugs.plesk.ru/show_bug.cgi?id=134509 for details.
                # However, using database charset may also lead to corruption if database has latin1 default
                # charset, but some tables or columns have non-latin1 charset.
                # The semi-solution is to query charset for tables and columns and if they all have
                # the same charset - use it, otherwise use utf-8.
                my $conn = $wrapMysql->{'CONNECTION'};
                my %collations = Db::MysqlUtils::getCollations( $conn );
                my %tablesCollations = Db::MysqlUtils::getTablesCollation( $conn );
                my @tablesCharsets = map { $collations{$_} } values(%tablesCollations);
                my %usedCharsets = map { $_, 1 } @tablesCharsets;
                for my $table (keys %tablesCollations) {
                    my %columnsCollations = Db::MysqlUtils::getColumnsCollation( $conn, $table );
                    $usedCharsets{$collations{$_}} = 1 for (values %columnsCollations);
                }

                my $dumpCharset = (scalar (keys %usedCharsets) == 1) ? (keys %usedCharsets)[0] : 'utf8';
                # ucs2, utf16, and utf32 cannot be used as a client character set (http://dev.mysql.com/doc/refman/5.5/en/charset-connection.html)
                # https://jira.sw.ru/browse/PPP-13515
                $dumpCharset = 'utf8' if ($dumpCharset =~ /^(ucs2|utf16|utf32)$/);
                $contentDescription{'dump_charset'} = $dumpCharset;

                # Dump database collation
                if ( $wrapMysql->{'EXECUTE'}->("SHOW VARIABLES LIKE \"collation_database\"") ) {
                    my $ptrRow = $wrapMysql->{'FETCHROW'}->();
                    my $collation = $ptrRow->[1] if $ptrRow;
                    $optional{'collation'} = $collation if $collation;
                    $wrapMysql->{'FINISH'}->();
                }
            }
            else {
                Logging::error( "Cannot connect to mysql $dbServerHost:$dbServerPort (database '$dbName')" );
                $skipContent = 1;
            }
        }

        $contentDescription{'create_local_dump'} = $self->checkExistDbServerOnDestination($dbType, $dbServerHost, $dbServerPort);
        if ($contentDescription{'create_local_dump'} != 0) {
            $contentDescription{'dir_for_local_dump'} = AgentConfig::dumpDir() . '/databases';
        }
    }

    $self->{packer}->setContentTransport($self->{packer}->getContentTransportType(), ($dbType eq 'mssql') ? 'windows' : 'unix');

    if( $sappId ){
        if ( 'domain' eq $parentType ) {
            $self->{packer}->addDomainSappDatabase( $dbId, $dbServerId, $parent, $sappId, $dbName, $dbType, \%optional, \%dbServer, \@dbUsers, \%contentDescription, $skipContent );
        }
        elsif ( 'subdomain' eq $parentType ) {
            $self->{packer}->addSubDomainSappDatabase( $dbId, $dbServerId, $parent, $sappId, $dbName, $dbType, \%optional, \%dbServer, \@dbUsers, \%contentDescription, $skipContent );
        }
        else {
            Logging::warning( "Error: makeDatabaseNode: Unexpected type of parent \"$parentType\"",'assert');
        }
    }
    else{
        if ( 'domain' eq $parentType ) {
            if (PleskVersion::atLeast(12, 1, 0)) {
                $optional{'related-sites'} = DAL::getDatabaseRelatedSites($dbId, $parent);
            }
            $self->{packer}->addDomainDatabase( $dbId, $dbServerId, $parent, $dbName, $dbType, \%optional, \%dbServer, \@dbUsers, \%contentDescription, $skipContent );
        }
        else {
            Logging::warning( "Error: makeDatabaseNode: Unexpected type of parent \"$parentType\"",'assert');
        }
    }

    $self->{isAnyDbDumpedOnDomain} = 1;

    Logging::endObject();
}

sub checkExistDbServerOnDestination {
    my ( $self, $type, $host, $port ) = @_;

    if ( scalar(@{$self->{existing_remote_db_servers}}) != 0 ) {
        foreach my $dbServerString (@{$self->{existing_remote_db_servers}}) {
            my ($exType, $exHost, $exPort) = split(/:/, $dbServerString);
            if ( $type eq $exType and $host eq $exHost and $port eq $exPort ) {
                return 1;
            }
        }
    }

    return 0;
}

sub makeMailUserNode {
    my ( $self, $domainId, $mailId, $passwd, $typePasswd, $domainName, $domainAsciiName, $parentType, $clientId ) = @_;

    my ( $sql, %mail, $item, $ptrRow, $ptrHash, $dir, $mbox_quota);

    $sql = "SELECT * FROM mail WHERE id = ?";
    unless ( ( $self->{dbh}->execute_rownum($sql, $mailId) ) && ( $ptrHash = $self->{dbh}->fetchhash() ) ) {
        $self->{dbh}->finish();
        my $msg = "Broken referencial integrity: Mail id $mailId is not found in mail";
        print STDERR "$msg\n";
        Logging::warning($msg,'BrokenDbIntegrity');
        return;
    }
    %mail = %{ $ptrHash };
    $self->{dbh}->finish();
    my $mailName = $mail{'mail_name'};
    Logging::beginObject('mailname',$mailName, undef);

    my $userUid = undef;
    my $cpAccessDefault = undef;
    if (exists $mail{'userId'} and $mail{'userId'} != 0) {
        if (PleskVersion::atLeast(12, 0, 0)) {
            $sql = "SELECT uuid, login, email, isLocked FROM smb_users WHERE id = ?";
            if ( $self->{dbh}->execute_rownum($sql, $mail{'userId'}) ) {
                my $fullMailName = $mailName . "@" . $domainName;
                while ( $ptrRow = $self->{dbh}->fetchrow() ) {
                    $userUid = $ptrRow->[0];
                    $cpAccessDefault = "true" if (!$self->{packer}->{migrationMode} && $ptrRow->[3] eq '0' && $ptrRow->[1] eq $fullMailName && $ptrRow->[2] eq $fullMailName);
                }
            }
        } else {
            $sql = "SELECT uuid FROM smb_users WHERE id = ?";
            if ( $self->{dbh}->execute_rownum($sql, $mail{'userId'}) ) {
                while ( $ptrRow = $self->{dbh}->fetchrow() ) {
                    $userUid = $ptrRow->[0];
                }
            }
        }

        $self->{dbh}->finish();
    }

    $userUid = HelpFuncs::generateUuid() if (!$userUid && !PleskVersion::atLeast(10, 0, 0));
    $self->{packer}->addMail( $domainId, $mailId, $mailName, $passwd, $typePasswd, $userUid, $cpAccessDefault );

    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 = ? AND value='true'";
                if ( $self->{dbh}->execute_rownum($sql, $mail{'perm_id'}) ) {
                    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, $userUid);
                    }
                }
                $self->{dbh}->finish();
            }
            elsif ( defined( $mail{'cp_access'} ) ) {
                $self->makeMailAccessUserRole($mailName, $domainName, $passwd, $typePasswd, $parentType, $clientId, undef, undef, $userUid);
            }
        }
    }

    if ( $mail{'mbox_quota'} ) {
        $mbox_quota = $mail{'mbox_quota'};

        $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 = ?";
    if (  $self->{dbh}->execute_rownum($sql, $mailId)
            and ( $ptrRow = $self->{dbh}->fetchrow() )
            and $ptrRow->[0] != 0 )
    {
        $enable_mailbox = 1;
    }
    $self->{dbh}->finish();

    if ($enable_mailbox) {
        $dir = AgentConfig::getPleskMailnamesDir($domainAsciiName, $mailName) . "Maildir";

        $self->{packer}->setMailBox( $mailId, $mailName, $domainAsciiName,
            ( $mail{'postbox'} =~ /true/ ? 'true' : 'false' ), $dir );
    }

    #
    # aliases
    #
    $sql = "SELECT alias FROM mail_aliases WHERE mn_id=?";
    if ( $self->{dbh}->execute_rownum($sql, $mailId) ) {
        while ( $ptrRow = $self->{dbh}->fetchrow() ) {
            $self->{packer}->addMailAliase( $mailId, $ptrRow->[0] );
        }
    }
    $self->{dbh}->finish();

    #
    # end aliases
    #

    #
    # mail forwarding
    #

    my @members = grep {$_ ne $mailName.'@'.$domainName} @{DAL::getMailRedirects($mailId)};

    my $forwarding_enabled = ($mail{'mail_group'} eq 'true');

    unless ( PleskVersion::atLeast( 10, 0, 0 ) ) {
        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 = AgentConfig::getPleskMailnamesDir($domainAsciiName, $mailName) . "\@attachments";

    my (@autos);
    $sql = "SELECT id FROM mail_resp WHERE mn_id=? ORDER BY id";
    if ( $self->{dbh}->execute_rownum($sql, $mailId) ) {
        while ( $ptrRow = $self->{dbh}->fetchrow() ) {
            push @autos, $ptrRow->[0];
        }
    }
    $self->{dbh}->finish();

    my @autoresponders;
    my @filelist;

    foreach my $id (@autos) {
        my %item =
        makeAutoresponderNode( $self, $id, $mailName . "@" . $domainName );

        push @autoresponders, \%item;

        if ( PleskVersion::atLeast( 11, 1, 0 ) ) {
            push @filelist, @{$item{'attach'}};
        }
    }

    if ( PleskVersion::atLeast( 11, 1, 0 ) ) {
        # In Plesk 11.1.0 there is no separate repository for autoresponder attaches. Every autoreponder has its own attach.
        # So no needs to call mailmng utility. Proper information could be taken from database.
    } else {
        my $util = AgentConfig::getMailmngMailnameUtil();

        my $mn = $mailName;
        $mn =~ s/\&/\\&/g;
        $util .= " --list-attachments --domain-name=$domainName --mailname=$mn";

        Logging::debug("Executing " . $util);

        open( LIST, "$util |" );
        while (<LIST>) {
            chomp;
            my ($file) = split /\t/;
            push @filelist, $file;
        }

        #clear list if utility failed
        unless ( close(LIST) ) {
            @filelist = ();
        }
    }

    $self->{packer}->setMailAutoresponders( $mailId, $mailName, $domainAsciiName, $dir,
        $mail{'autoresponder'}, \@autoresponders, \@filelist );

    #
    # end autoresponders
    #

    #
    # Addressbook
    #

    $self->makeAddressbookNode( $mailId, $mailName . '@' . $domainAsciiName );

    #
    # Spamassassin
    #

    $dir = AgentConfig::getPleskMailnamesDir($domainAsciiName, $mailName) . ".spamassassin";
    $self->makeSpamassassinNode( $mailId, $mail{'mail_name'}, $domainAsciiName, $mail{'spamfilter'}, $dir );

    #
    # End Spamassassin
    #

    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'} } );
        }
    }


    if ( exists $ptrHash->{'description'} ) {
        $self->{packer}->makeMailUserDescriptionNode( $mailId, $ptrHash->{'description'} );
    }

    $self->_makeMailUserOutgoingMessagesNode($mailId);

    Logging::endObject();
}

sub _makeMailUserOutgoingMessagesNode() {
    my ($self, $mailUserId) = @_;

    if (PleskVersion::atLeast(12, 0, 0)) {
        my $sql = "SELECT param, val FROM mn_param WHERE mn_id = ? AND param LIKE 'outgoing_messages_%'";
        if ($self->{dbh}->execute_rownum($sql, $mailUserId)) {
            while (my $ptrRow = $self->{dbh}->fetchrow()) {
                $self->{packer}->makeOutgoingMessagesParameter('mailuser', $mailUserId, $ptrRow->[0], $ptrRow->[1]);
            }
        }
        $self->{dbh}->finish();
    }

}

sub makeAutoresponderNode {
    my ( $self, $autoId, $mailName ) = @_;

    my ( $name, $value, $sql, %auto, $ptrRow, $item );

    $sql = "SELECT * FROM mail_resp WHERE id=?";
    unless ( ( $self->{dbh}->execute_rownum($sql, $autoId) ) && ( $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";
        Logging::warning($msg,'BrokenDbIntegrity');
        return;
    }

    %auto = %{ $ptrRow };
    $self->{dbh}->finish();

    #
    # forward
    #
    $sql = "SELECT address FROM resp_forward WHERE rn_id=?";
    if ( $self->{dbh}->execute_rownum($sql, $autoId) ) {
        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=?";
    if ( $self->{dbh}->execute_rownum($sql, $autoId) ) {
        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 = '?'";
    if ($self->{dbh}->execute_rownum($sql, $mailName) ) {
        while ( $ptrHash = $self->{dbh}->fetchhash() ) {
            push @params, $ptrHash;
        }
    }
    $self->{dbh}->finish();

    my $ptrRow;
    my %turbaVersion = undef;
    if (PleskVersion::atLeast( 11, 1, 13 )) {
        $sql = "SELECT value FROM ServiceNodeEnvironment WHERE section='componentsPackages' AND name = 'psa-turba' AND serviceNodeId=1";
    }elsif (PleskVersion::atLeast( 11, 1, 0 )) {
        $sql = "SELECT value FROM ServiceNodeProperties WHERE name = 'components.packages.psa-turba' AND serviceNodeId=1";
    } elsif (PleskVersion::atLeast( 10, 4, 0 )) {
        $sql = "SELECT value FROM ServiceNodeProperties WHERE name = 'server.packages.psa-turba' AND serviceNodeId=1";
    } else {
        $sql = "SELECT version FROM Components WHERE name = 'psa-turba'";
    }
    if ($self->{dbh}->execute_rownum($sql)) {
        while ( $ptrRow = $self->{dbh}->fetchrow() ) {
            # Format version is 2.3.5-cos5.build1011110311.18
            if ($ptrRow->[0]=~/(\d+)\.(\d+)\.(\d+)-\S*/) {
                $turbaVersion{'majorVersion'} = $1;
                $turbaVersion{'minorVersion'} = $2;
                $turbaVersion{'patchVersion'} = $3;
            }
        }
    }
    $self->{dbh}->finish();

    $self->{packer}->setMailAddressbook( $mailId, \@params, \%turbaVersion );
}

sub getWebspaceId {
    my ( $self, $domainId ) = @_;

    my $use_webspaces = PleskVersion::atLeast( 10, 0, 0 );

    return 0 unless $use_webspaces;

    my $webspace_id;
    my $sql = "SELECT webspace_id FROM domains WHERE id = ?";
    if ( $self->{dbh}->execute_rownum($sql, $domainId) 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 );
    if($use_webspaces){
        my $www_root = DAL::getDomainWwwRoot($domainId);
        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 = HostingDumper::getWebspaceRoot($parentDomainName, $self->{remoteWebNodes});
        $www_root = substr($www_root,length($parentDomainRoot));
        substr($www_root,0,1) = '' if substr($www_root,0,1) eq '/' || substr($www_root,0,1) eq '\\';
        return $www_root;
    }
    else {
        return ($absolute? HostingDumper::getWebspaceRoot( PleskStructure::getDomainAsciiNameFromId($domainId), $self->{remoteWebNodes} ) . '/' : '' ) .'httpdocs';
    }
}

sub getSubDomainWwwRoot {
    my ( $self, $subdomainId, $subdomainName, $domainId, $absolute ) = @_;

    my $use_webspaces = PleskVersion::atLeast( 10, 0, 0 );
    if($use_webspaces){
        my $www_root = DAL::getSubdomainWwwRoot($subdomainId);
        if($absolute) {
            return $www_root;
        }

        my $webspaceId = $self->getWebspaceId($domainId);
        $domainId = $webspaceId if $webspaceId;
        my $parentDomainRoot = HostingDumper::getWebspaceRoot( PleskStructure::getDomainAsciiNameFromId($domainId), $self->{remoteWebNodes} );
        $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? HostingDumper::getWebspaceRoot( PleskStructure::getDomainAsciiNameFromId($domainId), $self->{remoteWebNodes} ) . '/' : '' ) .'subdomains/' . $subdomainName . '/httpdocs';
    }
}

sub getDomainCgiRoot {
    my ( $self, $domainId, $absolute ) = @_;

    my $use_webspaces = PleskVersion::atLeast( 10, 0, 0 );
    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 = HostingDumper::getWebspaceRoot($parentDomainName, $self->{remoteWebNodes});

        my $cgi_root;
        if( DAL::getDomainCgiBinMode($domainId) eq 'www-root') {
            $cgi_root = DAL::getDomainWwwRoot($domainId) . '/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? HostingDumper::getWebspaceRoot( PleskStructure::getDomainAsciiNameFromId($domainId), $self->{remoteWebNodes} ) . '/' : '' ) .'cgi-bin';
    }
}

sub getSubDomainCgiRoot {
    my ( $self, $subdomainId, $subdomainName, $domainId, $absolute ) = @_;

    my $use_webspaces = PleskVersion::atLeast( 10, 0, 0 );
    if($use_webspaces){
        my $webspaceId = $self->getWebspaceId($domainId);
        my $parentDomainRoot = HostingDumper::getWebspaceRoot( PleskStructure::getDomainAsciiNameFromId($webspaceId? $webspaceId : $domainId), $self->{remoteWebNodes} );

        my $cgi_root;
        if( DAL::getDomainCgiBinMode($domainId) eq 'www-root') {
            $cgi_root = DAL::getSubdomainWwwRoot($subdomainId) . '/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? HostingDumper::getWebspaceRoot( PleskStructure::getDomainAsciiNameFromId($domainId), $self->{remoteWebNodes} ) . '/' : '' ) .'subdomains/' . $subdomainName . '/cgi-bin';
    }
}

# Get domain name that could be safely used in Plesk CLI tools
# This is a workaround for wildcard subdomains, to be correct display name should be
# used instead of name, but there are problems passing it to Plesk CLI tool when there
# are no locales installed (easy steps to reproduce such situation - obtain a Debian 6 Virtuozzo box)
# and name contains UTF-8 symbols
sub getCliDomainName {
    my ( $domainName ) = @_;
    $domainName =~ s/_/*/;
    return $domainName;
}

my %wwwRootIndex = {};

sub makePhostingNode {
    my ( $self, $ptrDomain, $ptrDomParams, $ptrSiteApplications, $is_site, $ips, $embeddedInfo ) = @_;

    Logging::beginObject('hosting',$ptrDomain->{'name'}, undef);
    my @SiteApplications = @{$ptrSiteApplications};

    unless ( ref($ptrDomain) =~ /HASH/ ) {
        Logging::warning("Error: makePhostNode: bad arguments",'assert');
        Logging::endObject();
        return undef;
    }

    my (
        $domainName, $domainRoot, $path,    $sql, $domainServiceDir,
        %hosting,    $domainId,   $xmlName, $fieldName, $id
    );
    $domainName = $ptrDomain->{'name'};
    $domainId   = $ptrDomain->{'id'};

    my $webspaceId = $self->getWebspaceId($domainId); # used for Site content-related subs
    my $webspaceName;

    my $remoteCommunicationIp = undef;

    if ($is_site) {
        $webspaceName = PleskStructure::getDomainAsciiNameFromId($webspaceId);
        if (exists $self->{remoteWebNodes}->{$webspaceName} && defined $self->{remoteWebNodes}->{$webspaceName}) {
            $self->{remoteWebNodes}->{$domainName} = $self->{remoteWebNodes}->{$webspaceName};
        } else {
            $self->{remoteWebNodes}->{$domainName} = undef;
        }
    } else {
        foreach my $ip (keys %{$ips}) {
            if ( PleskStructure::isRemoteCommunicationIp($ip) ) {
                $remoteCommunicationIp = PleskVersion::isShm() ?
                $self->{backupParameters}->{serviceNodeIp} : PleskStructure::getCommunicationIp($ip);
                Logging::debug("Remote IP detected:" . $remoteCommunicationIp);
            }
        }

        if (defined $remoteCommunicationIp) {
            $self->{remoteWebNodes}->{$domainName} = PleskVersion::isShm() ?
            undef : PleskStructure::getRemoteNodeId($remoteCommunicationIp);
        } else {
            $self->{remoteWebNodes}->{$domainName} = undef;
        }
    }

    my $osType = (PleskStructure::getServiceNodeProperty($self->{remoteWebNodes}->{$domainName}, "OS_WIN") eq "true") ? 'windows' : 'unix';
    $self->{packer}->setContentTransport($self->{packer}->getContentTransportType(), $osType);

    my %hostingParams;

    %hosting = %{DAL::getHostingParams($domainId)};

    if ( defined( $hosting{'webstat'} ) and $hosting{'webstat'} ) {
        $hostingParams{'webstat'} = $hosting{'webstat'};
    }

    if ( defined( $hosting{'ssl'} ) and $hosting{'ssl'} ) {
        $hostingParams{'https'} = $hosting{'ssl'};
    }

    if ( defined( $hosting{'same_ssl'} ) and ( $hosting{'same_ssl'} eq 'true' ) ) {
        $hostingParams{'shared-content'} = 'true';
    } else {
        $hostingParams{'shared-content'} = 'false';
    }

    my $wwwroot = $self->getDomainWwwRoot($domainId);

    if ($wwwroot eq '') {
        my $index = $webspaceId == 0 ? $domainId : $webspaceId;
        $wwwRootIndex{$index} += 1;
        $hostingParams{'www-root'} = "site" . $wwwRootIndex{$index};
    } else {
        $hostingParams{'www-root'} = $wwwroot;
    }

    my $domParams = $self->getDomainParams($ptrDomain);
    $hostingParams{'errdocs'} = 'true' if ($domParams->{'apacheErrorDocs'} eq 'true');

    if (defined $domParams->{'cgi_bin_mode'}) {
        $hostingParams{'cgi_bin_mode'} = $domParams->{'cgi_bin_mode'} if ($domParams->{'cgi_bin_mode'} =~ /^old-style$|^www-root$|^webspace$/);
    } elsif (!PleskVersion::atLeast( 11, 5, 0 )) {
        $hostingParams{'cgi_bin_mode'} = 'webspace';
    }

    if (defined( $hosting{'maintenance_mode'} )) {
        $hostingParams{'maintenance_mode'} = $hosting{'maintenance_mode'};
    }

    my %sysuser;

    #
    # sysuser
    #
    Logging::beginObject('sysuser', $domainName, undef);

    if ( $id = $hosting{'sys_user_id'} ) {
        %sysuser = $self->makePleskSysUserNode($id, $embeddedInfo);
        $sysuser{'relative_path'} = HostingDumper::getWebspaceRoot($domainName, $self->{remoteWebNodes});
    }

    #
    # end sysuser
    #
    Logging::endObject();

    my $domUser = lc( $sysuser{'login'} );

    my ($phpSettings, $scripting) = HostingDumper::getScripting(\%hosting);

    my %phpSettings = %{$phpSettings};

    #
    # 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 ) ) {
        my $published = $self->{packer}->getSb5SitePublished(getCliDomainName($domainName));
        $hostingParams{'sitebuilder-site-published'} = $published if defined $published;
    }

    my @sites;
    if ( PleskVersion::atLeast( 10, 0, 0 ) ) {
        @sites = @{ DAL::getSitesByWebspaceId($domainId) };
    }

    my @subdoms;

    $sql = "SELECT * FROM subdomains WHERE dom_id=? ORDER BY id";

    if ( $self->{dbh}->execute_rownum($sql, $domainId) ) {
        while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
            push @subdoms, { %{$ptrHash} };
        }
    }
    $self->{dbh}->finish();

    if ( PleskVersion::atLeast( 10, 2, 0 ) ) {
        my $pk = $self->getDomainCertificatePrivateKey($hosting{'certificate_id'}, $domainId);
        if ($pk) {
            $hostingParams{'certificate_ref'} = $self->getCertificateRef($pk);
        }
    }

    $domainRoot = HostingDumper::getDomainRoot($domainName, $self->{remoteWebNodes});
    my $webspaceRoot = ($webspaceName && PleskVersion::atLeast( 11, 1, 18 )) ? HostingDumper::getDomainRoot($webspaceName, $self->{remoteWebNodes}) : $domainRoot;
    if (PleskVersion::atLeast(11, 5, 23)) {
        if (PleskStructure::getServiceNodeProperty($self->{remoteWebNodes}->{$domainName}, "OS_WIN") eq "true") {
            $domainServiceDir = "$webspaceRoot/.plesk";
        } else {
            $domainServiceDir = HostingDumper::getSystemDomainRoot($domainName, $self->{remoteWebNodes});
        }
    } else {
        $domainServiceDir = $webspaceRoot;
    }

    if ('unix' eq $osType) {
        if (PleskVersion::atLeast(11, 5, 23)) {
            if ('true' eq $self->{packer}->getContentTransport()->hasLink("$domainRoot/conf", "$domainServiceDir/conf", 'remoteCommunicationIp' => $remoteCommunicationIp)) {
                $hostingParams{'original-conf-directory'} = "$domainRoot/conf/";
            } elsif (defined($ptrDomain->{'parentDomainName'})) {
                my ($subDomainDirName) = $domainName =~ /([^.]+)/;
                my $parentDomainRoot = HostingDumper::getDomainRoot($ptrDomain->{'parentDomainName'}, $self->{remoteWebNodes});
                if ('true' eq $self->{packer}->getContentTransport()->hasLink("$parentDomainRoot/subdomains/$subDomainDirName/conf", "$domainServiceDir/conf", 'remoteCommunicationIp' => $remoteCommunicationIp)) {
                    $hostingParams{'original-conf-directory'} = "$parentDomainRoot/subdomains/$subDomainDirName/conf/";
                } else {
                    $hostingParams{'original-conf-directory'} = "$domainServiceDir/conf/";
                }
            } else {
                $hostingParams{'original-conf-directory'} = "$domainServiceDir/conf/";
            }
        } else {
            $hostingParams{'original-conf-directory'} = "$domainServiceDir/conf/";
        }
    }

    if ( !$self->{only_mail_dump} ) {
        if ( PleskVersion::atLeast( 10, 4, 0 ) ) {
            if (exists $domParams->{'phpSettingsId'}) {
                %phpSettings = %{DAL::getPhpSettings($domParams->{'phpSettingsId'})};
                my $phpSettingsNoticeText = DAL::getPhpSettingsNoticeText($domParams->{'phpSettingsId'});
                $phpSettings{'notice'} = $phpSettingsNoticeText if defined $phpSettingsNoticeText;
            }
        }

        $self->{packer}->setDomainPhosting( $domainId,
            \%hostingParams,
            $is_site? undef: \%sysuser,
            $scripting,
            $ips,
            \%phpSettings
        );

        if (PleskVersion::atLeast( 11, 5, 19 )) {
            if (exists $embeddedInfo->{'web-settings'}) {
                foreach my $webSettings (@{$embeddedInfo->{'web-settings'}}) {
                    if ($webSettings->{'vhost-name'} eq $domainName) {
                        eval {require XML::Simple; 1;};
                        my $xs = XML::Simple->new(ForceArray => 1);
                        my $webSettingsXml = $xs->XMLout($webSettings, RootName => 'web-settings');
                        $webSettingsXml = Encode::encode('UTF-8', $webSettingsXml); # workaround for UTF8 symbols
                        $self->{packer}->appendNodeToPhosting($domainId, $webSettingsXml);
                    }
                }
            }
        }

    }
    else {
        if( @sites ) {
            $self->{packer}->setDomainPhostingEmpty( $domainId, $is_site? undef: \%sysuser, $ips );
        }
    }

    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($ptrDomain->{'displayName'});

            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

        if (!$self->{configuration_dump} ) {
            my @exclude_vhost_files;
            my $httpdocsDir = $self->getDomainWwwRoot($domainId);
            push @exclude_vhost_files, "$httpdocsDir/plesk-stat";

            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";

                    # Fix to dump subdomains content, which became sites after upgrade to 11.5 and some its content unavailable for sysuser
                    # http://plesk-process.parallels.com/TargetProcess2/Project/QA/Bug/View.aspx?BugID=141214
                    # Exclude conf directory, because its content must be moved to system directory on upgrade and exclude error_docs, because it does not used
                    if (PleskVersion::atLeast(11, 5, 19)) {
                        my $subdomainName = $ptrHash->{'name'};
                        $subdomainName =~ s/\.$ptrDomain->{'name'}//g;
                        my $subdomainDataDir = "subdomains/$subdomainName";
                        push @exclude_vhost_files, "$subdomainDataDir/conf" if (-d "$domainRoot/$subdomainDataDir/conf");
                        push @exclude_vhost_files, "$subdomainDataDir/error_docs" if (-d "$domainRoot/$subdomainDataDir/error_docs");
                    }
                }
            }
            if (@subdoms and not PleskVersion::atLeast(11, 5, 19)) {
                foreach my $ptrHash (@subdoms) {
                    my $subDomainRootDir = "subdomains/" . $ptrHash->{'name'};
                    push @exclude_vhost_files, $subDomainRootDir . "/error_docs";
                    push @exclude_vhost_files, $subDomainRootDir . "/conf";
                    push @exclude_vhost_files, $subDomainRootDir . "/httpdocs";
                    push @exclude_vhost_files, $subDomainRootDir . "/httpsdocs";
                    if ($ptrHash->{'sys_user_type'} eq 'native') {
                        push @exclude_vhost_files, $subDomainRootDir . "/cgi-bin";
                        push @exclude_vhost_files, $self->getSubDomainWwwRoot($ptrHash->{'id'}, $ptrHash->{'name'}, $domainId, undef);
                    }
                }
            }

            if (! PleskVersion::atLeast(11, 5, 19)) {
                push(@exclude_vhost_files,
                    'conf', '.plesk', 'pd', 'statistics'
                );
            }
            push(@exclude_vhost_files, 'logs');
            # chroot dirs:
            push(@exclude_vhost_files, 'bin', 'dev', 'lib', 'lib64', 'usr', 'libexec', 'etc', 'tmp', 'var');

            if ($self->{dump_vhost}) {
                $self->{packer}->setDomainUserDataContent( $domainId, $domainName, $domainRoot, { 'sysuser' => $domUser, 'exclude' => \@exclude_vhost_files, 'remoteCommunicationIp' => $remoteCommunicationIp } );
            } elsif (!$is_site) {
                $self->{packer}->setDomainPhostingFullContent($domainId, $domainName, $domainRoot, { 'sysuser' => $domUser, 'exclude' => \@exclude_vhost_files, 'remoteCommunicationIp' => $remoteCommunicationIp } );
            }
        }

        my $domainWwwRoot = $self->getDomainWwwRoot($domainId, 'absolute');

        if ( !$self->{configuration_dump} ) {
            if ($self->{dump_vhost}) {
                $self->{packer}->setDomainPhostingStatisticsContent( $domainId, $domainName, "$domainServiceDir/statistics", {"remoteCommunicationIp" => $remoteCommunicationIp} );
            } else {
                my $webstatRoot = $domainServiceDir . "/statistics/webstat";
                $self->{packer}->setDomainPhostingWebstatContent( $domainId, $domainName, $webstatRoot );
                $self->{packer}->setDomainPhostingWebstatSslContent( $domainId, $domainName, "$domainServiceDir/statistics/webstat-ssl" );
                $self->{packer}->setDomainPhostingFtpstatContent( $domainId, $domainName, "$domainServiceDir/statistics/ftpstat" );
                $self->{packer}->setDomainPhostingAnonFtpstatContent( $domainId, $domainName, "$domainServiceDir/statistics/anon_ftpstat" );
            }

            $self->{packer}->setDomainPhostingProtectedDirContent( $domainId, $domainName, "$domainServiceDir/pd" );

            if (!$self->{skip_logs}) {
                my $domainLogsDirPath;
                if (PleskVersion::atLeast( 11, 5, 23 )) {
                    $domainLogsDirPath = "$domainServiceDir/logs";
                } else {
                    $domainLogsDirPath = "$domainRoot/statistics/logs";
                }
                $self->{packer}->setDomainPhostingLogsContent( $domainId, $domainName, $domainLogsDirPath );
            }
        }

        if ( PleskVersion::atLeast( 10, 0, 0 ) ) {
            if ( !$self->{configuration_dump} && !$self->{only_mail_dump} ) {
                $self->addSb5DomainContent( $domainId, $domainName, $ptrDomParams->{'site_builder_site_id'});
            }
            else {
                Logging::debug("Skip domain sitebuilder content dumping due to settings");
            }
        }

        #-----------------------------------------------------------------
        # Dump of installed site applications
        #-----------------------------------------------------------------
        foreach my $sapp (@SiteApplications) {
            if ( !$sapp->isDomainSiteapp($ptrDomain->{'displayName'}) ) {
                next;
            }
            $self->dumpSiteApplication( $sapp, $domainId, 'domain', $webspaceId? $webspaceId:$domainId, $embeddedInfo);
        }

        $self->makeDomainLogrotationNode($domainId);
        $self->addAnonFtp( $domainId, $domainName, { 'sysuser' => $domUser } );
        #
        # protected dirs
        #
        if ( PleskVersion::atLeast( 10, 0, 0 ) ) {
            $sql = "SELECT id, path, realm, non_ssl, cgi_bin, `ssl` FROM protected_dirs WHERE dom_id=? ORDER BY id";
            if ( $self->{dbh}->execute_rownum($sql, $domainId) ) {
                my (@dirs);
                while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
                    push @dirs, [ @{$ptrRow} ];
                }
                foreach my $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];
                    $self->makeProtDirNode( $domainId, $pdirId, $pdirPath, $pdirTitle, $pdirNonSSL, $pdirSSL, $pdirCGI);
                }
            }
            $self->{dbh}->finish();
        }
        else {
            $sql = "SELECT id,path,realm,non_ssl,`ssl`,cgi_bin FROM protected_dirs WHERE dom_id=? ORDER BY id";
            if ( $self->{dbh}->execute_rownum($sql, $domainId) ) {
                my (@dirs);
                while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
                    push @dirs, [ @{$ptrRow} ];
                }
                foreach my $ptrRow (@dirs) {
                    unless ( defined( $hosting{'ssl'} ) and $hosting{'ssl'} =~ /true/ ) {
                        $ptrRow->[4] = 'false';    # bug 97159
                    }
                    $self->makeProtDirNode( $domainId, @{$ptrRow} );
                }
            }
            $self->{dbh}->finish();
        }
        #
        # end protected dirs
        #

        unless ($is_site) {
            #
            # web users
            #
            my $webs = DAL::getWebUsers($domainId);

            foreach my $ptrHash (@{$webs}) {
                $self->makeWebUserNode( $ptrHash, $domainId, $domainName );
            }
            #
            # end web users
            #
        }

        unless ($is_site) {
            #
            # ftpusers
            #
            my $ftps = DAL::getAdditionalFtpUsers($domainId);
            foreach my $ptrHash (@{$ftps}) {
                $self->makeSubFtpUserNode( $ptrHash, $domainId, $domainName );
            }
            #
            # end ftpusers
            #
        }

        #
        # subdomains
        #

        $self->{packer}->setEmptySubFtpUsersNode($domainId);
        foreach my $ptrHash (@subdoms) {
            $ptrHash->{'www-root'} = $self->getSubDomainWwwRoot($ptrHash->{'id'}, $ptrHash->{'name'}, $domainId);
            eval {
                $self->dumpSubDomain( $domainId, $ptrHash, $domainName, \@SiteApplications, $embeddedInfo );
                1;
            } or do {
                $self->excludeProblemSubdomainFromBackup( $domainId, $ptrHash, $@ );
            }
        }
        $self->{packer}->removePhostingChildNodeIfEmpty( $domainId, 'subdomain' );
        $self->{packer}->removePhostingChildNodeIfEmpty( $domainId, 'ftpuser' );

        #
        # end subdomains
        #
    }

    if( @sites ) {
        foreach my $ptrHash ( @sites ) {
            eval {
                $self->makeSiteNode( $ptrHash->{'name'}, $ptrHash->{'displayName'}, \@SiteApplications, $embeddedInfo );
                1;
            } or do {
                $self->excludeProblemSiteFromBackup( $domainId, $ptrHash, $@ );
            }
        }
        $self->{packer}->removePhostingChildNodeIfEmpty( $domainId, 'site' );
    }

    if ( !$self->{only_mail_dump} ) {
        #
        # configuration files (conf/vhost[_ssl].conf)
        #
        my $confDir      = PleskVersion::atLeast( 11, 5, 0 ) ? "$domainServiceDir/conf/" : "$domainRoot/conf/";
        my @excludeConfFiles;
        push(@excludeConfFiles, 'httpd.include', 'last_httpd.include', 'last_httpd.conf', '*.*_httpd.include', '*.*_httpd.conf');
        push(@excludeConfFiles, 'last_nginx.conf', '*.*_nginx.conf', 'nginx.conf');
        push(@excludeConfFiles, 'siteapp.d', 'stat_ttl.conf', 'webalizer.conf', 'prev_month.found');
        Logging::debug('Exclude conf files: ' . join(", ", @excludeConfFiles));
        $self->{packer}->setDomainPhostingConfContent( $domainId, $domainName, $confDir, { 'exclude' =>  \@excludeConfFiles } );
        #
        # end configuration files (conf/vhost[_ssl].conf)
        #

        #
        # Webalizer configuration
        #
        my ( $directRef, @hiddenRefs, @groupRefs );

        my $sql = "SELECT referrer FROM webalizer_hidden_referrer "
        . " WHERE dom_id=?";
        if ( $self->{dbh}->execute_rownum($sql, $domainId) ) {
            while ( my $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=?";
        if ( $self->{dbh}->execute_rownum($sql, $domainId) ) {
            while ( my $ptrRow = $self->{dbh}->fetchrow() ) {
                push @groupRefs, { 'ref' => $ptrRow->[0], 'name' => $ptrRow->[1] };
            }
        }
        $self->{dbh}->finish();

        $self->{packer}->setDomainWebalizer( $domainId, $directRef, \@hiddenRefs, \@groupRefs );

        #
        # 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'} );
        }

        if ( PleskVersion::atLeast( 11, 1, 0 ) and 'windows' eq $osType ) {
            if (exists $embeddedInfo->{'iis-application-pool'}) {
                my $iisAppPoolNode = $embeddedInfo->{'iis-application-pool'}->[0];
                eval {require XML::Simple; 1;};
                my $xs = XML::Simple->new(ForceArray => 1);
                $self->{packer}->setIisAppPool($domainId, $xs->XMLout($iisAppPoolNode, RootName => 'iis-application-pool'));
            }
        }
    }
    if ( !$self->{only_mail_dump} && PleskVersion::atLeast(12, 0, 11)) {
        my @wpInstances = @{DAL::getDomainWordPressInstances($domainId)};

        if ( @wpInstances ) {
            foreach my $instance (@wpInstances) {
                my @wpInstanceProperties = @{DAL::getWordPressInstanceProperties($instance->{'id'})};
                $self->{packer}->setNonApsWordpressInstance($domainId, $instance, \@wpInstanceProperties, 'true');
            }
        }
    }

    Logging::endObject();
}

sub dumpSiteApplication {
    my ( $self, $sapp, $parent, $parentType, $webspaceId, $embeddedInfo ) = @_;

    my $sapp_id = $parentType . "_" . $parent . "_" . $sapp->getInstallPrefix();

    my $prefix = $sapp->getInstallPrefix();

    my $licenseType = $sapp->getAPSClientItemLicenseType();

    if ( 'domain' eq $parentType ) {
        $self->{packer}->addDomainSapp(
            $parent, $sapp_id, $sapp, $licenseType
        );
    }
    elsif ( 'subdomain' eq $parentType ) {
        $self->{packer}->addSubDomainSapp(
            $parent, $sapp_id, $sapp, $licenseType
        );
    }
    else {
        Logging::warning(
            "Error: dumpSiteApplication: Unexpected type of parent \"$parentType\"",'assert');
    }

    $self->{packer}->setSappParams( $sapp_id, $sapp );


    #-----------------------------------------------------------------
    # Linked resources
    #-----------------------------------------------------------------

    # Databases
    foreach my $row ( $sapp->getDatabases() ) {
        $self->makeDatabaseNode( $row->{'id'}, $parent, $parentType, $sapp_id, $row, $embeddedInfo );
    }

    # Custom buttons
    foreach my $row ( $sapp->getCustomButtons() ) {
        $self->getCustomButtonById71( $row->{'id'}, $parent, $parentType, $sapp_id );
    }

    my $installDirApsRegistryId = undef;
    if ( PleskVersion::atLeast( 10, 3, 0 )) {
        $installDirApsRegistryId = $sapp->getInstallDirApsRegistryId();
    }
    $self->{packer}->makeSiteAppInstalled( $sapp_id, $prefix, $sapp->isSsl(),  $installDirApsRegistryId );

    if ( PleskVersion::atLeast( 10, 0, 0 ) ) {
        $self->{packer}->setSappEntryPoints( $sapp_id, $sapp );
    }

    if ( PleskVersion::atLeast( 10, 3, 0 ) ) {
        $self->{packer}->setSappApsControllerInfo( $sapp_id, $sapp );
    }

    # backupArchive

    if ( PleskVersion::atLeast( 11, 0, 0 )) {
        my $backupArchiveFile = $sapp->getBackupArchiveFile();

        unless ( open( BAFILE, "<$backupArchiveFile" ) ) {
            Logging::warning("Error: unable to open BackupArchive from APS controller:" . $backupArchiveFile);
        }

        my $backupArchiveContent;

        while(<BAFILE>) {
            $backupArchiveContent .= $_;
        }

        close(BAFILE);

        $self->{packer}->setSappApscNode($sapp_id, $backupArchiveContent);

    } elsif ( PleskVersion::atLeast( 10, 3, 0 )) {
        my $backupArchiveFile = $sapp->getBackupArchiveFile();
        my $backupArchiveFileName = undef;
        my $backupArchiveDir = undef;

        my $idx = rindex( $backupArchiveFile, '/' );

        if ( $idx > 0 ) {
            $backupArchiveDir = substr( $backupArchiveFile, 0, $idx);
            $backupArchiveFileName = substr( $backupArchiveFile, $idx+1);
        }

        if (defined $backupArchiveFileName) {
            if ( 'domain' eq $parentType ) {
                $self->{packer}->setDomainSappBackupArchiveContent( $parent, $sapp_id, $backupArchiveDir, $backupArchiveFileName, $webspaceId );
            }
            elsif ( 'subdomain' eq $parentType ) {
                $self->{packer}->setSubDomainSappBackupArchiveContent( $parent, $sapp_id, $backupArchiveDir, $backupArchiveFileName, $webspaceId );
            }
        } else {
            Logging::warning( "BackupArchive from APS controller is not found", 'assert' );
        }
    }

    if ( PleskVersion::atLeast( 10, 3, 0 ) ) {
        $self->{packer}->setSappApsLicense($sapp_id, $sapp);
    }

    if ( PleskVersion::atLeast( 11, 5, 22 ) ) {
        $self->{packer}->setSappSettings($sapp_id, $sapp);
    }

    if ( PleskVersion::atLeast( 12, 0, 11 ) ) {
        my $apsResourceId = $sapp->getApplicationApsResourceId();
        my $wpInstance = shift @{DAL::getWordPressInstancesByResourceId($apsResourceId)};
        if ( $wpInstance ) {
            my @wpInstanceProperties = @{DAL::getWordPressInstanceProperties($wpInstance->{'id'})};
            $self->{packer}->setSappWordPressInstance($sapp_id, $sapp, $wpInstance, \@wpInstanceProperties);
        }
    }

    if ($self->{include_app_distrib}) {
        if ( PleskVersion::atLeast( 10, 3, 0 ) ) {
            my %applicationsList = %{$self->getListOfInstalledApplicationsOnServer()};
            foreach my $installedSiteAppOnServer (keys %applicationsList) {
                if ($installedSiteAppOnServer eq $sapp->getCacheId()) {
                    my @applicationInfo = @{$applicationsList{$installedSiteAppOnServer}};
                    $self->{packer}->setServerSettings() unless defined $self->{packer}->{serverNode};
                    $self->{packer}->addServerAppPackage($sapp->getName(), $sapp->getVersion(), $sapp->getRelease(), $applicationInfo[3], $applicationInfo[4], $applicationInfo[5], $applicationInfo[6]);
                }
            }
        } elsif ( PleskVersion::atLeast( 10, 0, 0 ) ) {
            $self->{packer}->setServerAppVault();
            $self->{packer}->setServerPackagesPool();

            foreach my $ptrRow ( @{DAL::getApsPackages100()} ) {
                if ( $sapp->getName() eq $ptrRow->[0]
                    && $sapp->getVersion() eq $ptrRow->[1]
                    && $sapp->getRelease() eq $ptrRow->[2]
                ) {
                    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] );
                }
            }
        } else {
            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 ) = @_;

    my $logrotation_id = DAL::getDomainParam($domainId, 'logrotation_id');
    if ( defined $logrotation_id ) {
        $self->{packer}->setDomainLogrotation( $domainId, DAL::getLogrotation($logrotation_id) );
    }
}

#-----------------------------------------------------------------
#    $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, $embeddedInfo ) =  @_;
    unless ( ref($ptrSubDomain) =~ /HASH/ ) {
        Logging::warning("Error: dumpSubDomain: bad argumets",'assert');
        return;
    }

    my $subDomainName;
    my $subAsciiDomainName;

    $subDomainName = $ptrSubDomain->{'displayName'};
    $subAsciiDomainName = $ptrSubDomain->{'name'};

    # Subdomains may be SSL-enabled since 8.0
    my $https = '';
    my $shared_content = '';

    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 %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 %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};
        }
    }
    delete $scripting{'php_handler_type'} if exists $scripting{'php_handler_type'} and ( not exists $scripting{'php'} or $scripting{'php'} eq 'false');

    if ( PleskVersion::atLeast( 10, 2, 0 ) ) {
        my $pk = $self->getDomainCertificatePrivateKey($ptrSubDomain->{'certificate_id'}, $domainId);
        if ($pk) {
            $ptrSubDomain->{'certificate_ref'} = $self->getCertificateRef($pk);
        }
    }

    my $domainRoot = HostingDumper::getDomainRoot($domainName, $self->{remoteWebNodes});
    my $webspaceRoot = HostingDumper::getWebspaceRoot($domainName, $self->{remoteWebNodes});
    my $subDomainRoot = (PleskVersion::atLeast( 11, 1, 18 )) ?  "$webspaceRoot/$subAsciiDomainName" : "$domainRoot/subdomains/$subAsciiDomainName";
    $ptrSubDomain->{'original-conf-directory'} = "$subDomainRoot/conf/";

    $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 $subDomainWwwRoot = $self->getSubDomainWwwRoot($ptrSubDomain->{'id'}, $subAsciiDomainName, $domainId, 'absolute');
    if ( not $self->{configuration_dump} and not PleskVersion::atLeast(11,5,19)) {
        $self->{packer}->setSubDomainHttpdocsContent($ptrSubDomain->{'id'}, "$subDomainRoot/httpdocs", \@exclude_httpdocs_files, $ptrSubDomain->{'sys_user_type'} eq 'main' ? ($webspaceId ? $webspaceId : $domainId) : undef);
        $self->{packer}->setSubDomainHttpsdocsContent($ptrSubDomain->{'id'}, "$subDomainRoot/httpsdocs", \@exclude_httpsdocs_files);
        $self->{packer}->setSubDomainConfContent($ptrSubDomain->{'id'}, "$subDomainRoot/conf");
        $self->{packer}->setSubDomainErrorDocsContent($ptrSubDomain->{'id'}, "$subDomainRoot/error_docs");
    }

    # Dump of installed site applications
    foreach my $sapp (@SiteApplications) {
        if ( !$sapp->isSubdomainSiteapp( $ptrSubDomain->{'id'} ) ) {
            next;
        }
        $self->dumpSiteApplication( $sapp, $ptrSubDomain->{'id'}, 'subdomain', $webspaceId? $webspaceId:$domainId, $embeddedInfo);
    }
}

sub makeWebUserNode {
    my ( $self, $ptrWebUser, $domainId, $domainName ) = @_;
    unless ( ref($ptrWebUser) =~ /HASH/ ) {
        Logging::warning("Error: makeWebUserNode: bad argumets", 'assert');
        return;
    }
    my ( $home, $userName, $item, $xmlName, $fieldName );

    my %sysuser;

    %sysuser = $self->makePleskSysUserNode( $ptrWebUser->{'sys_user_id'} );
    $userName = $sysuser{'login'};

    my $domainRoot = HostingDumper::getDomainRoot($domainName, $self->{remoteWebNodes});

    my $webUserHome = $domainRoot . "/web_users/$userName";
    my $privateData = $domainRoot . "/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'} = HostingDumper::getWebspaceRoot($domainName, $self->{remoteWebNodes});

    $self->{packer}->addDomainSubFtpUser($domainId, $domainName, \%sysuser, $ptrFtpUser);
}

sub makeProtDirNode {
    my ( $self, $domainId, $pdirId, $pdirPath, $pdirTitle, $pdirNonSSL, $pdirSSL, $pdirCGI ) = @_;

    my ( $sql, $item, $ptrRow );

    # workaround of CLI inabliity to create '' directory.
    if ( $pdirPath eq '' ) {
        $pdirPath = '/';
    }

    $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=? ORDER BY p.id";

    my @users;
    if ( $self->{dbh}->execute_rownum($sql, $pdirId) ) {
        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 ( $ptrHash, $count, $domainRoot, $sql );
    $count = 0;
    $sql   = "SELECT * FROM anon_ftp WHERE dom_id=?";
    if ( $self->{dbh}->execute_rownum($sql, $domainId) ) {
        while ( $ptrHash = $self->{dbh}->fetchhash() ) {
            $domainRoot = HostingDumper::getDomainRoot($domainName, $self->{remoteWebNodes});
            my ( $pub_path, $incoming_path );

            $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::debug( "addSb5DomainContent: domain $domainName site_builder_site_id is not found. skip");
        return;
    }
    $self->{packer}->setSb5DomainContent( $domainId, $domainName, $uuid);
}

sub makePleskSysUserNode {
    my ( $self, $sysUserId, $embeddedInfo ) = @_;
    my %sysuser = %{ HostingDumper::getSysUserInfo($sysUserId) };

    Logging::trace("Making system user node: $sysuser{'login'}");

    my $sysUserInfo = undef;
    if (PleskVersion::atLeast(11, 1, 0) && defined($embeddedInfo)) {
        $sysUserInfo = (grep { $_->{name} eq $sysuser{login} } @{$embeddedInfo->{sysuser}})[0];
    }

    #
    # password node
    #

    if ( $sysuser{'account_id'} != 0 ) {
        ( $sysuser{'passwd'}, $sysuser{'passwdType'} ) = $self->makeAccountPasswordNode( $sysuser{'account_id'} );
    }

    #
    # end password node
    #

    #
    # crontab node
    #
    if (PleskVersion::atLeast(12, 1, 12)) {
        if (defined($sysUserInfo) && defined($sysUserInfo->{'scheduled-tasks'})) {
            my $scheduledTasks = $sysUserInfo->{'scheduled-tasks'}->[0];
            if (ref($scheduledTasks) =~ /HASH/ && defined($scheduledTasks->{'failure'})) {
                my $message = $scheduledTasks->{'failure'}[0]->{'message'}[0];
                Logging::warning($message);
            } else {
                $sysuser{'scheduled-tasks'} = $scheduledTasks;
            }
        }
    } elsif ( PleskVersion::atLeast( 11, 1, 0 ) ) {
        my $cron = $sysUserInfo->{cron}->[0];
        if (ref($cron) =~ /HASH/) {
            if (defined $cron->{'failure'}) {
                my $message = $cron->{'failure'}[0]->{'message'}[0];
                Logging::warning($message);
            } else {
                $sysuser{'cron'} = $cron->{'content'};
                $sysuser{'cron-encoding'} = $cron->{'encoding'};
            }
        } else {
            $sysuser{'cron'} = $sysUserInfo->{cron}->[0];
        }
    } else {
        my $cron = $self->getCron($sysuser{'login'});
        if (defined $cron) {
            $sysuser{'cron'} = $cron;
        }
    }

    #
    # end crontab node
    #

    return %sysuser;
}

sub getCron {
    my ( $self, $account ) = @_;

    my $crontabmng = AgentConfig::get("PRODUCT_ROOT_D") . "/admin/sbin/crontabmng";
    if ( -x $crontabmng ) {
        open( CRONTAB, "$crontabmng get $account |" );
        my $crontab = "";
        while (<CRONTAB>) {
            last if $_ eq "0\n";
            $crontab .= $_;
        }
        close(CRONTAB);
        if ( $crontab ne "\n" ) {
            return $crontab;
        }
    }
}

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, $ips ) = @_;

    return if ( $self->{only_mail_dump} );

    unless ( ref($ptrDomain) =~ /HASH/ ) {
        Logging::warning("Error: makeFhostingNode: bag arguments", 'assert');
        return;
    }
    my ( $sql, $domainId, $forward, $rowPtr );
    $domainId = $ptrDomain->{'id'};
    $sql      = "SELECT redirect FROM forwarding WHERE dom_id=?";
    unless ( ( $self->{dbh}->execute_rownum($sql, $domainId) ) && ( $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";
        Logging::warning($msg,'BrokenDbIntegrity');
        return;
    }

    ($forward) = @{ $rowPtr };
    $self->{packer}->setDomainFhosting( $domainId, $forward, $ips);
    $self->{dbh}->finish();
}

sub makeShostingNode {
    my ( $self, $ptrDomain, $ips ) = @_;

    return if ( $self->{only_mail_dump} );

    unless ( ref($ptrDomain) =~ /HASH/ ) {
        Logging::warning("Error: makeShostingNode: bag arguments", 'assert');
        return;
    }

    my ( $sql, $domainId, %forward, $hashPtr );
    $domainId = $ptrDomain->{'id'};

    $sql = "SELECT * FROM forwarding WHERE dom_id=?";
    unless ( ( $self->{dbh}->execute_rownum($sql, $domainId) ) && ( $hashPtr = $self->{dbh}->fetchhash() ) ) {
        $self->{dbh}->finish();
        my $msg = "Broken referencial integrity: forward for domain " . $ptrDomain->{'name'} . " is not found in forwarding";
        print STDERR "$msg\n";
        Logging::warning($msg,'BrokenDbIntegrity');
        return;
    }

    $self->{dbh}->finish();

    %forward = %{ $hashPtr };
    $self->{packer}->setDomainShosting( $domainId, \%forward, $ips );
    $self->{dbh}->finish();
}

sub _makePasswordData{
    my ($password, $type) = @_;

    if (!defined $password) {
        Logging::warning( "'undef' password passed to _makePasswordData. Change to empty!" );
        $password = '';
    }

    if (!$password) {
        $type = 'plain';
    }
    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, CommonPacker::normalizePasswordType($type) );
    }
    else {

        # generates a stub node
        ( $passwd, $type ) = _makePasswordData();
    }
    $self->{dbh}->finish();

    return ( $passwd, $type );
}

my @_planItemNames;

sub getPlanItemNames {
    my ( $self ) = @_;

    return \@_planItemNames if ( not PleskVersion::atLeast( 10, 2, 0 ) );

    if ( @_planItemNames ) {
        return \@_planItemNames;
    }

    my $planItemsPtr = DAL::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=? AND object_type='?'";
    if ( $self->{dbh}->execute_rownum($sql, $id, $type) ) {
        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;
    return $subscriptionNode;
}

sub makeSubscriptionNode {
    my ( $self, $type, $id ) = @_;

    my $rowPtr = DAL::getSubscription($type, $id);
    if ( !defined($rowPtr) ) {
        $self->{dbh}->finish();
        return $self->{packer}->makeSubscriptionNode(undef, undef, undef); # subscription node is needed for dump subscription properties
    }
    my $subscription_id = $rowPtr->{'id'};
    my $locked = $rowPtr->{'locked'};
    my $synchronized = defined$rowPtr->{'synchronized'} ? $rowPtr->{'synchronized'} : undef;
    my $custom = defined $rowPtr->{'custom'} ?  $rowPtr->{'custom'} : undef;
    my $externalId = defined $rowPtr->{'external_id'} ? $rowPtr->{'external_id'} : undef;

    $self->{dbh}->finish();

    my $subscriptionNode = $self->{packer}->makeSubscriptionNode($locked, $synchronized, $custom, $externalId);

    my %planQuantity;
    my $sql = "SELECT plan_id, quantity FROM PlansSubscriptions WHERE subscription_id = ?";
    if ( $self->{dbh}->execute_rownum($sql, $subscription_id) ) {
        while ( my $row = $self->{dbh}->fetchrow() ) {
            $planQuantity{$row->[0]} = $row->[1];
        }
    }
    $self->{dbh}->finish();

    while ( my ($planId, $quantity) = each(%planQuantity) ) {
        $sql = "SELECT type, uuid FROM Templates WHERE id = ?";
        unless ( $self->{dbh}->execute_rownum($sql, $planId) ) {
            Logging::warning( "Error during getting uuid from plan '$planId'");
            $self->{dbh}->finish();
            next;
        }
        if ( my $row = $self->{dbh}->fetchrow() ) {
            my ($planType, $planGuid) = @$row;
            my $is_addon;
            if ($planType eq 'domain_addon') {
                $is_addon = 'true';
            }
            $self->{packer}->addSubscriptionPlan($subscriptionNode, $quantity, $planGuid, $is_addon);
        }
        $self->{dbh}->finish();
    }

    return $subscriptionNode;
}

sub makeSpamassassinNode {
    my ( $self, $mailId, $mailNm, $domainAsciiname, $status, $dir ) = @_;

    my $mailname = $mailNm . '@' . $domainAsciiname;

    my $is_server = $mailname eq '*@*';

    my $spamFilter = DAL::getMailnameSpamfilter($mailname);
    unless ( exists $spamFilter->{'id'} ) {
        Logging::debug( "Unable get information about spam filter for $mailname");
        return;
    }

    my ( @blacckList, @whiteList, @unblackList, @unwhiteList, $action, $requiredScore, $subj_text );

    my $filter_id = $spamFilter->{'id'};
    if ( exists $spamFilter->{'reject_spam'} ) {
        $action = ( $spamFilter->{'reject_spam'} eq 'true') ? 'delete' : 'mark';
    }

    my $spamFilterPreferences = DAL::getSpamfilterPreferences($filter_id);
    unless ( $is_server || scalar(@$spamFilterPreferences) ) {
        $filter_id = DAL::getServerSpamfilterId();
        $spamFilterPreferences = DAL::getSpamfilterPreferences($filter_id) if defined $filter_id;
    }

    if(@$spamFilterPreferences) {
        foreach my $row (@$spamFilterPreferences) {
            if ($row->[0] eq "blacklist_from") {
                push @blacckList,  $row->[1];
            }
            elsif ($row->[0] eq "whitelist_from") {
                push @whiteList,   $row->[1];
            }
            elsif ($row->[0] eq "unblacklist_from") {
                push @unblackList, $row->[1];
            }
            elsif ($row->[0] eq "unwhitelist_from") {
                push @unwhiteList, $row->[1];
            }
            elsif ($row->[0] eq "required_score") {
                $requiredScore = $row->[1];
            }
            elsif ($row->[0] eq "rewrite_header") {
                $row->[1] =~ s/^subject //;
                $subj_text = $row->[1];
            }
            elsif ($row->[0] eq "action") {
                if ($row->[1] ne 'delete' && $row->[1] ne 'move') {
                    $action = 'mark';
                } else {
                    $action = $row->[1];
                }
            }
        }
    }

    my $spamServerSettings;
    if ($is_server) {
        $spamServerSettings = DAL::getSpamServerSettings();
        $status = $spamServerSettings->{'spamfilter_enabled'} if exists $spamServerSettings->{'spamfilter_enabled'};
    }

    $self->{packer}->setMailSpamSettings(
        $mailId, $mailNm, $domainAsciiname,
        ( $status eq 'true' ? 'on' : 'off' ),
        undef, $action,
        $requiredScore, undef, $subj_text,
        \@blacckList,    \@whiteList,
        \@unblackList,   \@unwhiteList,
        $dir, (keys(%$spamServerSettings)? $spamServerSettings : undef)
    );
}

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 $options;

    # several times there was empty button place in database, which is incorrect
    my $sql = "SELECT * FROM custom_buttons WHERE id=? AND place!=''";
    unless ( ( $self->{dbh}->execute_rownum($sql, $id) ) && ( $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 ){
        if ( 'domain' eq $parentType ) {
            $self->{packer}->addDomainSappCustomButton( $parent, $sappId, $id, $options, $customButtonsDir, $icon );
        }
        elsif ( 'subdomain' eq $parentType ) {
            $self->{packer}->addSubDomainSappCustomButton( $parent, $sappId, $id, $options, $customButtonsDir, $icon );
        }
        else {
            Logging::warning( "Error: getCustomButtonById71: Unexpected type of siteapp parent '$parentType'",'assert');
        }
    }
    else{
        if ( 'server' eq $parentType ) {
            $self->{packer}->addServerCustomButton( $id, $options, $customButtonsDir, $icon );
        }
        elsif ( 'client' eq $parentType || 'reseller' eq $parentType ) {
            $self->{packer}->addClientCustomButton( $parent, $id, $options, $customButtonsDir, $icon );
        }
        elsif ( 'domain-admin' eq $parentType ) {
            $self->{packer}->addDomainCustomButton( $parent, $id, $options, $customButtonsDir, $icon );
        }
        elsif ( 'mailuser' eq $parentType ) {
            $self->{packer}->addMailCustomButton( $parent, $id, $options, $customButtonsDir, $icon );
        }
        elsif ( 'sapp' eq $parentType ) {
            $self->{packer}->addSappCustomButton( $parent, $id, $options, $customButtonsDir, $icon );
        }
        else {
            Logging::warning("Error: getCustomButtonById71: Unexpected type of parent '$parentType'",'assert');
        }
    }
}

sub getCustomButtonsByOwner71 {
    my ( $self, $owner_type, $owner_id ) = @_;
    my @ids = @{DAL::getCustomButtonIdsByOwner71($owner_type, $owner_id)};
    return unless @ids;
    return  map { $self->getCustomButtonById71( $_, $owner_id, $owner_type ) } @ids;
}

sub getCustomButtonsByOwner {
    my $self = shift;
    return $self->getCustomButtonsByOwner71(@_)
}

sub makeDefaultIpNode {
    my ( $self ) = @_;
    my $ip = DAL::getDefaultIpAddress();
    $self->{packer}->setServerDefaultIp( $ip ) if $ip;
}

sub makeAdminInfoNode {
    my ( $self, $embeddedInfo ) = @_;

    my ( $max_btn_len, $send_announce, $external_id );

    my $ptrAdmin = DAL::getAdminMiscParams();

    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 ( PleskVersion::atLeast( 10, 0, 0 ) ) {
        $external_id = DAL::getClientExternalId( PleskStructure::getAdminId() );
    }

    $self->{packer}->addRootAdmin( PleskStructure::getAdminId(), PleskStructure::getAdminGuid() );

    my $passwd = AgentConfig::get { 'password' };

    my $cron = undef;
    if (PleskVersion::atLeast(12, 1, 12)) {
        my $scheduledTasks = $embeddedInfo->{'scheduled-tasks'}->[0];
        if (ref($scheduledTasks) =~ /HASH/ && defined($scheduledTasks->{'failure'})) {
            my $message = $scheduledTasks->{'failure'}[0]->{'message'}[0];
            Logging::warning($message);
        } else {
            $cron = $scheduledTasks;
        }
    } else {
        $cron = $self->getCron('root');
    }


    my $subscriptionDescriptions = DAL::getSubscriptionsAdminDescriptions();
    my $clientsDescriptions = DAL::getClientsDescriptions( PleskStructure::getAdminId() );
    my @allAdminDescriptions = (@{$subscriptionDescriptions}, @{$clientsDescriptions});

    $self->{packer}->setServerAdminInfo( $ptrAdmin, $passwd, $max_btn_len, $send_announce, $external_id, $cron, \@allAdminDescriptions );

    if (PleskVersion::atLeast(9, 0, 0)) {
        my $adminClientParams = DAL::getAdminClientParams();
        my $adminId = PleskStructure::getAdminId();
        $self->{packer}->makeClParamNode($adminId, 'admin', $adminClientParams);
    }

    $self->makeAdminLimitsAndPermissions();
}

sub dumpPlanItems {
    my ( $self) = @_;

    my @planItems = @{DAL::getPlanItems()};
    if ( @planItems ) {
        my $customButtonsDir = AgentConfig::get("PRODUCT_ROOT_D") . '/admin/htdocs/images/custom_buttons';
        foreach my $planItemPtr ( @planItems ) {
            my %planItemProps = %{DAL::getPlanItemProperties($planItemPtr->{'id'})};
            if ( keys %planItemProps ) {
                $self->{packer}->addPlanItemToServer($planItemPtr, \%planItemProps, $customButtonsDir);
            }
        }
    }
}

sub addServerIps {
    my ($self, $certPtr, $ipPtr) = @_;

    foreach my $ptrHash (@{DAL::getServerIps($certPtr)}) {
        if (defined $ipPtr) {
            foreach my $domainIp (@{$ipPtr}) {
                if ($domainIp eq $ptrHash->{'ip_address'}) {
                    $ptrHash->{'type' } = PleskStructure::isExclusiveIp( $ptrHash->{'ip_address'} ) ? 'exclusive' : 'shared';
                    $self->{packer}->addServerIp($ptrHash);
                }
            }
        } else {
            $ptrHash->{'type' } = PleskStructure::isExclusiveIp( $ptrHash->{'ip_address'} ) ? 'exclusive' : 'shared';
            $self->{packer}->addServerIp($ptrHash);
        }
    }
}

sub _appendTemplatesToServerNode {
    my ( $self ) = @_;
    my %params = %{DAL::getMisc()};
    if ( PleskVersion::atLeast( 8, 1, 0 ) ) {
        if (PleskVersion::atLeast( 10, 0, 0 )) {
            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 defined $params{'res_tmpl_list_id'};
        }
        $self->dumpResellersTemplates(\@{$self->{resellers}});
    }
}

sub makeServerNode {
    my ( $self, $embeddedInfo ) = @_;

    $self->{packer}->setServerSettings();

    if ( $self->{shallow_dump} ) {
        $self->_appendTemplatesToServerNode();
        return;
    }

    #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 ) ) {
            if (!$self->{only_mail_dump} ) {
                $self->{packer}->setSb5ServerContent();

                $self->{packer}->addServerCustomHealthConfig();
            }
        }

        if (!$self->{only_mail_dump}) {

            if (-d AgentConfig::get('HTTPD_VHOSTS_D')."/fs") {
                $self->{packer}->addFileSharingContent(AgentConfig::get('HTTPD_VHOSTS_D')."/fs");
            }

        }

    }

    #Dump system and default ip
    $self->addServerIps();

    $self->makeDefaultIpNode();

    #Dump hostname
    my $fullHostName = DAL::getFullHostName();
    $self->{packer}->setServerHostname($fullHostName) if $fullHostName;

    #Dump connection information to Billing
    my %adminClientParams = %{DAL::getAdminClientParams()};
    $self->{packer}->setPpbConnection(\%adminClientParams);

    # Dump database servers
    my %def = %{DAL::getDefaultServerParams()};

    foreach my $ptrHash (@{DAL::getDatabaseServers()}) {
        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}->addServerDb( $ptrHash, $passwd, $default );
    }

    my %params = %{DAL::getMisc()};
    my %mailSettings = %{DAL::getMailServerSettings()};

    if( !$self->{configuration_dump} ){
        # Dump key info
        if (!$self->{no_license}) {
            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( "Found sw key repository: $swkeyrepo" );
                            last;
                        }
                    }
                    close PHPINI;
                }
                if( -d "$swkeyrepo/keys" ){
                    my $keyDir = "$swkeyrepo/keys";
                    Logging::debug( "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 = AgentConfig::getLicenseCommand();
                            if ($cmd) {
                                $cmd .= " --get-instance-id $keyDir/$key";
                                my $exec = `$cmd`;
                                chomp($exec);
                                $self->{packer}->addServerKey( $key, $key, $keyDir, 'false', $exec);
                            }
                        }
                    }
                }
                else{
                    Logging::warning( "Keys directory '$swkeyrepo/keys' is not found. The keys are not included to backup." ,'assert');
                }
            }
            else{
                foreach my $ptrHash (@{DAL::getKeyHistory()}) {
                    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 );
                    }
                }
            }
        }
    }

    # Dump server settings

    $mailSettings{'rbl_server'} =~ s/;/,/g;

    # Dump mail settings
    my $mailmng = AgentConfig::getMailmngServerUtil();
    my $letter_size = undef;
    if ( -e $mailmng ) {
        $letter_size = `$mailmng --get-max-letter-size`;
        chomp($letter_size);
    }

    my @blackList = @{DAL::getBadmailfrom()};
    my %whiteList = %{DAL::getSmtpPoplocks()};
    my %ipAddresses = %{DAL::getIpAddresses()};
    my @externalWebmails = (PleskVersion::atLeast( 10, 1, 0 )) ? @{DAL::getExternalWebmail()} : undef;
    $self->{packer}->setServerMail($letter_size, \%params, \%mailSettings, \@blackList, \%whiteList, \%ipAddresses, \@externalWebmails);

    # dump dns
    $self->{packer}->setServerDNS( \%params, DAL::getDnsRecsT() );

    $self->{packer}->setServerWebSettings($embeddedInfo);

    # dump certificates
    my $defaultCert;
    foreach my $id (@{DAL::getCertificateIds($params{'cert_rep_id'})}) {
        if ( $params{'default_certificate_id'} == $id ) {
            $defaultCert = 1;
        }
        else {
            $defaultCert = 0;
        }
        $self->makeCertificateNode( $id, 0, 'server', $defaultCert );
    }

    $self->{packer}->addPanelCertificate() if (!$self->{packer}->{migrationMode});

    # dump custom buttons
    $self->getCustomButtonsByOwner( 'server', 0 );

    $self->{packer}->setControlsVisibility( \%params );

    if ( PleskVersion::atLeast(10, 4, 0) ) {
        $self->{packer}->dumpUiMode(\%params);
    }

    # dump templates
    $self->_appendTemplatesToServerNode();

    # 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 );

        # SSO branding
        $self->{packer}->setServerSSOBranding( DAL::getSSOBranding() );
    }



    if ( PleskVersion::atLeast( 10, 3, 0 ) ) {
        my %applicationsList = %{$self->getListOfInstalledApplicationsOnServer()};

        foreach my $application ( keys %applicationsList ) {
            my @applicationInfo = @{$applicationsList{$application}};
            $self->{packer}->addServerAppPackage( $applicationInfo[0], $applicationInfo[1], $applicationInfo[2], $applicationInfo[3], $applicationInfo[4], $applicationInfo[5], $applicationInfo[6], $applicationInfo[7]);
        }
    }
    elsif ( PleskVersion::atLeast( 10, 0, 0 ) ) {
        $self->{packer}->setServerAppVault();
        $self->{packer}->setServerPackagesPool();

        foreach my $ptrRow ( @{DAL::getApsPackages100()} ) {
            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] );
        }
    }
    else {

        $self->{packer}->setServerAppVault();

        my $distrib_path = AgentConfig::get("PRODUCT_ROOT_D") . "/var/apspkgarc";

        $self->{packer}->setServerPackagesPool();

        foreach my $ptrRow ( @{DAL::getSiteAppPackages()} ) {
            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 );
        }

        #Application Item

        $self->{packer}->setServerAppItemsPool();

        my $aishared = "ai.shared";
        if ( PleskVersion::atLeast( 9, 0, 0 ) ) {
            $aishared = "ai.disabled";
        }
        foreach my $ptrHash ( @{DAL::getAPSApplicationItems($aishared)} ) {
            $self->{packer}->addServerAppItem($ptrHash);
        }
        #Application License

        $self->{packer}->setServerAppLicensesPool();

        foreach my $license ( @{DAL::getAPSLicenses()} ) {
            $self->{packer}->addServerAppLicense( $license->[0], $license->[2], $license->[1] );
        }

    }
    unless ( PleskVersion::atLeast( 10, 0, 0 ) ) {
        # SiteBuilder Config
        my $config = DAL::getSBConfig();
        $self->{packer}->setServerSBConfig( $config ) if $config;
    }

    $self->{packer}->setServerBackupSettings( \%params );

    $self->dumpServerPreferences( \%params );

    $self->makeSpamassassinNode(undef, "*", "*", undef, undef);

    # Server wide grey-listing preferences
    if ( PleskVersion::atLeast( 9,0,0 ) ) {
        my $glParams = DAL::getGLParams();
        $glParams->{'personal-conf'} = defined($mailSettings{'spamfilter_use_mailuser_prefs'}) ? $mailSettings{'spamfilter_use_mailuser_prefs'} : "false";
        $self->{packer}->setGLServerSettings($glParams);
    }

    my $virusfilterNode = $self->{packer}->addServerVirusfilter($mailSettings{'virusfilter'});

    if ( PleskVersion::atLeast( 10, 0, 0 ) ) {
        $self->{packer}->setServerColdFusion(\%params);
    }

    if ( PleskVersion::atLeast( 10, 0, 0 ) ) {
        #Dump event handler
        $self->{packer}->setServerEventHandler(DAL::getEvents(), DAL::getActions(), \%params);
    }

    if ( PleskVersion::atLeast( 10, 0, 0 ) ) {
        $self->{packer}->addServerSiteIsolationConfig();
        $self->dumpServerNotifications();
    }

    $self->{packer}->addServerMailmanConfiguration();

    if (PleskVersion::atLeast(11, 1, 0)) {
        if (exists $params{'theme_skin'} and not $self->{configuration_dump}) {
            $self->{packer}->makeSkinNode($params{'theme_skin'}, 'admin', undef);
        }
    } elsif ( PleskVersion::atLeast( 10, 1, 0 ) ) {
        $self->{packer}->makeBrandingThemeNode('admin', undef);
    }

    if ( PleskVersion::atLeast( 10, 3, 0 ) ) {
        my %fsSettings = %{DAL::getFileSharingSettings()};
        $self->{packer}->dumpFileSharingServerSettings(\%fsSettings);
        if (!$self->{configuration_dump} && !$self->{only_mail_dump}) {
            $self->{packer}->dumpFileSharingUnlistedFiles(DAL::getFileSharingUnlistedFiles());
        }
    }

    $self->{packer}->setMiscParameters(\%params);

    $self->processExtensionPackage('server', PleskStructure::getAdminGuid(), undef);
    $self->{packer}->addCustomizationConfig();
    $self->{packer}->addRestrictedDomains();

    if (PleskVersion::atLeast(12, 0, 9) and not $self->{configuration_dump}) {
        $self->{packer}->addFail2ban();
    }

    if (PleskVersion::atLeast(12, 0, 11) and not $self->{configuration_dump}) {
        $self->addModSecurityContent($embeddedInfo);
    }
}

sub dumpServerNotifications {
    my ( $self ) = @_;

    my $expirationWarnDays = DAL::getExpirationWarnDays() || 1;

    $self->{packer}->addServerNotifications( $expirationWarnDays, DAL::getNotifications(), DAL::getNotes() );
}

sub dumpServerPreferences {
    my ( $self, $params ) = @_;

    $self->{packer}->addServerPreferences( $params );

    foreach my $access ( @{DAL::getCpAccess()} ) {
        $self->{packer}->addRestrictionItem( $access->[0], $access->[1], $access->[2] );
    }

    if ( PleskVersion::atLeast( 10, 1, 0 ) ) {
        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);
        }
    }

    if ( PleskVersion::atLeast( 10, 4, 0 ) ) {
        my $technicalDomainName = undef;
        if (defined($params->{'preview_zone_domain_id'})) {
            $technicalDomainName = DAL::getDomainNameById($params->{'preview_zone_domain_id'});
        } elsif (defined($params->{'preview_zone_external_domain_name'})) {
            $technicalDomainName = $params->{'preview_zone_external_domain_name'};
        }
        $self->{packer}->addTechnicalPreviewDomainNode($technicalDomainName) if $technicalDomainName;

        my $autoUpdates = DAL::getSingleSmbSetting('automaticUpdates');

        my $autoUpdatesValue = (defined($autoUpdates) && $autoUpdates eq 'true') ? 'true' : 'false';

        my $autoUpgradeToStable = (defined($params->{'autoupgrade_to_stable'}) && $params->{'autoupgrade_to_stable'} eq 'true') ? 'true' : 'false';

        my $autoUpgradeBranch = (defined($params->{'autoupgrade_branch'})) ? $params->{'autoupgrade_branch'} : 'release';

        $self->{packer}->addUpdateSettings($autoUpdatesValue, $autoUpgradeToStable, $autoUpgradeBranch);
    }

    if (PleskVersion::atLeast(11, 5, 21)) {
        my $ftpSettings = DAL::getFtpServerSettings();
        $self->{packer}->addFtpOverSslSettings($ftpSettings->{'ftpOverSsl'}) if exists $ftpSettings->{'ftpOverSsl'};
    }
}

sub getApsArchiveFileName {
    my ( $self, $distrib_path, $package_info ) = @_;

    my %mapHash = MiscConfigParser::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 $state = DAL::getDomainKeysState($domainId);
    return unless defined $state;

    my $publickKey = DAL::getDomainKeysPublicKey($domainName, $dnsZoneId);

    $self->{packer}->setDomainKeysDomainSupport( $domainId, $domainName, $state,
        '/etc/domainkeys/' . $domainName, $publickKey );
}

sub getOldWebMailStatus {
    my ($self, $dnsZoneId) = @_;
    my $sql;
    my $retCode = undef;
    $sql = "SELECT * FROM dns_recs WHERE dns_zone_id=?";
    if ( $self->{dbh}->execute_rownum($sql, $dnsZoneId) ) {
        while ( my $ptrHash = $self->{dbh}->fetchhash() ) {
            if ($ptrHash->{'displayHost'} =~ /.*webmail.*/) {
                $retCode = 1;
            }
        }
    }
    $self->{dbh}->finish();
    return $retCode;
}

sub dumpApsBundle {
    my ($self, $parentId, $parentType) = @_;

    my $properties = $self->getSubscriptionPropertiesHash($parentId, $parentType);

    my $filterId = $properties->{'aps_bundle_filter_id'};

    if ($filterId eq '') {
        return;
    }

    my $filterType = DAL::getApsBundleFilterType($filterId);
    my $items = DAL::getApsBundleFilterItems($filterId);
    $self->{packer}->makeApsBundleFilterNode($parentId, $parentType, $filterType, $items, undef);
}

sub fillDefaultClientPermissions {
    my $self = @_;

    my %defaultClientPermissions = (
        "allow_ftp_backups"       => "false"
        , "allow_local_backups"     => "false"
        , "allow_account_ftp_backups"   => "true"
        , "allow_account_local_backups" => "true"
        , "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"
    );

    my %permissions;
    unless (PleskVersion::atLeast(10, 0, 0)) {
        %permissions = %defaultClientPermissions;
    }
    return \%permissions;
}

sub fillDefaultDomainPermissions {
    my $self = @_;

    my %defaultDomainPermissions = (
        "allow_ftp_backups"       => "false"
        , "allow_local_backups"     => "false"
        , "allow_account_ftp_backups"   => "true"
        , "allow_account_local_backups" => "true"
        , "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"
    );

    my %defaultUnixSubscriptionPermissions = (
        "manage_dns"                        => "true",
        "manage_phosting"                   => "false",
        "manage_php_settings"               => "false",
        "manage_php_version"                => "true",
        "allow_insecure_sites"              => "true",
        "manage_anonftp"                    => "false",
        "manage_crontab"                    => "false",
        "manage_spamfilter"                 => "true",
        "manage_virusfilter"                => "true",
        "allow_local_backups"               => "true",
        "allow_ftp_backups"                 => "true",
        "allow_account_local_backups"       => "false",
        "allow_account_ftp_backups"         => "false",
        "manage_webstat"                    => "true",
        "manage_log"                        => "true",
        "access_appcatalog"                 => "true",

        "create_domains"                    => "true",
        "manage_subdomains"                 => "true",
        "manage_domain_aliases"             => "false",
        "manage_subftp"                     => "true",
        "manage_webapps"                    => "false",
        "manage_mail_settings"              => "true",
        "manage_maillists"                  => "true",
        "manage_performance"                => "false",
        "manage_quota"                      => "false",
        "select_db_server"                  => "false",
        "remote_db_connection"              => "false",
        "manage_website_maintenance"        => "true",
        "manage_wordpress_toolkit"          => "true",
        "manage_security_wordpress_toolkit" => "true",
        "manage_protected_dirs"             => "true",
        "access_service_users"              => "true",
        "manage_hosting_type"               => "true",
        "rename_domains"                    => "true",
        "manage_sh_access"                  => "false",
        "manage_not_chroot_shell"           => "false"
    );

    my %permissions;
    if (PleskVersion::atLeast(11, 5, 0)) {
        %permissions = %defaultUnixSubscriptionPermissions;
    } else {
        %permissions = %defaultDomainPermissions;
    }
    return \%permissions;
}

sub getServiceIps {
    my ($self, $domainId, $service , $ip) = @_;
    my @ips;
    my %serviceIpAdresses;
    if ( PleskVersion::atLeast( 10, 2, 0 ) ) {
        @ips = @{DAL::getServiceIps( $domainId, $service)};
    } else {
        if ($ip) {
            @ips = @{$ip};
        }
    }
    foreach my $serviceIpAddress (@ips) {
        $serviceIpAdresses{$serviceIpAddress} = PleskStructure::getIpType($serviceIpAddress);
    }
    return \%serviceIpAdresses;
}

sub getConcreteApplicationInfo {
    my ( $self, $ptrApplications, $registryUid ) = @_;
    my @applications = @{$ptrApplications};
    foreach my $application ( @applications ) {
        if ( $application->getRegistryUid() eq $registryUid) {
            return $application;
        }
    }
}

sub makeMailListsNode {
    my ($self, $domainId, $domainAsciiName, $ip) = @_;

    if ( !$self->{only_hosting_dump} ) {
        Logging::debug("Dumping maillists");
        my $maillistsStatus = $self->getDomainMaillistStatus($domainId);
        if (defined $maillistsStatus) {
            my $serviceIps = $self->getServiceIps( $domainId, 'maillists', $ip);
            $self->{packer}->setDomainMailLists( $domainId, $maillistsStatus, $serviceIps);

            my $archiveDir = AgentConfig::get("MAILMAN_VAR_D") . "/archives/private";

            my @maillists = @{DAL::getDomainMaillists($domainId)};
            if ( @maillists ) {
                my @archiveDirs;
                for my $ptrRow ( @maillists ) {
                    my $maillistDir = lc($ptrRow->[1]);
                    push @archiveDirs, $maillistDir  if -d "$archiveDir/" . $maillistDir;
                    my $datafile = $maillistDir . ".mbox";
                    push @archiveDirs, $datafile  if -d "$archiveDir/$datafile";
                    $self->makeMailmanMailListNode( $domainId, @{$ptrRow} );
                }
                if (@archiveDirs) {
                    my %mailmanUserInfo = AgentConfig::getMailmanUserInfo();
                    $self->{packer}->setDomainMailListContent( $domainId, $domainAsciiName, $archiveDir,
                        { 'include' => \@archiveDirs, 'follow_symlinks' => 1, 'sysuser' => $mailmanUserInfo{'user'}} );
                }
            }
        }
    } else {
        Logging::debug("Skip maillists due to settings");
    }
}

sub processExtensionPackage {
    my ( $self, $objectType, $objectName, $objectId ) = @_;

    if ($self->{capability_dump}) {
        # no need to process extension package for capability dump
        return;
    }

    my $ok = eval {
        if ( PleskVersion::atLeast( 11, 1, 13 ) ) {
            my ( $filter, $extensionPackage, $extensionData, $logs );
            $filter = ExtensionPackage::createFilterForExtensionPackage($objectType, $objectName);
            $extensionPackage = ExtensionPackage::getExtensionPackage($filter);
            if (defined $extensionPackage) {
                $extensionData = ExtensionPackage::getData($extensionPackage);
                $self->{packer}->makeExtensionPackageNode($objectType, $objectId, $extensionData);

                $logs = ExtensionPackage::getLogs($extensionPackage);
                foreach my $uuid (keys %{$logs}) {
                    my $messages = $logs->{$uuid};

                    foreach my $message (@{$messages}) {
                        my $loggingMethod = $message->getSeverity();
                        my $loggingMessage = $message->getText();

                        if (defined $message->getResolution()) {
                            $loggingMessage .= $message->getResolution();
                        }

                        if ($loggingMethod eq 'error') {
                            Logging::error($loggingMessage , 'msgtext', $uuid);
                        }elsif($loggingMethod eq 'warning') {
                            Logging::warning($loggingMessage , 'msgtext', $uuid);
                        }elsif($loggingMethod eq 'info') {
                            Logging::debug($loggingMessage , 'msgtext', $uuid);
                        }

                    }
                }
            }
        }
        1;
    };

    unless ($ok) {
        Logging::warning("Processing ExtensionPackage failed: " . $@);
    }
}

sub generateObjectUuidForLogging {
    my $self = shift;
    return join('#', @_);
}

sub getSiteStatus {
    my ( $self, $status, $domainPtr ) = @_;
    my %domain = %{$domainPtr};

    if ( $domain{'htype'} eq 'vrt_hst' && $Status::ENABLED == $status && !PleskVersion::atLeast(11, 5, 23) ) {
        my %hostingParams = %{DAL::getHostingParams($domain{'id'})};
        if ( defined( $hostingParams{'maintenance_mode'} ) && $hostingParams{'maintenance_mode'} eq 'true' ) {
            $status = $Status::CLIENT;
        }
    }

    return $status;
}

sub getDomainParams {
    my ( $self, $domainPtr ) = @_;
    my %domain = %{$domainPtr};
    my %domainParams = %{DAL::getDomainParams($domain{'id'})};

    if ( $domain{'htype'} eq 'vrt_hst' && $Status::ENABLED == $domain{'status'} && !PleskVersion::atLeast(11, 5, 23) ) {
        my %hostingParams = %{DAL::getHostingParams($domain{'id'})};
        if ( defined( $hostingParams{'maintenance_mode'} ) && $hostingParams{'maintenance_mode'} eq 'true' ) {
            $domainParams{'turnOffAction'} = 'suspend';
            $domainParams{'selfTurnOffAction'} = 'suspend';
        }
    }

    if (!PleskVersion::atLeast(11, 5, 23)) {
        $domainParams{'seoHttpRedirect'} = 'none';
    }

    return \%domainParams;
}

sub addUsers {
    my ( $self, $usersList, $callback, $callbackFailed ) = @_;

    for my $userLogin ( @{$usersList} ) {
        eval {
            my $userHash = DAL::getUserHash( $userLogin );
            next if (! keys %$userHash);  # if hash is empty (error occurred)
            my $assignedApplications = DAL::getUserAssignedApplications( $userHash->{'id'} );
            $userHash->{'assignedApplications'} = $assignedApplications;
            $callback->( $userLogin, $userHash );
            1;
        } or do {
            $self->excludeProblemUserFromBackup( $userLogin, $callbackFailed, $@ );
        }
    }
}

sub addModSecurityContent {
    my ($self, $embeddedInfo) = @_;
    return unless ('' ne $embeddedInfo && defined($embeddedInfo->{'web-settings'}));

    my $ruleSet = '';
    foreach my $webSettings (@{$embeddedInfo->{'web-settings'}}) {
        if ('' eq $webSettings) {
            last;
        }
        foreach my $setting (@{$webSettings->{'setting'}}) {
            my ($name) = @{$setting->{'name'}};
            my ($value) = @{$setting->{'value'}};
            if ('ruleSet' eq $name) {
                $ruleSet = $value;
                last;
            }
        }
    }
    return if ('' eq $ruleSet);

    my $rulesBaseDir = undef;
    my $cmd = AgentConfig::getModSecurityCtlUtil() . ' --rules-base-dir';
    Logging::debug("Exec: $cmd");
    my $ret = `$cmd`;
    if (0 == ($? >> 8)) {
        $rulesBaseDir = HelpFuncs::trim($ret);
    } else {
        Logging::warning('Cannot execute command: ' . $cmd . ', STDOUT: ' . $ret, 'UtilityError');
        return;
    }

    my @ruleSetDirs = ();
    my $cmd = AgentConfig::getModSecurityCtlUtil() . ' --list-rulesets';
    Logging::debug("Exec: $cmd");
    my $ret = `$cmd`;
    if (0 == ($? >> 8)) {
        foreach my $ruleSetDir (split(/\n/, $ret)) {
            if ($ruleSetDir =~ /\.saved-\d+/) {
                next;
            }
            push(@ruleSetDirs,  "$ruleSetDir/");
        }
    } else {
        Logging::warning('Cannot execute command: ' . $cmd . ', STDOUT: ' . $ret, 'UtilityError');
        return;
    }

    if (@ruleSetDirs) {
        $self->{packer}->addModSecurityContent($rulesBaseDir, @ruleSetDirs);
    }
}

sub getDomainCertificatePrivateKey {
    my ($self, $certificateId, $domainId) = @_;

    if (0 != $certificateId) {
        return DAL::getCertificatePrivateKey($certificateId);
    }
    my @defaultCerts = @{$self->getDomainDefaultCert($domainId)};
    if (@defaultCerts) {
        return DAL::getCertificatePrivateKey($defaultCerts[0]);
    }
    return undef;
}

sub getCertificateRef {
    my ($self, $privateKey) = @_;

    my $md5 = PerlMD5->new();
    $md5->add(HelpFuncs::urlDecode($privateKey));
    return $md5->hexdigest();
}

sub _initDomainContentStat {
    my ($self, $domainId) = @_;

    return if ($self->{packer}->{migrationMode});

    my $sql = "SELECT httpdocs + httpsdocs, dbases, mailboxes FROM disk_usage WHERE dom_id=?";
    if ($self->{dbh}->execute_rownum($sql, $domainId)) {
        if (my $ptrRow = $self->{dbh}->fetchrow()) {
            $self->{packer}->{stat}{vhostSizeOnFS} += @{$ptrRow}[0];
            $self->{packer}->{stat}{dbSizeOnFS} += @{$ptrRow}[1];
            $self->{packer}->{stat}{mailSizeOnFS} += @{$ptrRow}[2];
        }
    }
    $self->{dbh}->finish();
}

sub _reportDomainStatistics {
    my ($self) = @_;

    return if ($self->{packer}->{migrationMode});

    my $xml =
    "<stat>".
    "<vhost>".
    "<count>".$self->{packer}->{stat}{vhostDumpsCount}."</count>".
    "<size>".$self->{packer}->{stat}{vhostSizeOnFS}."</size>".
    "<dumpsize>".$self->{packer}->{stat}{vhostSizeDumped}."</dumpsize>".
    "</vhost>".
    "<db>".
    "<count>".$self->{packer}->{stat}{dbDumpsCount}."</count>".
    "<size>".$self->{packer}->{stat}{dbSizeOnFS}."</size>".
    "<dumpsize>".$self->{packer}->{stat}{dbSizeDumped}."</dumpsize>".
    "</db>".
    "<mail>".
    "<count>".$self->{packer}->{stat}{mailDumpsCount}."</count>".
    "<size>".$self->{packer}->{stat}{mailSizeOnFS}."</size>".
    "<dumpsize>".$self->{packer}->{stat}{mailSizeDumped}."</dumpsize>".
    "</mail>".
    "</stat>";

    my $cmd = AgentConfig::getBackupRestoreHelperUtil()
    . ' --report-backup-info "'.$xml.'"';
    Logging::debug("Exec: $cmd");
    `$cmd`;
}

1;
