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;

require "agent.include.pl";
# makePasswordNode, generateDumpId, XmlNode, makeXmlNode,
# makeMIMEBase64, urlDecode, addPermissionNode, addMailuserPermissionNode,
# getSqlList

use vars qw|@ISA|;

sub new {
  my $self = {};
  bless($self, shift);
  $self->_init(@_);
  return $self;
}

sub checkHost {
  my @problems;
  if (!AgentConfig::init()) {
    push @problems, "Unable to find /etc/psa/psa.conf configuration file";
  } else {
    eval { PleskVersion::init(AgentConfig::get('PRODUCT_ROOT_D')) };
    if ($@) {
      push @problems , $@;
    } else {
      if (!PleskVersion::isSupported()) {
        if (PleskVersion::atLeast(2, 5, 0)) {
          push @problems, "Migration from version >= " . PleskMaxVersion::get() . " is not supported";
        } else {
          push  @problems, "Migration from version < 2.5.0 is not supported";
        }
      }
    }

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

  return @problems;
}

# --- Public instance methods ---

sub _init {
  my ($self, $storagePolicy, $dumpStatusPolicy, $agentsSharedDir, $skipLogs) = @_;

  $self->{storage} = $storagePolicy;
  $self->{dump_status} = $dumpStatusPolicy;
  $self->{skip_logs} = $skipLogs;
  $self->{configuration_dump} = undef;
  $self->{shallow_dump} = undef;
  $self->{only_mail_dump} = undef;
  $self->{only_hosting_dump} = undef;
  $self->{description} = undef;

  #-----
  # Array of custom buttons IDs which are dumped within site applications
  #-----
  $self->{skip_custom_buttons} = [];

  AgentConfig::init() or die "Error: Plesk config file 'psa.conf' is not found\n";
  AgentConfig::setSharedDir($agentsSharedDir);

  AgentConfig::tarBin(); # Pre-caching of values
  AgentConfig::mysqlBin();

  PleskVersion::init(AgentConfig::get('PRODUCT_ROOT_D'));

  $self->{base64} = makeMIMEBase64();

  $self->{dbh} = Db::Connection::getConnection('type' => 'mysql',
                                               'user' => 'admin',
                                               'password' => AgentConfig::get('password'),
                                               'name' => 'psa',
                                               'host' => 'localhost',
                                               'utf8names' => PleskVersion::atLeast(8, 0, 0));

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

  if (PleskVersion::atLeast(7, 1, 0)) {
    Encoding::setDefaultEncoding("UTF-8");
  }
}

use vars qw|$FULL $SHALLOW $CONFIGURATION $ONLY_MAIL $ONLY_HOSTING|;
$FULL = 0;
$SHALLOW = 1;
$CONFIGURATION = 2;
$ONLY_MAIL = 4;
$ONLY_HOSTING = 8;

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

  if ($type & $SHALLOW) { $self->{shallow_dump} = 1; }
  if ($type & $CONFIGURATION) { $self->{configuration_dump} = 1; }
  if ($type & $ONLY_MAIL) { $self->{only_mail_dump} = 1; $self->{only_hosting_dump} = undef; }
  if ($type & $ONLY_HOSTING) { $self->{only_hosting_dump} = 1; $self->{only_mail_dump} = undef;}
}

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

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

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

  if (@missingDomains) {
    die "The following domains were not found on the host: " . (join ",", @missingDomains);
  }

  $self->{domains} = \@inputDomains;
}

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

  my @pleskClients = sort { $a cmp $b } PleskStructure::getClients();
  my @missingClients = arrayDifference(\@inputClients, \@pleskClients);

  if (@missingClients) {
    die "The following clients were not found on the host: " . (join ",", @missingClients);
  }

  $self->{clients} = \@inputClients;
}

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

  $self->{domains} = [arrayDifference($self->{domains}, \@inputDomains)];
}

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

  $self->{clients} = [arrayDifference($self->{clients}, \@inputClients)];
}

sub selectAll {
  my ($self) = @_;
  my @pleskClients = sort { $a cmp $b } PleskStructure::getClients();
  $self->{clients} = \@pleskClients;
}

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

  my @pleskDomains = sort { $a cmp $b } PleskStructure::getDomains();
  $self->{domains} = \@pleskDomains;
}

sub selectServerSettings {
  my ($self) = @_;
  $self->{server_settings} = 1;
}

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

  my $root = XmlNode->new('migration-dump', 'attributes' => {'agent-name' => 'PleskX', 'dump-version' => PleskMaxVersion::get()});
  if ($self->{description}) {
    $root->{'ADDCHILD'}->(XmlNode->new('description',
                                  'content' => $self->{description}));
  }

  if (exists $self->{clients}) {
    my %clientsDesc;

    # if domains & accounts specified simultaneously, then it's a backup and only selected
    # domains should be included in backup
    if (!exists $self->{domains}) {
      my @domains = sort { $a cmp $b } PleskStructure::getDomains();
      $self->{domains} = \@domains;
    }

    my $client;
    foreach $client (@{$self->{clients}}) {
      my @clientDomains = PleskStructure::getDomainsForClient($client);
      @clientDomains = sort { $a cmp $b } @clientDomains;

      my @domainsToDump = arrayIntersection(\@clientDomains, $self->{domains});
      $clientsDesc{$client} = \@domainsToDump;
    }

    $self->createClientsDump($root, \%clientsDesc);
    $self->makeServerNode($root) if exists $self->{server_settings};

  } elsif (exists $self->{domains}) {
    $self->createDomainsDump($root, $self->{domains});
    $self->makeServerNode($root) if exists $self->{server_settings};
  } elsif (exists $self->{server_settings}){
    $self->makeServerNode($root);
  } else {
    die "No objects to dump found";
  }

  return $self->{storage}->finish($root);
}

# --- Utility methods ---

#
# Converts the old country codes to the recent ones
#
sub normalizeCountry {
  my ($country) = @_;
  return 'RU' if $country eq 'SU';
  return 'GB' if $country eq 'UK';
  return $country;
}

#
# Converts 'crypt' -> 'encrypted'.
# Several versions of Plesk stored 'crypt' in psa.accounts.type table instead of 'encrypted'
# bug 47030
#
sub normalizePasswordType {
  my ($type) = @_;
  return "encrypted" if $type eq "crypt";
  return $type;
}

#
# Creates 'limit' node with given limit name and value
#
sub insertLimitNode {
  my ($node, $name, $value) = @_;
  $node->{'ADDCHILD'}->(XmlNode->new("limit",
                                     "attributes" => {"name" => $name},
                                     "content" => $value));
}

#
# Creates named item in domain template node
#
sub addTemplateItem {
  my ($node, $name, $value) = @_;
  $node->{'ADDCHILD'}->(XmlNode->new("template-item",
                                     "attributes" => {"name" => $name},
                                     "content" => $value));
}

#
# Creates node representing pre-8.0 DNS master record
#
sub makeOldMasterRec
{
  my ($ipAddress) = @_;

  return XmlNode->new("dnsrec",
                      "attributes" => {"src" => $ipAddress,
                                       "type" => "master"});
}

sub addUrlDecodedTextNode {
  my ($parent, $name, $value) = @_;

  $parent->{'ADDCHILD'}->(XmlNode->new($name, "content" => urlDecode($value))) if $value;
}

sub makeAnonftpPermissionNode {
  my ($parent, $name) = @_;
  $parent->{'ADDCHILD'}->(XmlNode->new('anonftp-permission',
                                       "attributes" => {"name" => $name}));
}

sub makeAnonftpLimitNode {
  my ($parent, $name, $value) = @_;
  if($value != 0) {
    $parent->{'ADDCHILD'}->(XmlNode->new("anonftp-limit",
                                         "attributes" => {"name" => $name},
                                         "content" => $value));
  }
}

sub parseCustomButtonOptions {
  my ($node, $options) = @_;

  #CUSTOM_BUTTON_PUBLIC
  $node->{'ATTRIBUTE'}->('visible-to-sublogins',
                         $options & 128 ? 'true' : 'false');
  #CUSTOM_BUTTON_INTERNAL
  $node->{'ATTRIBUTE'}->('open-in-same-frame',
                         $options & 256 ? 'true' : 'false');

  my %options =
    (
     "domain-id" => 1, #CUSTOM_BUTTON_DOM_ID
     "domain-name" => 32, #CUSTOM_BUTTON_DOM_NAME
     "ftp-login" => 512, #CUSTOM_BUTTON_FTP_USER
     "ftp-password" => 1024, #CUSTOM_BUTTON_FTP_PASS
     "client-id" => 2, #CUSTOM_BUTTON_CL_ID
     "client-company-name" => 4, #CUSTOM_BUTTON_CNAME
     "client-contact-name" => 8, #CUSTOM_BUTTON_PNAME
     "client-email" => 16, #CUSTOM_BUTTON_EMAIL
    );

  while (my ($optname, $optmask) = each %options) {
    if ($options & $optmask) {
      $node->{'ADDCHILD'}->(XmlNode->new("url-option",
                                    "attributes" => {"name" => $optname}));
    }
  }
}

sub getPostgresqlVersion {
  my $psql = AgentConfig::psqlBin();
  my @out = `$psql --version | awk '{print \$3}'`;
  chomp $out[0];
  if ($out[0]=~/(\d+\.\d+\.\d+).*/){
    return $1;
  }
}

# Returns the elements that present in first array, but not in the second.
# Arrays must be sorted.
# Linear complexity.

sub arrayDifference {
  my ($a1, $a2) = @_;
  my @ret;

  my $i1 = 0; my $i2 = 0;
  while ($i1 < scalar(@$a1) && $i2 < scalar(@$a2)) {
    if ($a1->[$i1] eq $a2->[$i2]) { $i1++; $i2++; next; }
    if ($a1->[$i1] lt $a2->[$i2]) { push @ret, $a1->[$i1]; $i1++; next; }
    if ($a1->[$i1] gt $a2->[$i2]) { $i2++; next; }
  }

  while ($i1 < scalar(@$a1)) {
    push @ret, $a1->[$i1];
    $i1++;
  }
  return @ret;
}

# Returns the elements that present in both arrays
# Arrays must be storted
# Linear complexity

sub arrayIntersection {
  my ($a1, $a2) = @_;
  my @ret;

  my $i1 = 0; my $i2 = 0;
  while ($i1 < scalar(@$a1) && $i2 < scalar(@$a2)) {
    if ($a1->[$i1] eq $a2->[$i2]) { push @ret, $a1->[$i1]; $i1++; $i2++; next; }
    if ($a1->[$i1] lt $a2->[$i2]) { $i1++; next; }
    if ($a1->[$i1] gt $a2->[$i2]) { $i2++; next; }
  }

  return @ret;
}

# --- Private instance methods ---

#
# maps to translate xmlName => fieldName
#

my %xmlPermissions = ('create_domains'=>1,
              'create_domains'=>1,
              'manage_phosting'=>1,
              'manage_php_safe_mode'=>1,
              'manage_quota'=>1,
              'manage_subdomains'=>1,
              'change_limits'=>1,
              'manage_dns'=>1,
              'manage_log'=>1,
              'manage_crontab'=>1,
              'manage_anonftp'=>1,
              'manage_webapps'=>1,
              'manage_sh_access'=>1,
              'manage_maillists'=>1,
              'make_dumps'=>1,
              'allow_local_backups'=>1,
              'allow_ftp_backups'=>1,
              'make_phosting'=>1,
              'manage_quota'=>1,
              'manage_not_chroot_shell'=>1,
              'manage_ftp_password'=>1,
              'cp_access'=>1,
              'remote_access_interface'=>1,
              'manage_domain_aliases'=>1,
              'manage_dashboard'=>1,
              'dashboard'=>1,
              'stdgui'=>1,
              'manage_virusfilter'=>1,
              'manage_spamfilter'=>1,
              'manage_webstat'=>1,
              'manage_performance'=>1,
              'select_db_server'=>1
             );

my %mailUserPermissions = (
              'cp_access' => 'cp-access',
              'manage_virusfilter' => 'manage-virusfilter',
              'manage_spamfilter' => 'manage-spamfilter'
			);

my %commonLimits = ('max_wu' => 1,
                    'max_db' => 1,
                    'max_pbox' => 1,
                    'mbox_quota' => 1,
                    'max_redir' => 1,
                    'max_mg' => 1,
                    'max_resp' => 1,
                    'disk_space' => 1,
                    'max_traffic' => 1
                   );

my %domainLimits25 = %commonLimits;
$domainLimits25{'max_box'} = 1;

my %domainLimits = %commonLimits;
$domainLimits{'max_box'} = 1;
$domainLimits{'expiration'} = 1;
$domainLimits{'max_subdom'} = 1;
$domainLimits{'max_maillists'} = 1;
$domainLimits{'max_webapps'} = 1;
$domainLimits{'max_dom_aliases'} = 1;

my %clientLimits25 = %domainLimits25;
$clientLimits25{'max_dom'} = 1;

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

my %typeOfField = ('expiration'=>'timestamp');

my %clientsAttribute = ('name'=>'login',
                        'contact'=>'pname',
                        'cr-date'=>'cr_date',
                       );

my %clientsInfo = ('company'=>'cname',
                   'phone'=>'phone',
                   'fax'=>'fax',
                   'address'=>'address',
                   'city'=>'city',
                   'state'=>'state',
                   'zip'=>'pcode',
                   'country'=>'country',
                   'email'=>'email',
                  );

my %domainUserAttribute = ('contact'=>'personalName',);

my %domainUserInfo = ('company'=>'companyName',
                      'phone'=>'phone',
                      'fax'=>'fax',
                      'address'=>'address',
                      'city'=>'city',
                      'state'=>'state',
                      'zip'=>'zip',
                      'email'=>'email',
                      'country'=>'country',);

my %hostingAttribute = ('https'=>'ssl',
                        'fp'=>'fp',
                        'fpssl'=>'fp_ssl',
                        'fpauth'=>'fp_enable',
                        'webstat'=>'webstat',
#                        'at-domains'=>'at_domains',
                       );

my %hostingScripting = ('ssi'=>'ssi',
                        'php'=>'php',
                        'php_safe_mode'=>'php_safe_mode',
                        'cgi'=>'cgi',
                        'perl'=>'perl',
                        'asp'=>'asp',
                        'python'=>'python',
                        'coldfusion'=>'coldfusion',
                        'asp_dot_net'=>'asp_dot_net',
                        'fastcgi'=>'fastcgi',
                        'miva'=>'miva'
                       );

my %webUserScripting = ('ssi'=>'ssi',
                        'php'=>'php',
                        'cgi'=>'cgi',
                        'perl'=>'perl',
                        'asp'=>'asp',
                        'python'=>'python',
                        'asp_dot_net'=>'asp_dot_net',
                        'fastcgi'=>'fastcgi',
                        'miva'=>'miva'
                       );

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

my %emptyableBoolElementInTemplate = (
                                      'asp' => 1,
                                      'cgi' => 1,
                                      'coldfusion' => 1,
                                      'pdir_plesk_stat' => 1,
                                      'perl' => 1,
                                      'php' => 1,
                                      'python' => 1,
                                      'ssi' => 1
                                     );

my %custom_button_owner_types = (
                                 "admin" => 1,        #IS_ADMIN
                                 "client" => 4,       #IS_CLIENT
                                 "domain-admin" => 8, #IS_DOMAIN_OWNER
                                 "mailuser" => 16,    #IS_MAIL_USER
                                );

my %locale_map = ('bg' => 'bg', 'hk' => 'zh-HK',
                'ru' => 'ru-RU', 'en' => 'en-US',
                'nl' => 'nl-NL', 'br' => 'pt-BR',
                'it' => 'it-IT', 'tr' => 'tr-TR',
                'es' => 'es-ES', 'pl' => 'pl-PL',
                'ca' => 'ca-ES', 'jp' => 'ja-JP',
                'tw' => 'zh-TW', 'fi' => 'fi-FI',
                'cn' => 'zh-CN', 'ko' => 'ko-KR',
                'fr' => 'fr-FR', 'pt' => 'pt-PT',
                'de' => 'de-DE', 'lt' => 'lt-LT'
                );

# list of attributes for template-item node in dump.xml
# needs to synchronize with template-item's ATTLIST of plesk.dtd
my %template_items = ('webmail' => 1, 'disk_space' => 1, 'wu_script' => 1,
                    'stat_ttl' => 1, 'maillists' => 1,
                    'nonexist_mail' => 1, 'catch_addr' => 1,
                    'bounce_mess' => 1, 'max_traffic' => 1,
                    'max_box' => 1, 'mbox_quota' => 1,
                    'max_redir' => 1, 'max_mg' => 1,
                    'max_resp' => 1, 'max_wu' => 1,
                    'max_db' => 1, 'max_maillists' => 1,
                    'max_webapps' => 1, 'max_subdom' => 1,
                    'expiration' => 1, 'vh_type' => 1,
                    'quota' => 1, 'ssl' => 1,
                    'same_ssl' => 1, 'fp' => 1,
                    'fp_ssl' => 1, 'fp_auth' => 1,
                    'asp' => 1, 'ssi' => 1,
                    'php' => 1, 'cgi' => 1,
                    'perl' => 1, 'python' => 1,
                    'coldfusion' => 1, 'webstat' => 1,
                    'errdocs' => 1, 'shell' => 1,
                    'pdir_plesk_stat' => 1, 'dns_type' => 1,
                    'max_dom_aliases' => 1,
					# client template
                    'allow_ftp_backups' => 1, 'allow_local_backups' => 1,
                    'change_limits' => 1, 'cp_access' => 1,
                    'create_domains' => 1, 'dashboard' => 1,
                    'excl_ip_num' => 1, 'manage_anonftp' => 1,
                    'manage_crontab' => 1, 'manage_dashboard' => 1,
                    'manage_dns' => 1, 'manage_domain_aliases' => 1,
                    'manage_drweb' => 1, 'manage_log' => 1,
                    'manage_maillists' => 1, 'manage_not_chroot_shell' => 1,
                    'manage_phosting' => 1, 'manage_quota' => 1,
                    'manage_sh_access' => 1, 'manage_spamfilter' => 1,
                    'manage_subdomains' => 1, 'manage_webapps' => 1,
                    'max_dom' => 1, 'remote_access_interface' => 1,
                    'stdgui' => 1, 'manage_php_safe_mode' => 1,
                    'manage_webstat' => 1, 'select_db_server' => 1
                   );

#
# end maps
#

sub addTar {
  my $self = shift;
  return if $self->{configuration_dump};
  return $self->{storage}->addTar(@_);
}

sub addDb {
  my $self = shift;
  return if $self->{configuration_dump};
  return $self->{storage}->addDb(@_);
}

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

  my @clients = PleskStructure::getClients();

  my %clientsDesc;

  my $client;
  foreach $client (@clients) {
    my @domains = PleskStructure::getDomainsForClient($client);
    $clientsDesc{$client} = \@domains;
  }

  $self->createClientsDump($root, \%clientsDesc);
}

