# Copyright 1999-2012. Parallels IP Holdings GmbH. All Rights Reserved.
package Packer;

use strict;
eval{require warnings;1;};

use Status;
use HelpFuncs;
use Storage::FileNameCreator;
use ArchiveContent::ArchiveContent;
use RsyncContent;
use CommonPacker;
use Mailman;
use Logging;
use PerlMD5;
use XPath;
use File::Temp;

use vars qw|@ISA|;

my $DEBUG = undef;

### Common function ###

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

sub _init {
  my ( $self, $version, $storagePolicy ) = @_;

  XmlNode::resetCompatibilityProcs();

  $self->{storage} = $storagePolicy;
  $self->{version} = $version;
  $self->{base64} = HelpFuncs::makeMIMEBase64();
  $self->{fnamecreator} = Storage::FileNameCreator->new();
  $self->setBackupProfileFileName( 'backup' );
  $self->{listingOnlyMode} = 0; # Enabled, while getting source content listing for migration
  $self->{migrationMode} = 0;   # Enabled, in case of migration, but disabled for backup
  $self->{ownerGuid} = '';
  $self->{ownerType} = '';
  $self->{roots} = [];
  $self->{content_transport_type} = 'archive';
  $self->{content_transport_os} = 'unix';
  $self->{content_transport} = ArchiveContent::ArchiveContent->new( $self->{storage}, $self );
  $self->{decrypt_full_dump} = 0;
  $self->{apache_user} = AgentConfig::getApacheUserInfo();
}

sub setContentTransport {
  my ($self, $contentTransportType, $osType) = @_;

  if ( $self->{content_transport_type} ne $contentTransportType or $self->{content_transport_os} ne $osType) {
    $self->{content_transport_type} = $contentTransportType;
    $self->{content_transport_os} = $osType;

    if ( $contentTransportType eq 'archive' ) {
      $self->{content_transport} = ArchiveContent::ArchiveContent->new($self->{storage}, $self);
    }

    if ( $contentTransportType eq 'rsync' ){
      $self->{content_transport} = RsyncContent->new($self->{storage}, $self, @_);
    }
  }
}

sub getContentTransportType {
  my ($self) = @_;
  return $self->{content_transport_type};
}

sub setBackupProfileFileName{
  my ($self, $profileName, $profileId ) = @_;
  $self->{backupname} = $self->{fnamecreator}->normalize_long_string( $profileName, $profileId );
  $profileId = '' if not defined $profileId;
  Logging::debug( "Set backup file name '$self->{backupname}' (profile '$profileName', id='$profileId')\n" );
}

sub setBackupOwnerGuid{
  my ($self, $ownerGuid, $ownertype ) = @_;
  $ownerGuid = '' if not $ownerGuid;
  $self->{ownerGuid} = $ownerGuid;
  $ownertype = '' if not $ownertype;
  $self->{ownerType} = $ownertype;
  Logging::debug( "Set backup owner guid '$ownerGuid', type '$ownertype'\n" );
}

sub getBackupOwnerGuid{
  my ($self) = @_;
  return $self->{ownerGuid};
}

sub startCollectStorageStatistics {
  my $self = shift;

  $self->{storage}->startCollectStatistics();
}

sub getStorageStatistics {
  my $self = shift;

  return $self->{storage}->getStatistics();
}

sub setRoot {
  my ( $self, $description, $content, $dumpformat ) = @_;

  $self->{root} = XmlNode->new( 'migration-dump',
    'attributes' =>
      { 'agent-name' => 'PleskX', 'dump-version' => $self->{version} } );
  $self->{root}->setAttribute( 'content-included', $content ? 'true' : 'false' ) if defined $content;
  $self->{root}->setAttribute( 'dump-format', $dumpformat ) if defined $dumpformat;
  my $dumpinfo = $self->{root}->getChild( 'dump-info', 1 );
  if ($description) {
    $dumpinfo->addChild( XmlNode->new( 'description', 'content' => $description ) );
  }

  my $contentTransportDescriptionNode = $self->{content_transport}->getContentTransportDescription();
  if ( defined $contentTransportDescriptionNode ) {
    my $contentTransportNode = XmlNode->new( 'content-transport' );
    $contentTransportNode->addChild( $contentTransportDescriptionNode );
    $dumpinfo->addChild ( $contentTransportNode );
  }

}

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

sub turnOnListingOnlyMode{
  my ($self) = @_;
  $self->{listingOnlyMode} = 1;
  Logging::debug( "The listing only mode mode is turned on" );
}

sub setDecryptFullDump {
  my ($self) = @_;
  $self->{decrypt_full_dump} = 1;
  Logging::debug( "Set to decrypt full dump" );
}

sub turnOffContent{
  my ($self) = @_;
  $self->{skip_content} = 1;
  Logging::debug( "The dump of content is switched off" );
}

sub isRootNode{
  my ($self, $node) = @_;
  foreach my $root( @{$self->{roots}} ){
    return 1 if $root==$node;
  }
  return 0;
}

sub finishChild{
    my ($self, $node, $path, $fileName ) = @_;
    my $ret;
    if( !$self->{migrationMode} && scalar( @{$self->{roots}} )==1  && $self->isRootNode($node) ) {
      $ret = $self->{storage}->finish( $self->{root}, $path, $fileName );
      die "Cannot create main dump file!" if $ret!=0;
      $ret = $self->{storage}->getMainDumpXmlRelativePath();
    }
    else {
      $ret = $self->{storage}->finishChild( $self->{root}, $node, $path, $fileName );
    }
    return $ret;
}

sub _decryptDump {
  my ( $self, $file ) = @_;
  my $encryptCmd = AgentConfig::getEncryptUtil();
  $encryptCmd .= " --decrypt-by-plesk ";
  $encryptCmd .= " -single-file " . $file;

  Logging::debug( "Exec: $encryptCmd" );
  my $ret = `$encryptCmd`;
  my $retCode = $? >> 8;
  if( $retCode!=0 ){
    Logging::error( "Cannot encrypt dump file (ErrorCode: $retCode, STDOUT:$ret) [Error:$!]. This is not fatal error!", 'UtilityError' );
  }
}

sub finishDomains {
  my ($self, $fileName, $fh, $usersContentSize, $infoxml) = @_;

  if ( exists $self->{domainNodes} ) {
    my ( $id, $node, $path );
    while ( ($id, $node ) = each( %{$self->{domainNodes}} ) ){
      $path = $self->getDomainsBackupPath( $id );
      Logging::debug( "Save domain dump $path" );
      XmlNode::setStartSavePath( $path );

      print $fh "<file>" . $path . "/" .$self->getDomainsBackupPath( $id, $fileName, 1 ) . ".xml" . "</file>\n";

      my $fileid = $self->finishChild( $node, $path, $self->getDomainsBackupPath( $id, $fileName, 1 ) );
      my $contentSize = $self->getContentSize($node);
      @{$infoxml->{$fileid}} = ( $contentSize, $self->getDomainObjectId( $id ), $node->getAttribute('guid') );
      $self->{storage}->createRepositoryIndex( $node->getAttribute('guid') . '_' . $self->getDomainObjectId( $id ) );

      #Keep total domain content size for each client
      my $clientId = PleskStructure::getClientIdForDomainId( $id );
      $usersContentSize->{$clientId} = (exists $usersContentSize->{$clientId}) ? $usersContentSize->{$clientId} + $contentSize : $contentSize;
    }
  }
}

sub finishClients {
  my ($self, $fileName, $fh, $selectResellers, $usersContentSize, $infoxml) = @_;

  if( exists $self->{clientNodes} ){
    my ( $id, $node, $path );
    while( ($id, $node ) = each( %{$self->{clientNodes}} ) ){
      my $fname;
      if (exists $self->{resellersNodes}->{$id}) {
        next if !$selectResellers;

        $path = $self->getResellersBackupPath( $id );
        $fname = $self->getResellersBackupPath( $id, $fileName, 1 );
        Logging::debug( "Save reseller dump $path" );
      } else {
        next if $selectResellers;

        $path = $self->getClientsBackupPath( $id );
        $fname = $self->getClientsBackupPath( $id, $fileName, 1 );
        Logging::debug( "Save client dump $path" );
      }

      XmlNode::setStartSavePath( $path );

      print $fh "<file>" . $path . "/" . $fname. ".xml" . "</file>\n";

      # Calculate current client size
      $usersContentSize->{$id} = (exists $usersContentSize->{$id})
        ? $usersContentSize->{$id} + $self->getContentSize($node)
        : $self->getContentSize($node);

      # Add current client size to the parent (reseller or admin) total content size
      my $parentId = PleskStructure::getClientParentId(PleskStructure::getClientNameFromId($id));
      $usersContentSize->{$parentId} = (exists $usersContentSize->{$parentId})
        ? $usersContentSize->{$parentId} + $usersContentSize->{$id}
        : $usersContentSize->{$id};

      my $fileid = $self->finishChild( $node, $path, $fname );
      @{$infoxml->{$fileid}} = ( $usersContentSize->{$id}, $self->getClientObjectId( $id ), $node->getAttribute('guid') );
      $self->{storage}->createRepositoryIndex( $node->getAttribute('guid') . '_' . $self->getClientObjectId( $id ) );
    }
  }
}

sub finishCustomers {
  my ($self, $fileName, $fh, $usersContentSize, $infoxml) = @_;
  $self->finishClients($fileName, $fh, 0, $usersContentSize, $infoxml);
}

sub finishResellers {
  my ($self, $fileName, $fh, $usersContentSize, $infoxml) = @_;
  $self->finishClients($fileName, $fh, 1, $usersContentSize, $infoxml);
}

sub finish {
    my ($self, $decryptPasswords) = @_;

    my %infoxml;

    my $ret = 0;
    my $fileName;
    my $mainDumpXmlFile;

    if( $self->{migrationMode} || scalar( @{$self->{roots}} )!=1 ){

        XmlNode::setStartSavePath( '' );

        if( $self->{listingOnlyMode} ) {
          $fileName = 'dump';
        } else {
          $fileName = $self->{fnamecreator}->getFileName( '', $self->{backupname}, '', 'info' );
        }

        $ret = $self->{storage}->finish( $self->{root}, '', $fileName );

        die "Cannot create main dump file!" if $ret!=0;

        if ($self->{listingOnlyMode} and $decryptPasswords) {
          $self->_decryptDump($self->{storage}->getFullOutputPath() . "/" . $fileName . ".xml");
        }

        return $ret if $self->{listingOnlyMode};

        $mainDumpXmlFile = $self->{storage}->getMainDumpXmlRelativePath();

        @{$infoxml{$mainDumpXmlFile}} = ( $self->getContentSize( $self->{root} ), $self->getAdminObjectId(), ( exists $self->{admin} ? $self->{admin}->getAttribute('guid') : $self->{ownerGuid} ) );
        $self->{storage}->createRepositoryIndex( ( exists $self->{admin} ? $self->{admin}->getAttribute('guid') : '' ) . '_' . $self->getAdminObjectId() );
    }
    $fileName = 'info';

    my ($fh, $targetFile);
    ($fh, $targetFile ) = File::Temp::tempfile( AgentConfig::getBackupTmpDir() . "/xmlFilesListXXXXXX", UNLINK => 1 );

    print $fh "<dump-files source-directory=\"".$self->{storage}->getFullOutputPath()."\">\n";
    print $fh "<file>" . $self->{storage}->getMainDumpXmlFile() . "</file>\n" if $self->{storage}->getMainDumpXmlFile();

    my %usersContentSize; # Summary size of each customer, reseller and admin content

    $self->finishDomains($fileName, $fh, \%usersContentSize, \%infoxml);
    $self->finishCustomers($fileName, $fh, \%usersContentSize, \%infoxml);
    $self->finishResellers($fileName, $fh, \%usersContentSize, \%infoxml);

    if (exists $infoxml{$mainDumpXmlFile}) {
      my $adminId = PleskStructure::getAdminId();
      @{$infoxml{$mainDumpXmlFile}}[0] += $usersContentSize{$adminId} if exists $usersContentSize{$adminId};
    }

    print $fh "</dump-files>";
    close $fh;


    if ($self->{decrypt_full_dump}) {
      $self->_decryptDump($targetFile);
    }

    my $path;

    #Create discovered files
    foreach my $fileid(  keys( %infoxml ) ){
       my $idx = rindex( $fileid, '/' );
       $path = $fileid;
       $path = substr( $path, 0, $idx ) if $idx>0;
       my $xmlsize = 0;
       my $files;
       foreach my $id( keys( %infoxml ) ){
         if( $idx<0 || index( $id, $path )==0 ){
            $files = $self->{storage}->getFilesFromId( $id );
            foreach my $filedata( @{$files} ) {
              $xmlsize += $filedata->[1];
            }
         }
       }
       $files = $self->{storage}->getFilesFromId( $fileid );
       if( scalar( @{$files} )==1 ){
          my $xmlfiledata = $files->[0];
          $xmlsize += @{$infoxml{$fileid}}[0];
          my $objId = @{$infoxml{$fileid}}[1];
          my $objGuid = @{$infoxml{$fileid}}[2];
          $self->{storage}->writeDiscovered( $self->{storage}->getFilePathFromId( $fileid ), $xmlfiledata->[0], $xmlsize, $self->{ownerGuid}, $self->{ownerType}, $objGuid, $objId );
       }
    }

    return 0;
}

sub _decryptDump {
  my ($self, $targetFile) = @_;
  my $encryptCmd = AgentConfig::getEncryptUtil();
  $encryptCmd .= " --decrypt-by-plesk ";
  $encryptCmd .= " -backup-files-map " . $targetFile;

  Logging::debug( "Exec: $encryptCmd" );
  my $ret = `$encryptCmd`;
  my $retCode = $? >> 8;
  if( $retCode!=0 ){
    Logging::error( "Cannot encrypt dump file (ErrorCode: $retCode, STDOUT:$ret) [Error:$!]. This is not fatal error!", 'UtilityError' );
  }
}

sub getContentSize{
  my ($self, $node) = @_;
  my $size = 0;

  foreach my $child ( $node->getChildren() ) {
    if( $child ) {
      if( $child->getName() eq 'cid' ){
        if( !defined($child->getAttribute('referrer')) ) {
          foreach my $cid ( $child->getChildren() ){
            $size += $cid->getAttribute('size');
          }
        }
      }
      else {
        $size += $self->getContentSize($child);
      }
    }
  }
  return $size;
}

### Server functions ###

sub setServerSettings {
  my ( $self ) = @_;
  my $serverNode = XmlNode->new('server');
  $self->{serverNode} = $serverNode;
}

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

  my $root = $self->{root};
  unless (defined $root)
  {
    Logging::warning("Root node is not set", 'PackerStructure');
    return;
  }

  if ( ref ($self->{serverNode}) =~ /XmlNode/ ) {
    $root->addChild($self->{serverNode});
  }
}

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

  my $serverNode = $self->{serverNode};

  my $path = $params->{'cf_instance_path'};

  return unless defined $path;

  my $type = ( $params->{'cf_instance_type'} eq 'j2ee' ) ? 'j2ee' : 'server';

  my $coldfusionNode = XmlNode->new('coldfusion');
  $coldfusionNode->setAttribute( 'instance-type', $type);
  $coldfusionNode->setAttribute( 'instance-path', $path);

  $serverNode->addChild($coldfusionNode);
}

sub setServerGappsInfo {
  my ($self, $values) = @_;
  my $root = $self->{serverNode};

  if (defined $values->{'gappsDomain'}
      and defined $values->{'gappsAdminLogin'}
      and defined $values->{'gappsAdminPassword'}
      and defined $values->{'gappsGanId'}
      and defined $values->{'gappsDevEmail'}
      and defined $values->{'gappsDevPassword'}
    ) {
    my $gappsNode = XmlNode->new('gapps-services');
    $gappsNode->addChild(XmlNode->new('gapps-domain', 'content' => $values->{'gappsDomain'}));
    $gappsNode->addChild(XmlNode->new('gapps-admin-login', 'content' => $values->{'gappsAdminLogin'}));
    $gappsNode->addChild(XmlNode->new('gapps-admin-password', 'content' => $values->{'gappsAdminPassword'}));
    $gappsNode->addChild(XmlNode->new('gapps-gan-id', 'content' => $values->{'gappsGanId'}));

    my $certificateText = undef;
    my $keyFile = AgentConfig::get("PRODUCT_ROOT_D") . "/var/gapps-private-key.pem";
    open KEYFILE, $keyFile;
    binmode(KEYFILE);
    while (<KEYFILE>) {
      $certificateText .= $_;
    }
    close KEYFILE;
    $gappsNode->addChild(XmlNode->new('gapps-private-key', 'content' => $self->{base64}->{'ENCODE'}->($certificateText) ));
    $gappsNode->addChild(XmlNode->new('adsense-dev-email', 'content' => $values->{'gappsDevEmail'}));
    $gappsNode->addChild(XmlNode->new('adsense-dev-password', 'content' => $values->{'gappsDevPassword'}));

    $root->addChild($gappsNode);
  }
}

sub addPanelCertificate {
  my ($self) = @_;
  my $serverNode = $self->{serverNode};

  my $path = AgentConfig::get('PRODUCT_ROOT_D') . "/admin/conf";
  my $httpsdFilename = 'httpsd.pem';

  if ( -e "$path/$httpsdFilename") {
    my $certNode = XmlNode->new('panel-certificate');

    my $content = undef;

    open HTTPSDFILE, $path . "/" . $httpsdFilename;
    binmode(HTTPSDFILE);
    while (<HTTPSDFILE>) {
      $content .= $_;
    }
    close HTTPSDFILE;

    $certNode->addChild(XmlNode->new('cp-certificate', 'content' => $self->{base64}->{'ENCODE'}->($content) ));

    my $rootchainFilename = 'rootchain.pem';
    if ( -e "$path/$rootchainFilename") {
      my $content = undef;
      open ROOTCHAINFILE, $path . "/" . $rootchainFilename;
      binmode(ROOTCHAINFILE);
      while (<ROOTCHAINFILE>) {
        $content .= $_;
      }
      close ROOTCHAINFILE;
      $certNode->addChild(XmlNode->new('cp-rootchain', 'content' => $self->{base64}->{'ENCODE'}->($content) ));
    }

    $serverNode->getChild( 'certificates', 1 )->addChild($certNode);
  }
}

sub addServerSiteIsolationConfig {
  my ($self) = @_;
  my $root = $self->{serverNode};

  my $path = AgentConfig::get('PRODUCT_ROOT_D') . "/admin/conf";
  my $file = 'site_isolation_settings.ini';

  if ( -e "$path/$file") {

    my $siteIsolationNode = XmlNode->new('site-isolation');

    my $configText = undef;

    open CONFIGFILE, $path . "/" . $file;
    binmode(CONFIGFILE);

    while (<CONFIGFILE>) {
      $configText .= $_;
    }

    close CONFIGFILE;

    $siteIsolationNode->addChild(XmlNode->new('config', 'content' => $self->{base64}->{'ENCODE'}->($configText) ));
    $root->addChild($siteIsolationNode);
  }
}

sub setServerEventHandler {
  my ($self, $ptrEvents, $ptrActions, $ptrParams) = @_;

  my $root = $self->{serverNode};
  my %events = %{$ptrEvents};
  my @actions = @{$ptrActions};
  my %params = %{$ptrParams};

  my $eventsNode = XmlNode->new('events');

  my $rotationNode = XmlNode->new('rotation');
  if ($params{'actionlog_rot_type'} eq 'forever') {
    $rotationNode->addChild(XmlNode->new('disabled'));
  }elsif($params{'actionlog_rot_type'} eq 'by_period') {
    my $byTimeNode = XmlNode->new('by-time');
    $byTimeNode->setAttribute('period-type', $params{'actionlog_rot_period'});
    $byTimeNode->setAttribute('period', $params{'actionlog_rot_num_periods'} );
    $rotationNode->addChild($byTimeNode);
  }elsif($params{'actionlog_rot_type'} eq 'by_number') {
    my $byNumberNode = XmlNode->new('by-entires');
    $byNumberNode->setAttribute('count', $params{'actionlog_rot_num_records'});
    $rotationNode->addChild($byNumberNode);
  }

  $eventsNode->addChild($rotationNode);

  foreach my $action (@actions) {
    my $eventNode = XmlNode->new('event');
    $eventNode->setAttribute('enabled', $action->{'enabled'});
    $eventNode->setAttribute('description', $action->{'descr'});
    $eventNode->setAttribute('name', $action->{'name'});
    if (exists $events{$action->{'id'}}) {
      foreach my $handler (@{$events{$action->{'id'}}}) {
        my $handlerNode = XmlNode->new('handler');
        $handlerNode->setAttribute('command', $handler->{'command'});
        $handlerNode->setAttribute('user', $handler->{'user'});
        $handlerNode->setAttribute('priority', $handler->{'priority'});
        $eventNode->addChild($handlerNode);
      }
    }
    $eventsNode->addChild($eventNode);
  }

  $root->addChild($eventsNode);
}

sub addServerNotifications {
  my ( $self, $expirationWarnDays, $ptrNotifications, $ptrNotes ) = @_;
  my $root = $self->{serverNode};
  my @notifications = @{$ptrNotifications};
  my %notes = %{$ptrNotes};

  my $notificationsNode = XmlNode->new('notifications');
  $notificationsNode->setAttribute( 'expiration-warning-days', $expirationWarnDays);
  foreach my $notification (@notifications) {
    my $notificationNode = XmlNode->new( 'notification' );
    $notificationNode->setAttribute( 'id',            $notification->{'id'} );
    $notificationNode->setAttribute( 'send2admin',    $notification->{'send2admin'}    ? 'true' : 'false' );
    $notificationNode->setAttribute( 'send2reseller', $notification->{'send2reseller'} ? 'true' : 'false' );
    $notificationNode->setAttribute( 'send2client',   $notification->{'send2client'}   ? 'true' : 'false' );
    $notificationNode->setAttribute( 'send2email',    $notification->{'send2email'}    ? 'true' : 'false' );

    $notificationNode->setAttribute( 'email', $notification->{'email'} ) if defined $notification->{'email'};
    $notificationNode->setAttribute( 'subj',  $notification->{'subj'} ) if defined $notification->{'subj'};

    if (exists $notes{ $notification->{'note_id'} } ) {
      $notificationNode->addChild( XmlNode->new( 'notice-text', 'content' => $notes{ $notification->{'note_id'} } ) );
    }
    $notificationsNode->addChild( $notificationNode );
  }

  $root->addChild($notificationsNode);
}