# $clients - ref to the hash clientNames to ref of array of domainNames
sub createClientsDump {
  my ($self, $root, $clients) = @_;

  my $client;
  my $domainsCount = 0;
  $domainsCount += scalar(@{$clients->{$_}}) foreach keys %{$clients};

  $self->{dump_status}->start(scalar(keys %{$clients}), $domainsCount);

  foreach $client (keys %{$clients}) {
     my $node = $self->makeClientNode($client,$clients->{$client});
    $root->{'ADDCHILD'}->($node);
    $node->ReleaseCode();
    #$root->{'ADDCHILD'}->($self->makeClientNode($client, $clients->{$client}));
  }

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

# $domains - ref to the array of domain names
sub createDomainsDump {
  my ($self, $root, $domains) = @_;

  $self->{dump_status}->start(0, scalar(@{$domains}));

  my $domain;
  foreach $domain (@{$domains}) {
     my $node = $self->makeDomainNode($domain);
     $root->{'ADDCHILD'}->( $node );
     $node->ReleaseCode();
    #$root->{'ADDCHILD'}->($self->makeDomainNode($domain));
  }

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

# Condition is not necessary if getting admin parameters
sub getMaxButtonLength {
  my ($self, $parent, $table, $condition) = @_;

  my $sql = "SELECT val FROM $table WHERE param='max_button_length'";
  $sql .= " AND $condition" if $condition;

  if ($self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow()) {
    if ($ptrRow->[0]) {
        $parent->{'ATTRIBUTE'}->('max-button-length', $ptrRow->[0]);
    }
  }
  $self->{dbh}->finish();
}

#Condition is not necessary if getting admin parameters
sub getSkin {
  my ($self, $parent, $table, $condition) = @_;

  return unless PleskVersion::atLeast(5, 0, 0);

  my ($skinId);

  if ($table eq 'dom_param' and !PleskVersion::atLeast(7,5,0)) {
    $skinId = "user_skin_id";
  } else {
    $skinId = "skin_id";
  }

  my $sql = "SELECT s.name FROM Skins as s, $table as t " .
    "WHERE t.param='$skinId' AND t.val = s.id";
  $sql .= " AND $condition" if $condition;

  if ($self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow()) {
    $parent->{'ATTRIBUTE'}->('skin', $ptrRow->[0]);
  }
  $self->{dbh}->finish();
}

sub getLockScreen {
  my ($self, $parent, $table, $condition) = @_;

  return unless PleskVersion::atLeast(7, 5, 2);

  my $sql = "SELECT val FROM $table WHERE param='disable_lock_screen'";
  $sql .= " AND $condition" if $condition;

  my $ptrRow;
  if ($self->{dbh}->execute_rownum($sql)
      and $ptrRow = $self->{dbh}->fetchrow()
      and $ptrRow->[0] eq 'true') {
    $parent->{'ATTRIBUTE'}->('lock-screen', 'false');
  }
  $self->{dbh}->finish();
}

#
# sub makeClientNode
#
#  arguments:
#         $clientId - ID of client
#         $full - dump all domains
#         $ptrDomainHash
#

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

  my ($item,$sql,$ptrHash,$value,%client,$ptrRow,$id,%clientParams);

  printToLog("Client '$clientName' is started");
  $self->{dump_status}->startClient($clientName);

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

  $self->{dbh}->execute($sql);
  %client = %{$self->{dbh}->fetchhash()};
  $self->{dbh}->finish();

  my $clientId = $client{'id'};

  $client{'country'} = normalizeCountry($client{'country'});

  my $root = XmlNode->new('client', 'attributes' => {'id' => $clientId});

  if (defined $client{'uid'}) {
    $root->{'ATTRIBUTE'}->('uid', $client{'uid'});
    $root->{'ATTRIBUTE'}->('ownership', $client{'ownership'} eq 'true' ? 'true' : 'false');
  }

  $root->setAttribute('guid', $client{'guid'}) if (defined $client{'guid'});

  my($attribute,$field,$name);

  if(exists $client{'locale'} && $client{'locale'}) {
    while (($name, $field) = each(%locale_map)) {
      if($client{'locale'} eq $name) {
        $client{'locale'} = $field;
      }
    }
  }

  while (($attribute,$field)=each(%clientsAttribute)){
    if(exists($client{$field})){
      unless (defined($client{$field})) {
        $client{$field} = '';
      }
      $root->{'ATTRIBUTE'}->($attribute,$client{$field});
    }
  }

  if (PleskVersion::atLeast(7, 1, 5)) {
    $sql = "SELECT password, type FROM accounts WHERE id = '"
      . $client{'account_id'} . "'";

    $self->{dbh}->execute($sql);
    $ptrHash = $self->{dbh}->fetchhash();
    $item = makePasswordNode($ptrHash->{'password'},
                             normalizePasswordType($ptrHash->{'type'}));
    $self->{dbh}->finish();
  } else {
    $item = makePasswordNode($client{'passwd'},'plain');
  }

  $root->{'ADDCHILD'}->($item);

  my $status = 0;
  if (PleskVersion::atLeast(5, 1, 0)) {
    $status = $client{'status'};
  } else {
    $status |= $Status::ADMIN if $client{'status'} eq 'false';
  }
  $root->{'ADDCHILD'}->(Status::make($status));

  unless ($self->{shallow_dump}) {

    while (($name, $field) = each(%clientsInfo)) {
      if (exists($client{$field}) && $client{$field}) {
         $root->{'ADDCHILD'}->(XmlNode->new('pinfo', 'content' => $client{$field}, 'attributes' => {'name' => $name}));
      }
    }

    # Locale changed format since 7.5.0, and we
    # don't bother converting older formats
    if (PleskVersion::atLeast(7, 5, 0)) {
      if (exists($client{'locale'}) && $client{'locale'}) {
        $root->{'ADDCHILD'}->(XmlNode->new('pinfo', 'content' => $client{'locale'}, 'attributes' => {'name' => 'locale'}));
      }
    }

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

    # button length
    $self->getMaxButtonLength($root, "cl_param", "cl_id=$clientId");
    $self->getSkin($root, "cl_param", "cl_id=$clientId");
    $self->getLockScreen($root, "cl_param", "cl_id=$clientId");

#
# limits
#
    if (PleskVersion::atLeast(6, 0, 0)) {
      $self->addClientLimits($root, $client{'limits_id'});
    } elsif (PleskVersion::atLeast(5, 0, 0)) {
      $self->addClientLimits($root, $clientParams{'lim_id'});
    } else {
      $self->addClientLimits25($root, $clientId);
    }

#
# end limits
#


    if (PleskVersion::atLeast(6, 0, 0)) {
      $self->addPermissions($root, $client{'perm_id'});
    } elsif (PleskVersion::atLeast(5, 0, 0)) {
      $self->addPermissions($root, $clientParams{'perm_id'});
    } else {
      $self->addPermissions25($root, $clientId);
    }

        #fix bug 88139
        if ((PleskVersion::atLeast(7,5,1) and !$client{'perm_id'}) or !PleskVersion::atLeast(7,5,1)) {
          addPermissionNode($root,'cp_access', 'true');
        }

        #fix bug 88544
        if (!PleskVersion::atLeast(7,5,4)) {
          addPermissionNode($root,'manage_not_chroot_shell', 'true');
        }

        if (!PleskVersion::atLeast(8,0,0)) {
          addPermissionNode($root,'stdgui', 'true');
        }

    $self->addClientMulitpleLoginPermission($root, $clientId);
#
# Domain skeleton
#
    if (PleskVersion::atLeast(6, 0, 0)) {
      my $skeletonPath = AgentConfig::get('HTTPD_VHOSTS_D') . "/.skel/$clientId";

      my $dumpFile = $self->addTar("$client{'login'}.skel", 'directory' => $skeletonPath);
      $root->{'ATTRIBUTE'}->('cid-skeleton', $dumpFile) if $dumpFile;
    }
  }


  my @ips = PleskStructure::getClientIps($clientName);

  my $ip_pool = XmlNode->new('ip_pool');
  $root->{'ADDCHILD'}->($ip_pool);

  foreach my $ip (@ips) {
    $ip_pool->{'ADDCHILD'}->(PleskStructure::makeIpNode($ip));
  }

  # Site Applications pool
  $self->dumpSiteAppPool($root, $clientId);

  $self->dumpDomainTemplates($root, $clientId);

#  my @domains = PleskStructure::getDomainsForClient($clientName);
#  for my $domainName (@domains) {
#    $root->{'ADDCHILD'}->($self->makeDomainNode($domainName));
#  }
  for my $domainName (@{$domains}) {
    my $dnode = $self->makeDomainNode($domainName);
    $root->{'ADDCHILD'}->($dnode);
    $dnode->ReleaseCode();
    #$root->{'ADDCHILD'}->($self->makeDomainNode($domainName));
  }

  $root->{'ADDCHILD'}->($_) foreach $self->getCustomButtonsByOwner("client", $clientId);

  $self->makeClientSiteBuilderNode($root, $clientId) if (PleskVersion::atLeast(8, 3, 0));

  return $root;
}

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

	my $sql = "SELECT sb_client_login, sb_reseller_id FROM SBResellers WHERE client_id='$clientId'";

	if ($self->{dbh}->execute_rownum($sql)) {
	    my $sbNode = XmlNode->new('sb-reseller');

	    while (my $ptrHash = $self->{dbh}->fetchhash()) {
	    	$sbNode->addChild(XmlNode->new('sb-client-login', 'content' => $ptrHash->{'sb_client_login'}));
		$sbNode->addChild(XmlNode->new('sb-reseller-id', 'content' => $ptrHash->{'sb_reseller_id'}));
	    }

	    $root->addChild($sbNode);
	}
	$self->{dbh}->finish();

}

#-----------------------------------------------------------------
#    Dumps information about client's site applications pool
#-----------------------------------------------------------------
sub dumpSiteAppPool( $ $ ) {
    my ($self, $root, $cl_id) = @_;

    # There were no commercial site applications prior to Plesk 7.1.0
    if (!PleskVersion::atLeast(7, 1, 0)) {
        return;
    }

    my $sql;

    if (PleskVersion::atLeast(8, 3, 0)) {
        $sql = "SELECT sap.name AS sapp_name, sap.version AS sapp_version, sap.release AS sapp_release, lt.license_type_hash AS license_type_id, ai.shared AS shared, cai.instances_limit AS instances_limit "
            . "FROM APSClientApplicationItems AS cai "
            . "INNER JOIN APSApplicationItems AS ai ON (ai.id = cai.app_item_id AND cai.client_id = '$cl_id') "
            . "INNER JOIN SiteAppPackages AS sap ON (sap.id=ai.pkg_id)"
            . "LEFT JOIN APSLicenseTypes AS lt ON (ai.license_type_id = lt.id) AND ai.shared = 'false'";
    }elsif (PleskVersion::atLeast(7, 1, 0)) {
        $sql = "SELECT sp.name AS sapp_name, sp.version AS sapp_version, sp.release AS sapp_release "
            . "FROM clients c INNER JOIN Repository r ON c.sapp_pool_id=r.rep_id "
            . "LEFT JOIN SiteAppPackages sp ON r.component_id=sp.id "
            . "WHERE c.id='$cl_id' AND (sp.access_level & 1) <> 0";
    }

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

        my $sapp_pool = XmlNode->new('sapp-pool');
        $root->{'ADDCHILD'}->($sapp_pool);

        while (my $ptrHash = $self->{dbh}->fetchhash()) {
            $self->makeSiteAppItemNode($sapp_pool, $ptrHash);
        }
    }
    $self->{dbh}->finish();
}


sub addPermissions25 {
  my ($self, $root, $id) = @_;

  # [42098] migration from 7.14 to 7.50 fails
  return unless ($id);

  my ($perm_name, $sql, $ptrRow, $value, $item);
  my @perm_names = ('create_domains', 'manage_dns', 'manage_log');

  foreach $perm_name (@perm_names) {
    $sql = "SELECT DISTINCT val FROM cl_param WHERE cl_id = '$id' AND param = '$perm_name'";
    if ($self->{dbh}->execute_rownum($sql)) {
        my $value = @{$self->{dbh}->fetchrow()}[0];
        addPermissionNode($root, $perm_name, $value);
    }
    $self->{dbh}->finish();
  }

  $sql = "SELECT DISTINCT val FROM cl_param WHERE cl_id='$id' AND (param='ip_based_allow' OR param='nb_allow')";
  if ($self->{dbh}->execute_rownum($sql)) {
    my $value = @{$self->{dbh}->fetchrow()}[0];
    addPermissionNode($root, 'manage_phosting', $value);
  }
  $self->{dbh}->finish();
}

sub addPermissions {
  my ($self, $root, $id) = @_;
  my ($sql, $ptrRow, $item, $name, $value);

  # [42098] migration from 7.14 to 7.50 fails
  return unless ($id);

  $sql = "SELECT permission,value FROM Permissions WHERE id=$id";
  if ($self->{dbh}->execute_rownum($sql)) {
    while ($ptrRow = $self->{dbh}->fetchrow()) {
      $name = $ptrRow->[0];
      $value = $ptrRow->[1];
      $name = "manage_virusfilter" if $name eq "manage_drweb";

      next unless exists $xmlPermissions{$name};

      if ($name eq 'make_dumps') {
        addPermissionNode($root, 'allow_local_backups', $value);
        addPermissionNode($root, 'allow_ftp_backups', $value);
        next;
      }

      addPermissionNode($root, $name, $value);
    }
  }
  $self->{dbh}->finish();

  $sql = "SELECT value FROM Permissions WHERE id = $id and ("
    . "permission = 'ipb_allow' or permission = 'nb_allow')";
  if ($self->{dbh}->execute_rownum($sql)) {
    my $value = @{$self->{dbh}->fetchrow()}[0];
    addPermissionNode($root, 'manage_phosting', $value);
  }
  $self->{dbh}->finish();
}

sub addDomainUserMultipleLoginPermission {
  my ($self, $root, $id) = @_;

  my $sql = "SELECT val FROM dom_param WHERE dom_id=$id AND param='multiply_login'";
  if ($self->{dbh}->execute_rownum($sql)) {
    my $multiple_sessions = $self->{dbh}->fetchrow()->[0];
    # Old plesks have bug when mn_param table contains 1 instead of 'true'. Fixing.
    if ($multiple_sessions eq '1') {
      $multiple_sessions = 'true';
    }
    addPermissionNode($root, 'multiple-sessions', $multiple_sessions eq 'true' ? 'true' : 'false');
  }
  $self->{dbh}->finish();
}

sub addClientMulitpleLoginPermission {
  my ($self, $root, $id) = @_;

  my $sql = "SELECT val FROM cl_param WHERE cl_id =$id AND param='multiply_login'";
  if ($self->{dbh}->execute_rownum($sql)) {
    addPermissionNode($root, 'multiple-sessions', @{$self->{dbh}->fetchrow()}[0]);
  }
  $self->{dbh}->finish();
}

sub addMailUserMultipleLoginPermission {
  my ($self, $root, $id) = @_;

  return unless PleskVersion::atLeast(6, 0, 0);

  my $sql = "SELECT val FROM mn_param WHERE mn_id=$id AND param='multiply_login'";
  if ($self->{dbh}->execute_rownum($sql)) {
    my $multiple_sessions = $self->{dbh}->fetchrow()->[0];
    # Old plesks have bug when mn_param table contains 1 instead of 'true'. Fixing.
    if ($multiple_sessions eq '1') {
      $multiple_sessions = 'true';
    }
    addMailuserPermissionNode($root, 'multiple-sessions', $multiple_sessions);
  }
  $self->{dbh}->finish();
}

sub insertLimit {
  my ($self, $root, $name, $value) = @_;

  if ($value eq '' || $value eq '-1') {
    insertLimitNode($root, $name, '-1');
    return;
  }

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

  if (!PleskVersion::atLeast(7, 0, 0) && $name eq 'mbox_quota') {
    $value = $value * 1024;
  }

  insertLimitNode($root, $name, $value);
}

sub addClientLimits {
  my ($self, $root, $clientId) = @_;
  return $self->addLimits($root, $clientId, \%clientLimits);
}

sub addClientLimits25 {
  my ($self, $root, $clientId) = @_;
  return $self->addLimits25($root, $clientId, \%clientLimits25, 'cl_id', 'cl_param');
}

sub addDomainLimits {
  my ($self, $root, $clientId) = @_;
  return $self->addLimits($root, $clientId, \%domainLimits);
}

sub addDomainLimits25 {
  my ($self, $root,$domainId) = @_;
  $self->addLimits25($root, $domainId, \%domainLimits25, 'dom_id', 'dom_param');
  #fix bug 50361
  my $ptrRow;
  my $sql ="SELECT traffic FROM hosting WHERE dom_id='$domainId'";
  if ($self->{dbh}->execute_rownum($sql) && ($ptrRow = $self->{dbh}->fetchrow())){
      $self->insertLimit($root, 'max_traffic', $ptrRow->[0]);
    }
    $self->{dbh}->finish();
      $sql = "SELECT size FROM domains WHERE id='$domainId'";
  if ($self->{dbh}->execute_rownum($sql) && ($ptrRow = $self->{dbh}->fetchrow())){

      $self->insertLimit($root, 'disk_space', $ptrRow->[0]);

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

sub addLimits25 {
  my($self, $root, $limitId, $limitNames, $idName, $paramName)=@_;

  if(!$limitId) {
    return;
  }

  my($value, $name, $count, $sql, $ptrRow);

  $sql = "SELECT val, param FROM $paramName WHERE $idName='$limitId'";
  if ($self->{dbh}->execute_rownum($sql)) {
    while ($ptrRow = $self->{dbh}->fetchrow()) {
      ($name, $value) = @{$ptrRow};
    }
  }

  foreach $name (%{$limitNames}) {
    $sql = "SELECT val FROM $paramName WHERE $idName = '$limitId' AND param = '$name'";

    if(($self->{dbh}->execute_rownum($sql)) && ($ptrRow = $self->{dbh}->fetchrow())) {
      ($value) = @{$ptrRow};
      if($name eq 'max_pbox') {
        $name = 'max_box';
      }
      $self->insertLimit($root, $name, $value);
    }
    $self->{dbh}->finish();
  }
}

#
# sub addLimits
#
# arguments:
#           $root    - XML node to add 'limit' node
#           $limitID - limit id
#
# return:
#           $count - number of the added nodes
#
sub addLimits {
  my ($self, $root, $limitId, $limitNames) = @_;

  my $count;

  if (!$limitId) {
    foreach my $key (keys %{$limitNames}) {
      next if $key eq 'max_pbox';
      $self->insertLimit($root, $key, "-1");
      $count++;
    }
    return $count;
  }

  my ($sql, $ptrRow, $value, $name);
  $sql = "SELECT limit_name,value FROM Limits WHERE id=$limitId";
  if ($self->{dbh}->execute_rownum($sql)) {
    $count=0;
    while ($ptrRow = $self->{dbh}->fetchrow()) {
      ($name, $value) = @{$ptrRow};
      next unless exists $limitNames->{$name};
      $self->insertLimit($root, $name, $value);
      $count++;
    }
  }
  $self->{dbh}->finish();
  return $count;
}

#
# Gather domain templates information
#
sub dumpDomainTemplates {
  my ($self, $root, $clientId, $tmpl_id) = @_;

  return unless PleskVersion::atLeast(5, 0, 0);

  my $sql;

  if ($clientId) {
    if (PleskVersion::atLeast(6, 0 ,0)) {
      $sql = "SELECT tmpl_id FROM clients WHERE id=$clientId";
    } else {
      $sql = "SELECT val FROM cl_param WHERE cl_id=$clientId AND ".
        "param = 'dom_tmpl_list_id'";
    }

    if ($self->{dbh}->execute_rownum($sql)) {
      $tmpl_id = @{$self->{dbh}->fetchrow()}[0];
    }
    $self->{dbh}->finish();
  }

  $self->dumpTemplates($root, $tmpl_id, 'domain-template');
}

sub dumpTemplates {
  my ($self, $root, $tmpl_id, $node_name) = @_;

  return unless $tmpl_id;

  my $sql;

  my %templateNames;
  my %templateNotes;
  $sql = "SELECT t.id, t.name, t.note_id FROM Templates t, Repository r "
    . "WHERE r.rep_id=$tmpl_id AND r.component_id = t.id";
  $self->{dbh}->execute($sql);
  while (my $ptrRow = $self->{dbh}->fetchrow()) {
    $templateNames{$ptrRow->[0]} = $ptrRow->[1];
    $templateNotes{$ptrRow->[0]} = $ptrRow->[2];
  }
  $self->{dbh}->finish();

  while (my ($tmpl_id, $tmpl_name) = each %templateNames) {
    my $node = XmlNode->new($node_name, 'attributes' => {'name' => $tmpl_name});

    if ($templateNotes{$tmpl_id}) {
      $node->{'ATTRIBUTE'}->('note-id', $templateNotes{$tmpl_id});
    }

    my $logrotation_id;
    my $sql = "SELECT element, value FROM TmplData WHERE tmpl_id=$tmpl_id";
    $self->{dbh}->execute($sql);
    while (my $ptrRow = $self->{dbh}->fetchrow()) {
      my ($element, $value) = @{$ptrRow};

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

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

      $value = -1 if ($element eq 'disk_space'
                      or $element eq 'max_traffic'
                      or $element eq 'mbox_quota') and $value == 0;

      $value = 'physical' if ($element eq 'vh_type' and $value ne 'none' and
                              !PleskVersion::atLeast(6, 0, 0));

      if ($element eq 'logrotation_id') {
        $logrotation_id = $value;
        next;
      }

      next if not exists $template_items{$element};

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

      $value*= 1024 if ($element eq 'mbox_quota' and $value != -1 and !PleskVersion::atLeast(7, 0, 0));
      $value*= 1024*1024 if ($element eq 'quota' and $value != -1 and !PleskVersion::atLeast(7, 0, 0));

      if ($value eq '' and exists $emptyableBoolElementInTemplate{$element}) {
        $value = 'false';
      }

      addTemplateItem($node, $element, $value);
    }
    $self->{dbh}->finish();

    if ($logrotation_id and ($node_name eq 'domain-template')) {
      $self->makeLogrotationNode($node, $logrotation_id);
    }

    unless (PleskVersion::atLeast(6, 0, 0)) {
      addTemplateItem($node, 'dns_type', 'master');
    }

    $root->{'ADDCHILD'}->($node);
  }
}

#
# Returns the XML node if domain service is present and undef
# otherwise
#

sub getDomainServiceStatus {
  my ($self, $domId, $service) = @_;

  my $status = 0;
  my $sql = "SELECT status FROM DomainServices "
    . " WHERE dom_id=$domId AND type='$service'";

  if ($self->{dbh}->execute_rownum($sql)    and my $ptrRow = $self->{dbh}->fetchrow()){
    $status = $ptrRow->[0];
  } else {
    $status = undef;
  }
  $self->{dbh}->finish();

  if (defined($status)) {
    return Status::make($status);
  }
}

#
# makeDomainNode
#
# arguments:
#           $domainId - domain id
#
# return:
#           $root - pointer to XML node
#
sub makeDomainNode {
  my($self, $domainName)=@_;

  printToLog("Domain '$domainName' is started");

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

  my($sql,%domain,%domParams,$ptrRow,$ptrHash,$id);

#
# get domain's info
#
  my $domainNameColumn = 'name';
  if (PleskVersion::atLeast(7, 1, 0)) {
    $domainNameColumn = 'displayName';
  }

  printToLog("Getting domain info");
  $sql = "SELECT * FROM domains WHERE $domainNameColumn = '$domainName'";

  unless($self->{dbh}->execute_rownum($sql)){
    $self->{dbh}->finish();
    return undef;
  }
  unless($ptrHash = $self->{dbh}->fetchhash()){
    $self->{dbh}->finish();
    return undef;
  }
  %domain=%{$ptrHash};
  $self->{dbh}->finish();

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

  $sql = "SELECT param,val FROM dom_param WHERE dom_id=$domainId";
  if($self->{dbh}->execute_rownum($sql)){
    while ($ptrRow = $self->{dbh}->fetchrow()){
      $domParams{$ptrRow->[0]}=$ptrRow->[1];
    }
  }
  $self->{dbh}->finish();

#
# end get domain's info
#

  if (PleskVersion::atLeast(7, 1, 0)) {
    $domainName = $domain{'displayName'};
  } else {
    $domainName = $domain{'name'};
  }

  my $root = XmlNode->new('domain',
                     'attributes' => {'name' => $domainName,
                                      'id' => $domainId});

  $root->setAttribute('cr-date', $domain{'cr_date'}) if exists $domain{'cr_date'};
  $root->setAttribute('guid', $domain{'guid'}) if defined $domain{'guid'};

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

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

  # Domain's IP address
  printToLog("Getting domain IP");
  my $ip = PleskStructure::getDomainIp($domainName);
  if ($ip) {
    $root->{'ADDCHILD'}->(PleskStructure::makeIpNode($ip));
  }

  #
  # Status
  #
  printToLog("Dupming domain status");

  my $status = 0;
  if (PleskVersion::atLeast(5, 1, 0)) {
    $status = $domain{'status'};
  } else {
    $status |= $Status::ADMIN if $domain{'admin_status'} eq 'false';
    $status |= $Status::CLIENT if $domain{'status'} eq 'false';
  }
  $root->{'ADDCHILD'}->(Status::make($status));

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

        printToLog("Dumping domain databases");

        $sql = "SELECT id FROM data_bases WHERE dom_id=$domainId";
        if ($self->{dbh}->execute_rownum($sql)) {

            my (@databases);
            while ($ptrRow = $self->{dbh}->fetchrow()) {
                push @databases,$ptrRow->[0];
            }
            $self->{dbh}->finish();

            foreach my $dbid (@databases) {
                my ($item) = $self->makeDatabaseNode($dbid);
                $root->addChild($item) if ref($item) =~ /XmlNode/;
            }
        }
        $self->{dbh}->finish();
        return $root;
  }

  # Check whether this domain is default on IP
  if (PleskVersion::atLeast(6, 0, 0)) {
    $sql = "SELECT COUNT(*) FROM IP_Addresses WHERE default_domain_id=$domainId";
    if ($self->{dbh}->execute_rownum($sql)) {
      if (@{$self->{dbh}->fetchrow()}[0] ne "0") {
        $root->{'ADDCHILD'}->(XmlNode->new('default-domain'));
      }
    }
    $self->{dbh}->finish();
  } else {
    if (PleskStructure::isExclusiveIp($ip)) {
      $root->{'ADDCHILD'}->(XmlNode->new('default-domain'));
    }
  }

  printToLog("Getting domain limits");
#
# limits
#
  if (!$self->{only_mail_dump}) {
    if (PleskVersion::atLeast(6, 0, 0)) {
      $self->addDomainLimits($root,$domain{'limits_id'});
    } elsif (PleskVersion::atLeast(5, 0, 0)) {
      $self->addDomainLimits($root,$domParams{'lim_id'});
    } else {
      $self->addDomainLimits25($root,$domainId);
    }
  }

#
# end limits
#

#
# permissions
#

#
# end permissions
#

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

    printToLog("Dumping domain mailsystem");

    my $mailServiceStatus;
    if (PleskVersion::atLeast(6, 0, 0)) {
      $mailServiceStatus = $self->getDomainServiceStatus($domainId, 'mail');
    } else {
      $mailServiceStatus = Status::make($Status::ENABLED);
    }

    if ($mailServiceStatus) {
      my $rootMail = XmlNode->new('mailsystem');
      $rootMail->{'ADDCHILD'}->($mailServiceStatus);

      if (PleskVersion::atLeast(6, 0, 0)) {
        $sql = "SELECT m.id, a.password, a.type FROM mail m".
          " LEFT JOIN accounts a ON m.account_id=a.id ".
          " WHERE m.dom_id=$domainId ORDER BY m.mail_name";
      } else {
        $sql = "SELECT id, password, 'plain' FROM mail ".
          " WHERE dom_id=$domainId ORDER BY mail_name";
      }

      if ($self->{dbh}->execute_rownum($sql)){
        my(@mails);
        while($ptrRow = $self->{dbh}->fetchrow()){
          push @mails,[@{$ptrRow}];
        }
        $self->{dbh}->finish();

        foreach $ptrRow (@mails){
          $ptrRow->[2] = normalizePasswordType($ptrRow->[2]);
          my $item = $self->makeMailUserNode(@{$ptrRow},$domainName, $domainAsciiName);
          $rootMail->{'ADDCHILD'}->($item) if ref($item) =~ /XmlNode/;
          $item->ReleaseCode() if ref($item) =~ /XmlNode/;
        }
      } else {
        $self->{dbh}->finish();
      }

      $self->getCatchAllAddress($rootMail, $domainId);

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

      $root->{'ADDCHILD'}->($rootMail);
    }
  }
  else{ printToLog("Skip domain mailsystem dump due to settings"); }

    my @SiteApplications;

    if (!$self->{only_mail_dump}) {
        # Domain's DNS settings
        my $dnsZoneNode = $self->makeDomainDnsZone(\%domain);
        $root->{'ADDCHILD'}->($dnsZoneNode) if $dnsZoneNode;

        # Domain's aliases
        $self->dumpDomainAliases($domainId, $root);

        #-----------------------------------------------------------------
        # Information about domain's site applications
        # should be extracted prior to database/hosting/custom-buttons
        # dump, as site applications' linked resources should be dumped
        # separately.
        #-----------------------------------------------------------------
        @SiteApplications = $self->getDomainSiteapps($domainId);

        #-----------------------------------------------------------------
        # Domain's databases
        #-----------------------------------------------------------------
        printToLog("Dumping domain databases");

        # Site applications' databases should not be dumped here
        my $exclude_list = '';
        {
            my @excluded_dbs;
            foreach my $sapp (@SiteApplications) {
                foreach my $row ($sapp->getDatabases()) {
                    push @excluded_dbs, $row->{'id'};
                }
            }
            if (@excluded_dbs) {
                $exclude_list = " AND id NOT IN(" . getSqlList(@excluded_dbs) . ")";
            }
        }

        # Databases without those used in site apps
        $sql = "SELECT id FROM data_bases WHERE dom_id=$domainId" . $exclude_list;
        if ($self->{dbh}->execute_rownum($sql)) {

            my (@databases);
            while ($ptrRow = $self->{dbh}->fetchrow()) {
                push @databases,$ptrRow->[0];
            }
            $self->{dbh}->finish();

            foreach my $dbid (@databases) {
                my ($item) = $self->makeDatabaseNode($dbid);
                $root->addChild($item) if ref($item) =~ /XmlNode/;
            }
        }
        $self->{dbh}->finish();
    }
    else{ printToLog("Skip dumping domain databases due to settings"); }

#
# maillist
#

  if (!$self->{only_hosting_dump}) {
    printToLog("Dumping domain maillists");
    if (PleskVersion::atLeast(6, 0, 0)) {

      my $maillistsStatus = $self->getDomainServiceStatus($domainId, 'maillists');
      if ($maillistsStatus) {
        my $mlNode = XmlNode->new('maillists');
        $mlNode->{'ADDCHILD'}->($maillistsStatus);

        $sql="SELECT id,name,status FROM MailLists WHERE dom_id=$domainId";
        if($self->{dbh}->execute_rownum($sql)){
          my (@mlists);
          while($ptrRow=$self->{dbh}->fetchrow()){
            push @mlists,[@{$ptrRow}];
          }
          $self->{dbh}->finish();
          foreach $ptrRow (@mlists){
            my $item = $self->makeMailListNode(@{$ptrRow});
            if(ref($item)=~/XmlNode/){
              $mlNode->{'ADDCHILD'}->($item);
              $item->ReleaseCode();
            }
          }
        }
        $self->{dbh}->finish();

        $root->{'ADDCHILD'}->($mlNode);
      }
    }
  }
  else{ printToLog("Skip dumping domain maillists due to settings"); }
#
# end maillist
#

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

  printToLog("Dumping domain statistics");

  $self->addCurrentTraffic($root,$domainId);

  if (PleskVersion::atLeast(6, 0, 0)) {
      my $cert_id = $domain{'cert_rep_id'};
      $cert_id = '' unless (defined($cert_id));
    $sql = "SELECT c.id FROM certificates c, Repository r ".
      "WHERE c.id=r.component_id AND r.rep_id='$cert_id' ORDER BY c.id";
  } else {
    $sql = "SELECT id FROM certificates WHERE dom_id=$domainId";
  }
  my @ids;
  if ($self->{dbh}->execute_rownum($sql)) {
    while ($ptrRow = $self->{dbh}->fetchrow()) {
      push @ids, $ptrRow->[0];
    }
  }
  $self->{dbh}->finish();

  foreach $id (@ids) {
    my $item = $self->makeCertificateNode($id);
    if (ref($item)=~/XmlNode/) {
      $root->{'ADDCHILD'}->($item);
    }
  }

  my $tomcatServiceStatus;
  if (PleskVersion::atLeast(6, 0, 0)) {
    $tomcatServiceStatus = $self->getDomainServiceStatus($domainId, 'tomcat');
  }

  if ($tomcatServiceStatus) {
    my $tomcatNode = XmlNode->new('tomcat');
    $tomcatNode->{'ADDCHILD'}->($tomcatServiceStatus);

    $sql = "SELECT wa.name,wa.status FROM WebApps wa LEFT JOIN DomainServices ds ON wa.domain_service_id=ds.id WHERE ds.dom_id = $domainId";

    my (%webapps,$webapp);

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

      while ($ptrRow = $self->{dbh}->fetchrow()) {
        $webapps{$ptrRow->[0]}    = $ptrRow->[1];
      }
    }
    $self->{dbh}->finish();

    foreach $webapp (keys %webapps) {
      $self->makeWebAppNode($tomcatNode,$webapp,$webapps{$webapp},$domainAsciiName);
    }

    $root->{'ADDCHILD'}->($tomcatNode);
  }

  my $item = $self->makeDomainUserNode($domainId);
  if(ref($item)=~/XmlNode/){
        printToLog("Getting domain permissions"); # bug 79536

        if ($id = $domParams{'perm_id'}) {
            $self->addPermissions($item,$id);
        }

        $root->{'ADDCHILD'}->($item);
  }

  my $hosting;
  if ($domainType  eq "vrt_hst") {
    $hosting = $self->makePhostingNode(\%domain, \@SiteApplications);

    if (defined $domParams{'wu_script'} && $domParams{'wu_script'} eq 'true') {
      $hosting->{'ATTRIBUTE'}->('wu_script', 'true');
    }
  } elsif ($domainType eq "std_fwd") {
    $hosting = $self->makeShostingNode(\%domain);
  } elsif ($domainType eq "frm_fwd") {
    $hosting = $self->makeFhostingNode(\%domain);
  }

  $root->{'ADDCHILD'}->($hosting) if $hosting;

  $root->{'ADDCHILD'}->($_)
    foreach $self->getCustomButtonsByOwner('domain-admin', $domainId);

  return $root;
}

#-----------------------------------------------------------------
#    Returns <dns> or <dns-zone> xml elements
#-----------------------------------------------------------------
sub makeDomainDnsZone( $ ) {
    my ($self, $domainHashPtr) = @_;

    printToLog("Dumping domain DNS");

    if (PleskVersion::atLeast(8, 0, 0)) {
        return $self->makeDnsZone($domainHashPtr->{'dns_zone_id'});
    } else {
        return $self->makeDnsZoneOld($domainHashPtr->{'id'});
    }
}

#-----------------------------------------------------------------
#    Returns <dns-zone-param> xml node
#-----------------------------------------------------------------
sub makeDnsZoneParam( $ $ $ ) {
  my ($self, $name, $db_unit, $db_value) = @_;

    my $dnsZoneParam = XmlNode->new('dns-zone-param', 'attributes' => {'name' => $name, 'value' => $db_value});

    my %units = (
        'second'  => 1,
        'minutes' => 60,
        'hours'   => 3600,
        'days'    => 86400,
        'weeks'   => 604800
    );
    my $string_unit;

    foreach my $quant (keys %units) {
        if ($units{$quant} == $db_unit) {
            $string_unit = $quant;
            last;
        }
    }
    $dnsZoneParam->{'ATTRIBUTE'}->('unit', $string_unit);

    return $dnsZoneParam;
}

#-----------------------------------------------------------------
#    Returns <dnsrec> xml node or false if given record should not be dumped
#-----------------------------------------------------------------
sub makeDnsRecord( $ ) {
  my ($self, $ptrHash) = @_;

    return if $ptrHash->{'type'} =~ /none/;
    return if $ptrHash->{'displayHost'} =~ /.*_domainkey.*/;

    if ($ptrHash->{'type'} eq 'TXT') {
        $ptrHash->{'val'} =~ s/"(.*)"/$1/;
    }

    # Fix broken CNAME mail records (bug #110731)
    if ($ptrHash->{'type'} eq 'CNAME' and $ptrHash->{'host'} eq "mail.".$ptrHash->{'val'}) {
        $ptrHash->{'type'} = 'A';
        $ptrHash->{'val'} =~ s/\.$//;
        $ptrHash->{'val'} = PleskStructure::getDomainIp($ptrHash->{'val'});
    }

	my $host = defined $ptrHash->{'displayHost'} ? $ptrHash->{'displayHost'} : $ptrHash->{'host'};
	my $val  = defined $ptrHash->{'displayVal'}  ? $ptrHash->{'displayVal'}  : $ptrHash->{'val'};

    if ($ptrHash->{'type'} eq 'SRV') {
      $host =~ s/\.$//;
      $val =~ s/\.$//;
    }

    my ($item) = XmlNode->new('dnsrec', 'attributes' => {'type' => $ptrHash->{'type'}, 'src' => $host});
    $item->setAttribute('dst', $val) if ($val);
    $item->setAttribute('opt', $ptrHash->{'opt'}) if (defined $ptrHash->{'opt'});

    return $item;
}

sub makeDnsZone( $ ) {
    my ($self, $dnsZoneId) = @_;

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

    if (!$self->{dbh}->execute_rownum($sql)) {
      $self->{dbh}->finish();
      Logging::error("Broken referencial integrity: DNS zone id $dnsZoneId is not found in dns_zone");
      return;
    }

    my $ptrHash = $self->{dbh}->fetchhash();

    my $email;
    if (length($ptrHash->{'email'}) != 0) {
      $email = $ptrHash->{'email'};
    } else {
      # should assume some default email or something...
      $email = 'root@localhost.localdomain';
    }

    my $dnsZone = XmlNode->new('dns-zone',
                          'attributes' => {'email' => $email, 'type' => $ptrHash->{'type'}},
                          'children' => [Status::make($ptrHash->{'status'})]);

    foreach my $zoneParam ('ttl', 'refresh', 'retry', 'expire', 'minimum') {
      $dnsZone->{'ADDCHILD'}->(
                               $self->makeDnsZoneParam($zoneParam,
                                                $ptrHash->{$zoneParam . '_unit'},
                                                $ptrHash->{$zoneParam}));
    }

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

    # dns records
    $sql = "SELECT * FROM dns_recs WHERE dns_zone_id=$dnsZoneId";
    if ($self->{dbh}->execute_rownum($sql)) {

        while (my $ptrHash = $self->{dbh}->fetchhash()) {
            my $dnsrec = $self->makeDnsRecord($ptrHash);
            if ($dnsrec) {
                $dnsZone->{'ADDCHILD'}->($dnsrec);
            }
        }
    }
    $self->{dbh}->finish();

    return $dnsZone;
}

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

    my $dnsNode = XmlNode->new('dns');

    my $sql = "SELECT dns_zone";
    if (PleskVersion::atLeast(5, 0, 0)) {
        $sql .= ",dns_type"
    }
    $sql .= " FROM domains WHERE id='$domainId'";

    if ($self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow()) {
        $dnsNode->{'ATTRIBUTE'}->('enabled', $ptrRow->[0] eq 'false' ? 'false' : 'true');
        $dnsNode->{'ATTRIBUTE'}->('type', (!$ptrRow->[1] or ($ptrRow->[1] eq 'master')) ? 'master' : 'slave');
    }
    $self->{dbh}->finish();

    $sql = "SELECT * FROM dns_recs WHERE dom_id=$domainId";
    if ($self->{dbh}->execute_rownum($sql)) {
      my $ptrHash;
        while ($ptrHash = $self->{dbh}->fetchhash()) {
            my $dnsrec = $self->makeDnsRecord($ptrHash);
            if ($dnsrec) {
                $dnsNode->{'ADDCHILD'}->($dnsrec);
            }
          $ptrHash = undef;
        }
    }
    $self->{dbh}->finish();

        # Add old master record

        $sql = "SELECT * FROM dns_masters WHERE dom_id = '$domainId'";

        if ($self->{dbh}->execute_rownum($sql)) {
           while (my $ptrHash = $self->{dbh}->fetchhash()) {
             $dnsNode->{'ADDCHILD'}->(makeOldMasterRec($ptrHash->{'ip_address'}));
           }
        }
        $self->{dbh}->finish();
    return $dnsNode;
}

#-----------------------------------------------------------------
#    Dumps domain aliases for given domain ID
#-----------------------------------------------------------------
sub dumpDomainAliases( $ $ ) {
    my ($self, $domainId, $root) = @_;

    # Domain aliases are introduced since Plesk 8.0.0
    if (!PleskVersion::atLeast(8, 0, 0)) {
        return;
    }

    printToLog('Dumping domain aliases... ', 1);

    my @aliases;

    my $sql = "SELECT * FROM domainaliases WHERE dom_id='$domainId'";
    if ($self->{dbh}->execute_rownum($sql)) {

        while (my $ptrHash = $self->{dbh}->fetchhash()) {
            push @aliases, $ptrHash;
        }
    }
    $self->{dbh}->finish();

    foreach my $alias (@aliases) {

        my $aliasNode = XmlNode->new('domain-alias',
                                'attributes' => {'name' => $alias->{'displayName'},
                                                 'mail' => $alias->{'mail'},
                                                 'web' => $alias->{'web'},
                                                 'dns' => defined $alias->{'dns'} ? $alias->{'dns'} : 'false'},
                                'children' => [Status::make($alias->{'status'})]);

        $aliasNode->setAttribute('tomcat', $alias->{'tomcat'}) if defined $alias->{'tomcat'};

        # DNS zone
        if ($alias->{'dns_zone_id'} != 0) {
            $aliasNode->{'ADDCHILD'}->($self->makeDnsZone($alias->{'dns_zone_id'}));
        }

        $root->{'ADDCHILD'}->($aliasNode);
    }

    printToLog('OK');
}