sub addServerCustomButton {
  my ( $self, $id, $optionsPtr, $customButtonsDir, $icon ) = @_;

  my $parent = $self->{serverNode};
  if ( !defined($parent) ) {
    Logging::warning('Unable to dump custom buttons settings, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $node = $self->makeCustomButtonNode( 'admin', undef, $id, $optionsPtr, $customButtonsDir, $icon );

  $parent->getChild( 'interface-preferences', 1 )->addChild($node);
}

my %dumpedSystemIps;

sub addServerIp {
  my ( $self, $ip ) = @_;

  $self->setServerSettings() unless defined $self->{serverNode};

  if ( ! $dumpedSystemIps{$ip->{'ip_address'}} ) {
    $self->makeSystemIpNode( $self->{serverNode}, $ip );
    $dumpedSystemIps{$ip->{'ip_address'}} = 1;
  }
}

sub setServerDefaultIp {
  my ( $self, $ip ) = @_;

  my $parent = $self->{serverNode};
  if ( !defined($parent) ) {
    Logging::warning('Unable to dump server default IP, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  $parent->getChild( 'properties', 1 )->addChild( XmlNode->new( 'default-ip', 'content' => $ip ) );
}

sub setServerHostname {
  my ( $self, $hostname ) = @_;

  my $parent = $self->{serverNode};
  if ( !defined($parent) ) {
    Logging::warning('Unable to dump server host name, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  $parent->getChild( 'properties', 1 )->addChild( XmlNode->new( 'hostname', 'content' => $hostname ) );
}

sub setServerAdminInfo {
  my ( $self, $ptrAdmin, $passwd, $max_btn_len, $send_announce, $external_id, $gappsInfo, $cron ) = @_;

  my $parent = $self->{admin};
  if ( !defined($parent) ) {
    Logging::warning('Unable to dump server admin user information, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  $parent->setAttribute( 'max-button-length', $max_btn_len ) if $max_btn_len;
  $parent->setAttribute( 'send-announce', $send_announce ) if $send_announce;
  $parent->setAttribute( 'external-id', $external_id ) if defined $external_id and $external_id ne '';

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

  my $pref = $parent->getChild( 'preferences', 1 );
  while ( my ( $name, $value ) = each( %{$ptrAdmin} ) ) {

    #Attributes transformation. Bug 97408
    $name =~ s/^admin_//g;
    next if not defined $adminInfo{$name};
    $pref->addChild( XmlNode->new( 'pinfo', 'attributes' => { 'name' => $adminInfo{$name} }, 'content'    => $value ) );
  }

  if (defined $cron) {
    $pref->addChild( XmlNode->new( 'cron', 'content' => $cron ) );
  }

  if (defined $gappsInfo) {
    $self->addGappsAccount($pref, $gappsInfo);
  }

}

sub addServerDb {
  my ( $self, $dbServerPtr, $passwd, $default ) = @_;

  my %dbServer = %{$dbServerPtr};

  my $parent = $self->{serverNode};
  if ( !defined($parent) ) {
    Logging::warning('Unable to dump server database, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $dbServerNode = $self->makeDbServerNodeWithoutCredentials(\%dbServer);

  if ( $default ) {
    $dbServerNode->setAttribute( 'default', 'true' );
  }

  if ( ($dbServer{'admin_login'}) and ($dbServer{'admin_login'} ne '')) {
    my $adminNode =
      XmlNode->new( 'db-admin',
      "attributes" => { "name" => "$dbServer{'admin_login'}" } );
    if ( $dbServer{'type'} eq 'mysql'
      && $dbServer{'host'} eq 'localhost' )
    {
      $dbServer{'admin_password'} = $passwd;
    }
    my $passwordNode =
      CommonPacker::makePasswordNode( $dbServer{'admin_password'}, 'plain' )
      ;    # password type for dbservers is always plain
    $adminNode->addChild($passwordNode);

    $dbServerNode->addChild($adminNode);
  }

  $parent->getChild( 'db-servers', 1 )->addChild($dbServerNode);
}

sub makeDbServerNodeWithoutCredentials {
  my ($self, $dbServer) = @_;

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

  $dbServerNode->setAttribute( 'type', $dbServer->{'type'} );

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

  return $dbServerNode;
}

sub addServerKey {
  my ( $self, $keyId, $keyName, $keyDir, $additional, $instance ) = @_;

  my $parent = $self->{serverNode};
  if ( !defined($parent) ) {
    Logging::warning('Unable to dump server license key, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

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

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

    $keyNode->setAttribute('instance-id', $instance) if $instance ne '';

    my $cid = $self->{content_transport}->addAdminContent(
      'key',
      undef,
      "$keyId.key",
      "directory" => $keyDir,
      "include"   => [$keyName]
    );
    $keyNode->getChild( 'content', 1, 1 )->addChild($cid) if $cid;

    $parent->getChild( 'keys', 1 )->addChild($keyNode);
  }

}

sub setServerMail {
  my ( $self, $letterSize, $paramsPtr, $blackListPtr, $whiteListPtr ) = @_;

  my %params = %{$paramsPtr};
  my @blackList = @{$blackListPtr};
  my %whiteList = %{$whiteListPtr};

  my $parent = $self->{serverNode};
  if ( !defined($parent) ) {
    Logging::warning('Unable to dump server mail settings, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

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

  if (defined $letterSize) {
    $mailNode->setAttribute('max-letter-size', $letterSize);
  }

  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'}" ne '' ) ? $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->setAttribute( 'pop-auth', $params{'disable_pop_auth'} ? 'false' : 'true' );
    $mailNode->setAttribute( 'smtp-auth', $params{'disable_smtp_auth'} ? 'false' : 'true' );
    $mailNode->setAttribute( 'poplock-time', $params{'poplock_time'} )  if ( !$params{'disable_pop_auth'} );
  }

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

  for my $listItem (@blackList) {
    $listNode->addChild( XmlNode->new( 'list-item', 'content' => $listItem ) );
  }
  $mailNode->addChild($listNode);

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

  for my $key (keys %whiteList) {
    $listNode->addChild(
      XmlNode->new(
        'list-item', 'content' => $key . '/' . $whiteList{$key}
      )
    );
  }
  $mailNode->addChild($listNode);

  $parent->addChild($mailNode);
}

sub setServerDNS {
  my ( $self, $paramsPtr, $recordsPtr ) = @_;

  my @records = @{$recordsPtr};

  my %params = %{$paramsPtr};

  my $parent = $self->{serverNode};
  if ( !defined($parent) ) {
    Logging::warning('Unable to dump server DNS settings, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

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

  $dnsZone->setAttribute( 'serial-format', $params{'soa_serial_format'} ) if exists $params{'soa_serial_format'};

  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 $zone_param ( keys %zone_params ) {
    my $soa_param = 'soa_' . $zone_param;

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

  # dns records
  for my $ptrHash (@records) {
    my $dnsrec = $self->makeDnsRecord($ptrHash);
    if ($dnsrec) {
      $dnsZone->addChild($dnsrec);
    }
  }

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

  my $subdomainOwnZone = 'false';
  if (exists($params{'subdomain_own_zones'})) {
    $subdomainOwnZone =  $params{'subdomain_own_zones'} eq 'false' ? 'false' : 'true';
  }else {
    $subdomainOwnZone = 'false';
  }

  $dnsNode->addChild( XmlNode->new( 'subdomain-own-zones', 'content' => $subdomainOwnZone ) );

  $parent->addChild($dnsNode);
}

sub addServerCertificate {
  my ( $self, $name, $cert, $csr, $ca_cert, $pvt_key, $default) = @_;

  my $parent = $self->{serverNode};
  if ( !defined($parent) ) {
    Logging::warning('Unable to dump server certificate, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

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

  addUrlDecodedTextNode( $root, 'certificate-data', $cert ) if defined($cert);
  addUrlDecodedTextNode( $root, 'signing-request',  $csr ) if defined($csr);
  addUrlDecodedTextNode( $root, 'ca-certificate',   $ca_cert ) if defined($ca_cert);
  addUrlDecodedTextNode( $root, 'private-key', $pvt_key ) if defined($pvt_key);
  $root->setAttribute( 'name', $name );

  if ($default) {
    $root->setAttribute('default', 'true');
  }

  $parent->getChild( 'certificates', 1 )->addChild($root);
}

sub addTemplateToServer {
  my ( $self, $templateType, $templateName, $dataPtr ) = @_;
  my %templates = (
    'reseller' => 'reseller-template',
    'domain' => 'domain-template',
    'domain_addon' => 'domain-template'
  );

  unless ( exists( $templates{$templateType} ) ){
    return;
  }

  $self->setServerSettings() unless defined $self->{serverNode};

  my $root = $self->{serverNode};
  my $node = $self->makeTemplateNode($templates{$templateType}, $templateName, $dataPtr);

  $root->getChild( 'account-templates', 1 )->addChild($node);

}

sub makePlanItemNode {
  my ( $self, $planItemPtr, $planItemPropsPtr, $customButtonsDir ) = @_;
  return unless ( ref($planItemPtr) =~ /HASH/ );
  return unless ( ref($planItemPropsPtr) =~ /HASH/ );

  return unless ( exists( $planItemPtr->{'name'}) && exists( $planItemPtr->{'classname'}) && exists( $planItemPtr->{'uuid'}) );

  my $planItemNode = XmlNode->new('plan-item');
  $planItemNode->setAttribute( 'name',    $planItemPtr->{'name'} );
  $planItemNode->setAttribute( 'type',    $planItemPtr->{'classname'} );
  $planItemNode->setAttribute( 'guid',    $planItemPtr->{'uuid'} );
  $planItemNode->setAttribute( 'visible', ($planItemPtr->{'isVisible'})? 'true' : 'false' );

  if ( ( $planItemPtr->{'classname'} eq 'Plan_Item_Custom') && ( defined $planItemPropsPtr->{'file'} ) ) {
    my $file = $planItemPropsPtr->{'file'};
    my $filename = "$customButtonsDir/$file";
    if( -f $filename) {
      my $cid = $self->{content_transport}->addAdminContent('icon', undef, 'icon_planitem'.$planItemPtr->{'id'}, 'directory' => $customButtonsDir, 'include' => [$file]);
      $planItemNode->getChild('content', 1, 1)->addChild($cid) if $cid;
    }
  }
  my $applicableNode = XmlNode->new( 'applicable' );
  $applicableNode->addChild( XmlNode->new( 'applicable-to-subscription' ) ) if ( $planItemPtr->{'applicableToSubscription'} == 1 );
  $applicableNode->addChild( XmlNode->new( 'applicable-to-site'         ) ) if ( $planItemPtr->{'applicableToSite'        } == 1 );
  $applicableNode->addChild( XmlNode->new( 'applicable-to-email'        ) ) if ( $planItemPtr->{'applicableToEmail'       } == 1 );
  $planItemNode->addChild( $applicableNode );

  my $propertiesNode = XmlNode->new( 'properties' );
  while ( my ($name,$value) = each( %{$planItemPropsPtr} ) ) {
    next if ($name eq '');
    my $planItemPropertyNode = XmlNode->new( 'plan-item-property' );
    $planItemPropertyNode->setAttribute( 'name', $name);
    if ( $value ne '' ) {
      $planItemPropertyNode->setText( $value );
    }
    $propertiesNode->addChild( $planItemPropertyNode );
  }
  $planItemNode->addChild( $propertiesNode );

  return $planItemNode;
}

sub addPlanItemToServer {
  my ( $self, $planItemPtr, $planItemPropsPtr, $customButtonsDir ) = @_;
  my $root = $self->{serverNode};

  my $node = $self->makePlanItemNode( $planItemPtr, $planItemPropsPtr, $customButtonsDir );

  $root->getChild( 'account-templates', 1 )->addChild($node) if defined $node;
}

sub setServerSSO {
  my ( $self, $paramsPtr, $cert, $idpCert ) = @_;

  my %params = %{$paramsPtr};

  my $root = $self->{serverNode};

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

  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 ( $cert )
  {
    $ssoNode->addChild( XmlNode->new( 'sso-certificate', 'content' => $cert ) );
  }

  if ( $idpCert ) {
    $ssoNode->addChild(
      XmlNode->new( 'sso-idp-cert', 'content' => $idpCert ) );
  }

  $root->addChild($ssoNode);
}

sub setServerSSOBranding {
  my ( $self, $brandingPtr ) = @_;

  my @branding = @{$brandingPtr};

  my $root = $self->{serverNode};

  my @ssoNodes = $root->getChildren('sso-settings');
  if ( scalar(@ssoNodes) < 1 ) {
    Logging::warning("Unable to dump SSO branding settings, because of dump XML structure is not full", 'PackerStructure');
    return;
  }
  my $ssoNode = $ssoNodes[0];

  my $ssoBrandingNode = XmlNode->new('sso-branding');
  if ( @branding ) {
    for my $ptrRow (@branding) {
      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);
  }
}

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

  my $root = $self->{serverNode};
  return $root->getChild('application-vault', 1);
}

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

  my $appVaultNode = $self->setServerAppVault();
  return $appVaultNode->getChild('sapp-packages-pool', 1);
}

my %dumpedApplications;

sub addServerAppPackage {
  my ($self, $name, $version, $release, $distrib_path, $file_name, $is_uploaded, $is_visible, $applicationPackage) = @_;

  my $fullname = $name."-".$version."-".$release;
  if (exists $dumpedApplications{$fullname}) {
        return;
  }

  $dumpedApplications{$fullname} = 1;

  my $appPackagesPoolNode = $self->setServerPackagesPool();

  my $packageNode = XmlNode->new('sapp-package');
  if ( defined $applicationPackage and $applicationPackage->getSappPackageId() ) {
      $packageNode->addChild(  XmlNode->new( 'sapp-package-id', 'content' => $applicationPackage->getSappPackageId() ) );
  }
  $packageNode->addChild(  XmlNode->new( 'sapp-name', 'content' => $name ) );
  $packageNode->addChild(  XmlNode->new( 'sapp-version', 'content' => $version ) )  if ( $version );
  $packageNode->addChild( XmlNode->new( 'sapp-release', 'content' => $release ) )  if ( $release );
  $packageNode->addChild( XmlNode->new( 'sapp-uploaded' ))  if ( defined $is_uploaded && $is_uploaded ne "0" );
  $packageNode->addChild( XmlNode->new( 'sapp-visible' ))  if ( defined $is_visible && $is_visible ne "0" );


  if( $distrib_path && $file_name )
  {
    my $real_path = $distrib_path . "/" . $file_name;

    if ( -e $real_path ) {
      my $cid = $self->{content_transport}->addAdminContent(
        'sapp-distrib',
        undef,
        "sapp-distrib." . HelpFuncs::generateProcessId(),
        "directory" => $distrib_path,
        "include"   => [$file_name]
      );
      $packageNode->getChild( 'content', 1, 1 )->addChild( $cid ) if $cid;
    }
    else { return; }

    if ( defined $applicationPackage ) {
      my $settings = $applicationPackage->getSettings();
      if ( $settings ) {
        my $settingsNode = XmlNode->new('sapp-settings');
        foreach my $setting ( keys %{$settings} ) {
            my $settingNode = XmlNode->new('setting');
            $settingNode->addChild( XmlNode->new( 'name', 'content' => $setting ) );
            if (ref($settings->{$setting}) =~ /ARRAY/) {
              foreach my $value ( @{$settings->{$setting}}) {
                $settingNode->addChild( XmlNode->new( 'value', 'content' => $value ) );
              }
            } else {
              $settingNode->addChild( XmlNode->new( 'value', 'content' => $settings->{$setting} ) );
            }
            $settingsNode->addChild($settingNode);
        }
        $packageNode->addChild($settingsNode);
      }
    }

    $appPackagesPoolNode->addChild($packageNode);
  }
}

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

  my $appVaultNode = $self->setServerAppVault();
  return $appVaultNode->getChild('sapp-items-pool',1);
}

sub addServerAppItem {
  my ($self, $paramsPtr) = @_;

  my %params = %{$paramsPtr};

  my $appItemsPoolNode = $self->setServerAppItemsPool();

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

  $appItemsPoolNode->addChild($appItemNode);
}

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

  my $appVaultNode = $self->setServerAppVault();
  return $appVaultNode->getChild('sapp-licenses-pool',1);
}

sub addServerAppLicense {
  my ($self, $keyNumber, $licenseType, $licenseText) = @_;

  my $licensePoolNode = $self->setServerAppLicensesPool();

  my $licenseNode = XmlNode->new(
    'sapp-license',
    'children' => [
      XmlNode->new( 'key-number',   'content' => $keyNumber ),
      XmlNode->new( 'license-type', 'content' => $licenseType ),
      XmlNode->new( 'license-text', 'content' => $licenseText )
    ]
  );

  $licensePoolNode->addChild($licenseNode);
}

sub setServerSBConfig {
  my ( $self, $configPtr ) = @_;

  my @config = @{$configPtr};

  my $serverNode = $self->{serverNode};

  my $sbNode = XmlNode->new('sb-config');
  for my $ptrHash (@config) {
    $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);
}

sub setGLServerSettings {
  my ( $self, $paramsPtr) = @_;

  my %params = %{$paramsPtr};

  my $serverNode = $self->{serverNode};

  my $glNode = XmlNode->new('grey-listing');

  $glNode->setAttribute('grey-interval', $params{'greyInterval'}) if defined $params{'greyInterval'};
  $glNode->setAttribute('expire-interval', $params{'expireInterval'}) if defined $params{'expireInterval'};
  $glNode->setAttribute('penalty-enabled', $params{'penaltyEnabled'}) if defined $params{'penaltyEnabled'};
  $glNode->setAttribute('penalty-interval', $params{'penaltyInterval'}) if defined $params{'penaltyInterval'};
  $glNode->setAttribute('enabled', $params{'enabled'}) if defined $params{'enabled'};
  $glNode->setAttribute('personal-conf', $params{'personal-conf'}) if defined $params{'personal-conf'};

  my $wlNode = XmlNode->new('white-list');

  foreach my $wdomain (@{$params{'white_domains'}}) {
        $wlNode->addChild(XmlNode->new('list-item', 'content' => $wdomain));
  }

  $glNode->addChild($wlNode);

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

  foreach my $bdomain (@{$params{'black_domains'}}) {
        $blNode->addChild(XmlNode->new('list-item', 'content' => $bdomain));
  }

  $glNode->addChild($blNode);

  $serverNode->addChild($glNode);

}

sub setControlsVisibility {
  my ( $self, $paramsPtr ) = @_;

  my %params = %{$paramsPtr};

  my $serverNode = $self->{serverNode};
  my $controlsVisibilityNode = XmlNode->new('controls-visibility');

  $controlsVisibilityNode->addChild( XmlNode->new( 'hide-domain-registration-buttons', 'content' => HelpFuncs::negation(HelpFuncs::checkValue($params{'domain_registration'},'true','false')) ) );
  $controlsVisibilityNode->addChild( XmlNode->new( 'hide-certificate-purchasing-buttons', 'content' => HelpFuncs::negation(HelpFuncs::checkValue($params{'cert_purchasing'},'true','false')) ) );
  $controlsVisibilityNode->addChild( XmlNode->new( 'hide-extra-services-buttons', 'content' => HelpFuncs::negation(HelpFuncs::checkValue($params{'extras'},'true','false')) ) );
  $controlsVisibilityNode->addChild( XmlNode->new( 'hide-mail-bouncing-controls', 'content' => HelpFuncs::negation(HelpFuncs::checkValue($params{'mail_bounce'},'true','false')) ) );
  $controlsVisibilityNode->addChild( XmlNode->new( 'hide-newsfeeds', 'content' => HelpFuncs::negation(HelpFuncs::checkValue($params{'newsfeeds'},'true','false')) ) );
  $controlsVisibilityNode->addChild( XmlNode->new( 'hide-virtuozzo-promotion', 'content' => HelpFuncs::negation(HelpFuncs::checkValue($params{'promo_virtuozzo'},'true','false')) ) );

  $controlsVisibilityNode->addChild( XmlNode->new( 'domain-registration-url', 'content' => $params{'domain_registration_url'})) if defined $params{'domain_registration_url'};
  $controlsVisibilityNode->addChild( XmlNode->new( 'domain-management-url', 'content' => $params{'domain_management_url'})) if defined $params{'domain_management_url'};
  $controlsVisibilityNode->addChild( XmlNode->new( 'cert-purchasing-url', 'content' => $params{'cert_purchasing_url'})) if defined $params{'cert_purchasing_url'};
  $controlsVisibilityNode->addChild( XmlNode->new( 'mpc-portal-url', 'content' => $params{'mpc_portal_url'})) if defined $params{'mpc_portal_url'};

  $serverNode->getChild( 'interface-preferences', 1 )->addChild($controlsVisibilityNode);
}

sub setServerBackupSettings {
  my ( $self, $paramsPtr ) = @_;

  my %params = %{$paramsPtr};

  my $serverNode = $self->{serverNode};

  my ($lowPriority,$doNotCompress,$maxProcesses);

  $lowPriority = $params{'bu_nice'};
  $doNotCompress = $params{'bu_nozip'};
  $maxProcesses = $params{'max_bu_proc_number'};

  if ( defined $lowPriority or defined $doNotCompress or defined $maxProcesses ) {
  	my $serverBackupSettingsNode = XmlNode->new('backup-settings');

  	$serverBackupSettingsNode->setAttribute( 'low-priority', $lowPriority)
			if defined $lowPriority and $lowPriority eq 'true';

  	$serverBackupSettingsNode->setAttribute( 'do-not-compress', $doNotCompress)
			if defined $doNotCompress and $doNotCompress eq 'true';

		$serverBackupSettingsNode->setAttribute( 'max-processes', $maxProcesses)
  		if defined $maxProcesses;

  	$serverNode->addChild($serverBackupSettingsNode);
  }
}

sub addServerPreferences {
  my ( $self, $paramsPtr ) = @_;

  my %params = %{$paramsPtr};

  my $serverNode = $self->{serverNode};

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

  if ( defined $params{'hide_top_advertisement'} ) {
    $serverPrefsNode->addChild(
      XmlNode->new(
        'hide-top-advertisement',
        'attributes' => { 'hide' => $params{'hide_top_advertisement'} }
      )
    );
  }
  if ( defined $params{'traffic_accounting'} ) {
    my $val = $params{'traffic_accounting'};
    if    ( $val == 1 ) { $val = 'in'; }
    elsif ( $val == 2 ) { $val = 'out'; }
    else                { $val = 'both'; }
    $serverPrefsNode->addChild(
      XmlNode->new(
        'traffic-direction', 'attributes' => { 'traffic' => $val }
      )
    );
  }
  if ( defined $params{'restart_apache_interval'} ) {
    $serverPrefsNode->addChild(
      XmlNode->new(
        'restart-apache', 'content' => $params{'restart_apache_interval'}
      )
    );
  }
  if ( defined $params{'stat_ttl'} ) {
    $serverPrefsNode->addChild(
      XmlNode->new( 'stat-keep', 'content' => $params{'stat_ttl'} ) );
  }
  if ( defined $params{'size_count_type'} ) {
    $serverPrefsNode->addChild(
      XmlNode->new(
        'disk-space-count-type',
        'attributes' => {
            'count-type' => $params{'size_count_type'} eq 'byte'
          ? 'byte'
          : 'block'
        }
      )
    );
  }

  my $duNode = XmlNode->new('disk-usage');
  $serverPrefsNode->addChild($duNode);
  $duNode->addChild( XmlNode->new('include-logs') )
    if defined $params{'include_logs'}
      and $params{'include_logs'} eq 'true';
  $duNode->addChild( XmlNode->new('include-databases') )
    if defined $params{'include_databases'}
      and $params{'include_databases'} eq 'true';
  $duNode->addChild( XmlNode->new('include-mailboxes') )
    if defined $params{'include_mailboxes'}
      and $params{'include_mailboxes'} eq 'true';
  $duNode->addChild( XmlNode->new('include-webapps') )
    if defined $params{'include_webapps'}
      and $params{'include_webapps'} eq 'true';
  $duNode->addChild( XmlNode->new('include-maillists') )
    if defined $params{'include_maillists'}
      and $params{'include_maillists'} eq 'true';
  $duNode->addChild( XmlNode->new('include-domaindumps') )
    if defined $params{'include_domaindumps'}
      and $params{'include_domaindumps'} eq 'true';
  $duNode->addChild( XmlNode->new('include-admindumps') )
    if defined $params{'include_admindumps'}
      and $params{'include_admindumps'} eq 'true';

  $serverPrefsNode->addChild( XmlNode->new( 'force-db-prefix', 'content' => $params{'force_db_prefix'} ) ) if defined $params{'force_db_prefix'};
  $serverPrefsNode->addChild( XmlNode->new( 'multiple-session', 'content' => $params{'multiply_login'} ) ) if defined $params{'multiply_login'};
  $serverPrefsNode->addChild( XmlNode->new( 'lock-screen', 'content' => ($params{'disable_lock_screen'} eq 'true' ? 'false' : 'true' ) ) ) if defined $params{'disable_lock_screen'};

  $serverPrefsNode->addChild( XmlNode->new( 'aps-catalog-url', 'content' => $params{'apscatalog_url'} ) ) if defined $params{'apscatalog_url'};

  $serverPrefsNode->addChild( XmlNode->new( 'mode', 'content' => (defined($params{'power_user_panel'}) and $params{'power_user_panel'} eq 'true') ? 'poweruser' : 'standard'));

  $serverNode->addChild($serverPrefsNode);
}

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

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

  if (Mailman::isMailmanNotConfigured() eq '1') {
    Logging::debug("Mailman is not configured. Nothing to backup");
    return;
  }

  my $adminPassword =  Mailman::getListPassword('mailman');
  my @owners = Mailman::getListOwners('mailman');

  if (defined $adminPassword and defined $owners[0]) {
    my $root = $self->{serverNode};

    my $mailmanNode = XmlNode->new('mailman');

    $mailmanNode->setAttribute('owner', $owners[0]);
    $mailmanNode->setAttribute('password', $adminPassword);

    $root->addChild($mailmanNode);
  }
}

sub addDisableMailUiOption {
  my ($self, $state) = @_;
  my $serverNode = $self->{serverNode};
  my $serverPrefNode = $serverNode->getChild( 'server-preferences', 1 );
  my $disableUiNode = XmlNode->new('disable-mail-ui', 'content' => $state);
  $serverPrefNode->addChild($disableUiNode);
}

sub setExternalWebmails {
  my ($self, $webmailsPtr) = @_;
  my $serverNode = $self->{serverNode};
  my $mailPrefNode = $serverNode->getChild( 'mail-settings', 1 );
  my @webmails = @{$webmailsPtr};

  foreach my $webmail (@webmails) {
    my $extWebMailNode = XmlNode->new('external-webmail');
    $extWebMailNode->addChild( XmlNode->new('name', 'content' => $webmail->{'name'}) );
    $extWebMailNode->addChild( XmlNode->new('url', 'content' => $webmail->{'url'}) );
    $extWebMailNode->addChild( XmlNode->new('enabled') ) if $webmail->{'enabled'} == 1;
    $mailPrefNode->addChild($extWebMailNode);
  }
}

sub addRestrictionItem {
  my ( $self, $type, $ip, $mask ) = @_;

  $ip = '' unless defined $ip;
  $mask = '' unless defined $mask;

  my $serverNode = $self->{serverNode};

  my $serverPrefNode = $serverNode->getChild( 'server-preferences', 1 );

  my $adminAccessRestrictionsNode = $serverPrefNode->getChild( 'admin-access-restrictions', 1 );

  my $restrictionItemNode = XmlNode->new( 'restriction-item' );
  $restrictionItemNode->setAttribute( 'type', $type eq 'allow'? 'allow' : 'deny' );
  $restrictionItemNode->setAttribute( 'ip-address', $ip );
  $restrictionItemNode->setAttribute( 'ip-subnet-mask', $mask );

  $adminAccessRestrictionsNode->addChild( $restrictionItemNode );
}

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

  return if defined $self->{admin};

  my $rootNode = $self->{root};
  if ( !defined($rootNode) ) {
    Logging::warning('Unable to dump Panel admin user, because of dump XML structure is not full', 'PackerStructure');
    return;
  }
  my $root = XmlNode->new( 'admin' );
  $self->{admin} = $root;
  $root->setAttribute( 'id', $id ) if $id;
  $root->setAttribute( 'guid', $guid ) if $guid;

  $rootNode->addChild($root);
}

### Client functions ##

sub addRootClient {
  my ( $self, $clientId, $clientPtr, $passwdPtr, $status ) = @_;

  my $rootNode = $self->{root};
  if ( !defined($rootNode) ) {
    Logging::warning('Unable to dump root client user, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $root = $self->makeClientNode($clientId, $clientPtr, $passwdPtr, $status, 1);

  $rootNode->addChild($root);
  push @{$self->{roots}}, $root;
}

sub addRootReseller{
  my ( $self, $clientId, $clientPtr, $passwdPtr, $status ) = @_;

  my $rootNode = $self->{root};
  if ( !defined($rootNode) ) {
    Logging::warning('Unable to dump reseller, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $root = $self->makeResellerNode($clientId, $clientPtr, $passwdPtr, $status, 1);

  $rootNode->addChild($root);
  push @{$self->{roots}}, $root;
}

sub addResellerClient {
  my ( $self, $resellerId, $clientId, $clientPtr, $passwdPtr, $status ) = @_;

  my %client = %{$clientPtr};
  my %passwd = %{$passwdPtr};

  my $resellerNode = $self->{resellersNodes}->{$resellerId};
  if (!defined($resellerNode))
  {
    Logging::warning('Unable to dump reseller\'s client, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $root = $self->makeClientNode($clientId, \%client, \%passwd, $status, 0);

  $resellerNode->getChild( 'clients', 1 )->addChild($root);
  $self->regClientObjectBackupPath( $self->getResellersBackupPath( $resellerId ), $clientId, $clientPtr->{'login'} );

  if (exists $self->{resellersShortNodes}->{$resellerId}) {
    $self->{resellersShortNodes}->{$resellerId}->addChild($self->{clientsShortNodes}->{$clientId});
  }
}

sub addAdminClient {
  my ( $self, $clientId, $clientPtr, $passwdPtr, $status ) = @_;

  my %client = %{$clientPtr};
  my %passwd = %{$passwdPtr};

  my $admin = $self->{admin};
  if (!defined($admin))
  {
    Logging::warning('Unable to dump admin\'s client, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $root = $self->makeClientNode($clientId, \%client, \%passwd, $status, 0);

  $admin->getChild( 'clients', 1 )->addChild($root);
  $self->regClientObjectBackupPath( $self->getAdminBackupPath(), $clientId, $clientPtr->{'login'} );
}

sub addAdminReseller {
  my ( $self, $clientId, $clientPtr, $passwdPtr, $status ) = @_;

  my $admin = $self->{admin};
  if (!defined($admin))
  {
    Logging::warning('Unable to dump admin\'s reseller, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $root = $self->makeResellerNode($clientId, $clientPtr, $passwdPtr, $status, 0);

  $admin->getChild( 'resellers', 1 )->addChild($root);
  $self->regResellersObjectBackupPath( $self->getAdminBackupPath(), $clientId, $clientPtr->{'login'} );
}

sub makeResellerNode{
  my ( $self, $clientId, $clientPtr, $passwdPtr, $status, $needFullInfo ) = @_;

  my $root = $self->processClientNode( $clientId, $clientPtr, $passwdPtr, $status, 1, $needFullInfo );

  return $root;

}

sub makeClientNode {
  my ( $self, $clientId, $clientPtr, $passwdPtr, $status, $needFullInfo ) = @_;

  my $root = $self->processClientNode( $clientId, $clientPtr, $passwdPtr, $status, 0, $needFullInfo );

  return $root;
}

sub processClientNode {
  my ( $self, $clientId, $clientPtr, $passwdPtr, $status, $is_reseller, $needFullInfo ) = @_;

  my $root = XmlNode->new( $is_reseller ? 'reseller' : 'client');

  $root->getChild( 'preferences', 1 );
  $root->getChild( 'properties', 1 );

  my %client = %{$clientPtr};
  my %passwd = %{$passwdPtr};

  $root->setAttribute( 'id', $clientId );

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

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

  $root->setAttribute( 'external-id', $client{'external_id'} ) if ( defined $client{'external_id'} and $client{'external_id'} ne '');

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

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

  if ( exists $client{'login'} ) {
    $root->setAttribute( 'name', ( defined $client{'login'} )? $client{'login'}:'' );
  }
  if ( exists $client{'pname'} ) {
    $root->setAttribute( 'contact', ( defined $client{'pname'} )? $client{'pname'}:'' );
  }
  if ( exists $client{'cr_date'} ) {
    $root->setAttribute( 'cr-date', ( defined $client{'cr_date'} )? $client{'cr_date'}:'' );
  }

  my $item;
  my $props = $root->getChild( 'properties', 1 );
  $item = CommonPacker::makePasswordNode( $passwd{'password'}, CommonPacker::normalizePasswordType( $passwd{'type'} ) );
  $props->addChild($item);

  $props->addChild( Status::make($status) );

  $self->{clientNodes}->{$clientId} = $root;
  $self->{resellersNodes}->{$clientId} = $root if $is_reseller;

  if ($needFullInfo or $self->{listingOnlyMode}) {
    return $root;
  } else {
    my $shortInfoNodeName = ($is_reseller ? 'reseller' : 'client') . '-info';
    my $shortInfoNode = XmlNode->new($shortInfoNodeName);
    $shortInfoNode->setAttribute('name', ( defined $client{'login'} )? $client{'login'}:'');
    $shortInfoNode->setAttribute( 'guid', $client{'guid'} ) if defined $client{'guid'};
    if ($is_reseller) {
      $self->{resellersShortNodes}->{$clientId} = $shortInfoNode;
    } else {
      $self->{clientsShortNodes}->{$clientId} = $shortInfoNode;
    }
    return $shortInfoNode;
  }

}

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

  my $root = $self->{clientNodes}->{$clientId};
  if ( !defined($root) ) {
    Logging::warning('Unable to finish client node, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  #TODO: uncomment
  #$root->ReleaseCode();
}

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

  my %client = %{$clientPtr};

  my $root = $self->{clientNodes}->{$clientId};
  if ( !defined($root) ) {
    Logging::warning('Unable to dump client personal information, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my ( $field, $name );

  my %clientsInfo = (
    'company' => 'cname',
    'phone'   => 'phone',
    'fax'     => 'fax',
    'address' => 'address',
    'city'    => 'city',
    'state'   => 'state',
    'zip'     => 'pcode',
    'country' => 'country',
    'email'   => 'email',
  );
  my $prefs = $root->getChild( 'preferences', 1 );
  while ( ( $name, $field ) = each(%clientsInfo) ) {
    if ( exists( $client{$field} ) && $client{$field} ) {
      $prefs->addChild(
        XmlNode->new(
          'pinfo',
          'content'    => $client{$field},
          'attributes' => { 'name' => $name }
        )
      );
    }
  }
}

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

  my $root = $self->{clientNodes}->{$clientId};
  if ( !defined($root) ) {
    Logging::warning('Unable to dump client locale, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  $root->getChild( 'preferences', 1 )->addChild(
    XmlNode->new(
      'pinfo',
      'content'    => $locale,
      'attributes' => { 'name' => 'locale' }
    )
  );
}

sub addClientLimit {
  my ( $self, $clientId, $name, $value ) = @_;

  my $root = $self->{clientNodes}->{$clientId};
  if ( !defined($root) ) {
    Logging::warning('Unable to dump client limits, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  $self->insertLimitNode( $root, $name, $value );
}

sub addClientPermission {
  my ( $self, $clientId, $name, $value ) = @_;

  my $root = $self->{clientNodes}->{$clientId};
  if ( !defined($root) ) {
    Logging::warning('Unable to dump client permissions, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  $self->makePermissionNode( $root->getChild( 'limits-and-permissions', 1 ), $name, $value );
}

sub addClientDomainSkeleton {
  my ( $self, $clientId, $path, $arc_path ) = @_;

  my $root = $self->{clientNodes}->{$clientId};
  if ( !defined($root) ) {
    Logging::warning('Unable to dump client\'s domain skeleton, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $dumpFile = $self->{content_transport}->addClientContent( 'skeleton', $clientId, $arc_path, 'directory' => $path );
  $root->getChild( 'content', 1, 1 )->addChild( $dumpFile) if $dumpFile;
}

sub addServerSkeleton {
  my ( $self, $path, $arc_path) = @_;

  my $root = $self->{serverNode};

  my $dumpFile = $self->{content_transport}->addAdminContent('skeleton', undef, $arc_path, 'directory' => $path);

  $root->getChild('content', 1, 1)->addChild($dumpFile) if $dumpFile;
}

sub addSmbDbDump {
  my ($self, $options) = @_;
  my $root = $self->{serverNode};
  $options->{utf8names} = 1;
  my $cid = $self->{content_transport}->addDbContent('smb-sqldump', $self->getAdminBackupPath('smbdb'), %{$options});
  $root->getChild('content', 1, 1)->addChild($cid) if $cid;
}

sub addApsCache {
  my ($self, $path) = @_;
  my $root = $self->{serverNode};
  my $dumpFile = $self->{content_transport}->addAdminContent('aps-cache', undef, 'aps_cache', 'directory' => $path, 'checkEmptyDir' => 1 );
  $root->getChild('content', 1, 1)->addChild($dumpFile) if $dumpFile;
}

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

  my $root = $self->{clientNodes}->{$clientId};
  if ( !defined($root) ) {
    Logging::warning('Unable to dump reseller\'s clients and domains, because of dump XML structure is not full', 'PackerStructure');
    return;
  }
  $root->addChild( XmlNode->new('domains') );
  $root->addChild( XmlNode->new('clients') );
}

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

  my %ips = %{$ipsPtr};

  my $root = $self->{clientNodes}->{$clientId};
  if ( !defined($root) ) {
    Logging::warning('Unable to dump client\'s IP settings, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

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

  while( my($ip,$iptype) = each(%ips) ){
   $ip_pool->addChild( $self->makeIpNode($ip, $iptype) );
  }
}

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

  my $parent = $self->{clientNodes}->{$clientId};
  if ( !defined($parent) ) {
    Logging::warning('Unable to dump client traffic, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  $parent->addChild( XmlNode->new( 'traffic', 'content' => $trafficValue ) );
}

sub addResellerDomainTemplate {
  my $self = shift;
  $self->addClientDomainTemplate( @_ );
}

sub addClientDomainTemplate {
  my ( $self, $clientId, $templateName, $dataPtr ) = @_;

  my $root = $self->{clientNodes}->{$clientId};

  my $node = $self->makeTemplateNode('domain-template', $templateName, $dataPtr);

  $root->getChild( 'preferences', 1 )->addChild($node);
}

sub addClientCustomButton {
    my ( $self, $clientId, $id, $optionsPtr, $customButtonsDir, $icon ) = @_;

    my $parent = $self->{clientNodes}->{$clientId};
    if ( !defined($parent) ) {
      Logging::warning('Unable to dump client\'s custom buttons settings, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

    my $node = $self->makeCustomButtonNode( 'clients', $clientId, $id, $optionsPtr, $customButtonsDir,
      $icon );

    $parent->getChild( 'preferences', 1 )->addChild($node);
}

sub setPpbConnection {
    my ($self, $clientParams) = @_;

    my $ppbUrl = undef;
    my $apiVersion = undef;

    my $parent;

    $parent = $self->{serverNode};

    $ppbUrl = $clientParams->{'ppb-url'} if exists $clientParams->{'ppb-url'};

    if (defined $ppbUrl && defined $apiVersion) {
        my $ppbConnectionNode = XmlNode->new('ppb-connection');
        $ppbConnectionNode->addChild(XmlNode->new('ppb-url', 'content' => $ppbUrl));
        $parent->getChild( 'properties', 1)->addChild($ppbConnectionNode);
    }
}

### Domain functions ##
my @_rootDomains;

sub addRootDomain {
  my ( $self, $domainId, $domainName, $domainAsciiName, $domainPtr, $ownerGuid ) = @_;
  $domainAsciiName = $domainName if not $domainAsciiName;

  my $needFullInfo = !$self->{migrationMode} || $self->{listingOnlyMode};
  my $root = $self->makeDomainNode($domainId, $domainName, $domainPtr, 0, $needFullInfo);
  push @_rootDomains, $root;
}

sub addRootDomains {
  my $self = shift;

  my $rootNode = $self->{root};
  if ( !defined($rootNode) ) {
    Logging::warning('Unable to dump root domains, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  for my $domainNode ( @_rootDomains ) {
    $rootNode->addChild($domainNode);
    push @{$self->{roots}}, $domainNode;
  }
}

sub addAdminDomain {
    my ( $self, $domainId, $domainName, $domainAsciiName, $domainPtr ) = @_;
    $domainAsciiName = $domainName if not $domainAsciiName;

    my $admin = $self->{admin};
    if ( !defined($admin) ) {
      Logging::warning('Unable to dump admin\'s domain, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

    my $root = $self->makeDomainNode($domainId, $domainName, $domainPtr, 0, 0);

    $admin->getChild( 'domains', 1 )->addChild($root);
    $self->regDomainObjectBackupPath( $self->getAdminBackupPath(), $domainId, $domainAsciiName );

}

sub addResellerDomain {
    my ( $self, $resellerId, $domainId, $domainName, $domainAsciiName, $domainPtr ) = @_;
    $domainAsciiName = $domainName if not $domainAsciiName;

    my $resellerNode = $self->getCashedClientNode($resellerId);
    return unless defined $resellerNode;

    my $root = $self->makeDomainNode($domainId, $domainName, $domainPtr, 0, 0);

    $resellerNode->getChild( 'domains', 1 )->addChild($root);
    $self->regDomainObjectBackupPath( $self->getResellersBackupPath( $resellerId ), $domainId, $domainAsciiName );

    if (exists $self->{resellersShortNodes}->{$resellerId}) {
      $self->{resellersShortNodes}->{$resellerId}->addChild($self->{domainShortNodes}->{$domainId});
    }
}

sub addClientDomain {
    my ( $self, $clientId, $domainId, $domainName, $domainAsciiName, $domainPtr ) = @_;
    $domainAsciiName = $domainName if not $domainAsciiName;

    my $clientNode = $self->getCashedClientNode($clientId);
    return unless defined $clientNode;

    my $root = $self->makeDomainNode($domainId, $domainName, $domainPtr, 0, 0);

    $clientNode->getChild( 'domains', 1 )->addChild($root);
    $self->regDomainObjectBackupPath( $self->getClientsBackupPath( $clientId ), $domainId, $domainAsciiName );

    if (exists $self->{clientsShortNodes}->{$clientId}) {
      $self->{clientsShortNodes}->{$clientId}->addChild($self->{domainShortNodes}->{$domainId});
    }
}

my %_rootUsers;
my %_rootRoles;

sub addDomainSite {
    my ( $self, $domainId, $siteId, $siteAsciiName, $siteName, $sitePtr ) = @_;

    my $domainNode = $self->{domainNodes}->{$domainId};
    if ( !defined($domainNode) ) {
      Logging::warning('Unable to dump domain site, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

    my $siteNode = $self->makeDomainNode($siteId, $siteName, $sitePtr, 1, 1);
    my $phostingNode = $domainNode->getChild( 'phosting', 1 );
    $phostingNode->getChild( 'sites', 1 )->addChild($siteNode);
    $self->regSiteObjectBackupPath( $self->getDomainsBackupPath( $domainId ), $siteId, $siteAsciiName );
}

sub addAdminUser {
    my ( $self, $userName, $userPtr ) = @_;

    my $adminNode = $self->{admin};
    if ( !defined($adminNode) ) {
      Logging::warning('Unable to dump admin user, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

    my $userNode = $self->makeUserNode($userName, $userPtr);
    $adminNode->getChild( 'users', 1 )->addChild($userNode) if defined $userNode;
}

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

  my $adminNode = $self->{admin};
  if ( !defined($adminNode) ) {
    Logging::warning('Unable to dump admin permissions, because of dump XML structure is not full', 'PackerStructure');
    return;
  }
  $self->makePermissionNode( $adminNode->getChild( 'limits-and-permissions', 1 ), $name, $value );
}

sub addClientUser {
    my ( $self, $ownerId, $userName, $userPtr ) = @_;

    my $clientNode = $self->getCashedClientNode($ownerId);
    return unless defined $clientNode;

    my $userNode = $self->makeUserNode($userName, $userPtr);
    $clientNode->getChild( 'users', 1 )->addChild($userNode) if defined $userNode;
}

sub addRootUser {
    my ( $self, $userName, $userPtr ) = @_;

    if (!exists $_rootUsers{$userName}) {
      my $userNode = $self->makeUserNode($userName, $userPtr);
      $_rootUsers{$userName} = $userNode if defined $userNode;
    }
}

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

    my $rootNode = $self->{root};
    if ( !defined($rootNode) ) {
      Logging::warning('Unable to dump root users, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

    if (%_rootUsers) {
      foreach my $userName (keys %_rootUsers) {
        $rootNode->addChild($_rootUsers{$userName});
      }
    }
}

sub addRoleToRoles {
    my ( $self, $roleNode, $rolesNode ) = @_;

    my @roles  = $rolesNode->getChildren( 'role' );
    foreach my $role ( @roles ) {
      return if $role->getAttribute('name') eq $roleNode->getAttribute('name');
    }
    $rolesNode->addChild( $roleNode );
}

sub addAdminRole {
    my ( $self, $roleName, $isBuiltIn, $permsPtr, $servicePermissions ) = @_;

    my $adminNode = $self->{admin};
    if ( !defined($adminNode) ) {
      Logging::warning('Unable to dump admin role, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

    my $roleNode = $self->makeRoleNode($roleName, $isBuiltIn, $permsPtr, $servicePermissions);
    $self->addRoleToRoles( $roleNode, $adminNode->getChild( 'roles', 1 ) );
}

sub addClientRole {
  my ( $self, $ownerId, $roleName, $isBuiltIn, $permsPtr, $servicePermissions ) = @_;

  my $clientNode = $self->getCashedClientNode($ownerId);
  return unless defined $clientNode;

  my $roleNode = $self->makeRoleNode($roleName, $isBuiltIn, $permsPtr, $servicePermissions);
  $self->addRoleToRoles( $roleNode, $clientNode->getChild( 'roles', 1 ) );
}

sub addRootRole {
  my ( $self, $roleName, $isBuiltIn, $permsPtr, $servicePermissions ) = @_;

  if (!exists $_rootRoles{$roleName}) {
      my $roleNode = $self->makeRoleNode($roleName, $isBuiltIn, $permsPtr, $servicePermissions);
      $_rootRoles{$roleName} = $roleNode if defined $roleNode;
    }
}

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

  my $rootNode = $self->{root};
  if ( !defined($rootNode) ) {
    Logging::warning('Unable to dump root roles, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  if (%_rootRoles) {
    foreach my $roleName (keys %_rootRoles) {
      $self->addRoleToRoles( $_rootRoles{$roleName}, $rootNode );
    }
  }
}

sub getCashedClientNode {
  my ($self, $clientId) = @_;
  my $root = $self->{resellersNodes}->{$clientId};
  unless ( defined($root) ) {
    $root = $self->{clientNodes}->{$clientId};
  }
  return $root if defined($root);

  my $caller = (caller(1))[3];
  if (defined $caller) {
    Logging::warning("Error in $caller : empty client parent node for id ($clientId)", 'PackerStructure');
  }
  else {
    Logging::warning('Unable to dump client settings, because of dump XML structure is not full', 'PackerStructure');
  }
  return;
}

my %_requiredLimits = (
  domain => ['max_subdom', 'max_dom_aliases', 'disk_space', 'max_traffic', 'max_wu', 'max_db',
             'max_box', 'mbox_quota', 'max_maillists', 'max_webapps', 'expiration', 'max_site'],
  client => ['max_dom', 'max_subdom', 'max_dom_aliases', 'disk_space', 'max_traffic', 'max_wu',
             'max_db', 'max_box', 'mbox_quota', 'max_maillists', 'max_webapps', 'expiration'],
  reseller => ['max_cl', 'max_dom', 'max_subdom', 'max_dom_aliases', 'disk_space', 'max_traffic', 'max_wu',
               'max_db', 'max_box', 'mbox_quota', 'max_maillists', 'max_webapps', 'expiration']
);

sub fixDefaultLimits {
  my ($self, $type, $id) = @_;
  my $node;
  my $requiredLimits;

  if ( ($type eq 'client') || ($type eq 'reseller') ) {
    $node = $self->getCashedClientNode($id);
  }
  elsif ($type eq 'domain') {
    $node = $self->getCashedDomainNode($id);
  }
  return unless defined $node;

  $requiredLimits = $_requiredLimits{$type};

  my %usedLimits;
  XPath::Select $node, 'limits-and-permissions/limit', sub {
    $usedLimits{ shift->getAttribute( 'name' ) } = 1;
  };

  for my $name ( @{$requiredLimits} ) {
    if ( !exists( $usedLimits{$name} ) ) {
      my $value = ( $name eq 'max_site' ) ? '1' : '-1';
      $self->insertLimitNode( $node, $name, $value );
    }
  }
}

my %smbUserPinfo = (
  'phone'         => 'phone',
  'phoneType'     => 'phone-type',
  'phone2'        => 'phone2',
  'phone2Type'    => 'phone2-type',
  'phone3'        => 'phone3',
  'phone3Type'    => 'phone3-type',
  'imNumber'      => 'im',
  'imType'        => 'im-type',
  'additionalInfo' =>  'comment',
  'email'         =>  'email',
  'companyName'   =>  'company',
  'fax'           =>  'fax',
  'address'       =>  'address',
  'city'          =>  'city',
  'state'         =>  'state',
  'zip'           =>  'zip',
  'country'       =>  'country'
);

sub makeUserNode {
  my ( $self, $userName, $userPtr ) = @_;

  my %user = %{$userPtr};

  my $userNode = XmlNode->new( 'user' );

  $userNode->setAttribute( 'name',        $userName );
  if (defined $user{'contactName'}) {
    $userNode->setAttribute( 'contact',     $user{'contactName'} );
  }else {
    $userNode->setAttribute( 'contact',     $userName );
  }
  $userNode->setAttribute( 'guid',        $user{'uuid'}) if defined $user{'uuid'};
  $userNode->setAttribute( 'email',       $user{'email'} ) if defined $user{'email'};
  $userNode->setAttribute( 'is-built-in', $user{'isBuiltIn'} ? 'true' : 'false' ) if exists $user{'isBuiltIn'};
  $userNode->setAttribute( 'cr-date',     $user{'creationDate'} ) if exists $user{'creationDate'};
  $userNode->setAttribute( 'external-id', $user{'externalId'} ) if defined $user{'externalId'};

  if (defined $user{'subscriptionDomainId'} and $user{'subscriptionDomainId'} != 0) {
    my $subscriptionName = PleskStructure::getDomainNameFromId($user{'subscriptionDomainId'});
    if ($subscriptionName) {
      $userNode->setAttribute( 'subscription-name', $subscriptionName );
    }else{
      $userNode->setAttribute( 'subscription-name', 'removed');
    }
  }

  $userNode->setAttribute( 'is-domain-admin', (defined $user{'isDomainAdmin'} and ( $user{'isDomainAdmin'} eq '1' ) )? 'true' : 'false' );
  $userNode->setAttribute( 'is-legacy-user', (defined $user{'isLegacyUser'} and ( $user{'isLegacyUser'} eq '1' ) ) ? 'true' : 'false' );
  $userNode->setAttribute( 'file-sharing-id', $user{'fileSharingId'} ) if defined $user{'fileSharingId'};

  $userNode->setAttribute( 'external-email', 'true' ) if not defined $user{'mailId'};

  my $propsNode = XmlNode->new('properties');
  my $passwordNode = CommonPacker::makePasswordNode($userPtr->{'password'}, (defined $userPtr->{'passwordType'})? $userPtr->{'passwordType'} : 'plain', (defined $userPtr->{'isBase64'} and $userPtr->{'isBase64'} eq '0') ? undef : 'base64');
  $propsNode->addChild($passwordNode);

  my $status = $userPtr->{'isLocked'} ? $Status::ADMIN : $Status::ENABLED;
  $propsNode->addChild(Status::make($status));

  $userNode->addChild($propsNode);

  my $lpNode = $userNode->getChild('limits-and-permissions', 1);
  $lpNode->addChild(XmlNode->new('role-ref', 'content' => $userPtr->{'SmbRoleName'}));

  foreach my $app (@{$user{'assignedApplications'}}) {
    $lpNode->addChild(XmlNode->new('assigned-application', 'content' => $app));
  }

  my $prefsNode = XmlNode->new('preferences');

  my @usersPhones;
  foreach my $pinfo (keys %user) {
    next unless defined $smbUserPinfo{$pinfo};
    if ( defined $user{$pinfo} and $user{$pinfo} ne '' ) {
      next if ( $smbUserPinfo{$pinfo} =~ /phone(\d?)-type/);
      if ($smbUserPinfo{$pinfo} =~ /^phone(\d?)$/) {
        push @usersPhones,  $user{$pinfo};
        next;
      }
      $prefsNode->addChild(XmlNode->new('pinfo',
                                        'attributes' => {'name' => $smbUserPinfo{$pinfo} },
                                        'content' => $user{$pinfo}
                                        )
                          );
    }
  }
  if (@usersPhones) {
    $prefsNode->addChild( XmlNode->new('pinfo',
                       'attributes' => { 'name' => 'phone' },
                       'content' => join(',', @usersPhones)
              )
    );
  }

  $userNode->addChild($prefsNode);

  return $userNode;
}

sub makeRoleNode {
  my ( $self, $roleName, $isBuiltIn, $permsPtr, $servicePermissions ) = @_;

  my $roleNode = XmlNode->new( 'role' );
  $roleNode->setAttribute( 'name', $roleName );
  $roleNode->setAttribute( 'is-built-in', $isBuiltIn? 'true' : 'false' );

  my $limitsAndPermsNode = XmlNode->new( 'limits-and-permissions' );
  $roleNode->addChild($limitsAndPermsNode);

  while ( my ( $name, $value ) = each( %{$permsPtr} ) ) {
    my $permNode = XmlNode->new( 'permission', 'attributes' => {
                                                'name'  => $name,
                                                'value' => $value? 'true' : 'false' }
                               );
    $limitsAndPermsNode->addChild( $permNode );
  }

  for my $perm (@$servicePermissions) {
    if ($perm->{classname} == "Smb_Service_Provider_Aps") {
      my $permNode = XmlNode->new('service-permission', 'attributes' => {
        'classname' => $perm->{'classname'},
        'description' => $perm->{'description'},
        'externalId' => $perm->{'externalId'},
        'permissionCode' => $perm->{'permissionCode'},
        'class' => $perm->{'class'},
        'value' => 'true',
      });
      $limitsAndPermsNode->addChild( $permNode );
    }
  }

  return $roleNode;
}

sub makeDomainNode {
    my ( $self, $domainId, $domainName, $domainPtr, $issite, $needFullInfo ) = @_;

    unless ( ref($domainPtr) =~ /HASH/ ) {
      Logging::warning("Unable to backup domain $domainName");
      return;
    }

    my %domain = %{$domainPtr};

    my $nodeName = ($issite? 'site' : 'domain');

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

    $root->getChild( 'preferences', 1 );
    $root->getChild( 'properties', 1 );
    $root->getChild( 'limits-and-permissions', 1 ) unless $issite;

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

    $root->setAttribute( 'external-id', $domain{'external_id'} ) if ( not $issite and defined $domain{'external_id'} and $domain{'external_id'} ne '');

    $root->setAttribute( 'vendor-guid', $domain{'vendor-guid'} ) if ( not $issite and defined $domain{'vendor-guid'} );

    $root->setAttribute( 'owner-guid', $domain{'owner-guid'} ) if ( not $issite and defined $domain{'owner-guid'} );

    $root->setAttribute( 'parent-domain-name', $domain{'parentDomainName'} ) if ( $issite and defined $domain{'parentDomainName'} );

    if($issite) {
      $self->{siteNodes}->{$domainId} = $root;
    }
    else {
      $self->{domainNodes}->{$domainId} = $root;
    }

    if ($needFullInfo or $self->{listingOnlyMode}) {
      return $root;
    } else {
      my $shortInfoNode = XmlNode->new('domain-info');
      $shortInfoNode->setAttribute('name', $domainName);
      $shortInfoNode->setAttribute('guid', $domain{'guid'}) if defined $domain{'guid'};
      unless($issite) {
        $self->{domainShortNodes}->{$domainId} = $shortInfoNode;
      }
      return $shortInfoNode;
    }
}

sub getCashedDomainNode {
  my ($self, $domainId) = @_;
  my $root = $self->{domainNodes}->{$domainId};
  unless ( defined($root) ) {
    $root = $self->{siteNodes}->{$domainId};
  }
  return $root if defined($root);

  my $caller = (caller(1))[3];
  if (defined $caller) {
    Logging::warning("Error in $caller : empty domain parent node for id ($domainId)", 'PackerStructure');
  }
  else {
    Logging::warning('Error: empty domain parent node', 'PackerStructure');
  }
  return;
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  #TODO: uncomment
  #$root->ReleaseCode();
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  $root->setAttribute( 'www', $status );
}

sub setDomainIP {
  my ( $self, $domainId, $ip, $iptype ) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  $root->getChild( 'properties', 1 )->addChild( $self->makeIpNode( $ip, $iptype ), 1 );
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  $root->getChild( 'properties', 1 )->addChild( Status::make($status) );
}

sub setWebspaceStatus {
  my ( $self, $domainId, $status, $siteStatus ) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  if (not defined $siteStatus) {
    $root->getChild( 'properties', 1 )->addChild( Status::make($status, 'webspace-status') );
    $root->getChild( 'properties', 1 )->addChild( Status::make($status) );
  } else {
    $root->getChild( 'properties', 1 )->addChild( Status::make($status, 'webspace-status') );
    $root->getChild( 'properties', 1 )->addChild( Status::make($siteStatus) );
  }
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $name = PleskStructure::getDomainNameFromId($domainId);
  my $aRecordIp;
  XPath::Select $root, 'properties/dns-zone/dnsrec', sub {
    my $rec = shift;
    if (($rec->getAttribute('type') eq 'A') or ($rec->getAttribute('type') eq 'AAAA') and ($rec->getAttribute('src') eq "$name.") ) {
      $aRecordIp = $rec->getAttribute('dst');
    }
  };
  return $aRecordIp;
}

sub addDomainDatabase {
  my ( $self, $dbId, $dbServerId, $domainId, $dbName, $dbType, $optionalPtr, $dbServerPtr, $dbUsersPtr, $contentDescriptionPtr, $skipContent ) = @_;

  $self->regDatabaseObjectBackupPath( $self->getDomainsBackupPath( $domainId ), $dbId, $dbName, $dbServerId );
  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $node = $self->makeDatabaseNode( $dbId, $dbName, $dbType, 'domain', $domainId, $optionalPtr,
                                      $dbServerPtr, $dbUsersPtr, $contentDescriptionPtr, $skipContent );
  my $parentGuid = $root->getAttribute('guid');
  $node->setAttribute( 'guid', $self->getDatabaseGuid( $parentGuid, $dbId ) ) if $parentGuid;
  $node->setAttribute( 'owner-guid', $parentGuid ) if $parentGuid;

  $root->getChild( 'databases', 1 )->addChild($node);
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  $root->getChild( 'preferences', 1 )->addChild( XmlNode->new('default-domain') );
}

sub addDomainLimit {
  my ( $self, $domainId, $name, $value ) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  $self->insertLimitNode( $root, $name, $value );
}

sub setDomainMailService {
  my ( $self, $domainId, $status, $ips ) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $mailServiceStatus = Status::make($status);

  my $rootMail = XmlNode->new('mailsystem');
  my $properties = $rootMail->getChild( 'properties', 1 );
  $properties->addChild($mailServiceStatus);

  foreach my $ip (keys %{$ips}) {
    my $ipNode = $self->makeIpNode($ip, $ips->{$ip});
    $properties->addChild($ipNode);
  }

  $root->addChild($rootMail);
}

sub addDomainCustomButton {
  my ( $self, $domainId, $id, $optionsPtr, $customButtonsDir, $icon ) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $node = $self->makeCustomButtonNode( 'domains', $domainId, $id, $optionsPtr, $customButtonsDir, $icon );

  $root->getChild( 'preferences', 1 )->addChild( $node);
}

sub addToPreferences {
  my ( $self, $objectType, $objectId, $node ) = @_;

  my $parent;
  if ($objectType eq 'domain') {
    $parent = $self->getCashedDomainNode($objectId);
  }
  elsif($objectType eq 'client') {
    $parent = $self->{clientNodes}->{$objectId};
  }
  if ( !defined($parent) ) {
    Logging::warning('Error: addToPreferences: empty parent node', 'PackerStructure');
    return;
  }

  $parent->getChild( 'preferences', 1 )->addChild( $node);
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $mailService = $root->getChild('mailsystem');
  unless (defined $mailService ) {
    Logging::warning("Error: setDomainCatchMail: there are no node 'mailsystem'", 'PackerStructure');
    return;
  }

  $mailService->getChild( 'preferences', 1 )->addChild(XmlNode->new( 'catch-all', 'content' => $catchAllAddr ) );
}

sub setDomainKeysDomainSupport {
  my ( $self, $domainId, $domainName, $state, $privateKeyPath, $publickKey ) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $mailService = $root->getChild('mailsystem');
  unless ( defined $mailService ) {
    Logging::warning("Error: setDomainKeysDomainSupport: there are no node 'mailsystem'", 'PackerStructure');
    return;
  }

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

  if ($state) {
    my $privateKey = $self->{content_transport}->addDomainContent( 'key', $domainId,
      "privatekey",
      'directory' => $privateKeyPath,
      'include'   => ['default']
    );

    $domainKeysNode->getChild( 'content', 1, 1 )->addChild( $privateKey ) if $privateKey;

    $domainKeysNode->setAttribute( 'public-key', $publickKey ) if ( defined($publickKey) && $publickKey );
  }

  $mailService->getChild( 'preferences', 1 )->addChild($domainKeysNode);
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $mailService = $root->getChild('mailsystem');
  if ( not $mailService ) {
    Logging::warning("Unable to dump domain webmail preferences, because of dump XML structure is not full", 'PackerStructure');
    return;
  }

  my $node = XmlNode->new( 'web-mail' );
  $node->setText( $webmail );
  $mailService->getChild( 'preferences', 1 )->addChild( $node );
}

sub setDomainGLSupport {
  my ( $self, $domainId, $state ) = @_;
  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $mailsystem = $root->getChild('mailsystem');
  my $node = XmlNode->new( 'grey-listing' );
  $node->setText($state);
  $mailsystem->getChild('preferences', 1)->addChild($node);
}

sub setDomainDnsZone {
  my ( $self, $domainId, $paramsPtr, $recordsPtr ) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  $self->makeDnsZone($root->getChild( 'properties', 1 ), $paramsPtr, $recordsPtr);
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $propertiesNode = $root->getChild( 'properties');
  if ( $propertiesNode ) {
    $propertiesNode->removeChildren('dns-zone');
  }
}

sub setDomainDnsOld {
  my ( $self, $domainId, $enabled, $type, $masterIp, $recordsPtr ) = @_;

  my @records = @{$recordsPtr};

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $properties = $root->getChild( 'properties', 1 );
  if ( !defined($properties) ) {
    Logging::warning("Error: setDomainDnsOld: empty 'properties' node", 'PackerStructure');
    return;
  }

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

  $dnsNode->setAttribute(
    'type',
    ( !$type || ( $type eq 'master' ) ) ? 'master' : 'slave'
  );

	$dnsNode->addChild(Status::make( $enabled eq 'true' ? 0 : 16 ));

  for my $hash (@records) {
    my $dnsrec = $self->makeDnsRecord($hash);
    if ($dnsrec) {
      $dnsNode->addChild($dnsrec);
    }
    $hash = undef;
  }

  if ( defined($masterIp) ) {
    $dnsNode->addChild( makeOldMasterRec($masterIp) );
  }

  $properties->addChild($dnsNode);
}

sub addDomainAlias {
  my ( $self, $domainId, $aliasPtr, $dnsParamsPtr, $dnsRecordsPtr) = @_;

  my %alias = %{$aliasPtr};

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

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

  $aliasNode->setAttribute( 'seo-redirect', $alias{'seoRedirect'} )
    if defined $alias{'seoRedirect'};

  # DNS zone

  if ( $alias{'dns_zone_id'} != 0 ) {
    $self->makeDnsZone( $aliasNode, $dnsParamsPtr, $dnsRecordsPtr );
  }

  $root->getChild( 'preferences', 1 )->addChild($aliasNode);
}

sub setDomainMailLists {
  my ( $self, $domainId, $status, $ips) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $mlNode = XmlNode->new('maillists');

  my $propertiesNode = $mlNode->getChild( 'properties', 1 );
  $propertiesNode->addChild( Status::make( $status ) );
  foreach my $ip (keys %{$ips}) {
    my $ipNode = $self->makeIpNode($ip, $ips->{$ip});
    $propertiesNode->addChild($ipNode);
  }

  $root->addChild($mlNode);
}

sub addDomainMailList {
  my ( $self, $domainId, $name, $passwd, $state, $ownersPtr, $membersPtr, $content_path, $content_file) = @_;

  my @owners = @{$ownersPtr};
  my %members = %{$membersPtr};

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my @mailListServs = $root->getChildren('maillists');
  if ( scalar(@mailListServs) < 1 ) {
    Logging::warning('Unable to dump domain maillist, because of dump XML structure is not full', 'PackerStructure');
    return;
  }
  my $mailListService = $mailListServs[0];

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

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

  $node->addChild( CommonPacker::makePasswordNode( $passwd, 'encrypted') );

  for my $memberEmail ( keys %members ) {
    my $member = XmlNode->new( 'recipient', 'content' => $memberEmail );
    if ( $members{$memberEmail} ne "" ) {
      $member->setAttribute( 'fullname', $members{$memberEmail} );
    }
    $node->addChild($member);
  }

  if ( -f "$content_path/$content_file" ) {
    my $cid = $self->{content_transport}->addDomainMailListContent( 'mailbox', $domainId,
      "$name.mlist",
      'directory'       => $content_path,
      'follow_symlinks' => 1,
      'sysuser' => AgentConfig::mailmanUser(),
      'include' => [$content_file]
    );
    $node->getChild( 'content', 1, 1 )->addChild( $cid ) if $cid;
  }

  $mailListService->addChild($node);
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

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

sub addDomainCertificate {
  my ( $self, $domainId, $name, $cert, $csr, $ca_cert, $pvt_key, $default) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $certNode = XmlNode->new('certificate');

  addUrlDecodedTextNode( $certNode, 'certificate-data', $cert ) if defined($cert);
  addUrlDecodedTextNode( $certNode, 'signing-request',  $csr ) if defined($csr);
  addUrlDecodedTextNode( $certNode, 'ca-certificate',   $ca_cert ) if defined($ca_cert);
  addUrlDecodedTextNode( $certNode, 'private-key', $pvt_key ) if defined($pvt_key);
  $certNode->setAttribute( 'name', $name );
  $certNode->setAttribute( 'default', 'true' ) if ( defined $default && $default == 1 );

  $root->getChild('certificates', 1 )->addChild($certNode);
}

sub setDomainTomcat {
  my ( $self, $domainId, $status, $ips) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $tomcatNode = XmlNode->new('tomcat');

  my $propertiesNode = $tomcatNode->getChild( 'properties', 1 );
  $propertiesNode->addChild( Status::make( $status ) );
  foreach my $ip (keys %{$ips}) {
    my $ipNode = $self->makeIpNode($ip, $ips->{$ip});
    $propertiesNode->addChild($ipNode);
  }
  $root->addChild($tomcatNode);
}

sub addDomainTomcatWebApp {
  my ( $self, $domainId, $domainName, $name, $status, $path) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my @tomcatServs = $root->getChildren('tomcat');
  if ( scalar(@tomcatServs) < 1 ) {
    Logging::warning('Unable to dump domain Tomcat web-applications, because of dump XML structure is not full', 'PackerStructure');
    return;
  }
  my $tomcatService = $tomcatServs[0];

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

  my $warFile = "$name.war";

  my $cid = $self->{content_transport}->addDomainContent( 'web-app', $domainId,
    "$name.webapp",
    "directory" => $path,
    "include"   => [$warFile]
  );
  $item->getChild( 'content', 1, 1 )->addChild( $cid ) if $cid;

  $tomcatService->addChild($item);
}

sub addDomainPersonalPermission {
  my ( $self, $domainId, $name, $value ) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  if ( !defined($root) ) {
    Logging::warning('Unable to dump domain personal permissions, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  $self->makePermissionNode( $root->getChild( 'limits-and-permissions', 1 ), $name, $value );
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $phostingNode = XmlNode->new('phosting');

  my $preferencesNode = XmlNode->new('preferences');
  $phostingNode->addChild( $preferencesNode );

  if (defined $sysuserPtr) {
    $preferencesNode->addChild($self->makeSysUser($sysuserPtr));
  }

  $root->addChild($phostingNode);

  $self->regPhostingObjectBackupPath( $self->getDomainsBackupPath( $domainId ), $domainId );
  $self->{phostingNodes}->{$domainId} = $phostingNode;
}

sub setDomainPhosting {
  my ( $self, $domainId, $paramsPtr, $sysuserPtr, $scriptingPtr, $ips, $phpSettingsPtr ) = @_;

  my %params = %{$paramsPtr};
  my %scripting = %{$scriptingPtr};

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $phostingNode = XmlNode->new('phosting');

  my $parentGuid = $root->getAttribute('guid');
  if ( $parentGuid ) {
    $phostingNode->setAttribute( 'guid', $self->getPhostingGuid( $parentGuid , $domainId ) );
    $phostingNode->setAttribute( 'owner-guid', $parentGuid );
  }

  ## %params ##
  my %hostingAttribute = (
    'https'   => 'ssl',
    'fp'      => 'fp',
    'fpssl'   => 'fp_ssl',
    'fpauth'  => 'fp_enable',
    'webstat' => 'webstat',
  );
  $phostingNode->setAttribute( 'shared-content', $params{'shared-content'} ) if defined($params{'shared-content'});
  while ( my ( $xmlName, $fieldName ) = each(%hostingAttribute) ) {
    # MySQL allows 'true', 'false' and '' in enums
    if ( defined( $params{$fieldName} )
      and $params{$fieldName} eq "true" )
    {
      $phostingNode->setAttribute( $xmlName, 'true' );
    }
  }

  if ( defined( $params{'webstat'} ) and $params{'webstat'} ) {
    $phostingNode->setAttribute( 'webstat', $params{'webstat'} );
  }

  if ( defined( $params{'https'} ) and $params{'https'} ) {
    $phostingNode->setAttribute( 'https', $params{'https'} );
  }

  if ( defined($params{'errdocs'} ) ) {
    $phostingNode->setAttribute( 'errdocs', 'true' );
  }

  if ( defined( $params{'www-root'} ) ) {
    $phostingNode->setAttribute( 'www-root', $params{'www-root'} );
  }

  if ( defined( $params{'cgi_bin_mode'} ) ) {
    $phostingNode->setAttribute( 'cgi_bin_mode', $params{'cgi_bin_mode'} );
  }

  if ( defined( $params{'sitebuilder-site-id'} ) ) {
    $phostingNode->setAttribute( 'sitebuilder-site-id', $params{'sitebuilder-site-id'} );
  }

  if ( defined( $params{'sitebuilder-site-published'} ) ) {
    $phostingNode->setAttribute( 'sitebuilder-site-published', $params{'sitebuilder-site-published'} );
  }

  if ( defined( $params{'wu_script'} ) ) {
    $phostingNode->setAttribute( 'wu_script', $params{'wu_script'} );
  }

  if ( defined( $params{'maintenance_mode'} ) ) {
    $phostingNode->setAttribute( 'maintenance-mode', $params{'maintenance_mode'} );
  }

  if ( defined( $params{'certificate_ref'} ) ) {
    $phostingNode->setAttribute( 'certificate', $params{'certificate_ref'} );
  }

  ## %sysuser ##
  my $prefs = $phostingNode->getChild( 'preferences', 1 );
  if (defined $sysuserPtr) {
    my $sysUserNode = $self->makeSysUser($sysuserPtr);
    $prefs->addChild($sysUserNode);
  }

  ## properties ##
  my $propertiesNode = $phostingNode->getChild( 'properties', 1);
  foreach my $ip (keys %{$ips}) {
    my $ipNode = $self->makeIpNode($ip, $ips->{$ip});
    $propertiesNode->addChild($ipNode);
  }

  ## %scripting ##
  my $scripringItem = XmlNode->new('scripting');
  while ( my ( $scriptXmlName, $value ) = each(%scripting) ) {
    $scripringItem->setAttribute( $scriptXmlName, $value ) if defined $value;
  }
  $phostingNode->getChild( 'limits-and-permissions', 1 )->addChild($scripringItem);

  if (defined $phpSettingsPtr and (scalar keys %{$phpSettingsPtr} != 0)) {
    $phostingNode->addChild($self->makePhpSettingsNode($phpSettingsPtr));
  }

  $root->addChild($phostingNode);

  $self->regPhostingObjectBackupPath( $self->getDomainsBackupPath( $domainId ), $domainId );
  $self->{phostingNodes}->{$domainId} = $phostingNode;

}

sub setDomainUserDataContent {
  my ( $self, $domainId, $domainName, $path, $optPtr) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;
  
  my @apacheFiles;
  my $cmd = "find $path \\( \\( -type d -user $self->{apache_user}{user} -group $self->{apache_user}{group} ! -perm -o+rw -prune \\) -o \\( ! -type d -user $self->{apache_user}{user} -group $self->{apache_user}{group} ! -perm -o+rw \\) \\) -printf \"%P\n\"";
  my $findCmd = `$cmd`;
  if ($? eq '0') {
    @apacheFiles = split "\n", $findCmd;
  }

  my $phostings = $root->getChild('phosting');
  my @exclude = (@apacheFiles) ? (@apacheFiles, @{$optPtr->{'exclude'}}) : @{$optPtr->{'exclude'}};
  my $cid_content = $self->{content_transport}->addDomainContent( 'user-data', $domainId,
    'user-data',
    'directory' => $path,
    'sysuser' => $optPtr->{'sysuser'},
    'exclude' => \@exclude,
    'remoteip' => $optPtr->{'remoteip'}
  );
  $phostings->getChild( 'content', 1, 1 )->addChild( $cid_content ) if $cid_content;
  @{$self->{domainVhost}->{$domainId}} = ( 'user-data', $cid_content, $path )  if $cid_content;

  if (@apacheFiles) {
    my $cidApache = $self->{content_transport}->addDomainContent( 'apache-files', $domainId,
      'apache-files',
      'directory' => $path,
      'sysuser' => $self->{apache_user}{'user'},
      'include' => \@apacheFiles,
      'remoteip' => $optPtr->{'remoteip'}
    );
    $phostings->getChild( 'content', 1, 1 )->addChild( $cidApache ) if $cidApache;
    @{$self->{domainVhost}->{$domainId}} = ( 'apache-files', $cidApache, $path )  if $cidApache;
  }
}

sub setDomainPhostingFullContent {
  my ( $self, $domainId, $domainName, $path, $optPtr) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $phostings = $root->getChild('phosting');
  my $cid_content = $self->{content_transport}->addDomainContent( 'vhost', $domainId,
    'vhost',
    'directory' => $path,
    'sysuser' => $optPtr->{'sysuser'},
    'exclude' => $optPtr->{'exclude'},
    'remoteip' => $optPtr->{'remoteip'}
  );
  $phostings->getChild( 'content', 1, 1 )->addChild( $cid_content ) if $cid_content;
  @{$self->{domainVhost}->{$domainId}} = ( 'vhost', $cid_content, $path )  if $cid_content;
}

sub setDomainMailNamesContent{
  my ( $self, $domainId, $domainAsciiName, $path, $sysuser, $remoteMailIp ) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my %options;
  $options{'directory'} = $path;
  $options{'sysuser'} = $sysuser;

  my $currentTransportOs = $self->{content_transport_os};

  if (defined $remoteMailIp) {
    $options{'remoteip'} = $remoteMailIp;

    my $isWin = PleskStructure::getServiceNodeProperty(PleskStructure::getRemoteNodeId($remoteMailIp), "OS_WIN") eq "true";

    if ( $isWin ) {
      $self->setContentTransport($self->getContentTransportType(), 'windows');
    } else {
      $self->setContentTransport($self->getContentTransportType(), 'unix');
    }
  }

  my $mailContent = $self->{content_transport}->addDomainContent( 'domainmail', $domainId,
      'mn',
      %options
  );

  if (defined $remoteMailIp) {
    $self->setContentTransport($self->getContentTransportType(), $currentTransportOs);
  }

  $root->getChild( 'mailsystem' )->getChild( 'content', 1, 1 )->addChild( $mailContent ) if $mailContent;
  @{$self->{domainMailContent}->{$domainId}} = ( 'domainmail', $mailContent, $path ) if $mailContent;
}

sub setDomainMailListContent{
  my ( $self, $domainId, $domainAsciiName, $path, $optPtr ) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $mailContent = $self->{content_transport}->addDomainContent( 'domainmaillist', $domainId,
      'ml',
      'directory' => $path,
      'include'   => $optPtr->{'include'},
      'follow_symlinks' => $optPtr->{'follow_symlinks'},
      'sysuser' => $optPtr->{'sysuser'}
  );

  $root->getChild( 'maillists' )->getChild( 'content', 1, 1 )->addChild( $mailContent ) if $mailContent;
  @{$self->{domainMailListContent}->{$domainId}} = ( 'domainmaillist', $mailContent, $path ) if $mailContent;
}

sub setDomainPhostingContentCommon {
  my ( $self, $domainId, $domainName, $path, $contentType ) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my @phostings = $root->getChildren('phosting');
  if ( scalar(@phostings) < 1 ) {
    Logging::warning('Unable to dump domain phosting content, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $cid_content = $self->{content_transport}->addDomainContent( $contentType, $domainId,
    $contentType,
    'directory' => $path
  );
  $phostings[0]->getChild( 'content', 1, 1 )->addChild( $cid_content ) if $cid_content;
  return $cid_content;
}

sub setDomainPhostingProtectedDirContent {
  my ( $self, $domainId, $domainName, $path ) = @_;
  $self->setDomainPhostingContentCommon($domainId, $domainName, $path, 'pd');
}

sub setDomainPhostingStatisticsContent {
  my ( $self, $domainId, $domainName, $path, $optPtr ) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my @phostings = $root->getChildren('phosting');
  if ( scalar(@phostings) < 1 ) {
    Logging::warning('Unable to dump domain phosting statistics, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $cid_content = $self->{content_transport}->addDomainContent( 'statistics', $domainId,
    'statistics',
    'directory' => $path,
    'remoteip' => $optPtr->{'remoteip'},
  );
  $phostings[0]->getChild( 'content', 1, 1 )->addChild( $cid_content ) if $cid_content;
  @{$self->{domainStatistics}->{$domainId}} = ( 'statistics', $cid_content, $path )  if $cid_content;
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my @phostings = $root->getChildren('phosting');
  if ( scalar(@phostings) < 1 ) {
    Logging::warning('Unable to dump domain phosting webstat content, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $cid_webstat = $self->{content_transport}->addDomainContent( 'webstat', $domainId,
    'webstat',
    'directory' => $path
  );
  $phostings[0]->getChild( 'content', 1, 1 )->addChild( $cid_webstat ) if $cid_webstat;
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my @phostings = $root->getChildren('phosting');
  if ( scalar(@phostings) < 1 ) {
    Logging::warning('Error: setDomainPhostingWebstatSslContent: there are no node "phosting"', 'PackerStructure');
    return;
  }

  my $cid_webstat_ssl = $self->{content_transport}->addDomainContent( 'webstat_ssl', $domainId,
    'webstat-ssl',
    'directory' => $path
  );
  $phostings[0]->getChild( 'content', 1, 1 )->addChild( $cid_webstat_ssl ) if $cid_webstat_ssl;
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my @phostings = $root->getChildren('phosting');
  if ( scalar(@phostings) < 1 ) {
    Logging::warning('Unable to dump domain phosting ftpstat content, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $cid_ftpstat = $self->{content_transport}->addDomainContent( 'ftp_stat', $domainId,
    'ftpstat',
    'directory' => $path
  );
  $phostings[0]->getChild( 'content', 1, 1 )->addChild( $cid_ftpstat ) if $cid_ftpstat;
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my @phostings = $root->getChildren('phosting');
  if ( scalar(@phostings) < 1 ) {
    Logging::warning('Error: setDomainPhostingLogsContent: there are no node "phosting"', 'PackerStructure');
    return;
  }

  my $cid_logs =  $self->{content_transport}->addDomainContent( 'logs', $domainId,
    'logs',
    'directory' => $path
  );
  $phostings[0]->getChild( 'content', 1, 1 )->addChild( $cid_logs ) if $cid_logs;
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my @phostings = $root->getChildren('phosting');
  if ( scalar(@phostings) < 1 ) {
    Logging::warning('Error: setDomainPhostingAnonFtpstatContent: there are no node "phosting"', 'PackerStructure');
    return;
  }

  my $cid_anon_ftpstat = $self->{content_transport}->addDomainContent( 'anon_ftpstat', $domainId,
    'anon-ftpstat',
    'directory' => $path
  );
  $phostings[0]->getChild( 'content', 1, 1 )->addChild( $cid_anon_ftpstat ) if $cid_anon_ftpstat;
}

sub setDomainPhostingConfContent {
  my ( $self, $domainId, $domainName, $path, $optPtr ) = @_;

  my @include = @{ $optPtr->{'include'} };

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my @phostings = $root->getChildren('phosting');
  if ( scalar(@phostings) < 1 ) {
    Logging::warning('Error: setDomainPhostingConfContent: there are no node "phosting"', 'PackerStructure');
    return;
  }

  if ( $#include != -1 ) {
    my $cid_conf;
    $cid_conf = $self->{content_transport}->addDomainContent( 'conf', $domainId,
      'conf',
      'directory' => $path,
      'include'   => \@include,
      'include_hidden_files' => 1
    );
    $phostings[0]->getChild( 'content', 1, 1 )->addChild( $cid_conf ) if $cid_conf;
  }
}

sub setDomainFrontPageAdmin {
  my ( $self, $domainId, $fpAdm, $fpPass, $fpPassType) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $phosting = $root->getChild('phosting');
  if ( not $phosting ) {
    Logging::warning('Error: setDomainFrontPageAdmin: there are no node "phosting"', 'PackerStructure');
    return;
  }

  my $item =
    XmlNode->new( 'fpuser',
    'attributes' => { 'name' => $fpAdm } );
  if ( $fpPass ) {
    my $passNode = CommonPacker::makePasswordNode( $fpPass, $fpPassType );
    $item->addChild($passNode);
  }

  $phosting->getChild( 'preferences', 1 )->addChild($item);
}

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

  my %logRotation = %{$logRotationPtr};

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $phosting = $root->getChild('phosting');
  if ( not $phosting ) {
    Logging::warning('Unable to dump domain log rotation settings, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $node;
  if (keys %logRotation) {
    $self->makeLogrotationNode( $phosting->getChild( 'preferences', 1 ), \%logRotation );
  }
}

sub setDomainAnonFtp {
  my ( $self, $domainId, $domainName, $propsPtr, $pub_path, $incoming_path, $optPtr ) = @_;

  my %props = %{$propsPtr};

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $phosting = $root->getChild('phosting');
  if ( not $phosting ) {
    Logging::warning('Error: setDomainAnonFtp: there are no node "phosting"', 'PackerStructure');
    return;
  }

  my $anonRoot = XmlNode->new('anonftp');
  if ( $props{'status'} =~ /true/ ) {
    $anonRoot->setAttribute( 'pub', 'true' );
  }
  if ( $props{'incoming'} =~ /true/ ) {
    $anonRoot->setAttribute( 'incoming', 'true' );
  }
  if ( defined( $props{'max_conn'} ) ) {
    makeAnonftpLimitNode( $anonRoot, 'max-connections',
      $props{'max_conn'} );
  }
  if ( defined( $props{'bandwidth'} ) ) {
    makeAnonftpLimitNode( $anonRoot, 'bandwidth',
      $props{'bandwidth'} );
  }
  if ( defined( $props{'quota'} ) ) {
    makeAnonftpLimitNode( $anonRoot, 'incoming-disk-quota',
      $props{'quota'} );
  }
  if ( defined( $props{'display_login'} ) ) {
    $anonRoot->setAttribute( 'display-login', $props{'display_login'} );
  }
  if ( $props{'incoming_readable'} =~ /true/ ) {
    makeAnonftpPermissionNode( $anonRoot, 'incoming-download' );
  }
  if ( $props{'incoming_subdirs'} =~ /true/ ) {
    makeAnonftpPermissionNode( $anonRoot, 'incoming-mkdir' );
  }
  if ( defined( $props{'login_text'} ) ) {
    my $loginMessageNode =
      XmlNode->new( 'login-message',
      'content' => $props{'login_text'} );
    $anonRoot->addChild($loginMessageNode);
  }

  $phosting->getChild( 'preferences', 1 )->addChild($anonRoot);
}

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

  my @pdirUsers = @{$pdirUsersPtr};

  my $parent = $self->getCashedDomainNode($domainId);
  return unless defined $parent;

  my $phostingNode = $parent->getChild('phosting');
  unless ( defined $phostingNode ) {
    Logging::warning('Error: addDomainProtectedDir: there are no node "phosting"', 'PackerStructure');
    return;
  }

  my $pdirNode = XmlNode->new('pdir');

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

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

  my $userNode;
  my $item;
  for my $user (@pdirUsers) {
    $userNode = XmlNode->new('pduser');
    $pdirNode->addChild($userNode);
    $userNode->setAttribute( 'name', $user->{'login'} );

    $item = CommonPacker::makePasswordNode( $user->{'passwd'}, CommonPacker::normalizePasswordType( $user->{'passwdType'} ) );
    $userNode->addChild($item);
  }

  $phostingNode->getChild( 'preferences', 1 )->addChild($pdirNode);
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $phostingNode = $root->getChild('phosting');

  my $ftpuserNode = XmlNode->new('ftpuser');
  $ftpuserNode->setAttribute('name', $sysuserPtr->{'login'});
  $ftpuserNode->addChild($self->makeSysUser($sysuserPtr));
  $phostingNode->getChild( 'ftpusers', 1 )->addChild($ftpuserNode);
}

sub setEmptySubFtpUsersNode {
  my ($self, $domainId) = @_;
  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $phostingNode = $root->getChild('phosting', 1);

  $phostingNode->getChild('ftpusers', 1);
}

sub removeSubFtpUserNodeIfEmpty {
  my ($self, $domainId) = @_;
  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $phostingNode = $root->getChild('phosting', 1);

  my $ftpusersNode = $phostingNode->getChild('ftpusers');
  if (not $ftpusersNode->getChildren('ftpuser')) {
    $phostingNode->removeChildren('ftpusers');
  }
}

sub addDomainWebUser {
  my ( $self, $domainId, $domainName, $sysuserPtr, $scriptingPtr, $webUserHome, $privatePath ) = @_;

  my %sysuser = %{$sysuserPtr};
  my %scripting = %{$scriptingPtr};

  my $parent = $self->getCashedDomainNode($domainId);
  return unless defined $parent;

  my $phostingNode = $parent->getChild('phosting');
  unless( defined $phostingNode ) {
    Logging::warning('Unable to dump domain webuser preferences, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $webuserNode = XmlNode->new('webuser');

  $webuserNode->addChild($self->makeSysUser(\%sysuser));

  my $userName = $sysuser{'login'};
  $webuserNode->setAttribute( 'name', $userName );

  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 $item = XmlNode->new('scripting');
  while ( my ( $xmlName, $fieldName ) = each(%webUserScripting) ) {
    if ( defined $scripting{$fieldName}
      and ( $scripting{$fieldName} eq 'true' or $scripting{$fieldName} eq 'false' ) )
    {
      $item->setAttribute( $xmlName, $scripting{$fieldName} );
    }
  }

  if (exists $scripting{'write_modify'} && defined $scripting{'write_modify'}) {
    $item->setAttribute('write_modify', $scripting{'write_modify'}) ;
  }

  $webuserNode->addChild($item);

  $phostingNode->getChild( 'webusers', 1 )->addChild($webuserNode);
}

sub setDomainWebalizer {
  my ( $self, $domainId, $directRef, $hiddenRefsPtr, $groupRefsPtr) = @_;

  my @hiddenRefs = @{$hiddenRefsPtr};
  my @groupRefs = @{$groupRefsPtr};

  my $parent = $self->getCashedDomainNode($domainId);
  return unless defined $parent;

  my $phostingNode = $parent->getChild('phosting');
  unless( defined $phostingNode ) {
    Logging::warning('Unable to dump domain webalizer settings, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

  my $webalizerNode = XmlNode->new("webalizer");

  if ($directRef)
  {
    $webalizerNode->setAttribute( 'hide-direct-referrer', 'true' );
  }

  for my $ref (@hiddenRefs) {
    $webalizerNode->addChild(
      XmlNode->new(
        "webalizer-hidden-referrer", 'content' => $ref
      )
    );
  }

  for my $groupRef (@groupRefs ) {
    $webalizerNode->addChild(
      XmlNode->new(
        'webalizer-group-referrer',
        'content'    => $groupRef->{'ref'},
        'attributes' => { 'group-name' => $groupRef->{'name'} }
      )
    );
  }

  $phostingNode->getChild( 'preferences', 1 )->addChild($webalizerNode);
}

sub setDomainPerfomance {
  my ( $self, $domainId, $max_connection, $traffic_bandwidth) = @_;

  my $parent = $self->getCashedDomainNode($domainId);
  return unless defined $parent;

  my $phostingNode = $parent->getChild('phosting');
  unless ( defined $phostingNode ) {
    Logging::warning('Unable to dump domain performance settings, because of dump XML structure is not full', 'PackerStructure');
    return;
  }

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

  $phostingNode->getChild( 'preferences', 1 )->addChild( $perfomanceNode );
}

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

  my $parent = $self->getCashedDomainNode($domainId);
  return unless defined $parent;

  my $phostingNode = $parent->getChild('phosting');
  unless ( defined $phostingNode ) {
    Logging::warning('Error: appendNodeToPhosting: there are no node "phosting"', 'PackerStructure');
    return;
  }

  $phostingNode->addChild(XmlNode->new(undef, 'raw' => $rawXml));
}

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

  my $parent = $self->getCashedDomainNode($domainId);
  return unless defined $parent;

  my $phostingNode = $parent->getChild('phosting');
  unless ( defined $phostingNode ) {
    Logging::warning('Error: setIisAppPool: there are no node "phosting"', 'PackerStructure');
    return;
  }

  my $phostingPreferencesNode = $phostingNode->getChild('preferences');
  unless ( defined $phostingPreferencesNode ) {
    Logging::warning('Error: setIisAppPool: there are no node "phosting/preferences"', 'PackerStructure');
    return;
  }

  $phostingPreferencesNode->addChild(XmlNode->new(undef, 'raw' => $info));
}


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

  my $parent = $self->getCashedDomainNode($domainId);
  return unless defined $parent;

  my $phostingNode = $parent->getChild('phosting');
  unless( defined $phostingNode ) {
    Logging::warning('Error: setDomainWebStat: there are no node "phosting"', 'PackerStructure');
    return;
  }
  my $webstatNode = XmlNode->new('web-stat');
  $webstatNode->setAttribute( 'stat-ttl', $stat_ttl ) if $stat_ttl;
  $phostingNode->getChild( 'preferences', 1 )->addChild( $webstatNode );
}

sub setDomainShosting {
  my ( $self, $domainId, $forward, $ips) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $shostingNode = XmlNode->new( 'shosting');
  $shostingNode->addChild(XmlNode->new('url', 'content' => $forward->{'redirect'}));

  my $propertiesNode = $shostingNode->getChild('properties', 1);

  foreach my $ip (keys %{$ips}) {
    my $ipNode = $self->makeIpNode($ip, $ips->{$ip});
    $propertiesNode->addChild($ipNode);
  }

  if (defined ($forward->{'http_code'})) {
	my $httpcodeNode = XmlNode->new('httpcode', 'content' => $forward->{'http_code'});
	$shostingNode->addChild($httpcodeNode);
  }

  $root->addChild($shostingNode);
}

sub setDomainFhosting {
  my ( $self, $domainId, $forward, $ips) = @_;

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $fhostingNode = XmlNode->new( 'fhosting');
  $fhostingNode->addChild(XmlNode->new('url', 'content' => $forward));

  my $propertiesNode = $fhostingNode->getChild('properties', 1);

  foreach my $ip (keys %{$ips}) {
    my $ipNode = $self->makeIpNode($ip, $ips->{$ip});
    $propertiesNode->addChild($ipNode);
  }

  $root->addChild($fhostingNode);
}

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

  my $parent = $self->getCashedDomainNode($domainId);
  return unless defined $parent;

  my $phostingNode = $parent->getChild('phosting');
  unless ( defined $phostingNode ) {
    Logging::warning('Error: addSite: there are no node "phosting"', 'PackerStructure');
    return;
  }

  my $siteNode = XmlNode->new( 'site' );

  $phostingNode->getChild( 'sites', 1 )->addChild($siteNode);

}

###### SubDomain functions ######

sub addSubDomain {
  my ( $self, $domainId, $ptrSubDomain, $subDomainName, $subAsciiDomainName, $https, $shared_content, $sysuserPtr, $scriptingPtr) = @_;
  $subAsciiDomainName = $subDomainName if not $subAsciiDomainName;
  my $subDomainId = $ptrSubDomain->{'id'};
  $self->regSubdomainObjectBackupPath( $self->getPhostingBackupPath( $domainId ), $subDomainId, $subAsciiDomainName, $domainId );

  my %sysuser = %{$sysuserPtr};
  my %scripting = %{$scriptingPtr};

  my $parent = $self->getCashedDomainNode($domainId);
  return unless defined $parent;

  my $phosting = $parent->getChild('phosting');
  if ( not $phosting ) {
    Logging::warning('Error: addSubdomain: there are no node "phosting"', 'PackerStructure');
    return;
  }

  my $root = XmlNode->new('subdomain', 'attributes' => {'id' => $subDomainId});
  $self->{subDomainNodes}->{$subDomainId} = $root;
  my $parentGuid = $parent->getAttribute('guid');
  $root->setAttribute( "guid", $self->getSubdomainGuid( $parentGuid, $subDomainId ) ) if $parentGuid;
  my $phostingGuid = $phosting->getAttribute('guid');
  $root->setAttribute( 'owner-guid', $phostingGuid ) if $phostingGuid;

  $root->setAttribute( 'name', $subDomainName );

  if ( 'true' eq $https ) {
    $root->setAttribute( 'https', 'true' );
  }

  if ( $shared_content ne '' ) {
    $root->setAttribute( 'shared-content', $shared_content );
  }

  if ( exists $ptrSubDomain->{'www-root'} ) {
    $root->setAttribute( 'www-root', $ptrSubDomain->{'www-root'} );
  }

  if ( exists $ptrSubDomain->{'maintenance_mode'} ) {
    $root->setAttribute( 'maintenance-mode', $ptrSubDomain->{'maintenance_mode'});
  }

  if ( exists $ptrSubDomain->{'certificate_ref'} ) {
    $root->setAttribute( 'certificate', $ptrSubDomain->{'certificate_ref'});
  }

  ## %sysuser ##

  if (%sysuser)
  {
    $root->addChild($self->makeSysUser(\%sysuser));
  }

  ## %scripting ##

  my $scripringItem = XmlNode->new('scripting');
  while ( my ( $xmlName, $value ) = each(%scripting) ) {
      $scripringItem->setAttribute( $xmlName, $value );
  }
  $root->addChild($scripringItem);

  $phosting->getChild( 'subdomains', 1 )->addChild($root);

}

sub setSubDomainHttpdocsContent {
  my ( $self, $subDomainId, $path, $exclude_httpdocs_files_ptr, $webspaceId ) = @_;

  my @exclude_httpdocs_files = @{$exclude_httpdocs_files_ptr};

  my $root = $self->{subDomainNodes}->{$subDomainId};

  my $cid_docroot = $self->{content_transport}->addSubDomainPhostingContent( 'docroot', $subDomainId, $webspaceId,
    "httpdocs",
    "directory" => $path,
    "exclude"   => \@exclude_httpdocs_files
  );
  $root->getChild( 'content', 1, 1 )->addChild( $cid_docroot ) if $cid_docroot;
}

sub setSubDomainHttpsdocsContent {
  my ( $self, $subDomainId, $path, $exclude_httpsdocs_files_ptr ) = @_;

  my @exclude_httpsdocs_files = @{$exclude_httpsdocs_files_ptr};

  my $root = $self->{subDomainNodes}->{$subDomainId};

  my $cid_docroot_ssl = $self->{content_transport}->addSubDomainPhostingContent('docroot_ssl', $subDomainId, undef,
    "httpsdocs",
    "directory" => $path,
    "exclude"   => \@exclude_httpsdocs_files
  );
  $root->getChild( 'content', 1, 1 )->addChild( $cid_docroot_ssl ) if $cid_docroot_ssl;
}

sub setSubDomainErrorDocsContent {
  my ( $self, $subDomainId, $path ) = @_;
  my $subdomainNode = $self->{subDomainNodes}->{$subDomainId};

  my $cid_conf = $self->{content_transport}->addSubDomainPhostingContent( 'error_docs', $subDomainId, undef,
    "error-docs",
    "directory" => $path
  );
  $subdomainNode->getChild( 'content', 1, 1 )->addChild( $cid_conf ) if $cid_conf;

}

sub setSubDomainConfContent {
  my ( $self, $subDomainId, $path ) = @_;

  my $root = $self->{subDomainNodes}->{$subDomainId};

  my $cid_conf = $self->{content_transport}->addSubDomainPhostingContent( 'conf', $subDomainId, undef,
    "conf",
    "directory" => $path
  );
  $root->getChild( 'content', 1, 1 )->addChild( $cid_conf ) if $cid_conf;
}
###### End SubDomain functions ######

####### Mail functions #######

sub addMail {
    my ( $self, $domainId, $mailId, $mailName, $passwd, $typePasswd, $userUid ) = @_;

    $self->regMailnameObjectBackupPath( $self->getDomainsBackupPath( $domainId ), $mailId, $mailName, $domainId );

    my $parent = $self->getCashedDomainNode($domainId);
    return unless defined $parent;

    my $mailService = $parent->getChild('mailsystem');
    if ( not $mailService ) {
      Logging::warning("Error: Packer.addMail: there are no node 'mailsystem'", 'PackerStructure');
      return;
    }

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

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

    my $parentGuid = $parent->getAttribute('guid');
    $root->setAttribute( 'guid', $self->getMailnameGuid( $parentGuid, $mailId ) ) if $parentGuid;
    $root->setAttribute( 'owner-guid', $parentGuid ) if $parentGuid;

    my $props = $root->getChild( 'properties', 1 );
    $root->getChild( 'preferences', 1 );

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

      $props->addChild( $item );
    }

    $self->{mailNodes}->{$mailId} = $root;

    $mailService->getChild( 'mailusers', 1 )->addChild($root);
}

sub setMailBoxQuota {
    my ( $self, $mailId, $mbox_quota ) = @_;

    my $root = $self->{mailNodes}->{$mailId};

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

sub setMailBox {
    my ( $self, $mailId, $mailName, $domainAsciiName, $enabled, $path ) = @_;

    my $root = $self->{mailNodes}->{$mailId};
    if ( !defined($root) ) {
      Logging::warning('Unable to dump $mailName mailbox settings, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

    my $item = XmlNode->new( 'mailbox', 'attributes' => { 'type' => 'mdir' } );
    $root->getChild( 'preferences', 1 )->addChild($item);

    $item->setAttribute( 'enabled', $enabled );

    my $cid = $self->{content_transport}->addMailnameContent( 'mailbox', $mailId, "mdir",
      'directory' => $path, 'include_hidden_files' => 1 );
    $item->getChild( 'content', 1, 1 )->addChild( $cid ) if $cid;
}

sub addMailAliase {
    my ( $self, $mailId, $name ) = @_;

    my $root = $self->{mailNodes}->{$mailId};
    if ( !defined($root) ) {
      Logging::warning('Unable to dump $name mail alias settings, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

    my $item = XmlNode->new( 'alias', 'content' => $name );
    $root->getChild( 'preferences', 1 )->addChild($item);
}

sub setMailForwarding {
    my ( $self, $mailId, $enabled, $membersPtr ) = @_;

    unless ( ref($membersPtr) =~ /ARRAY/ ) {
      Logging::warning("Error: setMailForwarding: membersPtr is not ref array",'assert');
      return;
    }

    my @members = @{$membersPtr};

    my $root = $self->{mailNodes}->{$mailId};
    if ( !defined($root) ) {
      Logging::warning('Unable to dump mail forwarding settings, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

    $root->setAttribute( 'forwarding-enabled', $enabled? 'true': 'false' );

    my $item;
    foreach my $member (@members) {
      $item = XmlNode->new( 'forwarding', 'content' => $member );
      $root->getChild( 'preferences', 1 )->addChild($item);
    }
}

sub setMailAutoresponders {
    my ( $self, $mailId, $mailName, $domainAsciiName, $dir, $enabled, $autorespondersPtr, $attachesPtr ) = @_;

    unless ( ref($autorespondersPtr) =~ /ARRAY/ ) {
      Logging::warning("Error: setMailAutoresponders: autorespondersPtr is not ref array",'assert');
      return;
    }
    my @autoresponders = @{$autorespondersPtr};

    unless ( ref($attachesPtr) =~ /ARRAY/ ) {
      Logging::warning("Error: setMailAutoresponders: attachesPtr is not ref array",'assert');
      return;
    }
    my @attaches = @{$attachesPtr};

    my $root = $self->{mailNodes}->{$mailId};
    if ( !defined($root) ) {
      Logging::warning('Error: setMailAutoresponders: empty parent node', 'PackerStructure');
      return;
    }

    my $autorespondersNode = XmlNode->new( 'autoresponders' );

    my $cid = $self->{content_transport}->addMailnameContent( 'attaches', $mailId,  "attach",
      "directory" => $dir, 'checkEmptyDir' => 1 );
    $autorespondersNode->getChild( 'content', 1, 1 )->addChild( $cid ) if $cid;

    foreach my $file (@attaches) {
      $autorespondersNode->addChild(
        XmlNode->new( 'attach', 'attributes' => { 'file' => $file } ) );
    }

    foreach my $autoresponder (@autoresponders) {
      my $node = $self->makeAutoresponderNode ($enabled, $autoresponder, $mailName);
      $autorespondersNode->addChild($node);
    }

    $root->getChild( 'preferences', 1 )->addChild($autorespondersNode);
}

sub setMailAddressbook {
    my ( $self, $mailId, $paramsPtr, $turbaVersion ) = @_;

    my @params = @{$paramsPtr};

    my $parent = $self->{mailNodes}->{$mailId};
    if ( !defined($parent) ) {
      Logging::warning('Unable to dump mailbox addressbook, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

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

    if ($turbaVersion->{'majorVersion'} = 2 && $turbaVersion->{'minorVersion'} > 2 ) {
        $objectFields{'name'} = 'lastname';
        $objectFields{'home-address'} = 'homestreet';
        $objectFields{'work-address'} = 'workstreet';
        $objectFields{'first-name'} = 'firstname';
        $objectFields{'middle-names'} = 'middlenames';
    }

    foreach my $ptrHash (@params) {
      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->setAttribute( $field, $value );
          }
        }
        $node->addChild($user);
      }
    }

    $parent->getChild( 'preferences', 1 )->addChild($node);
}

sub setMailSpamSettings {
    my ( $self, $mailId, $mailName, $domainAsciiName, $status, $server_conf,
      $action, $requiredScore, $modify_subj, $subj_text,
      $blackListPtr, $whiteListPtr, $unblackListPtr, $unwhiteListPtr, $path, $server ) = @_;

    my @blackList = @{$blackListPtr};
    my @whiteList = @{$whiteListPtr};
    my @unblackList = @{$unblackListPtr};
    my @unwhiteList = @{$unwhiteListPtr};

    my $parent;
    if (defined($mailId)) {
    	$parent = $self->{mailNodes}->{$mailId};
    }elsif (defined($server)) {
    	$parent = $self->{serverNode};
    }
    if ( !defined($parent) ) {
      Logging::warning('Unable to dump SPAM filter settings, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

    my $item = XmlNode->new( 'spamassassin' );

    $item->setAttribute( 'status', $status ) if defined($status);
    $item->setAttribute( 'action', $action ) if ( defined($action) && $action );
    $item->setAttribute( 'server-conf', $server_conf )
      if ( defined($server_conf) && $server_conf );
    $item->setAttribute( 'hits', $requiredScore )
      if ( defined($requiredScore) && $requiredScore );
    $item->setAttribute( 'modify-subj', $modify_subj )
      if ( defined($modify_subj) && $modify_subj );
    $item->setAttribute( 'subj-text', $subj_text )
      if ( defined($subj_text) && $subj_text );
    $item->setAttribute( 'max-spam-threads', $server->{'spamfilter_max_children'})
      if ( defined($server->{'spamfilter_max_children'}) && $server->{'spamfilter_max_children'});

    foreach my $blMail (@blackList) {
      $item->addChild( XmlNode->new( 'blacklist-member', 'content' => $blMail ) );
    }
    foreach my $wtMail (@whiteList) {
      $item->addChild( XmlNode->new( 'whitelist-member', 'content' => $wtMail ) );
    }
    foreach my $ublMail (@unblackList) {
      $item->addChild(
        XmlNode->new( 'unblacklist-member', 'content' => $ublMail ) );
    }
    foreach my $uwtMail (@unwhiteList) {
      $item->addChild(
        XmlNode->new( 'unwhitelist-member', 'content' => $uwtMail ) );
    }

    if (defined($mailId) && -d "$path") {
      my $cid = $self->{content_transport}->addMailnameContent( 'spam-assasin', $mailId, "sa", "directory" => $path );
      $item->getChild( 'content', 1, 1 )->addChild( $cid ) if $cid;
    }

    if (defined($mailId)) {
	    $parent->getChild( 'preferences', 1 )->addChild($item);
    }elsif(defined($server)) {
    	    $parent->addChild($item);
    }
}

sub setMailVirusSettings {
    my ( $self, $mailId, $state ) = @_;

    my $parent = $self->{mailNodes}->{$mailId};
    if ( !defined($parent) ) {
      Logging::warning('Unable to dump virus protection settings, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

    my $item = XmlNode->new('virusfilter');
    $item->setAttribute( 'state', $state );

    $parent->getChild( 'preferences', 1 )->addChild($item);
}

sub addMailCustomButton {
    my ( $self, $mailId, $id, $optionsPtr, $customButtonsDir, $icon ) = @_;

    my $parent = $self->{mailNodes}->{$mailId};
    if ( !defined($parent) ) {
      Logging::warning('Unable to dump mail custom buttons settings, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

    my $node = $self->makeCustomButtonNode( 'mails', $mailId, $id, $optionsPtr, $customButtonsDir, $icon );

    $parent->getChild( 'preferences', 1 )->addChild($node);
}
####### End Mail functions #######

####### Siteapps functions #######

sub addDomainSapp {
  my ( $self, $domainId, $sappId, $sapp, $licenseType ) = @_;
  my $parent = $self->getCashedDomainNode($domainId);
  return unless defined $parent;

  my $phosting = $parent->getChild('phosting');
  if ( not $phosting ) {
    Logging::warning('Unable to dump domain site applications, because of dump XML structure is not full','assert');
    return;
  }

  my $sapp_installed = $self->makeSappNode($sappId, $sapp, $licenseType);

  $self->{sappNodes}->{$sappId} = $sapp_installed;
  $phosting->getChild( 'applications', 1 )->addChild($sapp_installed);
}

sub addSubDomainSapp {
  my ( $self, $subDomainId, $sappId, $sapp, $licenseType ) = @_;
  my $root = $self->{subDomainNodes}->{$subDomainId};

  my $sapp_installed = $self->makeSappNode( $sappId, $sapp, $licenseType );

  $self->{sappNodes}->{$sappId} = $sapp_installed;
  $root->getChild( 'applications', 1 )->addChild($sapp_installed);
}

sub setSappParams {
  my ( $self, $sappId, $sapp ) = @_;

  my $paramsPtr = $sapp->getParams();

  my %params = %{$paramsPtr};

  my $sapp_installed = $self->{sappNodes}->{$sappId};

  while ( my ( $param_name, $param_value ) = each %params )
  {
    my $name;
    my $value;
    if ( $sapp->encodedParams() ) {
      $name = $param_name;
      $value = $param_value;
    }
    else {
      $name = $self->{base64}->{'ENCODE'}->($param_name);
      $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
          )
        ]
      )
    );
  }
}

sub setSappSettings {
  my ( $self, $sappId, $sapp ) = @_;

  my $settingsPtr = $sapp->getSettings();

  if (scalar keys %{$settingsPtr} > 0) {

    my %settings = %{$settingsPtr};

    my $sappInstalledNode = $self->{sappNodes}->{$sappId};

    my $sappSettingsNode = XmlNode->new('sapp-settings');

    while ( my ( $setting_name, $setting_value ) = each %settings )
    {
      $sappSettingsNode->addChild(
        XmlNode->new(
          'setting',
          'children' => [
            XmlNode->new(
              'name',
              'content'    => $setting_name
            ),
            XmlNode->new(
              'value',
              'content'    => $setting_value
            )
          ]
        )
      );
    }
    $sappInstalledNode->addChild($sappSettingsNode);
  }
}

sub setSappEntryPoints {
  my ( $self, $sappId, $sapp ) = @_;

  my $sapp_installed = $self->{sappNodes}->{$sappId};

  my @ep = $sapp->getEntryPoints();

  foreach my $node (@ep) {
    my $epNode = XmlNode->new('sapp-entry-point');
    $epNode->addChild(XmlNode->new('label', 'content' => $node->{'label'}));
    $epNode->addChild(XmlNode->new('description', 'content' => $node->{'description'}));
    $epNode->addChild(XmlNode->new('hidden', 'content' => $node->{'hidden'}));
    $epNode->addChild(XmlNode->new('http', 'content' => $node->{'http'}));
    $epNode->addChild(XmlNode->new('icon', 'content' => $node->{'icon'})) if defined $node->{'icon'};

    my $permissionsNode = XmlNode->new('limits-and-permissions');
    $permissionsNode->addChild(
      XmlNode->new('permission', 'attributes' => {'name' => $node->{'permission-name'} , 'class' => $node->{'permission-class'}})
    );
    $epNode->addChild($permissionsNode);
    $sapp_installed->addChild($epNode);
  }
}

sub setDomainSappBackupArchiveContent {
  my ( $self, $domainId, $sappId, $path, $include, $webspaceId ) = @_;
  my $sapp_installed = $self->{sappNodes}->{$sappId};
  my $cid = $self->{content_transport}->addPhostingContent( 'sapp-apsc', $domainId, $webspaceId,
    "sapp-apsc." . $sappId,
    "directory"    => $path,
    "include"      => [$include]
  );
  $sapp_installed->getChild( 'content', 1, 1 )->addChild( $cid ) if $cid;
}

sub setSubDomainSappBackupArchiveContent {
  my ( $self, $subdomainId, $sappId, $path, $include, $webspaceId ) = @_;

  my $sapp_installed = $self->{sappNodes}->{$sappId};

  my $cid = $self->{content_transport}->addSubDomainPhostingContent( 'sapp-apsc', $subdomainId, $webspaceId,
    "sapp-apsc." . $sappId,
    "directory"    => $path,
    "include"      => [$include]
  );
  $sapp_installed->getChild( 'content', 1, 1 )->addChild( $cid ) if $cid;
}


sub addDomainSappDatabase {
  my ( $self, $dbId, $dbServerId, $domainId, $sappId, $dbName, $dbType, $optionalPtr, $dbServerPtr, $dbUsersPtr, $contentDescriptionPtr, $skipContent ) = @_;

  $self->regDatabaseObjectBackupPath( $self->getDomainsBackupPath( $domainId ), $dbId, $dbName, $dbServerId );
  my $parent = $self->{sappNodes}->{$sappId};

  my $domain = $self->getCashedDomainNode($domainId);

  my $node = $self->makeDatabaseNode( $dbId, $dbName, $dbType, 'domain', $domainId, $optionalPtr,
                                      $dbServerPtr, $dbUsersPtr, $contentDescriptionPtr, $skipContent );
  my $domainGuid = $domain->getAttribute('guid');
  $node->setAttribute( 'guid', $self->getDatabaseGuid( $domainGuid, $dbId ) ) if $domainGuid;
  $node->setAttribute( 'owner-guid', $domainGuid ) if $domainGuid;
  $node->setAttribute( 'aps-registry-id', $optionalPtr->{'aps-registry-id'}) if $optionalPtr->{'aps-registry-id'};

  $parent->addChild($node);
}

sub addSubDomainSappDatabase {
  my ( $self, $dbId, $dbServerId, $subdomainId, $sappId, $dbName, $dbType, $optionalPtr, $dbServerPtr, $dbUsersPtr, $contentDescriptionPtr, $skipContent ) = @_;

  $self->regDatabaseObjectBackupPath( $self->getSubdomainsBackupPath( $subdomainId ), $dbId, $dbName, $dbServerId );
  my $subdomain = $self->{subDomainNodes}->{$subdomainId};

  my $parent = $self->{sappNodes}->{$sappId};

  my $node = $self->makeDatabaseNode( $dbId, $dbName, $dbType, 'subdomain', $subdomainId, $optionalPtr,
                                      $dbServerPtr, $dbUsersPtr, $contentDescriptionPtr, $skipContent );
  my $subDomainGuid = $subdomain->getAttribute('guid');
  $node->setAttribute( 'guid', $self->getDatabaseGuid( $subDomainGuid, $dbId ) ) if $subDomainGuid;
  $node->setAttribute( 'owner-guid', $subDomainGuid ) if $subDomainGuid;
  $node->setAttribute( 'aps-registry-id', $optionalPtr->{'aps-registry-id'}) if $optionalPtr->{'aps-registry-id'};

  $parent->addChild($node);
}

sub addDomainSappCustomButton {
    my $self = shift;
    $self->addSappCustomButton( 'domains', @_ );
}

sub addSubDomainSappCustomButton {
    my $self = shift;
    $self->addSappCustomButton( 'subdom', @_ );
}

sub addSappCustomButton {
    my ( $self, $parentType, $parentid, $sappId, $id, $optionsPtr, $customButtonsDir, $icon ) = @_;

    my $parent = $self->{sappNodes}->{$sappId};
    if ( !defined($parent) ) {
      Logging::warning('Unable to dump site applications custom buttons settings, because of dump XML structure is not full', 'PackerStructure');
      return;
    }

    my $node = $self->makeCustomButtonNode( $parentType, $parentid, $id, $optionsPtr, $customButtonsDir, $icon );

    $parent->addChild($node);
}

sub setSappApscNode {
  my ( $self, $sappId, $content) = @_;

  my $parent = $self->{sappNodes}->{$sappId};

  my $sappApscNode = XmlNode->new('sapp-apsc', 'content' => $self->{base64}->{'ENCODE'}->($content));

  $parent->addChild($sappApscNode);
}
####### End Siteapps functions #######

sub insertLimitNode {
    my ( $self, $node, $name, $value ) = @_;
    my $limits = $node->getChild( 'limits-and-permissions', 1 );
    $limits->addChild( XmlNode->new( "limit",
        "attributes" => { "name" => $name },
        "content"    => $value ) );
}

sub makePermissionNode {
  my ($self, $parent, $name, $allowed) = @_;

  #[Bug 111477] Value "-1" for attribute allowed of permission is not among the enumerated set
  if ($allowed =~ /-1/) {
   	$allowed = 'false';
  }
  $parent->addChild(  XmlNode->new('permission', 'attributes' => { 'name' => $name, 'value' => $allowed}) );
}

###### Backup content structure ######

sub getPhostingGuid{
   my ( $self, $ownerguid, $id ) = @_;
   return "$ownerguid\_ph_$id";
}

sub getDatabaseGuid{
   my ( $self, $ownerguid, $id ) = @_;
   return "$ownerguid\_db_$id";
}

sub getSubdomainGuid{
   my ( $self, $ownerguid, $id ) = @_;
   return "$ownerguid\_sb_$id";
}

sub getMailnameGuid{
   my ( $self, $ownerguid, $id ) = @_;
   return "$ownerguid\_mn_$id";
}

sub regObjectBackupPath{
 my( $self, $basePath, $group, $key, $objectId, $parentId ) = @_;
 $basePath .= '/' if $basePath;
 my $path = "$basePath$group";
 $path = $basePath if $group eq 'admin';
 $self->{backupPath}->{"$group\_$key"} = $path;
 $self->{objectId}->{"$group\_$key"} = $objectId;
 $self->{parentId}->{"$group\_$key"} = $parentId;
 return $self->getBackupPath( $group, $key, undef, 0 );
}

sub regAdminObjectBackupPath{
 my( $self, $basePath ) = @_;
 return $self->regObjectBackupPath( $basePath, 'admin', 'admin', '' );
}

sub regResellersObjectBackupPath{
 my( $self, $basePath, $key, $resellerLogin ) = @_;
 return $self->regObjectBackupPath( $basePath, 'resellers', $key, $self->{fnamecreator}->normalize_short_string( $resellerLogin, $key ) );
}

sub regClientObjectBackupPath{
 my( $self, $basePath, $key, $clientLogin ) = @_;
 return $self->regObjectBackupPath( $basePath, 'clients', $key, $self->{fnamecreator}->normalize_short_string( $clientLogin, $key ) );
}

sub regDomainObjectBackupPath{
 my( $self, $basePath, $key, $domainAscName ) = @_;
 return $self->regObjectBackupPath( $basePath, 'domains', $key, $self->{fnamecreator}->normalize_long_string( $domainAscName, $key ) );
}

sub regSiteObjectBackupPath{
 my( $self, $basePath, $key, $siteName ) = @_;
 return $self->regObjectBackupPath( $basePath, 'sites', $key, $self->{fnamecreator}->normalize_long_string( $siteName, $key ) );
}

sub regMailnameObjectBackupPath{
 my( $self, $basePath, $key, $mailName, $domainId ) = @_;
 return $self->regObjectBackupPath( $basePath, 'mailnames', $key, $self->{fnamecreator}->normalize_short_string( $mailName, $key ), $domainId );
}

sub regDatabaseObjectBackupPath{
 my( $self, $basePath, $key, $databaseName, $dbServerId ) = @_;
 return $self->regObjectBackupPath( $basePath, 'databases', $key, $self->{fnamecreator}->normalize_short_string( "$databaseName\_$dbServerId", $key ) );
}

sub regSubdomainObjectBackupPath{
 my( $self, $basePath, $key, $subdomainName, $domainId ) = @_;
 return $self->regObjectBackupPath( $basePath, 'subdomains', $key, $self->{fnamecreator}->normalize_short_string( $subdomainName, $key ), $domainId );
}

sub regPhostingObjectBackupPath{
 my( $self, $basePath, $key ) = @_;
 return $self->regObjectBackupPath( $basePath, 'phosting', $key, '' );
}

sub getObjectId{
 my( $self, $group, $key ) = @_;
 $key = "$group\_$key";
 return 'phosting' if $group eq 'phosting';
 return 'admin' if $group eq 'admin';
 return $self->{objectId}->{$key};
}

sub getParentId{
 my( $self, $group, $key ) = @_;
 $key = "$group\_$key";
 return $self->{parentId}->{$key}  if exists $self->{parentId} && exists $self->{parentId}->{$key};
 return;
}

sub getPhostingParentId{
 my( $self, $key ) = @_;
 return $key;
}

sub getSubdomainParentId{
 my( $self, $key ) = @_;
 return $self->getParentId( "subdomains", $key );
}

sub getMailNameParentId{
 my( $self, $key ) = @_;
 return $self->getParentId( "mailnames", $key );
}

sub getBackupPath{
 my( $self, $group, $key, $fileName, $onlyFileName ) = @_;
 $key = "$group\_$key";
 my $res = '';
 if( not $onlyFileName ) {
     $res .=  $self->{backupPath}->{$key};
     $res .= '/' . $self->{objectId}->{$key} if $group ne 'phosting' and $group ne 'admin';
 }
 $res = $self->{fnamecreator}->getFileName( $res, $self->{backupname}, $self->{objectId}->{$key}, $fileName ) if defined $fileName;
 return $res;
}

sub getAdminBackupPath{
 my( $self, $fileName, $onlyFileName ) = @_;
 return $self->getBackupPath( 'admin', 'admin', $fileName, $onlyFileName );
}

sub getResellersBackupPath{
 my( $self, $key, $fileName, $onlyFileName ) = @_;
 return $self->getBackupPath( 'resellers', $key, $fileName, $onlyFileName );
}

sub getClientsBackupPath{
 my $self = shift;
 my $key = shift;
 return $self->getBackupPath( 'clients', $key, @_ ) if exists $self->{objectId}->{"clients_$key"};
 return $self->getResellersBackupPath( $key, @_ );
}

sub getDomainsBackupPath{
 my( $self, $key, $fileName, $onlyFileName ) = @_;
 if ( defined $self->{domainNodes}->{$key} ) {
    return $self->getBackupPath( 'domains', $key, $fileName, $onlyFileName );
 }
 return $self->getBackupPath( 'sites', $key, $fileName, $onlyFileName );
}

sub getSitesBackupPath{
 my $self = shift;
 return $self->getBackupPath( 'sites', @_ );
}

sub getPhostingBackupPath{
 my $self = shift;
 return $self->getBackupPath( 'phosting', @_ );
}

sub getDatabasesBackupPath{
 my $self = shift;
 return $self->getBackupPath( 'databases', @_ );
}

sub getMailnamesBackupPath{
 my $self = shift;
 return $self->getBackupPath( 'mailnames', @_ );
}

sub getSubdomainsBackupPath{
 my $self = shift;
 return $self->getBackupPath( 'subdomains', @_ );
}

sub getAdminObjectId{
 my( $self ) = @_;
 return $self->getObjectId( 'admin', 'admin' );
}

sub getResellerObjectId{
 my( $self, $key ) = @_;
 return $self->getObjectId( 'resellers', $key );
}

sub getClientObjectId{
 my( $self, $key ) = @_;
 return $self->getResellerObjectId( $key ) if exists $self->{objectId}->{"resellers_$key"};
 return $self->getObjectId( 'clients', $key );
}

sub getDomainObjectId{
 my( $self, $key ) = @_;
 return $self->getObjectId( 'domains', $key );
}

sub getPhostingObjectId{
 my( $self, $key ) = @_;
 return $self->getObjectId( 'phosting', $key );
}

sub getDatabaseObjectId{
 my( $self, $key ) = @_;
 return $self->getObjectId( 'databases', $key );
}

sub getMailnameObjectId{
 my( $self, $key ) = @_;
 return $self->getObjectId( 'mailnames', $key );
}

sub getSubdomainObjectId{
 my( $self, $key ) = @_;
 return $self->getObjectId( 'subdomains', $key );
}
###### End Backup content structure ######

sub addTemplateItem {
    my ( $self, $node, $name, $value ) = @_;
    $node->addChild( XmlNode->new( "template-item",
        "attributes" => { "name" => $name },
        "content"    => $value ) );
}

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

  my $templatePlanItemNode = XmlNode->new( "template-plan-item",
                                           "attributes" => { "name" => $name },
                                           "content"    => $value );
  return $templatePlanItemNode;
}

sub addTemplatePlanItem {
  my ( $self, $node, $name, $value ) = @_;

  my $templatePlanItemNode = $self->makeTemplatePlanItem( $name, $value );
  $node->addChild( $templatePlanItemNode ) if defined $templatePlanItemNode;
}

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

  my %logRotation = %{$logRotationPtr};

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

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

  my $rottype;
  if ( $logRotation{'period_type'} eq 'by_time' ) {
    $rottype = XmlNode->new( 'logrotation-period',
                             'attributes' => { 'period' => $logRotation{'period'} } );
  }
  else {
    $rottype = XmlNode->new( 'logrotation-maxsize',
                             'content' => $logRotation{'period'} );
  }
  $item->addChild($rottype);
  $parent->addChild($item);
}

sub makeDatabaseNode {
    my ( $self, $dbId, $dbName, $dbType, $parent, $parentId, $optionalPtr, $dbServerPtr, $dbUsersPtr, $contentDescriptionPtr, $skipContent ) = @_;

    my %optional = %{$optionalPtr};
    my %dbServer = %{$dbServerPtr};
    my @dbUsers = @{$dbUsersPtr};
    my %contentDescription = %{$contentDescriptionPtr};

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

    $root->setAttribute( 'version', $optional{'version'} )
      if $optional{'version'};
    $root->setAttribute( 'charset', $optional{'charset'} )
      if $optional{'charset'};
    $root->setAttribute( 'version', $optional{'version'} )
      if $optional{'version'};
    $root->setAttribute( 'db-existent', $optional{'db-existent'} )
      if $optional{'db-existent'};
    $root->setAttribute( 'prefix', $optional{'prefix'} )
      if $optional{'prefix'};


    if (%dbServer) {
      my $dbServerNode = XmlNode->new('db-server');
      $dbServerNode->setAttribute( 'type', $dbServer{'type'} );
      $dbServerNode->addChild( XmlNode->new( 'host', 'content' => $dbServer{'host'} ) );
      $dbServerNode->addChild( XmlNode->new( 'port', 'content' => $dbServer{'port'} ) );

      $root->addChild($dbServerNode);
    }

    $self->makeDatabaseUsersNodes($root, \@dbUsers);

    if (%contentDescription and !$skipContent) {
      my $cid;
      if( $parent eq 'subdomain' ) {
        $cid = $self->{content_transport}->addSubdomainDbContent( 'sqldump', $parentId, $dbId, %contentDescription );
      }
      else{
        $cid = $self->{content_transport}->addDbContent( 'sqldump', $dbId, %contentDescription );
      }
      $root->getChild( 'content', 1, 1 )->addChild( $cid ) if $cid;
    }

    $self->{databaseNodes}->{$dbId} = $root;
    return $root;
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $rootNode = $root->getChild( 'databases', 1 );

  my $globalDbUsersNode = XmlNode->new('dbusers');

  $self->makeDatabaseUsersNodes($globalDbUsersNode, $dbUsersPtr);

  $rootNode->addChild($globalDbUsersNode);
}

sub makeDatabaseUsersNodes {
    my ( $self, $parentNode, $dbUsersPtr ) = @_;

    my @dbUsers = @{$dbUsersPtr};

    my $item;
    foreach my $user (@dbUsers) {
      my $dbUserRoot = XmlNode->new( 'dbuser',
        'attributes' => { 'name' => $user->{'login'}, 'id' => $user->{'id'} } );

      if (defined($user->{'default'})) {
          $dbUserRoot->setAttribute('default', 'true');
      }else {
          $dbUserRoot->setAttribute('default', 'false');
      }

      $dbUserRoot->setAttribute('aps-registry-id', $user->{'aps-registry-id'}) if $user->{'aps-registry-id'};
	  $dbUserRoot->setAttribute('db-user-existent', $user->{'db-user-existent'}) if $user->{'db-user-existent'};

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

      $dbUserRoot->addChild($item);

      if ($user->{'host'}) {
        my %dbServer;
        $dbServer{'host'} = $user->{'host'};
        $dbServer{'port'} = $user->{'port'};
        $dbServer{'type'} = $user->{'dbservertype'};

        my $dbServerNode = $self->makeDbServerNodeWithoutCredentials(\%dbServer);
        $dbUserRoot->addChild($dbServerNode);
      }

      $parentNode->addChild($dbUserRoot);
    }
}

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

    my %auto = %{$autoPtr};

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

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

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

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

    $root->setAttribute( 'status', ($enabled eq 'true' && $auto{'resp_on'} eq 'true') ? 'on' : 'off' );

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

    #
    # forward
    #
    if ( $auto{'redirect'} ) {
      $root->setAttribute( 'redirect', $auto{'redirect'} );
    }

    #
    # end forward
    #

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

    my @attach = @{$auto{'attach'}};
    if ( @attach ) {
      foreach my $file (@attach) {
        $root->addChild( XmlNode->new( 'attach', 'attributes' => { 'file' => $file } ) );
      }
    }

    return $root;
}

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

    #CUSTOM_BUTTON_PUBLIC
    $node->setAttribute( 'visible-to-sublogins', $options & 128 ? 'true' : 'false' );

    #CUSTOM_BUTTON_INTERNAL
    $node->setAttribute( 'open-in-same-frame', $options & 256 ? 'true' : 'false' );

    $node->setAttribute( 'no-frame', $options & 64 ? '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 addAdminContentProxy{
    my $self = shift;
    $self->{content_transport}->addAdminContent(@_);
}

sub addClientContentProxy{
    my $self = shift;
    $self->{content_transport}->addClientContent(@_);
}

sub addDomainContentProxy{
    my $self = shift;
    $self->{content_transport}->addDomainContent(@_);
}

sub addMailnameContentProxy{
    my $self = shift;
    $self->{content_transport}->addMailnameContent(@_);
}

sub addCustomButtonNode {
  my ( $self, $optionsPtr) = @_;

  my %options = %{$optionsPtr};

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

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

  foreach my $key (keys %attributes) {
    $node->setAttribute( $attributes{$key}, $options{$key} ) if defined $options{$key};
  }

  return $node;

}

sub makeCustomButtonNode65 {
  my ( $self, $optionsPtr ) = @_;
  my $node = $self->addCustomButtonNode($optionsPtr);

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

  return $node;

}

sub makeCustomButtonNode {
  my ( $self, $parentType, $parentId, $id, $optionsPtr, $customButtonsDir, $icon ) = @_;

  my $node = $self->addCustomButtonNode($optionsPtr);
  my %options = %{$optionsPtr};

  if ( -e $customButtonsDir . "/" . $icon ) {
    if ($icon) {
      my $proc;
      if( $parentType eq 'admin' ) { $proc = \&addAdminContentProxy; }
      elsif( $parentType eq 'clients' ) { $proc = \&addClientContentProxy; }
      elsif( $parentType eq 'domains' ) { $proc = \&addDomainContentProxy; }
      elsif( $parentType eq 'mails' ) { $proc = \&addMailnameContentProxy; }
      else{
        Logging::warning( "Error: makeCustomButtonNode: unsupported parent type '$parentType' ",'assert');
      }
      if( $proc ) {
        my $cid_icon =
          ( AgentConfig::tarVersionMoreThan15() )
          ? $self->$proc(
          'icon',
          $parentId,
          $icon . "." . $id,
          "directory" => $customButtonsDir,
          "add_file"  => $icon
          )
          : $self->$proc(
          'icon',
          $parentId,
          $icon . "." . $id,
          "directory" => $customButtonsDir,
          "include"   => [$icon]
          );

        $node->getChild( 'content', 1, 1 )->addChild( $cid_icon ) if $cid_icon;
      }
    }
  }
  parseCustomButtonOptions( $node, $options{'options'} );

  # exists $options{'plan_item_name'}   <=>   (PleskVersion::atLeast( 10, 1, 0) and not PleskVersion::isSmb())
  if ( defined $options{'plan_item_name'} && ( '' ne $options{'plan_item_name'} ) ) {
    my $templatePlanItemNode = $self->makeTemplatePlanItem( $options{'plan_item_name'}, '' );
    $node->addChild( $templatePlanItemNode );
  }

  return $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->setAttribute( 'unit', $string_unit );

  return $dnsZoneParam;
}

sub makeDnsZone {
  my ( $self, $parent, $paramsPtr, $recordsPtr ) = @_;

  my %params = %{$paramsPtr};
  my @records = @{$recordsPtr};

  my $email;
  if ( defined($params{'email'}) and (length( $params{'email'} ) != 0) ) {
    $email = $params{'email'};
  }
  else {

    # should assume some default email or something...
    $email = 'root@localhost.localdomain';
  }

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

  if ( exists $params{'serial_format'} ){
     $dnsZone->setAttribute( 'serial-format', $params{'serial_format'} );
  }

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

  # dns records
  for my $hash (@records) {
    my $dnsrec = $self->makeDnsRecord( $hash );
    if ($dnsrec) {
      $dnsZone->addChild($dnsrec);
    }
  }

  $parent->addChild($dnsZone);
}

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

  unless (ref($hashPtr) =~ /HASH/)
  {
    Logging::warning('Error: makeDnsRecord: hashPtr is not hash ptr', 'assert');
    return;
  }
  my %hash = %{$hashPtr};

  return unless $hash{'type'} =~ /^A|AAAA|NS|MX|CNAME|PTR|TXT|master|SRV|AXFR$/;
  return if $hash{'displayHost'} && $hash{'displayHost'} =~ /.*_domainkey.*/;

  my $host =
    defined $hash{'displayHost'}
    ? $hash{'displayHost'}
    : $hash{'host'};
  my $val =
    defined $hash{'displayVal'}
    ? $hash{'displayVal'}
    : $hash{'val'};

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

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

  return $item;
}

#
# 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" => HelpFuncs::urlDecode($value) ) )
    if $value;
}

sub makeSysUser {
  my ( $self, $sysuserPtr ) = @_;

  my %sysuser = %{$sysuserPtr};

  #
  # attributes
  #

  my $root =
    XmlNode->new( 'sysuser',
    'attributes' => { 'name' => lc( $sysuser{'login'} ) } );
  $root->setAttribute( 'shell', $sysuser{'shell'} ) if $sysuser{'shell'} and $sysuser{'shell'} ne '/bin/false';

  my $quota;
  if ( $sysuser{'quota'} ) {
    $quota = $sysuser{'quota'};
    $root->setAttribute( 'quota', $quota ) if $quota;
  }

  if ( $sysuser{'home'} ) {
    my $home = $sysuser{'home'};
    if (exists $sysuser{'relative_path'}) {
      if (substr($home,0, length($sysuser{'relative_path'})) eq $sysuser{'relative_path'} ) {
        $home = substr($home, length($sysuser{'relative_path'}));
      }
    }
    $root->setAttribute( 'home', $home) if $home;
  }

  #
  # end attributes
  #

  $root->addChild( CommonPacker::makePasswordNode( $sysuser{'passwd'}, $sysuser{'passwdType'} ) );

  if ( $sysuser{'cron'} ) {
    my $cronNode = XmlNode->new( 'cron', 'content' => $sysuser{'cron'} );
    if ( $sysuser{'cron-encoding'} ) {
      $cronNode->setAttribute( 'encoding', $sysuser{'cron-encoding'} );
    }
    $root->addChild( $cronNode );
  }

  return $root;
}

sub makeSappNode {

  my ( $self, $sappId, $sapp, $licenseType ) = @_;

  my $name = $sapp->getName();
  my $version = $sapp->getVersion();
  my $release = $sapp->getRelease();
  my $description = $sapp->getDescription();
  my $isCommercial = $sapp->isCommercial();
  my $isIntegrated = $sapp->isIntegrated();
  my $prefix = $sapp->getInstallPrefix();
  my $isSsl = $sapp->isSsl();
  my $packageId = $sapp->getSappPackageId();

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

  #-----------------------------------------------------------------
  # sapp-spec
  #-----------------------------------------------------------------
  my $sapp_spec = XmlNode->new('sapp-spec');
  if ( $packageId ) {
    $sapp_spec->addChild(
      XmlNode->new( 'sapp-package-id', 'content' => $packageId ) );
  }

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

  $sapp_installed->addChild($sapp_spec);

  if ( defined($licenseType) ) {
    $sapp_installed->addChild(
      XmlNode->new(
        'sapp-installed-license-type',
        'content' => $licenseType
      )
    );
  }

  return $sapp_installed;
}

sub makeSiteAppInstalled{
  my ( $self, $sappId, $prefix, $isSsl, $apsRegistryId ) = @_;

  my $sapp_installed = $self->{sappNodes}->{$sappId};

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

sub setSappApsControllerInfo {
  my ( $self, $sappId, $sapp ) = @_;
  my $sapp_installed = $self->{sappNodes}->{$sappId};

  if ( $sapp->getContext() ) {
    $sapp_installed->addChild( XmlNode->new( 'context', 'attributes' => { "type" =>  $sapp->getContext() } ) );
  }

  if ( $sapp->getApplicationApsRegistryId() ) {
    $sapp_installed->addChild( XmlNode->new( 'aps-registry-id', 'content' => $sapp->getApplicationApsRegistryId() ) );
  }
}

sub setSappApsLicense {
  my ( $self, $sappId, $sapp ) = @_;

  my $sapp_installed = $self->{sappNodes}->{$sappId};

  if ( $sapp->isContainLicense() ) {
    my $licenseNode = XmlNode->new('sapp-license');
    $licenseNode->addChild( XmlNode->new('aps-registry-id', 'content' => $sapp->getLicenseApsRegistryId())) if $sapp->getLicenseApsRegistryId();
    $licenseNode->addChild( XmlNode->new('license-type', 'content' => $sapp->getLicenseType()));
    $licenseNode->addChild( XmlNode->new('activation-code', 'content' => $sapp->getActivationCode()));
    $licenseNode->addChild( XmlNode->new('use-stub', 'content' => $sapp->getUseStub()));
    $sapp_installed->addChild($licenseNode);
  }
}

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 makeSystemIpNode {
  my ( $self, $parent, $ptrIp ) = @_;
  my $correctMask = HelpFuncs::blockToNum( $ptrIp->{'mask'} );
  my $md5 = PerlMD5->new();
  $md5->add(HelpFuncs::urlDecode($ptrIp->{'pvtkey'}));
  $parent->getChild( 'properties', 1 )->addChild(
    XmlNode->new(
      'system-ip',
      'attributes' => { 'certificate' => $md5->hexdigest() },
      '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 makeTemplateNode {
  my ( $self, $nodeName, $templateName, $data ) = @_;

  my ( $templateAttrs, $templatePtr, $planItemsPtr, $apsBundleFilterItemsPtr, $filterType, $phpSettingsPtr, $defaultDbServersPtr );

  $templateAttrs = $data->{'attributes'};
  $templatePtr = $data->{'data'};
  $planItemsPtr = $data->{'items'};
  $apsBundleFilterItemsPtr = $data->{'aps-filter-items'};
  $filterType = $data->{'aps-filter-type'};
  $phpSettingsPtr = $data->{'php-settings'};
  $defaultDbServersPtr = $data->{'default-db-servers'};

  my %logRotation = %{$data->{'log-rotation'}};
  my %ipPool = %{$data->{'ip-pool'}};

  my $node =
    XmlNode->new( $nodeName,
    'attributes' => { 'name' => $templateName } );

  for my $ptrRowAttrs ( @{$templateAttrs} ) {
      my ($attrName, $attrValue ) = @{$ptrRowAttrs};
      $node->setAttribute( $attrName, $attrValue );
  }

  for my $ptrRow ( @{$templatePtr} ) {
      my ( $element, $value ) = @{$ptrRow};
      $self->addTemplateItem( $node, $element, $value );
  }

  while ( my ($itemName, $itemValue) = each ( %{$planItemsPtr} ) ) {
    $self->addTemplatePlanItem( $node, $itemName, $itemValue );
  }

  if (keys %logRotation){
      $self->makeLogrotationNode( $node, \%logRotation );
  }

  if (%ipPool and scalar(keys %ipPool) > 0){
	  my $ipPoolNode = XmlNode->new( 'ip_pool' );

	  for my $ip ( keys %ipPool ) {
        $ipPoolNode->addChild(
	      $self->makeIpNode($ip, $ipPool{$ip} )
	    );
	  }

      $node->addChild($ipPoolNode);
  }

  if (@{$apsBundleFilterItemsPtr}) {
    $self->makeApsBundleFilterNode(undef, undef, $filterType, $apsBundleFilterItemsPtr, $node);
  }

  if (defined $phpSettingsPtr and $nodeName ne 'reseller-template') {
    $node->addChild($self->makePhpSettingsNode($phpSettingsPtr));
  }

  if (defined $defaultDbServersPtr and $nodeName eq 'domain-template') {
    $node->addChild($self->makeDefaultDbServersNode($defaultDbServersPtr));
  }

  return $node;
}

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

  my $parent = $self->getCashedDomainNode($domainId);

  $parent->getChild( 'preferences', 1 )->addChild($self->makeDefaultDbServersNode($defaultDbServersPtr));

}

sub makeDefaultDbServersNode {
  my ($self, $defaultDbServersPtr) = @_;

  my $defaulDbServersNode = XmlNode->new('default-db-servers');

  foreach my $dbServer (@{$defaultDbServersPtr}) {
    $defaulDbServersNode->addChild($self->makeDbServerNodeWithoutCredentials($dbServer));
  }

  return $defaulDbServersNode;
}

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

  my $parent = $self->getCashedDomainNode($domainId);
  return unless defined $parent;

  if (defined $phpSettingsPtr) {
    $parent->addChild($self->makePhpSettingsNode($phpSettingsPtr));
  }
}

sub makePhpSettingsNode {
  my ($self, $phpSettingsPtr) = @_;

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

  if (exists $phpSettingsPtr->{'notice'}) {
    $node->addChild(XmlNode->new('notice-text', 'content' => $phpSettingsPtr->{'notice'}));
    delete $phpSettingsPtr->{'notice'};
  }

  foreach my $key (keys %{$phpSettingsPtr}) {
    my $settingNode = XmlNode->new('setting');
    $settingNode->addChild(XmlNode->new('name', 'content' => $key));
    $settingNode->addChild(XmlNode->new('value', 'content' => $phpSettingsPtr->{$key}));
    $node->addChild($settingNode);
  }

  return $node;
}

sub makeIpNode {
  my ($self, $ip, $iptype) = @_;
  return XmlNode->new(
    'ip',
    'children' => [
      XmlNode->new( 'ip-type',    content => $iptype ),
      XmlNode->new( 'ip-address', content => $ip )
    ]
  );
}

sub addServerVirusfilter {
  my ($self, $virusfilter) = @_;

  my $root = $self->{serverNode};

  my $content = defined($virusfilter) ? $virusfilter : 'none';

  my $virusfilterNode = XmlNode->new('virusfilter', 'content' => $content);
  $virusfilterNode->setAttribute( 'state', defined($virusfilter) ? 'inout' : 'none' );

  $root->addChild($virusfilterNode);

  return $virusfilterNode;
}

sub getSb5SitePublished {
  my ($self, $name) = @_;

  my $sb5SitePublishedUtil = AgentConfig::sb5SitePublishedUtil();
  my $cmd = $sb5SitePublishedUtil . " --is-site-builder-site-published " . $name;
  my $result = `$cmd`;
  chomp($result);
  my $retCode = $? >> 8;
  if( $retCode != 0 ) {
    Logging::warning( "Unable to backup Site Builder publishing status on site $name (ErrorCode: $retCode, STDOUT:$result).", 'UtilityError' );
    return;
  }
  return ($result eq 'true')? 'true' : 'false';
}

sub setSb5ServerContent {
  my ($self) = @_;
  my $root = $self->{serverNode};
  my $sb5backupUtil = AgentConfig::sb5BackupUtil();
  my $dstFile = HelpFuncs::mktemp("sb5XXXXXXXX");
  my $cmd = "$sb5backupUtil --backup --target=server_settings --log=stdout --file=$dstFile";
  my $result = `$cmd`;
  my $retCode = $? >> 8;

  Logging::debug("Backing up sitebuilder server settings:");
  Logging::debug($cmd);
  Logging::debug($result);

  if( $retCode == 0 ) {
    if( -e "$dstFile.zip") {
      my $cid = $self->{content_transport}->addAdminContent('sb5-server', undef, 'sb_server', "directory" => ".", "include"   => [$dstFile.".zip"]);
      $root->getChild('content', 1, 1)->addChild($cid) if $cid;
    }
    else {
      Logging::warning( "Sitebuilder backup was not created.", 'UtilityError' );
    }
  }
  else {
    Logging::warning( "Return code of sitebuilder bru utility: ".$retCode.". Some errors occured during sitebuilder backup. Please see psadump.log file for more information", 'UtilityError' );
  }
  unlink "$dstFile.zip" if( -e "$dstFile.zip");
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $sb5backupUtil = AgentConfig::sb5BackupUtil();
  my $dstFile = HelpFuncs::mktemp("sb5XXXXXXXX");
  my $cmd = "$sb5backupUtil --backup --target=site --uuid=$uuid --log=stdout --file=$dstFile";
  my $result = `$cmd`;
  my $retCode = $? >> 8;

  Logging::debug("Backing up sitebuilder settings for site $domainName:");
  Logging::debug($cmd);
  Logging::debug($result);
  if( $retCode==0 ){
    if( -e "$dstFile.zip") {
      my $cid = $self->{content_transport}->addDomainContent('sb5-site', $domainId, 'sb_site', "directory" => ".", "include"   => [$dstFile.".zip"]);
      if ( $cid ) {
        my @phostings = $root->getChildren('phosting');
        if ( @phostings ) {
          $phostings[0]->getChild('content', 1, 1)->addChild($cid);
        }
        else {
          Logging::warning('Error: setSb5DomainContent: there are no node "phosting"', 'PackerStructure');
        }
      }
    }
    else {
      Logging::warning( "Sitebuilder backup was not created." , 'UtilityError' );
    }
  }
  else {
    Logging::warning( "Return code of sitebuilder bru utility: ".$retCode.". Some errors occured during sitebuilder backup. Please see psadump.log file for more information", 'UtilityError' );
  }
  unlink "$dstFile.zip" if( -e "$dstFile.zip");
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $backupUtil = AgentConfig::sbbackupBin();
  my $dstFile = HelpFuncs::mktemp("siteeditorXXXXXXXX");
  my $cmd = "$backupUtil --target=site --name=$domainName --log=stdout --file=$dstFile";
  my $result = `$cmd`;
  my $retCode = $? >> 8;

  Logging::debug("Backing up siteeditor settings for site $domainName:");
  Logging::debug($cmd);
  Logging::debug($result);
  if( $retCode==0 ){
    if( -e "$dstFile.zip") {
      my $cid = $self->{content_transport}->addDomainContent('sb-dump', $domainId, 'sb_dump', "directory" => ".", "include"   => [$dstFile.".zip"]);
	  if ( $cid ) {
        my @phostings = $root->getChildren('phosting');
        if ( @phostings ) {
          $phostings[0]->getChild('preferences', 1)->getChild('sb-domain', 1)->getChild('content', 1, 1)->addChild($cid);
        }
        else {
          Logging::warning('Error: setSmbSiteEditorDomainContent: there are no node "phosting"', 'PackerStructure');
        }
      }
    }
    else {
      Logging::warning( "SiteEditor backup was not created." , 'UtilityError' );
    }
  }
  else {
    Logging::warning( "Return code of SiteEditor sbbackup utility: ".$retCode.". Some errors occured during SiteEditor backup. Please see psadump.log file for more information", 'UtilityError' );
  }
  unlink "$dstFile.zip" if( -e "$dstFile.zip");
}

sub addFileSharingContent {
  my ($self, $path) = @_;
  my $root = $self->{serverNode};

  my $cid = $self->{content_transport}->addAdminContent('file-sharing', undef, 'file_sharing', 'directory' => $path, 'checkEmptyDir' => 1 );
  $root->getChild('content', 1, 1)->addChild($cid) if $cid;
}

sub addFileSharingPasswd {
  my ($self, $path) = @_;
  my $root = $self->{serverNode};

  my $cid = $self->{content_transport}->addAdminContent('file-sharing-passwd', undef, 'file_sharing_passwd', 'directory' => $path, 'checkEmptyDir' => 1 );
  $root->getChild('content', 1, 1)->addChild($cid) if $cid;
}

sub addServerCustomApacheTemplates {
  my ($self, $path) = @_;
  my $root = $self->{serverNode};

  my $dumpFile = $self->{content_transport}->addAdminContent('custom-apache-templates', undef, 'custom_apache_templates', 'directory' => $path);
  $root->getChild('content', 1, 1)->addChild($dumpFile) if $dumpFile;

}

sub addServerCustomHealthConfig {
  my ($self) = @_;
  my $root = $self->{serverNode};

  my $path = AgentConfig::get('PRODUCT_ROOT_D') . "/var";
  my $file = 'custom-health-config.xml';

  if ( -e "$path/$file") {
    my $dumpFile = $self->{content_transport}->addAdminContent('drilldowns-config', undef, 'dd_conf', 'directory' => $path, 'include' => [$file]);
    $root->getChild('content', 1, 1)->addChild($dumpFile) if $dumpFile;
  }
}

sub addCustomizationConfig {

  my ($self) = @_;
  my $root = $self->{serverNode};

  my $path = AgentConfig::get('PRODUCT_ROOT_D') . "/admin/conf";
  my $file = 'customizations.conf';

  if ( -e "$path/$file") {

    my $customizationNode = XmlNode->new('customization');

    my $configText = undef;

    open CONFIGFILE, $path . "/" . $file;
    binmode(CONFIGFILE);

    while (<CONFIGFILE>) {
      $configText .= $_;
    }

    close CONFIGFILE;

    $customizationNode->addChild(XmlNode->new('config', 'content' => $self->{base64}->{'ENCODE'}->($configText) ));
    $root->addChild($customizationNode);
  }
}

sub addClientGappsAccount {
  my ($self, $clientId, $values) = @_;
  my $root = $self->{clientNodes}->{$clientId};

  my $parent = $root->getChild('properties', 1);

  $self->addGappsAccount($parent, $values);
}

sub addGappsAccount {
  my ($self, $parentNode, $values) = @_;

  if (defined $values->{'gappsLogin'}
      and defined $values->{'gappsPassword'}
      and defined $values->{'gappsTosAccepted'}) {

    my $accountNode = XmlNode->new('gapps-account');

    $accountNode->addChild( XmlNode->new('gapps-login', 'content' => $values->{'gappsLogin'}));
    $accountNode->addChild( XmlNode->new('gapps-password', 'content' => $values->{'gappsPassword'}));
    $accountNode->addChild( XmlNode->new('gapps-accepted', 'content' => $values->{'gappsTosAccepted'}));

    $parentNode->addChild($accountNode);

  }
}

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

  my %domParams = %{$ptrDomParams};

  if ( defined $domParams{'unity_mobile_site_dns_target'}
       and defined $domParams{'unity_mobile_site_key'}
       and defined $domParams{'unity_mobile_site_prefix'}
  ) {

    my $root = $self->getCashedDomainNode($domainId);

    my $umNode = XmlNode->new('unity-mobile-integration');
    my $siteNode = XmlNode->new('mobile-site');
    $siteNode->addChild(XmlNode->new('dns-target', 'content' => $domParams{'unity_mobile_site_dns_target'}));
    $siteNode->addChild(XmlNode->new('key', 'content' => $domParams{'unity_mobile_site_key'}));
    $siteNode->addChild(XmlNode->new('prefix', 'content' => $domParams{'unity_mobile_site_prefix'}));
    $umNode->addChild($siteNode);
    $root->getChild( 'preferences', 1 )->addChild($umNode);
  }
}

sub makeApsBundleFilterNode {
  my ($self, $parentId, $parentType, $filterType, $filterItems, $parentNode) = @_;

  my $apsBundleNode = XmlNode->new('aps-bundle');

  my $filterNode = XmlNode->new('filter', 'attributes' => { 'type' => $filterType});
  $apsBundleNode->addChild($filterNode);

  foreach my $itemHash (@{$filterItems}) {
    my ($name, $value) = each %{$itemHash};
    my $itemNode = XmlNode->new('item');
    $itemNode->addChild(XmlNode->new('name', 'content' => $name));
    $itemNode->addChild(XmlNode->new('value', 'content' => $value));
    $filterNode->addChild($itemNode);
  }

  if (!defined($parentNode)) {
    if ($parentType eq 'client') {
      $parentNode = $self->getCashedClientNode($parentId);
    }elsif ($parentType eq 'domain') {
      $parentNode = $self->getCashedDomainNode($parentId);
    }

    $parentNode->getChild('preferences', 1)->addChild($apsBundleNode);
  }else{
    $parentNode->addChild($apsBundleNode);
  }
}

sub makeSkinNode {
  my ($self, $skinName, $login, $id) = @_;
  return $self->makeBrandingCid($id, $login, "-name " . $skinName, "theme-skin");
}

sub makeBrandingThemeNode {
  my ($self, $login, $id) = @_;
  return $self->makeBrandingCid($id, $login, "-vendor " . $login, "branding-theme");
}

sub makeBrandingCid {
  my ($self, $id, $login, $cmdArgs, $cidType) = @_;

  my $parentNode;
  if (defined $id) {
    $parentNode = $self->{resellersNodes}->{$id};
  }else {
    $parentNode = $self->{serverNode};
  }

  my $command = AgentConfig::brandingUtil();
  my $dstFile = HelpFuncs::mktemp("brandthemeXXXXXXXX");

  if (defined $command) {
      my $cmd = $command . " --pack " . $cmdArgs . " -destination " . $dstFile;
      Logging::debug("Exec: $cmd");
      my $ret = `$cmd`;
      my $retCode = $? >> 8;
      if( $retCode!=0 and $retCode != 13){
        Logging::warning( "Cannot pack theme for vendor " . $login . "STDOUT:" . $ret,'UtilityError');
        return;
      }
      if ( -e $dstFile) {
        my $cid = undef;
        if (defined $id) {
          $cid = $self->{content_transport}->addClientContent($cidType, $id, 'branding', 'directory' => '.', 'include' => [$dstFile]);
        }else{
          $cid = $self->{content_transport}->addAdminContent($cidType, undef, 'branding', 'directory' => '.', 'include' => [$dstFile]);
        }
        if ($cid) {
          $parentNode->getChild('content', 1, 1)->addChild($cid);
        }
      }
  } else {
    Logging::warning( "Utility for dumping branding theme is unavailable ", 'UtilityError');
    unlink $dstFile if ( -e $dstFile);
    return;
  }
  unlink $dstFile if ( -e $dstFile);
}

sub addCrontabSecureSettings {
  my ($self, $params) = @_;
  my $serverNode = $self->{serverNode};
  my $serverPrefNode = $serverNode->getChild( 'server-preferences', 1 );
  $serverPrefNode->addChild(XmlNode->new('crontab-secure-shell', 'content' => $params->{'crontab_secure_shell'})) if defined $params->{'crontab_secure_shell'};
  $serverPrefNode->addChild(XmlNode->new('crontab-secure-shell-compatibility-mode', 'content' => $params->{'crontab_secure_shell_compatibility_mode'} eq 'true' ? 'true' : 'false')) if defined $params->{'crontab_secure_shell_compatibility_mode'};
}

sub addKavSettingsParam {
  my ($self, $kavSettingsNode, $param, $value) = @_;

  my %allowedParams = (
      'Check' => 1,
      'AddXHeaders' => 1,
      'AdminAddress' => 1,
      'QuarantinePath' => 1,
      'FilterByName' => 1,
      'FilterByMIME' => 1,
      'SkipByName' => 1,
      'SkipByMIME' => 1,
      'Quarantine' => 1,
      'AdminNotify' => 1,
      'AdminAction' => 1,
      'SenderNotify' => 1,
      'RecipientNotify' => 1,
      'RecipientAttachReport' => 1,
      'RecipientAction' => 1,
      'CuredAdminAction' => 1,
      'CuredRecipientAction' => 1,
      'InfectedAdminAction' => 1,
      'SuspiciousAdminAction' => 1,
      'InfectedRecipientAction' => 1,
      'SuspiciousRecipientAction' => 1,
      'FilteredRecipientAction' => 1,
      'FilteredQuarantine' => 1
    );

  if ( $allowedParams{$param} ) {
    $kavSettingsNode->addChild( XmlNode->new( 'param', 'content' => $value, 'attributes' => { 'name' => $param } ) );
  }
  return;
}

sub makeSubscriptionNode {
  my ($self, $locked, $synchronized) = @_;

  my $subscriptionNode = XmlNode->new( 'subscription' );

  if (defined $locked) {
    $subscriptionNode->setAttribute('locked', $locked);
  }

  if (defined $synchronized) {
    $subscriptionNode->setAttribute('synchronized', $synchronized);
  }

  return $subscriptionNode;
}

sub addSubscriptionPlan {
  my ($self, $subscriptionNode, $quantity, $plan_guid, $is_addon) = @_;

  return unless ( ( defined $quantity ) && ( defined $plan_guid ) );
  my $planNode = XmlNode->new( 'plan' );
  $planNode->setAttribute('quantity', $quantity);
  $planNode->setAttribute('plan-guid', $plan_guid);
  if ( defined $is_addon) {
    $planNode->setAttribute('is-addon', $is_addon);
  }
  $subscriptionNode->addChild($planNode);
  return;
}

sub dumpFileSharingServerSettings {
  my ($self, $settingsPtr) = @_;

  my %settings = %{$settingsPtr};

  my $fsNode = XmlNode->new('file-sharing-settings');

  my $parentNode = $self->{serverNode};

  foreach my $key (keys %settings) {
    $fsNode->addChild(XmlNode->new('setting','attributes' =>
      { 'name' => $key, 'value' => $settings{$key}} ) );
  }
  $parentNode->addChild($fsNode);
}

sub dumpUiMode {
  my ($self, $paramsPtr) = @_;

  my %params = %{$paramsPtr};

  my $parent = $self->{serverNode};

  my $uiMode = undef;

  if (defined $params{'classic_panel'} && $params{'classic_panel'} eq 'true') {
    $uiMode = 'classic';
  } else {
    if (defined $params{'simple_panel'} && $params{'simple_panel'} eq 'true') {
      $uiMode = (defined $params{'simple_panel_lock'} && $params{'simple_panel_lock'} eq 'true') ? 'simple-lock' : 'simple';
    } else {
      $uiMode = 'normal';
    }
  }

  if (defined $uiMode) {
    $parent->getChild( 'interface-preferences', 1 )->addChild(XmlNode->new('ui-mode', 'content' => $uiMode));
  }
}

sub addTechnicalPreviewDomainNode {
  my ($self, $domainName) = @_;
  my $serverNode = $self->{serverNode};
  my $serverPrefNode = $serverNode->getChild( 'server-preferences', 1 );
  $serverPrefNode->addChild(XmlNode->new('technical-domain', 'content' => $domainName));
}

sub addShowCommercialAppsNode {
  my ($self, $value) = @_;
  my $serverNode = $self->{serverNode};
  my $serverPrefNode = $serverNode->getChild( 'server-preferences', 1 );
  $serverPrefNode->addChild(XmlNode->new('show-commercial-apps', 'content' => $value));
}

sub addUpdateSettings {
  my ($self, $autoUpdatesValue, $autoUpgradeToStable, $autoUpgradeBranch) = @_;

  my $serverNode = $self->{serverNode};
  my $serverPrefNode = $serverNode->getChild( 'server-preferences', 1 );
  my $updateSettingsNode = XmlNode->new('update-settings');
  $updateSettingsNode->addChild(XmlNode->new('autoupdates', 'content' => $autoUpdatesValue));
  $updateSettingsNode->addChild(XmlNode->new('autoupgrade-stable', 'content' => $autoUpgradeToStable));
  $updateSettingsNode->addChild(XmlNode->new('release-tier', 'content' => $autoUpgradeBranch));

  $serverPrefNode->addChild($updateSettingsNode);
}

sub setMiscParameters {
  my ($self, $paramsPtr) = @_;

  my $serverNode = $self->{serverNode};
  my %params = %{$paramsPtr};

  my $miscNode = $serverNode->getChild( 'misc', 1 );

  foreach my $name (keys %params) {
    my $paramNode = XmlNode->new('param');
    $paramNode->addChild(XmlNode->new('name', 'content' => $name));
    $paramNode->addChild(XmlNode->new('value', 'content' => $params{$name}));
    $miscNode->addChild($paramNode);
  }
}

sub makeExtensionPackageNode {
  my ($self, $objectType, $objectId, $data) = @_;

  my ($parentNode, $dataNode);

  if ($objectType eq 'client') {
    $parentNode = $self->{clientNodes}->{$objectId};
  } elsif ($objectType eq 'reseller') {
    $parentNode = $self->{resellersNodes}->{$objectId};
  } elsif ($objectType eq 'domain') {
    $parentNode = $self->{domainNodes}->{$objectId};
  } elsif ($objectType eq 'server') {
    $parentNode = $self->{serverNode};
  }

  $dataNode = XmlNode->new('pmm-extension-package');
  $dataNode->setTextAsIs($data);
  $parentNode->addChild($dataNode);
}

sub makeCapabilityInfoNode {
  my ($self, $capabilityInfo, $objectId) = @_;

  my ($parentNode, $capabilityNode, $name, $value);

  $parentNode = $self->{domainNodes}->{$objectId};

  $capabilityNode = XmlNode->new('capability-info');

  foreach my $key (keys %{$capabilityInfo}) {
    my $info = $capabilityInfo->{$key};
    my $node = $self->_makeCapabilitySectionNode($key, $info);
    $capabilityNode->addChild($node);
  }

  $parentNode->addChild($capabilityNode);
}

sub makeServerCapabilityInfoNode {
  my ($self, $capabilityInfo) = @_;

  my $parentNode = $self->{serverNode};
  my $capabilityNode = XmlNode->new('capability-info');
  my $sectionNode;

  foreach my $key (keys %{$capabilityInfo}) {
    if ($key eq 'database-servers') {
      $sectionNode = XmlNode->new($key);
      my $servers = $capabilityInfo->{$key};
      for my $server (@$servers) {
        my $serverNode = XmlNode->new('database-server');

        my ($name, $value);
        while ( ( $name, $value ) = each(%{$server}) ) {
          $serverNode->addChild(XmlNode->new($name, 'content' => $value));
        }

        $sectionNode->addChild($serverNode);
      }
    } else {
      $sectionNode = $self->_makeCapabilitySectionNode($key, $capabilityInfo->{$key});
    }
    $capabilityNode->addChild($sectionNode);
  }

  $parentNode->addChild($capabilityNode);
}

sub _makeCapabilitySectionNode {
  my ($self, $sectionName, $values) = @_;

  my $node = XmlNode->new($sectionName);
  my ($name, $value);
  while ( ( $name, $value ) = each(%{$values}) ) {
    if (ref($value) =~ /ARRAY/) {
      foreach my $valueItem (@{$value}) {
        $node->addChild($self->_makeNameValueNode($self->_getChildNodeName($sectionName), $name, $valueItem));
      }
    } else {
      $node->addChild($self->_makeNameValueNode($self->_getChildNodeName($sectionName), $name, $value));
    }
  }

  return $node;
}

sub _getChildNodeName {
  my ($self, $parentNodeName) = @_;

  my %nodesMapping = (
    'components' => 'component'
    , 'resource-usage' => 'resource'
    , 'miscellaneous' => 'setting'
    , 'apache-modules' => 'module'
  );

  return $nodesMapping{$parentNodeName};
}

sub _makeNameValueNode {
  my ($self, $nodeName, $name, $value) = @_;

  my $node = XmlNode->new($nodeName);
  my $nameNode = XmlNode->new('name', 'content' => $name);
  my $valueNode = XmlNode->new('value', 'content' => $value);
  $node->addChild($nameNode);
  $node->addChild($valueNode);

  return $node;
}

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

  my $root = $self->getCashedDomainNode($domainId);
  return unless defined $root;

  my $properties = $root->getChild( 'properties', 1 );
  my $domParam = XmlNode->new('dom-param');
  foreach my $key (keys %{$domainParams}) {
    $domParam->addChild($self->_makeNameValueNode('param', $key, $self->{base64}->{'ENCODE'}->($domainParams->{$key})));
  }
  $properties->addChild($domParam);
}

sub makeSubscriptionPropertiesNode {
  my ($self, $parentNode, $subscriptionProperties) = @_;

  my $propertiesNode = XmlNode->new('properties');
  foreach my $key (keys %{$subscriptionProperties}) {
    $propertiesNode->addChild($self->_makeNameValueNode('property', $key, $self->{base64}->{'ENCODE'}->($subscriptionProperties->{$key})));
  }
  $parentNode->addChild($propertiesNode);
}

sub assignApplicationInfoToMailUser {
  my ($self, $applicationApsRegistryId, $resourceInfo) = @_;

  my $mailUserNode = $self->{mailNodes}->{$resourceInfo->{'id'}};
  return unless defined $mailUserNode;

  my $apsServices = $mailUserNode->getChild('aps-services', 1);

  my $apsService = XmlNode->new('aps-service');
  $apsService->addChild(XmlNode->new('application-registry-id', 'content' => $applicationApsRegistryId));
  $apsService->addChild(XmlNode->new('resource-registry-id', 'content' => $resourceInfo->{'apsRegistryId'}));

  $apsServices->addChild($apsService);
}

1;