#-----------------------------------------------------------------
#    Returns array of SiteApp objects, representing all site applications
#    installed on domain and its subdomains.
#-----------------------------------------------------------------
sub getDomainSiteapps( $ ) {
    my ($self, $dom_id) = @_;

    # Site applications migration is supported since Plesk 7.0.0
    if (!PleskVersion::atLeast(7, 0, 0)) {
        return;
    }

    my @SiteApplications;
    my @sapp_ids;

    # ids of site apps installed on domain
    my $sql = "SELECT id FROM SiteApps WHERE dom_type='domain' AND dom_id='$dom_id'";
    if ($self->{dbh}->execute_rownum($sql)) {
        while (my $idRow = $self->{dbh}->fetchrow()) {
            push @sapp_ids, $idRow->[0];
        }
    }
    $self->{dbh}->finish();

    # subdomains
    $sql = "SELECT id FROM subdomains WHERE dom_id=$dom_id";
    my (@subdoms);
    if ($self->{dbh}->execute_rownum($sql)) {
        while (my $subdomRow = $self->{dbh}->fetchrow()) {
            push @subdoms, $subdomRow->[0];
        }
    }
    $self->{dbh}->finish();

    # ids of site apps installed on subdomains
    foreach my $subdom_id (@subdoms) {
        $sql = "SELECT id FROM SiteApps WHERE dom_type='subdomain' AND dom_id='$subdom_id'";
        if ($self->{dbh}->execute_rownum($sql)) {
            while (my $idRow = $self->{dbh}->fetchrow()) {
                push @sapp_ids, $idRow->[0];
            }
        }
        $self->{dbh}->finish();
    }

    # create SiteApp objects
    foreach my $sapp_id (@sapp_ids) {
        my $sapp = new SiteApp($sapp_id, $self->{dbh});
        if ($sapp->error()) {
            printToError($sapp->error());
            next;
        }
        push @SiteApplications, $sapp;
    }

    return @SiteApplications;
}


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

  my $sql;

  if (PleskVersion::atLeast(8, 0, 0)) {
    $sql = "SELECT r.* FROM dns_recs r, domains d WHERE d.id=$domainId "
         . "AND d.dns_zone_id=r.dns_zone_id AND ( r.type = 'CNAME' or r.type = 'A' ) AND r.host = 'www.$domainName.'";
  } else {
      $sql = "SELECT * FROM dns_recs WHERE dom_id = $domainId AND ( type = 'CNAME' OR  type = 'A' ) " .
    "AND host = 'www.$domainName.'";
  }

  if ($self->{dbh}->execute_rownum($sql)) {
    $parent->{'ATTRIBUTE'}->('www', 'true');
  } else {
    $parent->{'ATTRIBUTE'}->('www', 'false');
  }
  $self->{dbh}->finish();
}

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

  my($sql,$root,$item,$ptrHash,$xmlName,$fieldName);

  $sql="SELECT * FROM dom_level_usrs WHERE dom_id=$domainId";
  unless($self->{dbh}->execute_rownum($sql)){
    $self->{dbh}->finish();
    return undef;
  }
  my %domainUser=%{$self->{dbh}->fetchhash()};
  $self->{dbh}->finish();
  $root = XmlNode->new('domainuser');

  if (defined $domainUser{'uid'}) {
    $root->{'ATTRIBUTE'}->('uid', $domainUser{'uid'});
    $root->{'ATTRIBUTE'}->('ownership', $domainUser{'ownership'} eq 'true' ? 'true' : 'false');
  }

  if ($domainUser{'state'}) {
    $root->{'ATTRIBUTE'}->('status', $domainUser{'state'});
  }

  if (PleskVersion::atLeast(7, 1, 5)) {
    if (defined $domainUser{'account_id'} and $domainUser{'account_id'} != 0) {
      $root->{'ADDCHILD'}->($self->makeAccountPasswordNode($domainUser{'account_id'}));
    }
  } else {
    $root->{'ADDCHILD'}->(makePasswordNode($domainUser{'passwd'}, 'plain'));
  }

  if (PleskVersion::atLeast(5, 0, 0) && defined($domainUser{'card_id'})) {
    $sql = "SELECT * from Cards WHERE id=$domainUser{'card_id'}";
    if($self->{dbh}->execute_rownum($sql)){
      $ptrHash = $self->{dbh}->fetchhash();
      while (($xmlName,$fieldName)=each %domainUserAttribute){
        if($ptrHash->{$fieldName}){
          $root->{'ATTRIBUTE'}->($xmlName,$ptrHash->{$fieldName});
        }
      }

      while (($xmlName, $fieldName) = each %domainUserInfo) {
        my $value = $ptrHash->{$fieldName};
        if ($value) {
          $value = normalizeCountry($value) if $fieldName eq 'country';
          $root->{'ADDCHILD'}->(XmlNode->new('pinfo', 'content' => $value, 'attributes' => {'name' => $xmlName}));
        }
      }
    }
    $self->{dbh}->finish();
  }

  $self->addDomainUserLocale($root,$domainId);

  if (PleskVersion::atLeast(7, 1, 0)) {
    $self->addPermissions($root, $domainUser{'perm_id'});
  }
  $self->addDomainUserMultipleLoginPermission($root, $domainId);

  if (!PleskVersion::atLeast(8,0,0)) {
    addPermissionNode($root,'stdgui', 'true');
  }

  # max button length
  $self->getMaxButtonLength($root, "dom_param", "dom_id=$domainId");
  $self->getSkin($root, "dom_param", "dom_id=$domainId");
  $self->getLockScreen($root, "dom_param", "dom_id=$domainId");

  return $root;
}

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

  my $locale = PleskVersion::atLeast(7, 1, 0) ? "locale" : "user_locale";

  my $sql = "SELECT val FROM dom_param WHERE dom_id = $domainId AND param = '$locale'";
  my $ptrRow;
  if ($self->{dbh}->execute_rownum($sql) && ($ptrRow = $self->{dbh}->fetchrow())) {
    if($ptrRow->[0]){
      my ($key, $value);
      while (($key,$value) = each(%locale_map)){
        if ($ptrRow->[0] eq $key) {
          $ptrRow->[0] = $value;
        }
      }
      $root->{'ADDCHILD'}->(XmlNode->new('pinfo', 'content' => $ptrRow->[0], 'attributes' =>{'name' => 'locale'}));
    }
  }
  $self->{dbh}->finish();
}
#
# addCurrentTraffic - add current traffic
#
#     arguments:
#                 $root - XML node to add traffic's nodes
#                 $domainId - ID of domain
#
sub addCurrentTraffic {
  my ($self, $root, $domainId) = @_;

  my $trafficValue = '';

  if (PleskVersion::atLeast(6, 0, 0)) {
    my $sql = "SELECT http_in, http_out, ftp_in, ftp_out, smtp_in, smtp_out, ".
      "pop3_imap_in, pop3_imap_out, date FROM DomainsTraffic WHERE dom_id=$domainId";

    if ($self->{dbh}->execute_rownum($sql)) {
      my @keys = ('http', 'ftp', 'smtp', 'pop3-imap');
      my ($key, $i, $ptrRow);
      while ($ptrRow = $self->{dbh}->fetchrow()) {
        for($i = 0; $i < @keys*2; ++$i) {
          if($ptrRow->[$i]) {
            $trafficValue .= $ptrRow->[8];
            $trafficValue .= ' ';
            $trafficValue .= $keys[$i/2];
            $trafficValue .= ' ';
            $trafficValue .= ($i%2 ? 'out' : 'in');
            $trafficValue .= ' ';
            $trafficValue .= $ptrRow->[$i];
            $trafficValue .= "\n";
          }
        }
      }
    }
    $self->{dbh}->finish();
  } else {
    my $sql = "SELECT transfer, date FROM stat WHERE dom_id=$domainId";
    if ($self->{dbh}->execute_rownum($sql)) {
      my $ptrRow;
      while ($ptrRow = $self->{dbh}->fetchrow()) {
        if ($ptrRow->[0]) {
          $trafficValue .= $ptrRow->[1];
          $trafficValue .= ' http in ';
          $trafficValue .= $ptrRow->[0];
          $trafficValue .= "\n";
        }
      }
    }
    $self->{dbh}->finish();
  }

  $root->{'ADDCHILD'}->(XmlNode->new('traffic', 'content' => $trafficValue));
}


# There are 3 types of nonexistent user mail handling:
# Bounce with message (bounce:message text)
# Catch to address (email@address)
# SMTP reject (reject)
# Returns bounce|catch|reject or empty string
sub getNonexistentMode {
    my ($self, $domainId) = @_;

    my $sql = "SELECT p.value FROM Parameters p, DomainServices ds " .
    "WHERE ds.parameters_id = p.id AND ds.dom_id = $domainId AND p.parameter = 'nonexist_mail'";
    my $mode;
    if ($self->{dbh}->execute_rownum($sql)) {
      $mode = @{$self->{dbh}->fetchrow()}[0];
    } else {
      $mode = "bounce:This address no longer accepts mail.";
    }
    $self->{dbh}->finish();

    return $mode;
}

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

    my $sql;
    if (PleskVersion::atLeast(6, 0, 0)) {
        my $mode = $self->getNonexistentMode($domainId);

        if ($mode =~ /^catch$/) {
            $sql = "SELECT p.value FROM Parameters p, DomainServices ds " .
            "WHERE ds.parameters_id = p.id AND ds.dom_id = $domainId AND p.parameter = 'catch_addr'";

        } elsif ($mode =~ /^bounce$/) {
            $sql = "SELECT CONCAT('bounce:', p.value) FROM Parameters p, DomainServices ds " .
            "WHERE ds.parameters_id = p.id AND ds.dom_id = $domainId AND p.parameter = 'bounce_mess'";

        } elsif ($mode =~ /^reject$/) {
            $sql = "SELECT 'reject'";
        }

    } elsif (PleskVersion::atLeast(5, 0, 0)) {
        $sql = "SELECT p.value FROM Parameters p, dom_param dp WHERE " .
        "dp.dom_id = $domainId AND dp.param='param_id' AND dp.val = p.id AND p.parameter = 'catch_addr'";

    } else {
        $sql = "SELECT dp.val FROM dom_param dp WHERE dp.dom_id = $domainId AND dp.param = 'catch_addr'";
    }

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

    if ($self->{dbh}->execute_rownum($sql)) {
        my $catchAllAddr = @{$self->{dbh}->fetchrow()}[0];
        if ($catchAllAddr) {
            $parent->{'ADDCHILD'}->(XmlNode->new('catch-all', 'content' => $catchAllAddr));
        }
    }
    $self->{dbh}->finish();
}

#
# makeMailListNode
#
#   arguments:
#              $mlistId - ID of the mail list
#              $mlistName - name
#              $mllistState - state of maillist
#
#   return:
#              $root - XML node
#
sub makeMailListNode {
  my ($self, $mlistId, $mlistName, $mlistState) = @_;

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

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

  my $root = XmlNode->new('maillist',
                     'attributes'=>{'name', $mlistName},
                     'children' => [Status::make($mlistState)]);

  for my $listOwner (@owners) {
    $root->{'ADDCHILD'}->(XmlNode->new('owner', 'content' => $listOwner));
  }

  $root->{'ADDCHILD'}->(makePasswordNode(Mailman::getListPassword($mlistName)), 'encrypted');

  my %listMembers = Mailman::getListMembers($mlistName);
  for my $memberEmail (keys %listMembers) {
    my $member = XmlNode->new('recipient', 'content' => $memberEmail);
    if ($listMembers{$memberEmail} ne "") {
      $member->{'ATTRIBUTE'}->('fullname', $listMembers{$memberEmail});
    }
    $root->{'ADDCHILD'}->($member);
    $member->ReleaseCode();
  }

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

  if (-f "$archiveDir/$archive") {
    my $cid = $self->addTar("$mlistName.mlist",
                            'directory' => $archiveDir,
                            'include' => [$archive],
                            'follow_symlinks' => 1);
    $root->{'ATTRIBUTE'}->('cid', $cid) if ($cid);
  }

  return $root;
}

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

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

  $sql = "SELECT * FROM certificates WHERE id=$certId";
  unless($self->{dbh}->execute_rownum($sql)){
    &printToError("Error: makeCertificateNode: certificate ID '$certId' is not found");
    return undef;
  }

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

  $root = XmlNode->new('certificate');

  if (PleskVersion::atLeast(6, 0, 0)) {
    addUrlDecodedTextNode($root, 'certificate-data', $cert{'cert'});
    addUrlDecodedTextNode($root, 'signing-request', $cert{'csr'});
    addUrlDecodedTextNode($root, 'ca-certificate', $cert{'ca_cert'});
  } elsif (PleskVersion::atLeast(5, 0, 0)) {
    addUrlDecodedTextNode($root, 'certificate-data', $cert{'pub_key'});
  }
  addUrlDecodedTextNode($root, 'private-key', $cert{'pvt_key'});
  if (PleskVersion::atLeast(6, 0, 0)) {
      $root->{'ATTRIBUTE'}->('name', $cert{'name'});
  } else {
      # certificate names were not used in Plesk prior to 6.0
      $root->{'ATTRIBUTE'}->('name', $certId);
  }

  return $root;
}

sub makeDatabaseNode {
  my ($self, $dbId) = @_;
  my ($ptrHash, $ptrRow, $item, $charset, $dbServerHost, $dbServerPort);

  my $sql = "SELECT name, type FROM data_bases WHERE id=$dbId";
  unless ($self->{dbh}->execute_rownum($sql)) {
    $self->{dbh}->finish();
    return;
  }
  my ($dbName, $dbType) = @{$self->{dbh}->fetchrow()};
  $self->{dbh}->finish();
  
  if ($dbName =~ /^-.*/) {
    Logging::error("Panel does not allow to back up databases which name starts with the dash (-) symbol. Please rename database " . $dbName . " and run tha backup task again");
    return;    
  }

  if ($dbType eq "postgres" or $dbType eq "postgresql") {
    $dbType = "postgresql";
  } elsif ($dbType ne "mysql") {
    printToError("Unknown database type: $dbType");
    return;
  }

  my $root = XmlNode->new('database', 'attributes' => {'name' => $dbName, 'type' => $dbType});

  # Remote databases hosting was introduced in 8.0.0
  if (PleskVersion::atLeast(8, 0, 0)) {
    my $sql = "SELECT host,port,ds.type FROM DatabaseServers as ds, data_bases as db WHERE "
      . "ds.id = db.db_server_id AND db.id = $dbId";
    $self->{dbh}->execute_rownum($sql);

    $ptrRow = $self->{dbh}->fetchrow();

    $dbServerHost = ($ptrRow->[0] ne 'localhost') ? $ptrRow->[0] : 'localhost';
    $dbServerPort = $ptrRow->[1];

    my $dbServerNode = XmlNode->new('db-server');
    $dbServerNode->{'ATTRIBUTE'}->('type', $ptrRow->[2]);
    $dbServerNode->{'ADDCHILD'}->(XmlNode->new('host', 'content' => "$ptrRow->[0]"));
    $dbServerNode->{'ADDCHILD'}->(XmlNode->new('port', 'content' => "$ptrRow->[1]"));

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

    $root->{'ADDCHILD'}->($dbServerNode);

  }

  return $root if $self->{shallow_dump};

  printToLog("Database $dbName");

  if (PleskVersion::atLeast(7, 5, 0)) {
    $sql = "SELECT login, password, type FROM db_users, accounts WHERE "
      . "db_users.db_id = $dbId AND db_users.account_id = accounts.id";

    $self->{dbh}->execute_rownum($sql);
    while ($ptrHash = $self->{dbh}->fetchhash()) {
      $ptrHash->{'password'} ||= ''; # NULL -> ''
      my $dbUserRoot = XmlNode->new('dbuser', 'attributes' => {'name' => $ptrHash->{'login'}});

      $item = makePasswordNode($ptrHash->{'password'},
                               normalizePasswordType($ptrHash->{'type'}));

      $dbUserRoot->{'ADDCHILD'}->($item);
      $root->{'ADDCHILD'}->($dbUserRoot);
    }
  } else {
    $self->{dbh}->execute_rownum("SELECT login, passwd FROM db_users WHERE db_id = $dbId");

    while ($ptrHash = $self->{dbh}->fetchhash()) {
      my $dbPasswdType;

      # In Plesk 7.1.5 DB password type wasn't described in the DB. We
      # can distinguish the type only using length of password.
      if (length($ptrHash->{'passwd'}) > 14) {
        $dbPasswdType = 'encrypted';
      } else {
        $dbPasswdType = 'plain';
      }

      if (!defined $ptrHash->{'passwd'}) { $ptrHash->{'passwd'} = ''; }

      my $dbUserRoot = XmlNode->new('dbuser', 'attributes' => {'name' => $ptrHash->{'login'}});

      $item = makePasswordNode($ptrHash->{'passwd'}, $dbPasswdType);

      $dbUserRoot->{'ADDCHILD'}->($item);
      $root->{'ADDCHILD'}->($dbUserRoot);
    }
  }
  $self->{dbh}->finish();

  my ($dbUser, $dbPasswd, $plesk_7);

  # remote database hosting regularizes the database connectivity even for local hosts.
  # thanks goes to xman.
  if (PleskVersion::atLeast(8, 0, 0)) {

    my $sql = "SELECT admin_login, admin_password FROM DatabaseServers as ds, data_bases as db "
      . "WHERE ds.id = db.db_server_id AND db.id = $dbId";
    $self->{dbh}->execute_rownum($sql);
    ($dbUser, $dbPasswd) = @{$self->{dbh}->fetchrow()};
    $self->{dbh}->finish();

    if ($dbType eq "mysql" and $dbServerHost eq 'localhost') {
      $dbPasswd = AgentConfig::get('password');
    }

    if ($dbType eq "postgresql") {
      $root->{'ATTRIBUTE'}->('version', getPostgresqlVersion());
    }
  } else {
    if ($dbType eq "mysql") {
      $dbUser = 'admin';
      $dbPasswd = AgentConfig::get('password');
    } else {
      $sql = "SELECT param, val FROM misc WHERE param RLIKE '^postgresql_admin_'";
      if ($self->{dbh}->execute_rownum($sql)) {
        while ($ptrRow=$self->{dbh}->fetchrow()) {
          my ($name, $value) = @{$ptrRow};
          $dbUser = $value if $name =~ /login$/;
          $dbPasswd = $value if $name =~ /passwd$/;
        }
      }
      $self->{dbh}->finish();

      $plesk_7 = 1;
    }
  }

  my $cid = $self->addDb("$dbName.$dbType", "name" => $dbName, "type" => $dbType,
                         "user" => $dbUser, "password" => $dbPasswd,
                         "host" => $dbServerHost, "port" => $dbServerPort, "plesk_7" => $plesk_7);
  $root->{'ATTRIBUTE'}->('cid',$cid) if $cid;

  if ($dbType eq "postgresql") {
     my $psql = AgentConfig::psqlBin();

     #[Bug 119082]
     #$charset = `PGUSER=$dbUser PGPASSWORD='$dbPasswd' $psql -l template1 | grep '^[ \t]*$dbName ' | awk '{print \$5}'`;
     my $wrapPgsql = 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 '') { $root->{'ATTRIBUTE'}->('charset', $charset); }
         }
         $wrapPgsql->{'FINISH'}->();
       }
     }
     else{ printToLog( "Cannot connect to postgresql $dbServerHost:$dbServerPort (database '$dbName')" ) }
  }

  if ($dbType eq "mysql") {
    $root->{'ATTRIBUTE'}->('version', Db::MysqlUtils::getVersion());
     my $wrapMysql = getDbConnect($dbType, $dbUser, $dbPasswd, $dbName, $dbServerHost, undef, undef, undef, undef, $dbServerPort );
     if( ref($wrapMysql) eq 'HASH' ){
        if( $wrapMysql->{'EXECUTE'}->("SHOW VARIABLES LIKE \"character_set_database\"") ){
           my $ptrRow = $wrapMysql->{'FETCHROW'}->();
           my $charset = $ptrRow->[1] if $ptrRow;
           $root->{'ATTRIBUTE'}->('charset', $charset) if $charset;
           $wrapMysql->{'FINISH'}->();
        }
     }
  }

  return $root;
}

#
# makeMailUserNode
#
# argumets:
#           $mailId - mail ID
#           $passwd - password
#           $typePasswd - type of password
#           $domainName - name of domain
#
# return:
#           $root = XML node 'mailuser'
#
sub makeMailUserNode {
  my ($self, $mailId, $passwd, $typePasswd, $domainName, $domainAsciiName) = @_;

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

  $sql = "SELECT * FROM mail WHERE id = $mailId";
  unless($self->{dbh}->execute_rownum($sql)){
    printToError("Error: makeMailUserNode: mail ID '$mailId' is not found");
    return undef;
  }
  %mail = %{$self->{dbh}->fetchhash()};
  $self->{dbh}->finish();
  my $mailName = $mail{'mail_name'};

  my $root = XmlNode->new('mailuser', 'attributes' => {'id' => $mailId,
                                                 'name' => $mailName});

  if (defined($passwd) && $passwd) {
    if ($passwd =~ /NULL/) {
      $item = makePasswordNode('', 'plain');
    } else {
      $item = makePasswordNode($passwd,$typePasswd);
    }

    $root->{'ADDCHILD'}->($item);
  }

  # bug 78844
  if (defined $mail{'perm_id'}) {
      $sql = "SELECT permission, value FROM Permissions WHERE id = $mail{'perm_id'}";
      unless($self->{dbh}->execute_rownum($sql)) {
          printToError("Error: makeMailUserNode: unable to get permissions for mail ID '$mailId'");
      }

      while ($ptrRow = $self->{dbh}->fetchrow()) {
          my ($name, $value) = @{$ptrRow};
          next unless exists $mailUserPermissions{$name};
          addMailuserPermissionNode($root, $mailUserPermissions{$name}, $value);
      }

      $self->{dbh}->finish();
  }
  else {
      addMailuserPermissionNode($root, 'cp-access', defined($mail{'cp_access'}) ? $mail{'cp_access'} : 'false');
  }

  # multiple login
  $self->addMailUserMultipleLoginPermission($root, $mailId);


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

    if (!PleskVersion::atLeast(6, 0, 0) and $mbox_quota ne "-1") {
      $mbox_quota = $mbox_quota * 1024;
    }

    $root->{'ATTRIBUTE'}->('mailbox-quota',$mbox_quota);
  }

  my $enable_mailbox = $mail{'postbox'} =~ /true/;

  # Check whether there autoresponder with attach
  # On 'Olde Pleskes' there was bug allowing attaches
  # when mailbox in turned off, so we have to explicitly
  # turn mailbox on if there is attach.
  $sql = "SELECT COUNT(ra.filename) FROM mail_resp AS mr, resp_attach as ra " .
    "WHERE ra.rn_id = mr.id AND mr.mn_id = $mailId";
  if ($self->{dbh}->execute_rownum($sql) and ($ptrRow = $self->{dbh}->fetchrow())
      and $ptrRow->[0] != 0) {
    $enable_mailbox = 1;
  }
  $self->{dbh}->finish();

  if ($enable_mailbox) {
    $item = XmlNode->new('mailbox', 'attributes' => {'type' => 'mdir'});
    $root->{'ADDCHILD'}->($item);

    $item->{'ATTRIBUTE'}->('enabled', $mail{'postbox'} =~ /true/ ? 'true' : 'false');

    $dir=AgentConfig::get('QMAIL_MAILNAMES_D')."/$domainAsciiName/$mailName/Maildir";

    my $cid = $self->addTar("$mailName\@$domainAsciiName.mdir", 'directory' => $dir);
    $item->{'ATTRIBUTE'}->('cid',$cid) if $cid;
  }

#
# aliases
#
  if (PleskVersion::atLeast(5, 0, 0)) {
    $sql="SELECT alias FROM mail_aliases WHERE mn_id=$mailId";
    if($self->{dbh}->execute_rownum($sql)){
      while ($ptrRow=  $self->{dbh}->fetchrow()){
        $item=XmlNode->new('alias', 'content' => $ptrRow->[0]);
        $root->{'ADDCHILD'}->($item);
      }
    }
    $self->{dbh}->finish();
  }
#
# end aliases
#

#
# redirect
#
  if ($mail{'redir_addr'}) {
    $item = XmlNode->new('redirect', 'content' => $mail{'redir_addr'}, 'attributes' => {'enabled' => $mail{'redirect'}});
#    $item->{'ATTRIBUTE'}->('enabled', $mail{'redirect'});
    $root->{'ADDCHILD'}->($item);
  }
#
# end redirect
#

#
# mail group
#
  $root->{'ATTRIBUTE'}->('mailgroup-enabled', $mail{'mail_group'});

  $sql="SELECT address FROM mail_redir WHERE mn_id=$mailId";
  if($self->{dbh}->execute_rownum($sql)){
    while ($ptrRow=  $self->{dbh}->fetchrow()){
      if($ptrRow->[0] ne $mailName . "@" . $domainName) {
        $item=XmlNode->new('mailgroup-member', 'content' => $ptrRow->[0]);
        $root->{'ADDCHILD'}->($item);
      }
    }
  }
  $self->{dbh}->finish();

#
# end mail group
#

#
# autoresponders
#
  my $autorespondersNode = XmlNode->new('autoresponders', 'attributes' => {'enabled' => $mail{'autoresponder'}});

  $dir=AgentConfig::get('QMAIL_MAILNAMES_D')."/$domainAsciiName/$mailName/\@attachments";
  my $cid = $self->addTar("$mailName\@$domainAsciiName.attach", "directory" => $dir);
  $autorespondersNode->{'ATTRIBUTE'}->('cid_attaches',$cid) if $cid;

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

  my %attaches;
  my @autoresponders;

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

    push @autoresponders, $item;
  }

  my $util = AgentConfig::get("PRODUCT_ROOT_D") . "/admin/sbin/";
  if (PleskVersion::atLeast(6, 5, 0)) {
     $util .= "mailmng --list-attachments --domain-name=$domainName --mailname=$mailName";
  } else {
     $util .= "list_files -1A '" . AgentConfig::get("QMAIL_MAILNAMES_D") . "/$domainAsciiName/$mailName/\@attachments/'";
  }

  my @filelist;
  open(LIST, "$util |");
  while (<LIST>) {
     chomp;
     my ($file) = split /\t/;
     push @filelist, $file;
  }
  #clear list if utility failed
  unless (close(LIST)) {
    @filelist = ();
  }

  # Old Plesk utilities printed return code on stdout (#66905)
  unless (PleskVersion::atLeast(7, 1, 0)) {
     if ($#filelist != -1 && $filelist[$#filelist] =~ /^\d$/) {
         $#filelist -= 1;
     }
  }
  unless($self->{configuration_dump}) {
    foreach my $file (@filelist) {
      $autorespondersNode->{'ADDCHILD'}->(XmlNode->new('attach', 'attributes' => {'file' => $file}));
    }
  }

  foreach my $autoresponder (@autoresponders) {
    $autorespondersNode->{'ADDCHILD'}->($autoresponder);
  }
  $root->{'ADDCHILD'}->($autorespondersNode);
#
# end autoresponders
#

#
# Addressbook
#
  if (PleskVersion::atLeast(6, 0, 0)) {
    $self->makeAddressbookNode($root, $mailName . '@' . $domainAsciiName);
  }

#
# Spamassassin
#
  if (PleskVersion::atLeast(7, 0, 0)) {

    if (PleskVersion::atLeast(8, 1, 0)) {
      $item = $self->makeSpamassassinNode81($root,$mail{'mail_name'}.'@'.$domainAsciiName, $mail{'spamfilter'});
    } else {
      $item = $self->makeSpamassassinNode70($root,$mail{'mail_name'}.'@'.$domainAsciiName);
    }

    if($item) {
      $dir = AgentConfig::get('QMAIL_MAILNAMES_D')."/$domainAsciiName/$mailName/.spamassassin";
      my $cid = $self->addTar("$mailName\@$domainAsciiName.sa", "directory" => $dir);
      $item->{'ATTRIBUTE'}->('cid', $cid) if $cid;
    }
  }
#
# End Spamassassin
#

  if (PleskVersion::atLeast(8, 1, 1)) {
	if ($mail{'virusfilter'} ne 'none') {
		my %states_map = ('incoming' => 'in', 'outgoing' => 'out', 'any' => 'inout');
                if( defined( $states_map{$mail{'virusfilter'}} ) ) {
			my $item = XmlNode->new('virusfilter');
			$item->{'ATTRIBUTE'}->('state', $states_map{$mail{'virusfilter'}} );
			$root->{'ADDCHILD'}->($item);
                }
	}
  }
  # FIXME: why so crude check here?
  elsif (-e "/etc/drweb/users.conf") {
      $item = $self->makeDrwebNode($root, $mailName.'@'.$domainName);
  }

  if (PleskVersion::atLeast(6, 0, 0)) {
    $self->getMaxButtonLength($root, "mn_param", "mn_id=$mailId");
    $self->getSkin($root, "mn_param", "mn_id=$mailId");
    $self->getLockScreen($root, "mn_param", "mn_id=$mailId");
  }

  $root->{'ADDCHILD'}->($_) foreach $self->getCustomButtonsByOwner('mailuser', $mailId);

  return $root;
}

#
# makeAutoresponderNode
#
#  arguments:
#             $id - ID of autoreponder
#
#  return:
#             $root - XML node
#
sub makeAutoresponderNode {
  my($self, $autoId, $mailName)=@_;

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

  $sql = "SELECT * FROM mail_resp WHERE id=$autoId";
  unless ($self->{dbh}->execute_rownum($sql)) {
    &printToError("Error: makeAutoresponderNode: autoresponder # $autoId is not found");
    return undef;
  }

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

  my $root = XmlNode->new('autoresponder');

  $value = $self->{base64}->{'ENCODE'}->($auto{'text'});

  $item=XmlNode->new('text','content' => $value);
  if (exists $auto{'charset'} && $auto{'charset'} ne '') {
    $item->{'ATTRIBUTE'}->('charset', $auto{'charset'});
  }
  $root->{'ADDCHILD'}->($item);

  if (exists $auto{'content_type'} && $auto{'content_type'}) {
    $root->{'ATTRIBUTE'}->('content-type', $auto{'content_type'});
  }

  $name = $auto{'resp_name'};
  $root->{'ATTRIBUTE'}->('name',$name);

  if ($auto{'resp_on'}=~/false/){
    $root->{'ATTRIBUTE'}->('status','off');
  }

  unless ($auto{'key_where'}=~/no/){
    $value=$auto{'key_where'}.':'.$auto{'keystr'};
    $root->{'ATTRIBUTE'}->('require',$value);
  }

  if($auto{'subject'}){
    $value = $self->{base64}->{'ENCODE'}->($auto{'subject'});
    chomp $value;
    $root->{'ATTRIBUTE'}->('subject',$value);
  }

  if ($auto{'reply_to'} and $auto{'reply_to'} ne $mailName) {
    $root->{'ATTRIBUTE'}->('replyto',$auto{'reply_to'});
  }

#
# forward
#
  $sql="SELECT address FROM resp_forward WHERE rn_id=$autoId";
  if($self->{dbh}->execute_rownum($sql)){
    my (@list);
    while($ptrRow=$self->{dbh}->fetchrow()){
      # skip empty entries - found somewhere @ the wild Net
      push @list, $ptrRow->[0] if $ptrRow->[0];
    }
    if (@list) {
      $root->{'ATTRIBUTE'}->('redirect',join(',',@list));
    }
  }
  $self->{dbh}->finish();
#
# end forward
#

  $root->{'ADDCHILD'}->(XmlNode->new('autoresponder-limit', 'content' => $auto{'ans_freq'}, 'attributes' => {'name' => 'ans-freq'}));

  $root->{'ADDCHILD'}->(XmlNode->new('autoresponder-limit', 'content' => $auto{'mem_limit'}, 'attributes' => {'name' => 'ans-count'}));

  if (!$self->{configuration_dump}) {
    $sql = "SELECT filename FROM resp_attach WHERE rn_id=$autoId";
    if ($self->{dbh}->execute_rownum($sql)) {
      while ($ptrRow = $self->{dbh}->fetchrow()) {
        $root->{'ADDCHILD'}->(XmlNode->new('attach', 'attributes' => {'file' => $ptrRow->[0]}));
      }
    }
    $self->{dbh}->finish();
  }

  return $root;
}


sub makeAddressbookNode {
  my ($self, $parent, $mailName) = @_;

  my $sql = "SELECT Db FROM mysql.db WHERE db='horde'";
  return unless $self->{dbh}->execute_rownum($sql);
  $self->{dbh}->finish();

  $sql = "SHOW TABLES FROM horde LIKE 'turba_objects'";
  return unless $self->{dbh}->execute_rownum($sql);
  $self->{dbh}->finish();

  my $node = XmlNode->new('addressbook');

  my %objectFields = ('id'=>'id',
                      'name'=>'name',
                      'alias'=>'alias',
                      'email'=>'email',
                      'title'=>'title',
                      'company'=>'company',
                      'home-phone'=>'homephone',
                      'work-phone'=>'workphone',
                      'mobile-phone'=>'cellphone',
                      'fax'=>'fax',
                      'home-address'=>'homeaddress',
                      'work-address'=>'workaddress',
                      'notes'=>'notes');

  my $ptrHash;
  $sql = "SELECT * FROM horde.turba_objects WHERE owner_id = '$mailName'";
  $self->{dbh}->execute_rownum($sql);

  while ($ptrHash = $self->{dbh}->fetchhash()) {
    if ($ptrHash->{'object_type'} eq 'Group') {

      my $group = XmlNode->new('addressbook-group', 'attributes' => {'id' => $ptrHash->{'object_id'}});

      my $members = $ptrHash->{'object_members'};
      while ($members =~ /"(.*?)"/g) {
        my $member = $1;
        chomp $member;

        $group->{'ADDCHILD'}->(XmlNode->new('addressbook-member', 'attributes' => {'id' => $member}));
      }

      $node->{'ADDCHILD'}->($group);
    } else {
      my $user = XmlNode->new('addressbook-contact');
      my ($field, $attr);
      while (($field, $attr) = each %objectFields) {
        if (defined $ptrHash->{'object_' . $attr}) {
          # Here 'encoded' attribute means there is no meaning to
          # convert data to UTF as it was already converted manually
          my $value = $ptrHash->{'object_' . $attr};
          $value = $self->{base64}->{'ENCODE'}->($value) if $attr ne 'id';
          $user->{'ATTRIBUTE'}->($field, $value);
        }
      }
      $node->{'ADDCHILD'}->($user);
    }
  }
  $self->{dbh}->finish();

  $parent->{'ADDCHILD'}->($node);
}

sub makePhostingNode {
  my ($self, $ptrDomain, $ptrSiteApplications) = @_;

  my @SiteApplications = @{$ptrSiteApplications};

  unless(ref($ptrDomain)=~/HASH/){
    &printToError("Error: makePhostNode: bag arguments");
    return undef;
  }
  my $root=XmlNode->new('phosting');

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

    # shared http/https content
    if (PleskVersion::atLeast(7, 5, 3)) {
        my $sql = "SELECT same_ssl FROM hosting WHERE dom_id=$domainId AND same_ssl='true'";
        if ($self->{dbh}->execute_rownum($sql)) {
            $root->{'ATTRIBUTE'}->('shared-content', 'true');
        }
        $self->{dbh}->finish();
    }

    #-----------------------------------------------------------------
    # Site applications content should not be included into dump
    # together with other website content.
    #-----------------------------------------------------------------
    my @exclude_httpdocs_files;
    my @exclude_httpsdocs_files;
    my @exclude_cgi_bin_files;
    foreach my $sapp (@SiteApplications) {
      next if !$sapp->isDomainSiteapp();

      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
    $domainRoot = AgentConfig::get("HTTPD_VHOSTS_D") . "/$domainName";

    if (-d $domainRoot) {
      my $cid_docroot = $self->addTar("$domainName.httpdocs",
                                      "directory" => "$domainRoot/httpdocs",
                                      "exclude" => \@exclude_httpdocs_files);
      $root->{'ATTRIBUTE'}->('cid_docroot', $cid_docroot) if $cid_docroot;

      my $cid_docroot_ssl = $self->addTar("$domainName.httpsdocs",
                                          "directory" => "$domainRoot/httpsdocs",
                                          "exclude" => \@exclude_httpsdocs_files);
      $root->{'ATTRIBUTE'}->('cid_docroot_ssl', $cid_docroot_ssl) if $cid_docroot_ssl;

      my $cid_cgi = $self->addTar("$domainName.cgi-bin",
                                  "directory" => "$domainRoot/cgi-bin",
                                  "exclude" => \@exclude_cgi_bin_files);
      $root->{'ATTRIBUTE'}->('cid_cgi', $cid_cgi) if $cid_cgi;

      my $webstatRoot = (PleskVersion::atLeast(5,0,0)) ? $domainRoot."/statistics/webstat" : $domainRoot."/httpdocs/webstat";

      my $cid_webstat = $self->addTar("$domainName.webstat",
                                      "directory" => "$webstatRoot");
      $root->{'ATTRIBUTE'}->('cid_webstat', $cid_webstat) if $cid_webstat;

      my $cid_webstat_ssl = $self->addTar("$domainName.webstat-ssl",
                                          "directory" => "$domainRoot/statistics/webstat-ssl");
      $root->{'ATTRIBUTE'}->('cid_webstat_ssl', $cid_webstat_ssl) if $cid_webstat_ssl;

      my $cid_ftpstat = $self->addTar("$domainName.ftpstat",
                                      "directory" => "$domainRoot/statistics/ftpstat");
      $root->{'ATTRIBUTE'}->('cid_ftpstat', $cid_ftpstat) if $cid_ftpstat;

      my $cid_error_docs = $self->addTar("$domainName.error-docs",
                                      "directory" => "$domainRoot/error_docs");
      $root->{'ATTRIBUTE'}->('cid_error_docs', $cid_error_docs) if $cid_error_docs;

      my $cid_private = $self->addTar("$domainName.private",
                                      "directory" => "$domainRoot/private");
      $root->{'ATTRIBUTE'}->('cid_private', $cid_private) if $cid_private;

      my $cid_logs = $self->addTar("$domainName.logs",
                                    "directory" => "$domainRoot/statistics/logs") unless $self->{skip_logs};
      $root->setAttribute('cid_logs', $cid_logs) if $cid_logs;

      my $cid_anon_ftpstat = $self->addTar("$domainName.anon-ftpstat",
                                            "directory" => "$domainRoot/statistics/anon_ftpstat");
      $root->setAttribute('cid_anon_ftpstat', $cid_anon_ftpstat) if $cid_anon_ftpstat;
    }

  $sql = "SELECT * FROM hosting WHERE dom_id=$domainId";
  if($self->{dbh}->execute_rownum($sql)) {
    %hosting = %{$self->{dbh}->fetchhash()};
  }
  $self->{dbh}->finish();

  while (($xmlName,$fieldName) = each(%hostingAttribute)) {
    # MySQL allows 'true', 'false' and '' in enums
    if (defined($hosting{$fieldName}) and $hosting{$fieldName} eq "true") {
      $root->{'ATTRIBUTE'}->($xmlName, 'true');
    }
  }

  if (defined($hosting{'webstat'}) and $hosting{'webstat'}) {
      $root->{'ATTRIBUTE'}->('webstat', $hosting{'webstat'});
  }

  $sql = "SELECT val FROM dom_param WHERE dom_id='$domainId' "
    . "AND param='apacheErrorDocs'";
  if ($self->{dbh}->execute_rownum($sql)) {
    if ($ptrRow = $self->{dbh}->fetchrow()) {
      my $errdocs = (@{$ptrRow})[0];
      if (defined($errdocs) and $errdocs eq 'true') {
        $root->{'ATTRIBUTE'}->('errdocs', 'true');
      }
    }
  }
  $self->{dbh}->finish();

#
# sysuser
#
  Logging::info("Dumping system user");

  if (PleskVersion::atLeast(5, 0, 0)) {
    if($id = $hosting{'sys_user_id'}){
      $item= $self->makePleskSysUserNode($id);
      if(ref($item)=~/XmlNode/){
        $root->{'ADDCHILD'}->($item);
      }
    }
  } else {
    $item = $self->makeSyntheticSysUserNode($hosting{'login'}, $hosting{'passwd'}, 'plain');
    if(ref($item) =~ /XmlNode/) {
      $root->{'ADDCHILD'}->($item);
    }
  }
#
# end sysuser
#

#
# scripting
#
  $item = XmlNode->new('scripting');
  while(($xmlName,$fieldName)=each(%hostingScripting)){
    next if $fieldName=~/asp_dot_net/;
    if(defined($hosting{$fieldName}) and $hosting{$fieldName}=~/true/){
      $item->{'ATTRIBUTE'}->($xmlName,'true');
    }
  }
  $root->{'ADDCHILD'}->($item);
#
# end scripting
#

    #-----------------------------------------------------------------
    # Dump of installed site applications
    #-----------------------------------------------------------------
    foreach my $sapp (@SiteApplications) {
        if (!$sapp->isDomainSiteapp()) {
            next;
        }
        $root->{'ADDCHILD'}->($self->dumpSiteApplication($sapp, AgentConfig::get("HTTPD_VHOSTS_D") . "/$domainName"));
    }

#
# frontpage user
#
  if (exists $hosting{'fp_adm'} and $hosting{'fp_adm'}) {
    $item = XmlNode->new('fpuser', 'attributes' => {'name' => $hosting{'fp_adm'}});
    if($hosting{'fp_pass'}){
      my $passNode = &makePasswordNode($hosting{'fp_pass'},'plain');
      $item->{'ADDCHILD'}->($passNode);
    }
    $root->{'ADDCHILD'}->($item);
  } elsif (exists $hosting{'fp'} and $hosting{'fp'} eq 'true' and
            !$hosting{'fp_adm'} and !$hosting{'fp_pass'}) {

    # Workaround for older Plesks: during upgrade 1.3 -> 2.0 (or something)
    # frontpage administrator records were lost
    $item = XmlNode->new('fpuser');
    if (PleskVersion::atLeast(5, 0, 0)) {
        my %sysuser = $self->getSysUserInfo($hosting{'sys_user_id'});
        $item->{'ATTRIBUTE'}->('name', lc($sysuser{'login'}));
        if (PleskVersion::atLeast(6, 0, 0)) {
           $item->{'ADDCHILD'}->($self->makeAccountPasswordNode($sysuser{'account_id'}));
        } else {
           $item->{'ADDCHILD'}->(makePasswordNode($sysuser{'passwd'}, 'plain'));
        }
    } else {
        $item->{'ATTRIBUTE'}->('name', $hosting{'login'});
        $item->{'ADDCHILD'}->(makePasswordNode($hosting{'passwd'}, 'plain'));
    }
    $root->{'ADDCHILD'}->($item);
  }
#
# end frontpage user
#

  $self->makeDomainLogrotationNode($root, $domainId);
  $self->addAnonFtp($root,$domainId,$domainName);

#
# protected dirs
#
  if (PleskVersion::atLeast(8, 0, 0)) {
    $sql = "SELECT id,path,realm,non_ssl,`ssl`,cgi_bin FROM protected_dirs WHERE dom_id=$domainId ORDER BY id";
  } elsif (PleskVersion::atLeast(6, 0, 0)) {
    $sql = "SELECT id,path,realm,non_ssl,`ssl` FROM protected_dirs WHERE dom_id=$domainId ORDER BY id";
  } else {
    $sql = "SELECT id,path,realm FROM protected_dirs WHERE dom_id=$domainId ORDER BY id";
  }

  if($self->{dbh}->execute_rownum($sql)){
    my (@dirs);
    while ($ptrRow = $self->{dbh}->fetchrow()){
      push @dirs,[@{$ptrRow}];
    }
    $self->{dbh}->finish();
    foreach $ptrRow (@dirs){
      unless (defined($hosting{'ssl'}) and $hosting{'ssl'}=~/true/){
        $ptrRow->[4] = 'false'; # bug 97159
      }
      $item = $self->makeProtDirNode(@{$ptrRow});
      if(ref($item)=~/XmlNode/){
        $root->{'ADDCHILD'}->($item);
      }
    }
  }
  $self->{dbh}->finish();
#
# end protected dirs
#

#
# web users
#
  $sql = "SELECT * FROM web_users WHERE dom_id=$domainId ORDER BY id";
  if($self->{dbh}->execute_rownum($sql)){
    my (@webs);
    while ($ptrHash = $self->{dbh}->fetchhash()){
      push @webs,{%{$ptrHash}};
    }
    $self->{dbh}->finish();
    foreach $ptrHash (@webs){
      $item = $self->makeWebUserNode($ptrHash,$domainName);
      if(ref($item)=~/XmlNode/){
        $root->{'ADDCHILD'}->($item);
      }
    }
  } else {
    $self->{dbh}->finish();
  }
#
# end web users
#

#
# subdomains
#
  if (PleskVersion::atLeast(7, 0, 0)) {
    $sql = "SELECT * FROM subdomains WHERE dom_id=$domainId ORDER BY id";

    my (@subdoms);
    if($self->{dbh}->execute_rownum($sql)){
      while ($ptrHash = $self->{dbh}->fetchhash()){
        push @subdoms,{%{$ptrHash}};
      }
    }
    $self->{dbh}->finish();
    foreach $ptrHash (@subdoms){
      $item = $self->dumpSubDomain($ptrHash, $domainName, \@SiteApplications);
      if(ref($item)=~/XmlNode/){
        $root->addChild($item);
      }
    }
  }
#
# end subdomains
#

  #
  # configuration files (conf/vhost[_ssl].conf)
  #
  $domainRoot = AgentConfig::get("HTTPD_VHOSTS_D").'/'.$domainName;
  my $confDir = $domainRoot . '/conf/';
  my $vhostConf = 'vhost.conf';
  my $vhostSslConf = 'vhost_ssl.conf';
  my @confFiles;
  push @confFiles, $vhostConf if (-f $confDir . $vhostConf);
  push @confFiles, $vhostSslConf if (-f $confDir . $vhostSslConf);
  if ($#confFiles != -1) {
    my $cid_conf = $self->addTar("$domainName.conf",
                                 "directory" => $confDir,
                                 "include" => \@confFiles);
    $root->{'ATTRIBUTE'}->('cid_conf', $cid_conf) if $cid_conf;
  }
  #
  # end configuration files (conf/vhost[_ssl].conf)
  #

  #
  # Webalizer configuration
  #
  if (PleskVersion::atLeast(7, 5, 3)) {
    my $node = XmlNode->new("webalizer");

    my $sql = "SELECT referrer FROM webalizer_hidden_referrer "
      . " WHERE dom_id=$domainId";
    if ($self->{dbh}->execute_rownum($sql)) {
      while ($ptrRow = $self->{dbh}->fetchrow()) {
        if ($ptrRow->[0] eq "Direct Request") {
          $node->{'ATTRIBUTE'}->('hide-direct-referrer', 'true');
        } else {
          $node->{'ADDCHILD'}->(XmlNode->new("webalizer-hidden-referrer", 'content' => $ptrRow->[0]));
        }
      }
    }
    $self->{dbh}->finish();

    $sql = "SELECT referrer, group_name FROM webalizer_group_referrer "
      . " WHERE dom_id=$domainId";
    if ($self->{dbh}->execute_rownum($sql)) {
      while ($ptrRow = $self->{dbh}->fetchrow()) {
        $node->{'ADDCHILD'}->(XmlNode->new('webalizer-group-referrer', 'content' => $ptrRow->[0], 'attributes' => { 'group-name' => $ptrRow->[1] }));
      }
    }
    $self->{dbh}->finish();

    $root->{'ADDCHILD'}->($node);
  }

  #
  # SiteBuilder connfiguration
  #

    $self->makeDomainSiteBuilderNode($root, $domainId) if PleskVersion::atLeast(8, 3, 0);

  #
  # Perfomance
  #

  if ((defined($hosting{'max_connection'}) and $hosting{'max_connection'}) or
      (defined($hosting{'traffic_bandwidth'}) and $hosting{'traffic_bandwidth'})) {

      my $perfomance = XmlNode->new('perfomance');
      $perfomance->addChild(XmlNode->new('max-connections', 'content' => $hosting{'max_connection'}));
      $perfomance->addChild(XmlNode->new('traffic-bandwidth', 'content' => $hosting{'traffic_bandwidth'}));

      $root->addChild($perfomance);
  }


  # Cron


  return $root;
}

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

    my $sql = "SELECT sb_site_id, sb_siteowner_id, sb_siteowner_login FROM SBSites WHERE virtualHost_id = '$domainId'";

    if ($self->{dbh}->execute_rownum($sql)) {
      my $sbNode = XmlNode->new('sb-domain');
      while (my $ptrHash = $self->{dbh}->fetchhash()) {
      	$sbNode->addChild(XmlNode->new('sb-site-id', 'content' => $ptrHash->{'sb_site_id'}));
	$sbNode->addChild(XmlNode->new('sb-siteowner-id', 'content' => $ptrHash->{'sb_siteowner_id'}));
	$sbNode->addChild(XmlNode->new('sb-siteowner-login', 'content' => $ptrHash->{'sb_siteowner_login'}));
      }
      $root->addChild($sbNode);
    }
    $self->{dbh}->finish();
}

#-----------------------------------------------------------------
#    Returns XmlNode of sapp-installed type or false
#-----------------------------------------------------------------
sub dumpSiteApplication( $ $ )
{
    my ($self, $sapp, $vhost_path) = @_;

    my $sapp_installed = XmlNode->new('sapp-installed');

    #-----------------------------------------------------------------
    # sapp-spec
    #-----------------------------------------------------------------
    my $sapp_spec = XmlNode->new('sapp-spec');
    $sapp_spec->{'ADDCHILD'}->(XmlNode->new('sapp-name', 'content' => $sapp->getName()));
    if ($sapp->getVersion()) {
        $sapp_spec->{'ADDCHILD'}->(XmlNode->new('sapp-version', 'content' => $sapp->getVersion()));
    }
    if ($sapp->getRelease()) {
        $sapp_spec->{'ADDCHILD'}->(XmlNode->new('sapp-release', 'content' => $sapp->getRelease()));
    }
    if ($sapp->getDescription()) {
      my $desc = $self->{base64}->{'ENCODE'}->($sapp->getDescription());
        $sapp_spec->{'ADDCHILD'}->(XmlNode->new('sapp-description', 'attributes' => {'encoding' => 'base64'}, 'content' => $desc));
    }
    if ($sapp->isCommercial()) {
        $sapp_spec->{'ADDCHILD'}->(XmlNode->new('sapp-commercial'));
    }
    if ($sapp->isIntegrated()) {
        $sapp_spec->{'ADDCHILD'}->(XmlNode->new('sapp-integrated'));
    }

    $sapp_installed->{'ADDCHILD'}->($sapp_spec);

    #-----------------------------------------------------------------
    #sapp-param*
    #-----------------------------------------------------------------
    while (my ($param_name, $param_value) = each %{$sapp->getParams()}) {
      my $name  = $self->{base64}->{'ENCODE'}->($param_name);
      my $value = $self->{base64}->{'ENCODE'}->($param_value);

        $sapp_installed->{'ADDCHILD'}->(XmlNode->new('sapp-param',
                'children' => [XmlNode->new('sapp-param-name',  'attributes' => {'encoding' => 'base64'}, 'content' => $name),
                               XmlNode->new('sapp-param-value', 'attributes' => {'encoding' => 'base64'}, 'content' => $value)]));
    }

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

    # Databases
    foreach my $row ($sapp->getDatabases()) {
      my $database = $self->makeDatabaseNode($row->{'id'});
      if (ref($database) =~ /XmlNode/) {
        $database->{'ATTRIBUTE'}->('sapp-param', $row->{'param'}) if defined $row->{'param'};
        $sapp_installed->{'ADDCHILD'}->($database);
      }
    }

    # Custom buttons
    foreach my $row ($sapp->getCustomButtons()) {
        my $cb = $self->getCustomButtonById71($row->{'id'});
        if (ref($cb) =~ /XmlNode/) {
            $sapp_installed->{'ADDCHILD'}->($cb);
            push @{$self->{skip_custom_buttons}}, $row->{'id'};
        }
    }

    #-----------------------------------------------------------------
    # Site application content
    #-----------------------------------------------------------------
    my $prefix = $sapp->getInstallPrefix();

    # sapp-scripts
    {
      my $vault_dir = "$vhost_path/vault_scripts/$prefix";
      my $dump_id = generateDumpId();

      my $cid = $self->addTar("sapp-scripts." . $dump_id, "directory" => $vault_dir);
      $sapp_installed->{'ADDCHILD'}->(XmlNode->new('sapp-scripts', 'content' => $cid)) if $cid;

      $vault_dir = "$vhost_path/vault_https_scripts/$prefix";
      $cid = $self->addTar("sapp-https-scripts." . $dump_id, "directory" => $vault_dir);
      $sapp_installed->{'ADDCHILD'}->(XmlNode->new('sapp-https-scripts', 'content' => $cid)) if $cid;
    }

    # sapp-installdir
    my $sapp_installdir = XmlNode->new('sapp-installdir',
                                  'children' => [XmlNode->new('sapp-prefix', 'content' => $prefix)]);
    if ($sapp->isSsl()) {
        $sapp_installdir->{'ADDCHILD'}->(XmlNode->new('sapp-ssl'));
    }
    $sapp_installed->{'ADDCHILD'}->($sapp_installdir);

    # cgi-bin
    {
      my $cgibin_dir = "$vhost_path/cgi-bin/" . $prefix;
      my $cid = $self->addTar("sapp-cgi-bin." . generateDumpId(),
                              "directory" => $cgibin_dir,
                              "include" => $sapp->{cgi_bin},
                              "no_recursion" => 1);
      $sapp_installed->{'ADDCHILD'}->(XmlNode->new('sapp-cgi-bin', 'content' => $cid)) if $cid;
    }

    # htdocs
    my $htdocs_dir = $vhost_path . ($sapp->isSsl() ? "/httpsdocs/" : "/httpdocs/") . $prefix;

    # add '.htpasswd' files to dump, bug 93109
    my ($file, %files);
    @files{@{$sapp->{htdocs}}} = ();
    foreach $file (@{$sapp->{htdocs}}) {
        if ($file =~ /\.htaccess$/) {
            my $_file = $file;
            $_file =~ s/\.htaccess$/.htpasswd/;
               if (!exists $files{$_file} && -e "$htdocs_dir/$_file") {
                   push @{$sapp->{htdocs}}, $_file;
            }
        }
    }

    my $cid = $self->addTar("sapp-htdocs." . generateDumpId(),
                            "directory" => $htdocs_dir,
                            "include" => $sapp->{htdocs},
                            "no-recursion" => 1);
    $sapp_installed->{'ADDCHILD'}->(XmlNode->new('sapp-htdocs', 'content' => $cid)) if $cid;

    if (PleskVersion::atLeast(8, 3, 0)) {
        $sapp_installed->addChild(XmlNode->new('sapp-installed-license-type', 'content' => $sapp->getAPSClientItemLicenseType()));
    }

    return $sapp_installed;
}

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

  if (PleskVersion::atLeast(5, 0, 0)) {
    my $sql = "SELECT val FROM dom_param WHERE param = 'logrotation_id' "
      . "AND dom_id=$domainId";
    if ($self->{dbh}->execute_rownum($sql) and my $ptrRow = $self->{dbh}->fetchrow()) {
      my $logrotation_id = $ptrRow->[0];
      $self->{dbh}->finish();
      $self->makeLogrotationNode($parent, $logrotation_id);
    }
    $self->{dbh}->finish();
  }
}

sub makeLogrotationNode {
  my ($self, $parent, $logrotation_id) = @_;

  my $sql = "SELECT * FROM log_rotation WHERE id=$logrotation_id";

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

    if ($logRotation{'id'}) {
      my $item = XmlNode->new('logrotation', 'attributes' => { 'max-number-of-logfiles' => $logRotation{'max_number_of_logfiles'}, 'compress' => $logRotation{'compress_enable'}});

      $item->{'ATTRIBUTE'}->('email', $logRotation{'email'}) unless $logRotation{'email'} eq '';
      $item->{'ATTRIBUTE'}->('enabled', $logRotation{'turned_on'});

      my $rottype;
      if($logRotation{'period_type'} eq 'by_time') {
        $rottype = XmlNode->new('logrotation-period', 'attributes' => { 'period' => $logRotation{'period'}});
      } else {
        if (PleskVersion::atLeast(7, 0, 0)) {
          $rottype = XmlNode->new('logrotation-maxsize', 'content' => $logRotation{'period'});
        } else {
          $rottype = XmlNode->new('logrotation-maxsize', 'content' => $logRotation{'period'}*1024);
        }
      }
      $item->{'ADDCHILD'}->($rottype);
      $parent->{'ADDCHILD'}->($item);
    }
  }
  $self->{dbh}->finish();
}

#-----------------------------------------------------------------
#    $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, $ptrSubDomain, $domainName, $ptrSiteApplications) = @_;
    unless (ref($ptrSubDomain) =~ /HASH/) {
        printToError("Error: dumpSubDomain: bad argumets");
        return undef;
    }

    my $root = XmlNode->new('subdomain');

    my $subDomainName;
    if (PleskVersion::atLeast(7, 1, 0)) {
        $subDomainName = $ptrSubDomain->{'displayName'};
    } else {
        $subDomainName = $ptrSubDomain->{'name'};
    }
    $root->setAttribute('name', $subDomainName);

    # Subdomains may be SSL-enabled since 8.0
    if (PleskVersion::atLeast(8, 0, 0)) {
        if ('true' eq $ptrSubDomain->{'ssl'}) {
            $root->setAttribute('https', 'true');
        }
        if ('true' eq $ptrSubDomain->{'same_ssl'}) {
            $root->setAttribute('shared-content', 'true');
        }
    }

    #-----------------------------------------------------------------
    # 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 $subDomainRoot = AgentConfig::get("HTTPD_VHOSTS_D") . "/$domainName/subdomains/$subDomainName";
    if (-d $subDomainRoot) {
      my $cid_docroot = $self->addTar("$subDomainName.$domainName.httpdocs",
                                      "directory" => "$subDomainRoot/httpdocs",
                                      "exclude" => \@exclude_httpdocs_files);
      $root->setAttribute('cid_docroot', $cid_docroot) if $cid_docroot;

      my $cid_docroot_ssl = $self->addTar("$subDomainName.$domainName.httpsdocs",
                                          "directory" => "$subDomainRoot/httpsdocs",
                                          "exclude" => \@exclude_httpsdocs_files);
      $root->setAttribute('cid_docroot_ssl', $cid_docroot_ssl) if $cid_docroot_ssl;

      my $cid_cgi = $self->addTar("$subDomainName.$domainName.cgi-bin",
                                  "directory" => "$subDomainRoot/cgi-bin",
                                  "exclude" => \@exclude_cgi_bin_files);
      $root->setAttribute('cid_cgi', $cid_cgi) if $cid_cgi;

      my $cid_conf = $self->addTar("$subDomainName.$domainName.conf",
                                   "directory" => "$subDomainRoot/conf");
      $root->setAttribute('cid_conf', $cid_conf) if $cid_conf;
    }

    # sysuser
    if ($ptrSubDomain->{'sys_user_type'} =~ /native/) {
        my $item = $self->makePleskSysUserNode($ptrSubDomain->{'sys_user_id'});
        if (ref($item) =~ /XmlNode/) {
            $root->addChild($item);
        }
    }

    # scripting
    my $item = XmlNode->new('scripting');
    while (my ($xmlName, $fieldName) = each(%subDomainScripting)) {
        if ($ptrSubDomain->{$fieldName} =~ /true/) {
            $item->setAttribute($xmlName, 'true');
        }
    }
    $root->addChild($item);

    # Dump of installed site applications
    foreach my $sapp (@SiteApplications) {
        if (!$sapp->isSubdomainSiteapp($ptrSubDomain->{'id'})) {
            next;
        }
        $root->addChild($self->dumpSiteApplication($sapp, $subDomainRoot));
    }

    return $root;
}


#
# makeWebUserNode
#
#  argumenets:
#       $ptrWebUser - pointer to hash with web user's info (row of table 'web_users')
#       $domainName - name of domain
#
#  return:
#       $root = XML node
#
sub makeWebUserNode {
  my($self, $ptrWebUser, $domainName)=@_;
  unless(ref($ptrWebUser)=~/HASH/){
    &printToError("Error: makeWebUserNode: bad argumets");
    return undef;
  }
  my($root,$home,$fileName,$userName,$item,$xmlName,$fieldName);
  $root = XmlNode->new('webuser');

  if (PleskVersion::atLeast(5, 0, 0)) {
    $item = $self->makePleskSysUserNode($ptrWebUser->{'sys_user_id'});
    if(ref($item)=~/XmlNode/){
      $root->addChild($item);
      my %sysUserInfo = $self->getSysUserInfo($ptrWebUser->{'sys_user_id'});
      $userName = $sysUserInfo{'login'};
      $root->setAttribute('name', $userName);
    }
  } else {
    $userName = $ptrWebUser->{'username'};
    my @pwnam = getpwnam($userName);
    my $password = $pwnam[1];
    $item = $self->makeSyntheticSysUserNode($userName, $password, 'encrypted');
    $root->addChild($item);
    $root->setAttribute('name',$userName);
  }

  my $subDomainRoot=AgentConfig::get("HTTPD_VHOSTS_D")."/$domainName/web_users/$userName";
  my $cid = $self->addTar("$domainName\_$userName", "directory" => $subDomainRoot);
  $root->setAttribute('cid',$cid) if $cid;

  my $privateData = AgentConfig::get("HTTPD_VHOSTS_D")."/$domainName/private/$userName";
  my $cid_private = $self->addTar("$domainName\_$userName.private",
    "directory" => $privateData);
  $root->setAttribute('cid_private', $cid_private) if $cid_private;

#
# scripting
#
  $item = XmlNode->new('scripting');
  while(($xmlName,$fieldName)=each(%webUserScripting)){
    if (defined $ptrWebUser->{$fieldName} and $ptrWebUser->{$fieldName}=~/true/) {
      $item->setAttribute($xmlName,'true');
    }
  }
  $root->addChild($item);
#
# end scripting
#

  return $root;
}

#
# makeProtDirNode - make protected directory node
#
# arguments:
#        $pdirId - ID
#        $pdirPath - path of protected directory
#        $pdirTitle - header text
#        $pdirNonSSL - whether is directory in http
#        $pdirSSL - or in https
#
# return:
#        $root - XML node
#
sub makeProtDirNode {
  my($self, $pdirId, $pdirPath, $pdirTitle, $pdirNonSSL, $pdirSSL, $pdirCGI) = @_;

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

  $root = XmlNode->new('pdir');
  # workaround of CLI inabliity to create '' directory.
  if ($pdirPath eq '') {
    $pdirPath = '/';
  }

  $root->{'ATTRIBUTE'}->('name', $pdirPath);
  if($pdirTitle) {
    $root->{'ATTRIBUTE'}->('title', $pdirTitle);
  }
  if ($pdirNonSSL) {
    $root->{'ATTRIBUTE'}->('nonssl', $pdirNonSSL);
  }
  if ($pdirSSL) {
    $root->{'ATTRIBUTE'}->('ssl', $pdirSSL);
  }
  if ($pdirCGI) {
    $root->{'ATTRIBUTE'}->('cgi', $pdirCGI);
  }

  if (PleskVersion::atLeast(7, 1, 0)) {
    $sql = "SELECT p.login, a.password, a.type FROM pd_users p ".
      " LEFT JOIN accounts a ON p.account_id = a.id ".
      " WHERE p.pd_id=$pdirId ORDER BY p.id";
  } else {
    $sql = "SELECT p.login, p.passwd, 'plain' FROM pd_users p ".
      " WHERE p.pd_id=$pdirId ORDER BY p.id"
  }

  if ($self->{dbh}->execute_rownum($sql)) {
    while ($ptrRow=$self->{dbh}->fetchrow()) {
      $userNode = XmlNode->new('pduser');
      $root->{'ADDCHILD'}->($userNode);
      $userNode->{'ATTRIBUTE'}->('name', $ptrRow->[0]);
      $item = makePasswordNode($ptrRow->[1],
                               normalizePasswordType($ptrRow->[2]));
      $userNode->{'ADDCHILD'}->($item);
    }
  }
  $self->{dbh}->finish();
  return $root;
}

sub addAnonFtp {
  my($self, $root, $domainId, $domainName) = @_;
  unless (ref($root) =~ /XmlNode/) {
    printToError("Error: addAnonFtp: bad argumets");
    return;
  }

  my($anonRoot,$ptrHash,$count,$domainRoot,$sql);
  $count=0;
  $sql="SELECT * FROM anon_ftp WHERE dom_id=$domainId";
  if($self->{dbh}->execute_rownum($sql)){
    while($ptrHash=$self->{dbh}->fetchhash()){

      $anonRoot = XmlNode->new('anonftp');
      $root->{'ADDCHILD'}->($anonRoot);
      if($ptrHash->{'status'}=~/true/){
        $anonRoot->{'ATTRIBUTE'}->('pub','true');
      }
      if($ptrHash->{'incoming'}=~/true/){
        $anonRoot->{'ATTRIBUTE'}->('incoming','true');
      }
      if (defined($ptrHash->{'max_conn'})) {
        makeAnonftpLimitNode($anonRoot,'max-connections', $ptrHash->{'max_conn'});
      }
      if (defined($ptrHash->{'bandwidth'})) {
        makeAnonftpLimitNode($anonRoot, 'bandwidth', $ptrHash->{'bandwidth'});
      }
      if (defined($ptrHash->{'quota'})) {
        makeAnonftpLimitNode($anonRoot, 'incoming-disk-quota', $ptrHash->{'quota'});
      }
      if (defined($ptrHash->{'display_login'})) {
        $anonRoot->{'ATTRIBUTE'}->('display-login', $ptrHash->{'display_login'});
      }
      if ($ptrHash->{'incoming_readable'} =~ /true/) {
        makeAnonftpPermissionNode($anonRoot, 'incoming-download');
      }
      if ($ptrHash->{'incoming_subdirs'} =~ /true/) {
        makeAnonftpPermissionNode($anonRoot, 'incoming-mkdir');
      }
      if (defined($ptrHash->{'login_text'})) {
        my $loginMessageNode = XmlNode->new('login-message', 'content' => $ptrHash->{'login_text'});
        $anonRoot->{'ADDCHILD'}->($loginMessageNode);
      }
      $domainRoot = AgentConfig::get("HTTPD_VHOSTS_D") . '/' . $domainName;
      if (-d $domainRoot) {
        my $cid = $self->addTar("$domainName.anonftp.pub",
                                "directory" => "$domainRoot/anon_ftp/pub");
        $anonRoot->{'ATTRIBUTE'}->('cid',$cid) if $cid;

        my $cid_incoming = $self->addTar("$domainName.anonftp.incoming",
                                         "directory" => "$domainRoot/anon_ftp/incoming");
        $anonRoot->{'ATTRIBUTE'}->('cid_incoming',$cid_incoming) if $cid_incoming;
      }
      $count++;
    }
  }
  $self->{dbh}->finish();
  return $count;
}

sub getSysUserInfo {
  my ($self, $sysUserId) = @_;

  my $sql = "SELECT * FROM sys_users WHERE id=$sysUserId";
  unless ($self->{dbh}->execute_rownum($sql)) {
    printToError("Error: makeSysUser: sys user #$sysUserId is not found");
    return;
  }
  my %sysuser = %{$self->{dbh}->fetchhash()};
  $self->{dbh}->finish();

  return %sysuser;
}

sub makePleskSysUserNode {
  my ($self, $sysUserId) = @_;
  my %sysuser = $self->getSysUserInfo($sysUserId);

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

#
# attributes
#

  my $root = XmlNode->new('sysuser', 'attributes' => {'name' => lc($sysuser{'login'})});
  $root->{'ATTRIBUTE'}->('shell',$sysuser{'shell'}) if $sysuser{'shell'};

  my $quota;
  if($sysuser{'quota'}){
    if (PleskVersion::atLeast(7, 0, 0)) {
      $quota = $sysuser{'quota'};
    } else {
      if ($sysuser{'quota'} ne "-1") {
        $quota = $sysuser{'quota'}*1024*1024;
      }
    }
    $root->{'ATTRIBUTE'}->('quota',$quota) if $quota;
  }

#
# end attributes
#

#
# password node
#
  if (PleskVersion::atLeast(6, 0, 0)) {
    if ($sysuser{'account_id'} != 0) {
      $root->{'ADDCHILD'}->($self->makeAccountPasswordNode($sysuser{'account_id'}));
    }
  } else {
    $root->{'ADDCHILD'}->(makePasswordNode($sysuser{'passwd'}, 'plain'));
  }
#
# end password node
#

#
# crontab node
#
  my $crontabmng = AgentConfig::get("PRODUCT_ROOT_D") . "/admin/sbin/crontabmng";
  if (-x $crontabmng) {
    open (CRONTAB, "$crontabmng get $sysuser{'login'} |");
    my $crontab = "";
    while (<CRONTAB>) {
      last if $_ eq "0\n";
      $crontab .= $_;
    }
    close (CRONTAB);
    if ($crontab ne "\n") {
      $root->{'ADDCHILD'}->(XmlNode->new('cron', 'content' => $crontab));
    }
  }
#
# end crontab node
#
  return $root;

}

sub makeSyntheticSysUserNode {
  my ($self, $name, $password, $passtype) = @_;

  my $root = XmlNode->new('sysuser', 'attributes' => { 'name' => lc($name)});

  my $passwdNode = &makePasswordNode($password, $passtype);
  $root->{'ADDCHILD'}->($passwdNode);
  return $root;
}

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

  unless(ref($ptrDomain)=~/HASH/){
    &printToError("Error: makeFhostingNode: bag arguments");
    return undef;
  }
  my($sql,$domainId,$root,$forward);
  $domainId=$ptrDomain->{'id'};
  $sql="SELECT redirect FROM forwarding WHERE dom_id=$domainId";
  if($self->{dbh}->execute_rownum($sql)){
    ($forward)=@{$self->{dbh}->fetchrow()};
    $root=XmlNode->new('fhosting','content' => $forward);
  }else{
    &printToError("Error: makeFhostingNode: forward for domain '".$ptrDomain->{'name'}."' is not found");
  }
  $self->{dbh}->finish();

  return $root;
}

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

  unless(ref($ptrDomain)=~/HASH/){
    &printToError("Error: makeShostingNode: bag arguments");
    return undef;
  }

  my($sql,$domainId,$root,$forward);
  $domainId=$ptrDomain->{'id'};
  $sql="SELECT redirect FROM forwarding WHERE dom_id=$domainId";
  if($self->{dbh}->execute_rownum($sql)){
    ($forward)=@{$self->{dbh}->fetchrow()};
    $root=XmlNode->new('shosting','content' => $forward);
  }else{
    &printToError("Error: makeShostingNode: forward for domain '".$ptrDomain->{'name'}."' is not found");
  }
  $self->{dbh}->finish();

  return $root;
}

# sub getClientIps {
#   my ($clientId) = @_;

#   if (PleskVersion::atLeast(6, 0, 0)) {
#   } elsif (PleskVersion::atLeast(5, 0, 0)) {
#     my $sql = "SELECT d.name, d.id, h.ip_address, a.status, d.cl_id FROM domains d "
#       . "LEFT JOIN hosting h ON h.dom_id = d.id "
#       . "LEFT JOIN anon_ftp a ON a.dom_id = d.id"
#       . "WHERE cl_id = $clientId";
#   } else {
#   }
# }

#
# Returns password node for given account ID
#
sub makeAccountPasswordNode
{
  my ($self, $accountId) = @_;

    my $node;

    if ($self->{dbh}->execute_rownum("SELECT password, type FROM accounts WHERE id='$accountId'")) {
        my ($passwd, $type) = @{$self->{dbh}->fetchrow()};
        $node = &makePasswordNode($passwd,
                                  normalizePasswordType($type));
    } else {
        # generates a stub node
        $node = &makePasswordNode();
    }
    $self->{dbh}->finish();

    return $node;
}

sub makeSpamassassinNode81 {
  my ($self, $root, $mailname, $status) = @_;

  my $sql = "SELECT id, preferences, reject_spam FROM spamfilter WHERE username = '$mailname'";
  unless($self->{dbh}->execute_rownum($sql)) {
    Logging::warning("Error during getting information about spam filter for $mailname");
    $self->{dbh}->finish();
    return;
  }

  my $row = $self->{dbh}->fetchrow();
  my $filter_id = $row->[0];
  my $reject_spam = $row->[2];
  my $server = 0;

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

    if ($server) {
       $sql = "SELECT id FROM spamfilter WHERE username = '*@*'";
       $self->{dbh}->execute_rownum($sql);
       $row = $self->{dbh}->fetchrow();
       $filter_id = $row->[0];
       $self->{dbh}->finish();
    }

    $sql = "SELECT preference, value FROM spamfilter_preferences WHERE spamfilter_id = '$filter_id' ORDER BY length(preference), preference";
  }
  while (!$self->{dbh}->execute_rownum($sql) && !$server++);

  my $item = XmlNode->new('spamassassin', 'attributes' => {
  								'status' => $status eq 'true' ? 'on' : 'off',
  								'action' => $reject_spam eq 'true' ? 'del' : 'mark'
  							});

  while($row = $self->{dbh}->fetchrow()) {
    $item->addChild(XmlNode->new('blacklist-member', 'content' => $row->[1])) if $row->[0] eq "blacklist_from";
    $item->addChild(XmlNode->new('whitelist-member', 'content' => $row->[1])) if $row->[0] eq "whitelist_from";
    $item->addChild(XmlNode->new('unblacklist-member', 'content' => $row->[1])) if $row->[0] eq "unblacklist_from";
    $item->addChild(XmlNode->new('unwhitelist-member', 'content' => $row->[1])) if $row->[0] eq "unwhitelist_from";
    $item->setAttribute('hits', $row->[1]) if ($row->[0] eq "required_score");

    if ($row->[0] eq "rewrite_header") {
       $row->[1] =~ s/^subject //;
	   $item->setAttribute('subj-text', $row->[1]);
    }
  }
  $self->{dbh}->finish();

  $root->addChild($item);

  return $item;
}

sub makeSpamassassinNode70 {
  my ($self, $root, $mailName) = @_;

  my  $sql = "SELECT count(*) FROM sa_conf WHERE mailname='$mailName'";
  unless($self->{dbh}->execute_rownum($sql)){
  &printToError("Error during getting information about spam filter for $mailName ");

  return undef;

  }
  my @row = @{$self->{dbh}->fetchrow()};
  $self->{dbh}->finish();
  if ($row[0]==0) {
      return;
  }

  $sql = "SELECT * FROM sa_conf WHERE mailname='$mailName'";
  unless ($self->{dbh}->execute_rownum($sql)) {
    printToError("Error during getting information about spam filter for $mailName");
    return;
  }
  my %spam = %{$self->{dbh}->fetchhash()};
  $self->{dbh}->finish();

  my $item;
  if ($spam{'flt_enabled'} eq 'both') {
    $item = XmlNode->new('spamassassin', 'attributes' => { 'status' => 'on', 'server-conf' => 'true'});
  } elsif ($spam{'flt_enabled'} eq 'user'){
    $item = XmlNode->new('spamassassin', 'attributes' => { 'status' => 'on', 'server-conf' => 'false'});
  } else {
    #skip creation
    return ;
  }
  $item->{'ATTRIBUTE'}->('hits', $spam{'hits_required'});
  if ($spam{'spam_action'} eq 'save') {
    $item->{'ATTRIBUTE'}->('action', 'mark');
  }else{
    $item->{'ATTRIBUTE'}->('action', 'del');
  }

  if ($spam{'rw_subject_tag'}) {
    $item->{'ATTRIBUTE'}->('modify-subj', 'true');
    $item->{'ATTRIBUTE'}->('subj-text', $spam{'rw_subject_tag'});
  } else {
    $item->{'ATTRIBUTE'}->('modify-subj', 'false');
  }

  $self->addSpamassassinPatterns($item, $spam{'mailname'}, 'black');
  $self->addSpamassassinPatterns($item, $spam{'mailname'}, 'white');

  $root->{'ADDCHILD'}->($item);
  return $item;
}

sub addSpamassassinPatterns {
  my ($self, $root, $mailname, $color) = @_;

  my $sql = "SELECT pattern FROM sa_list WHERE mailname='$mailname' AND color = '$color'";
  if (!$self->{dbh}->execute($sql)) {
    printToError("Error during getting information about ${color}list patterns for $mailname");
    return;
  }
  my $row;
  while ($row = $self->{dbh}->fetchrow()) {
    my ($pattern) = @{$row};
    $root->{'ADDCHILD'}->(XmlNode->new("${color}list-member", 'content' => $pattern));
  }
  $self->{dbh}->finish();
}

sub makeDrwebNode {
  my ($self, $root, $mailname) = @_;
  my $drwebFile = "/etc/drweb/users.conf";
  unless (open(DRWEBFILE, "<$drwebFile")) {
    printToLog("Error: unable to open $drwebFile");
    return;
  }
  while (<DRWEBFILE>) {
    chomp;
    next if /^#/; #skip comments
    next if /^\[/;
    next if /^\s*$/;
    my @parts = split /\s+/, $_, 4;
    if ($parts[3] =~ /$mailname/) {
      my $item = XmlNode->new('drweb');
      if ($parts[1] =~ /to/) {
        $item->{'ATTRIBUTE'}->('state', 'in');
      } elsif ($parts[1] =~ /from/) {
        $item->{'ATTRIBUTE'}->('state', 'out');
      } else {
        $item->{'ATTRIBUTE'}->('state', 'inout');
      }
      $root->{'ADDCHILD'}->($item);
      last;
    }
  }
  close(DRWEBFILE);
}

sub makeWebAppNode {
  my ($self, $root, $webAppName, $webAppStatus, $dom_name) = @_;

  # webapp is useless without cid
  return if $self->{configuration_dump};

  $webAppStatus ||= 0;

  my $item = XmlNode->new('webapp',
                     'attributes' => {'name' => $webAppName},
                     'children' => [Status::make($webAppStatus)]);

  my $warDir = AgentConfig::get("CATALINA_HOME") . "/psa-wars/$dom_name";
  my $warFile = "$webAppName.war";

  my $cid = $self->addTar("$dom_name.$webAppName.webapp",
                          "directory" => $warDir,
                          "include" => [$warFile]);
  $item->{'ATTRIBUTE'}->('cid',$cid) if $cid;

  $root->{'ADDCHILD'}->($item);
}

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

	# several times there was empty button place in database, which is incorrect
    my $sql = "SELECT sort_key, place, text, url, conhelp, options, file FROM custom_buttons WHERE id=$id AND place!=''";
    if (!$self->{dbh}->execute_rownum($sql)) {
        $self->{dbh}->finish();
        return;
    }

    my $ptrHash = $self->{dbh}->fetchhash();
    $self->{dbh}->finish();

    my $node = XmlNode->new('custom-button');

    my %attributes = ( 'url' => 'url',
                       'text' => 'text',
                       'sort_key' => 'sort-priority',
                       'place' => 'interface-place',
                       'conhelp' => 'conhelp' );

    while (my ($dbfield, $nodefield) = each %attributes) {
        my $val = $ptrHash->{$dbfield};
        $val = 'button' if ($dbfield eq 'text' && $val eq '');
        $val = '/' if ($dbfield eq 'url' && $val eq '');
        $node->setAttribute($nodefield, $val) if $val ne '';
    }

    my $customButtonsDir = AgentConfig::get("PRODUCT_ROOT_D") . '/admin/htdocs/images/custom_buttons';

    my $icon = $ptrHash->{'file'};
    if (-e $customButtonsDir."/".$icon) {
      if ($icon) {

        my $cid_icon = (AgentConfig::tarVersionMoreThan15()) ?
            $self->addTar($icon.".".$id,
                            "directory" => $customButtonsDir,
                            "add_file" => $icon)
            :$self->addTar($icon.".".$id,
                            "directory" => $customButtonsDir,
                            "include" => [$icon]);

        $node->setAttribute('cid-icon', $cid_icon) if $cid_icon;
      }
    }
    parseCustomButtonOptions($node, $ptrHash->{'options'});

    return $node;
}

sub getCustomButtonsByOwner71 {
  my ($self, $owner_type, $owner_id) = @_;

    my $level = $custom_button_owner_types{$owner_type};
    my $sql = "SELECT id FROM custom_buttons WHERE level='$level' AND level_id='$owner_id'";
    if (!$self->{dbh}->execute_rownum($sql)) {
        $self->{dbh}->finish();
        return;
    }

    my @ids;
    while (my $ptrRow = $self->{dbh}->fetchrow()) {
        my $id = $ptrRow->[0];
        if (grep /^$id$/, @{$self->{skip_custom_buttons}}) {
            next;
        }
        push @ids, $id;
    }
    $self->{dbh}->finish();

  return map { $self->getCustomButtonById71($_) } @ids;
}

sub getCustomButtonsByOwner65 {
  my ($self, $owner_type, $owner_id) = @_;

  return if ($owner_type ne "client");

  my $sql = "SELECT text, url, conhelp, options FROM custom_buttons WHERE " .
    "cl_id=$owner_id";

  my @nodes;

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

    my $ptrHash;
    while ($ptrHash = $self->{dbh}->fetchhash()) {

      my $node = XmlNode->new('custom-button', 'attributes' => {'sort-priority' => '100', 'interface-place' => 'domain'});
      my %attributes =
        (
         'url' => 'url',
         'text' => 'text',
         'conhelp' => 'conhelp'
        );

      while (my ($dbfield, $nodefield) = each %attributes) {
        $node->{'ATTRIBUTE'}->($nodefield, $ptrHash->{$dbfield});
      }

      ## 128 == CUSTOM_BUTTON_PUBLIC
      parseCustomButtonOptions($node, $ptrHash->{'options'} | 128);

      push @nodes, $node;
    }
  }
  $self->{dbh}->finish();
  return @nodes;
}

sub getCustomButtonsByOwner {
  my ($self, @args) = @_;
  return $self->getCustomButtonsByOwner71(@args) if (PleskVersion::atLeast(7, 1, 0));
  return $self->getCustomButtonsByOwner65(@args) if (PleskVersion::atLeast(6, 5, 0));
  return ();
}

sub makeDefaultIpNode {
  my ($self, $parent) = @_;
  my $sql = "SELECT ip_address FROM IP_Addresses ip, misc m WHERE ip.id = m.val AND m.param = 'def_ip_id'";
  $self->{dbh}->execute_rownum($sql);
  my $ptrHash = $self->{dbh}->fetchhash();
  $parent->{'ADDCHILD'}->(XmlNode->new('default-ip', 'content' => $ptrHash->{'ip_address'}));
  $self->{dbh}->finish();
}

sub makeSystemIpNode {
  my ($self, $parent, $ptrIp) = @_;
  my $correctMask = $self->blockToNum($ptrIp->{'mask'});
  $parent->{'ADDCHILD'}->(XmlNode->new('system-ip', 'attributes' => {'certificate' => $ptrIp->{'certificate'}}, 'children' => [XmlNode->new('ip', 'children' => [XmlNode->new('ip-type', 'content' => "$ptrIp->{'type'}"), XmlNode->new('ip-address', 'content' => $ptrIp->{'ip_address'})]), XmlNode->new('ip-netmask', 'content' => "$correctMask") , XmlNode->new('ip-interface', 'content' => "$ptrIp->{'iface'}")]));
}

sub makeAdminInfoNode {
  my ($self, $parent, $ptrAdmin) = @_;
  my ($name,$value);
  $parent->{'ADDCHILD'}->(XmlNode->new('admin-password', 'content' => AgentConfig::get{'password'}));
  while (($name, $value) = each(%{$ptrAdmin})) {
    #Attributes transformation. Bug 97408
    $name=~s/^admin_//g;
    next if $name=~/_u?id$/;
    next if $name=~/isPrefEdit$/;
    next if $name=~/news_dismissed$/;
    next if $name=~/ownership$/;
    $name = 'zip' if $name=~/pcode/;
    $name = 'company' if $name=~/cname/;
    $name = 'name' if $name=~/pname/;
    $parent->{'ADDCHILD'}->(XmlNode->new('pinfo', 'attributes' => {'name' => $name}, 'content' => $value));
  }
}

sub blockToNum {
  my ($self, $mask) = @_;
  my $longMask = unpack("N", pack("C4", split(/\./, $mask)));
  my $block;
  for ($block = 0; $block < 32; $block = $block + 1) {
    my $tmp = 2**(32-$block-1);
    last if !($longMask & $tmp);
  }
  return $block;
}

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

  my $serverNode = XmlNode->new('server');

  #Dump system and default ip
  my $sql = "SELECT ip_address, mask, iface, i.type, c.name certificate FROM IP_Addresses i INNER JOIN certificates c ON c.id = i.ssl_certificate_id";
  my %ips;
  $self->{dbh}->execute_rownum($sql);
  while (my $ptrHash = $self->{dbh}->fetchhash()){
    $self->makeSystemIpNode($serverNode, $ptrHash);
  }
  $self->{dbh}->finish();

  $self->makeDefaultIpNode($serverNode);

  #Dump hostname
  $sql = "SELECT val FROM misc WHERE param = 'FullHostName'";
  $self->{dbh}->execute_rownum($sql);
  my $ptrHash = $self->{dbh}->fetchhash();
  $serverNode->{'ADDCHILD'}->(XmlNode->new('hostname', 'content' => "$ptrHash->{'val'}"));
  $self->{dbh}->finish();

  #Dump admin info
  $sql = "SELECT param, val FROM misc WHERE param RLIKE '^admin'"; # do not use LIKE, bug #106566
  my %admin;
  $self->{dbh}->execute_rownum($sql);
  while (my $ptrHash = $self->{dbh}->fetchhash()){
    $admin{$ptrHash->{'param'}} = $ptrHash->{'val'};
  }
  $self->{dbh}->finish();
  $self->makeAdminInfoNode($serverNode, \%admin);

  # Dump database servers
  if(PleskVersion::atLeast(8,0,0)){
    $sql = "SELECT param, val FROM misc WHERE param RLIKE '^default_server_'";
    my %def;

    $self->{dbh}->execute_rownum($sql);
    while (my $ptrHash = $self->{dbh}->fetchhash()){
       $def{$ptrHash->{'param'}} = $ptrHash->{'val'};
    }
    $self->{dbh}->finish();

    $sql = "SELECT id, host, port, type, admin_login, admin_password FROM DatabaseServers";
    $self->{dbh}->execute_rownum($sql);

    while (my $ptrHash = $self->{dbh}->fetchhash()){
      my $dbServerNode = XmlNode->new('db-server');
      my $param = 'default_server_'.$ptrHash->{'type'};

      if (exists $def{$param} && $def{$param} == $ptrHash->{'id'}) {
        $dbServerNode->{'ATTRIBUTE'}->('default', 'true');
      }
      $dbServerNode->{'ATTRIBUTE'}->('type',$ptrHash->{'type'});

      $dbServerNode->{'ADDCHILD'}->(XmlNode->new('host', 'content' => "$ptrHash->{'host'}"));
      $dbServerNode->{'ADDCHILD'}->(XmlNode->new('port', 'content' => "$ptrHash->{'port'}"));

      if ($ptrHash->{'admin_login'} ne '') {
        my $adminNode = XmlNode->new('admin',"attributes" => {"name" => "$ptrHash->{'admin_login'}"});
        if ($ptrHash->{'type'} eq 'mysql' && $ptrHash->{'host'} eq 'localhost') {
          $ptrHash->{'admin_password'} = AgentConfig::get('password');
        }
        my $passwordNode = makePasswordNode($ptrHash->{'admin_password'},'plain'); # password type for dbservers is always plain
        $adminNode->{'ADDCHILD'}->($passwordNode);

        $dbServerNode->{'ADDCHILD'}->($adminNode);
      }

      $serverNode->{'ADDCHILD'}->($dbServerNode);
    }
    $self->{dbh}->finish();
  }

  if (!$self->{shallow_dump} and !$self->{configuration_dump}) {
	  # Dump key info
	  $sql = "SELECT * FROM key_history WHERE filename IS NOT NULL";
	  $self->{dbh}->execute_rownum($sql);

	  while (my $ptrHash = $self->{dbh}->fetchhash()){
    	next if ($ptrHash->{'filename'}=~/\~$/);
	    my @keyPath = split(/\//,$ptrHash->{'filename'});
    	my ($keyName,$keyDir,$additional);
	    if ($keyPath[3] eq 'key.d') {
	      $keyName = $keyPath[4];
    	  $keyDir = "/etc/psa/key.d/";
	      $additional = "true";
	    } else {
	      $keyName = $keyPath[3];
	      $keyDir = "/etc/psa/";
	      $additional = "false";
	    }

	    if (-e $keyDir."/".$keyName) {

	      my $keyNode = XmlNode->new('key', 'attributes' => {'additional' => $additional});

	      my $cid = $self->addTar("$ptrHash->{'plesk_key_id'}.key",
                              "directory" => $keyDir, "include" => [$keyName]);
	      $keyNode->{'ATTRIBUTE'}->('cid', $cid) if $cid;

    	  $serverNode->{'ADDCHILD'}->($keyNode);
	    }
	  }

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

  my %params;

  # TODO: implement server settings backups for earlier Plesks
  if (PleskVersion::atLeast(8, 1, 0)) {

	# Dump server settings

	my $ptrRow;

	$self->{dbh}->execute_rownum('SELECT param, val FROM misc');

	while($ptrRow = $self->{dbh}->fetchrow()) {
		$params{$ptrRow->[0]} = $ptrRow->[1];
	}
	$self->{dbh}->finish();

	my @unessential_params = ('spf_rules', 'spf-guess', 'spf-exp', 'rbl_server', 'spf_behavior',
			'disable_pop_auth', 'disable_smtp_auth', 'spf_enabled', 'use_vocabulary',
			'allow_short_pop3_names', 'dns_zone_status');

	foreach my $param (@unessential_params) {
		if (!exists $params{$param}) {
			$params{$param} = '';
		}
	}

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

	# Dump mail settings
	my $mailmng = AgentConfig::get('PRODUCT_ROOT_D') . '/admin/sbin/mailmng';
	my $letter_size = `$mailmng --get-max-letter-size`;
	chomp($letter_size);

	my $mailNode = XmlNode->new('mail-settings',
		'attributes' => {
				'max-letter-size' => $letter_size,
				'relay' => $params{'relay'},
				'use-vocabulary' => $params{'use_vocabulary'} eq 'true' ? 'true' : 'false',
				'short-pop3-names' => $params{'allow_short_pop3_names'} eq 'enabled' ? 'true' : 'false',
		                'message-submission' => $params{'message_submission'} eq 'true' ? 'true' : 'false',
                               'sign-outgoing-mail' => $params{'domain_keys_sign'} eq 'true' ? 'true' : 'false',
                               'verify-incoming-mail' => $params{'domain_keys_verify'} eq 'true' ? 'true' : 'false',
					});

	my $spfNode = XmlNode->new('spf', 'attributes' => {
					'status' => (exists $params{'spf_enabled'} and $params{'spf_enabled'} eq 'true') ? 'true' : 'false'});
	$spfNode->setAttribute('spf-behavior', ($params{'spf_behavior'} != '') ? $params{'spf_behavior'} : 1);

	$spfNode->{'ADDCHILD'}->(XmlNode->new('spf-rules', 'content' => $params{'spf_rules'}));
	$spfNode->{'ADDCHILD'}->(XmlNode->new('spf-guess', 'content' => $params{'spf_guess'}));
	$spfNode->{'ADDCHILD'}->(XmlNode->new('spf-exp', 'content' => $params{'spf_exp'}));

	$mailNode->{'ADDCHILD'}->($spfNode);

	my $rblNode = XmlNode->new('rbl', 'attributes' => {
				'status' => (exists $params{'rbl'} and $params{'rbl'} eq 'true') ? 'true' : 'false'});
	$rblNode->{'ADDCHILD'}->(XmlNode->new('rbl-server', 'content' => $params{'rbl_server'}));

	$mailNode->{'ADDCHILD'}->($rblNode);

	if ($params{'relay'} eq 'auth') {
		$mailNode->{'ATTRIBUTE'}->('pop-auth', $params{'disable_pop_auth'} ? 'false' : 'true');
		$mailNode->{'ATTRIBUTE'}->('smtp-auth', $params{'disable_smtp_auth'} ? 'false' : 'true');
		$mailNode->{'ATTRIBUTE'}->('poplock-time', $params{'poplock_time'}) if (!$params{'disable_pop_auth'});
	}

	# black list
	my $listNode = XmlNode->new('black-list');

	$self->{dbh}->execute_rownum('SELECT domain FROM badmailfrom');
	while($ptrRow = $self->{dbh}->fetchrow()) {
		$listNode->{'ADDCHILD'}->(XmlNode->new('list-item', 'content' => $ptrRow->[0]));
	}
	$self->{dbh}->finish();
	$mailNode->{'ADDCHILD'}->($listNode);

	# white list
	$listNode = XmlNode->new('white-list');

	$self->{dbh}->execute_rownum('SELECT ip_address, ip_mask FROM smtp_poplocks');
	while($ptrRow = $self->{dbh}->fetchrow()) {
		$listNode->{'ADDCHILD'}->(XmlNode->new('list-item', 'content' => $ptrRow->[0] . '/' . $ptrRow->[1]));
	}
	$self->{dbh}->finish();
	$mailNode->{'ADDCHILD'}->($listNode);

	$serverNode->{'ADDCHILD'}->($mailNode);

	# dump dns
	my $dnsNode = XmlNode->new('dns-settings', 'attributes' => {'recursion' => defined $params{'dns_recursion'} ? $params{'dns_recursion'} : 'any'});

	my $dnsZone = XmlNode->new('dns-zone',
					'attributes' => {'email' => 'root@localhost.localdomain', 'type' => 'master'},
					'children' => [Status::make($params{'dns_zone_status'} ne 'false' ? 0 : 16)]);

	my %zone_params = (
		'ttl'		=> 1*86400,
		'refresh'	=> 3*3600,
		'retry'		=> 1*3600,
		'expire'	=> 7*86400,
		'minimum'	=> 3*3600
		);

	my %zone_units = (
		'ttl'		=> 86400,
		'refresh'	=> 3600,
		'retry'		=> 3600,
		'expire'	=> 86400,
		'minimum'	=> 3600
		);

	foreach my $param (keys %zone_params) {
		my $soa_param = 'soa_' . $param;

		$dnsZone->{'ADDCHILD'}->(
								$self->makeDnsZoneParam($param,
									exists $params{$soa_param.'_unit'} ? $params{$soa_param.'_unit'} : $zone_units{$param},
									exists $params{$soa_param} ? $params{$soa_param} : $zone_params{$param},
							)
						);
	}

	# dns records
	$self->{dbh}->execute_rownum('SELECT * FROM dns_recs_t');
	while (my $ptrHash = $self->{dbh}->fetchhash()) {
		my $dnsrec = $self->makeDnsRecord($ptrHash);
		if ($dnsrec) {
			$dnsZone->{'ADDCHILD'}->($dnsrec);
		}
	}
	$self->{dbh}->finish();

	$dnsNode->{'ADDCHILD'}->($dnsZone);

	# dump common acl for dns zone

	my $aclNode = XmlNode->new('common-acl');

	foreach my $param (keys %params) {
		if ($param =~ /^DNS_Allow_Transfer/) {
			$aclNode->{'ADDCHILD'}->(XmlNode->new('list-item', 'content' => $params{$param}));
		}
	}

	$dnsNode->{'ADDCHILD'}->($aclNode);

	$serverNode->{'ADDCHILD'}->($dnsNode);

	# dump certificates
	my $cert_id = $params{'cert_rep_id'};
	$sql = "SELECT c.id FROM certificates c, Repository r " .
				"WHERE c.id=r.component_id AND r.rep_id='$cert_id' ORDER BY c.id";

	$self->{dbh}->execute_rownum($sql);
	my @ids;
	while ($ptrRow = $self->{dbh}->fetchrow()) {
		push @ids, $ptrRow->[0];
	}
	$self->{dbh}->finish();

	foreach my $id (@ids) {
		my $certNode = $self->makeCertificateNode($id);
		if ($params{'default_certificate_id'} == $id) {
			$certNode->{'ATTRIBUTE'}->('default', 'true');
		}

		$serverNode->{'ADDCHILD'}->($certNode);
	}

	# dump custom buttons
	$serverNode->{'ADDCHILD'}->($_) foreach $self->getCustomButtonsByOwner('admin', 0);

	# dump domain templates
	$self->dumpTemplates($serverNode, $params{'dom_tmpl_list_id'}, 'domain-template');

	# dump client templates
	$self->dumpTemplates($serverNode, $params{'cl_tmpl_list_id'}, 'client-template');
  }

	if (PleskVersion::atLeast(8, 2, 0)) {
		# dump SSO
		if (defined $params{'sso_server'}) {
			my $ownership = defined $params{'admin_ownership'} && $params{'admin_ownership'} eq 'true' ? 'true' : 'false';
			my $enabled = defined $params{'sso_enabled'} && $params{'sso_enabled'} eq 'true' ? 'true' : 'false';

			my $ssoNode = XmlNode->new('sso-settings',
				'attributes' =>	{'enabled' => $enabled, 'admin-ownership' => $ownership});

			my @settings	 = ('sso_server', 'sso_relay', 'sso_application_id', 'sso_idp_api_version', 'sso_idp_id');

			my %settings_map = ('sso_server' => 'sso-server',
								'sso_relay' => 'sso-relay',
								'sso_application_id' => 'sso-application-id',
								'sso_idp_api_version' => 'sso-idp-api-version',
								'sso_idp_id' => 'sso-idp-id');

			foreach my $setting (@settings) {
				$ssoNode->{'ADDCHILD'}->(XmlNode->new($settings_map{$setting}, 'content' => $params{$setting}))
					if (defined $params{$setting});
			}

			if (defined $params{'sso_certificate'} && -r $params{'sso_certificate'}) {
				my $cert;

				open(CERT, $params{'sso_certificate'});
				read(CERT, $cert, -s CERT);
				close(CERT);

				$ssoNode->{'ADDCHILD'}->(XmlNode->new('sso-certificate', 'content' => $cert));
			}

			if (defined $params{'sso_idp_cert'} && -r $params{'sso_idp_cert'}) {
				my $idpCert;
				open(IDPCERT, $params{'sso_idp_cert'});
				read(IDPCERT, $idpCert, -s IDPCERT);
				close(IDPCERT);
				$ssoNode->addChild(XmlNode->new('sso-idp-cert', 'content' => $idpCert));
			}



            if (PleskVersion::atLeast(8, 3, 0)) {
                # SSO branding
                my $ssoBrandingNode = XmlNode->new('sso-branding');
                $sql = "SELECT * FROM SSOBranding";
                if ($self->{dbh}->execute_rownum($sql)) {
                    while (my $ptrRow = $self->{dbh}->fetchrow()) {
                        my $recordNode = XmlNode->new('record', 'children' => [XmlNode->new('idp-url', 'content' => $ptrRow->[1]),
                                                                               XmlNode->new('http-request-domain', 'content' => $ptrRow->[0])]);
                        $ssoBrandingNode->addChild($recordNode);
                    }
                    $ssoNode->addChild($ssoBrandingNode);
                }
            }

			$serverNode->{'ADDCHILD'}->($ssoNode);

			# Dump misc parameters

			if (defined $params{'force_db_prefix'}) {
				$serverNode->{'ADDCHILD'}->(XmlNode->new('force-db-prefix', 'content' => $params{'force_db_prefix'}));
			}

		}
	}

    if (PleskVersion::atLeast(8, 3, 0)) {
      # Application Vault
      my $appVaultNode = XmlNode->new('application-vault');

      # Application Package
      my $appPackagePoolNode = XmlNode->new('sapp-packages-pool');

      my $distrib_path = AgentConfig::get("PRODUCT_ROOT_D"). "/var/apspkgarc";
      my %mapHash = $self->parseApsIndexFile($distrib_path."/archive-index.xml");
      my $real_path;

      $sql = "SELECT `name`, `version`, `release` FROM SiteAppPackages";
      $self->{dbh}->execute_rownum($sql);

      while (my $ptrRow = $self->{dbh}->fetchrow()) {
        my $packageNode = XmlNode->new('sapp-package');
        $packageNode->addChild(XmlNode->new('sapp-name', 'content' => $ptrRow->[0]));
        $packageNode->addChild(XmlNode->new('sapp-version', 'content' => $ptrRow->[1])) if ($ptrRow->[1]);
        $packageNode->addChild(XmlNode->new('sapp-release', 'content' => $ptrRow->[2])) if ($ptrRow->[2]);
        my $distrib_name = $ptrRow->[0]."-".$ptrRow->[1]."-".$ptrRow->[2]; # What if 1 and 2 does not exist, or they exist always?

	my $file_name;
	foreach my $tfile_name (keys %mapHash) {
	  if ($distrib_name eq join("-", @{$mapHash{$tfile_name}})) {
	    $file_name = $tfile_name;
	  }
	}

        $real_path = $distrib_path."/".$file_name;

        if (-f $real_path) {
          my $cid = $self->addTar("sapp-distrib.".generateDumpId(), "directory" => $distrib_path, "include" => [$file_name]);
          $packageNode->addChild(XmlNode->new('sapp-distrib', 'content' => $cid)) if $cid;
        }else {next;}

        $appPackagePoolNode->addChild($packageNode);
      }
      $self->{dbh}->finish();
      $appVaultNode->addChild($appPackagePoolNode);

      #Application Item
      my $appItemPoolNode = XmlNode->new('sapp-items-pool');
      $sql = "SELECT lt.license_type_hash AS license_type_id, sap.name AS sapp_name, sap.version AS sapp_version, sap.release AS sapp_release, ai.shared AS shared, ai.disabled AS disabled "
            ."FROM APSApplicationItems AS ai INNER JOIN SiteAppPackages sap ON (sap.id = ai.pkg_id) "
            ."LEFT JOIN APSLicenseTypes AS lt ON (lt.id = ai.license_type_id)";
      $self->{dbh}->execute_rownum($sql);

      while ($ptrHash = $self->{dbh}->fetchhash()) {
        $self->makeSiteAppItemNode($appItemPoolNode, $ptrHash);
      }
      $self->{dbh}->finish();
      $appVaultNode->addChild($appItemPoolNode);

      #Application License
      my $licensePoolNode = XmlNode->new('sapp-licenses-pool');
      $sql = "SELECT l.key_number, l.source, lt.license_type_hash FROM APSLicenses l, APSLicenseTypes lt WHERE l.license_type_id = lt.id";
      $self->{dbh}->execute_rownum($sql);

      while (my $ptrRow = $self->{dbh}->fetchrow()) {
        my $licenseNode = XmlNode->new('sapp-license', 'children' => [XmlNode->new('key-number', 'content' => $ptrRow->[0]),
                                                                     XmlNode->new('license-type', 'content' => $ptrRow->[2]),
                                                                     XmlNode->new('license-text', 'content' => $ptrRow->[1])]);
        $licensePoolNode->addChild($licenseNode);
      }
      $self->{dbh}->finish();
      $appVaultNode->addChild($licensePoolNode);

      $serverNode->addChild($appVaultNode);

      # SiteBuilder Config

      $sql = "SELECT * FROM SBConfig";
      if ($self->{dbh}->execute_rownum($sql)) {
      	my $sbNode = XmlNode->new('sb-config');
	while ($ptrHash = $self->{dbh}->fetchhash()) {
	  $sbNode->addChild(XmlNode->new('sb-param', 'children' => [XmlNode->new('sb-param-name', 'content' => $ptrHash->{'param_name'}),
	  							    XmlNode->new('sb-param-value', 'content' => $ptrHash->{'param_value'})]));
	}
	$serverNode->addChild($sbNode);
      }
    }

    if (PleskVersion::atLeast(8,4,0)) {
      my $serverPrefsNode = XmlNode->new('server-preferences');
      if (defined $params{'forbid_create_dns_subzone'}) {
        $serverPrefsNode->{'ADDCHILD'}->(XmlNode->new('forbid-create-dns-subzone', 'content' => $params{'forbid_create_dns_subzone'}));
      }
      if (defined $params{'force_db_user_prefix'}) {
        $serverPrefsNode->{'ADDCHILD'}->(XmlNode->new('force-db-user-prefix', 'content' => $params{'force_db_user_prefix'}));
      }
      if (defined $params{'db_user_length'}) {
        $serverPrefsNode->{'ADDCHILD'}->(XmlNode->new('db-user-length', 'content' => $params{'db_user_length'}));
      }

      $serverNode->addChild($serverPrefsNode);
    }

  $parent->{'ADDCHILD'}->($serverNode);
}

sub makeSiteAppItemNode() {
  my ($self, $parent, $ptrParams) = @_;
  my $appItemNode = XmlNode->new('sapp-item');
  $appItemNode->setAttribute('enabled', ($ptrParams->{'disabled'} eq 'true') ? 'false' : 'true') if defined $ptrParams->{'disabled'};
  my $appSpecNode = XmlNode->new('sapp-spec');
  $appSpecNode->addChild(XmlNode->new('sapp-name', 'content' => $ptrParams->{'sapp_name'}));
  $appSpecNode->addChild(XmlNode->new('sapp-version', 'content' => $ptrParams->{'sapp_version'})) if defined $ptrParams->{'sapp_version'};
  $appSpecNode->addChild(XmlNode->new('sapp-release', 'content' => $ptrParams->{'sapp_release'})) if defined $ptrParams->{'sapp_release'};
  $appItemNode->addChild($appSpecNode);
  $appItemNode->addChild(XmlNode->new('license-type', 'content' => defined($ptrParams->{'license_type_id'}) ? $ptrParams->{'license_type_id'}: "0"));
  $appItemNode->addChild(XmlNode->new('shared', 'content' => $ptrParams->{'shared'})) if defined $ptrParams->{'shared'};
  $appItemNode->addChild(XmlNode->new('description', 'content' => $ptrParams->{'description'})) if defined $ptrParams->{'description'};
  $appItemNode->addChild(XmlNode->new('instances-limit', 'content' => $ptrParams->{'instances_limit'})) if defined $ptrParams->{'instances_limit'};

  $parent->addChild($appItemNode);
}

sub parseApsIndexFile() {
  my ($self, $file) = @_;

  my %xmlHash;
  my $name;
  if (open(FILE, $file)) {
    while(<FILE>) {
      chomp;
      if ($_=~/<([A-Za-z\-]*)\s+name=\"(.*)\">/) {
        $name = $2;
      }elsif ($_=~/<([A-Za-z\-]*)>(.*)<\/([A-Za-z\-]*)>/) {
        push @{$xmlHash{$name}}, $2 unless $1 eq "valid";
      }
    }
    close FILE;
  }else{
    printToError("Unable to open ".$file);
  }
  return %xmlHash;
}

sub getDomainKeysDomainSupport() {
  my ($self, $parent, $domainId, $domainName, $dnsZoneId) = @_;
  my $sql = "SELECT p.value FROM Parameters p, DomainServices ds " .
  "WHERE ds.parameters_id = p.id AND ds.dom_id = $domainId AND p.parameter = 'domain_keys_sign'";

  my $state;
  if ($self->{dbh}->execute_rownum($sql)) {
    $state = @{$self->{dbh}->fetchrow()}[0];
  } else {
    $self->{dbh}->finish();
    return;
  }
  $self->{dbh}->finish();

  my $domainKeysNode = XmlNode->new('domain-keys', 'attributes' => {'state' => $state });

  if ($state) {
    my $privateKey = $self->addTar("$domainName.sfdk.privatekey",
                            'directory' => '/etc/domainkeys/'.$domainName,
                            'include' => ['default']);

    $domainKeysNode->setAttribute('private-key', $privateKey) if $privateKey;

    $sql = "SELECT * FROM dns_recs WHERE dns_zone_id=$dnsZoneId AND displayHost='default._domainkey.$domainName.'";
    if ($self->{dbh}->execute_rownum($sql)) {
        while (my $ptrHash = $self->{dbh}->fetchhash()) {
          chop $ptrHash->{'val'};
          my $str = substr($ptrHash->{'val'}, 2);
          $domainKeysNode->setAttribute('public-key', $str);
        }
    }
    $self->{dbh}->finish();


  }
  $parent->addChild($domainKeysNode);
}

1;

# Local Variables:
# mode: cperl
# cperl-indent-level: 2
# indent-tabs-mode: nil
# tab-width: 4
# End: