# Copyright 1999-2016. Parallels IP Holdings GmbH. All Rights Reserved.
package RsyncContent;

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

use Logging;
use Storage::FileStorage;
use Cwd;

use vars qw|@ISA|;

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

sub _init {
  my $self = shift;
  my $storage = shift;
  my $packer = shift;
  my %options = @_;
  $self->{storage} = $storage;
  $self->{packer} = $packer;
  $self->{options} = {%options};
  my %notSupportedRsyncContent;
  $notSupportedRsyncContent{'sb5-server'} = 1;
  $notSupportedRsyncContent{'sapp-distrib'} = 1;
  $notSupportedRsyncContent{'drilldowns-config'} = 1;
  $notSupportedRsyncContent{'key'} = 1;
  $notSupportedRsyncContent{'sb5-site'} = 1;
  $notSupportedRsyncContent{'sb-dump'} = 1;
  $notSupportedRsyncContent{'sapp-apsc'} = 1;
  $notSupportedRsyncContent{'web-app'} = 1;
  $notSupportedRsyncContent{'branding-theme'} = 1;
  $notSupportedRsyncContent{'theme-skin'} = 1;

  $self->{notSupportedContent} = \%notSupportedRsyncContent;

  Logging::debug("Rsync content transport has been initialized");
}

### Public routines ###
# addAdminContent,addResellerContent,addClientContent,addDomainContent,addPhostingContent,\
# addSubDomainContent,addSubDomainPhostingContent,addMailnameContent,addDbContent,addSubdomainDbContent

sub addAdminContent{
  my $self = shift;
  my $cid_type = shift;
  my $id = shift;
  my $file = shift;

  if (exists $self->{notSupportedContent}->{$cid_type}) {
    return $self->_addTar($cid_type, $self->{packer}->getAdminBackupPath( $file ) , @_);
  }

  $self->_addContent($cid_type, $id, $file, @_);
}

sub addResellerContent{
  my $self = shift;
  my $cid_type = shift;
  my $id = shift;
  my $file = shift;

  if (exists $self->{notSupportedContent}->{$cid_type}) {
    return $self->_addTar($cid_type, $self->{packer}->getResellersBackupPath( $id, $file ) , @_);
  }

  $self->_addContent($cid_type, $id, $file, @_);
}

sub addClientContent{
  _addContent(@_);
}

sub addDomainContent{
  my $self = shift;
  my $cid_type = shift;
  my $id = shift;
  my $file = shift;

  if (exists $self->{notSupportedContent}->{$cid_type}) {
    return $self->_addTar( $cid_type, $self->{packer}->getDomainsBackupPath($id, $file ), @_);
  }
  $self->_addContent($cid_type, $id, $file, @_);
}

sub addPhostingContent{
  my $self = shift;
  my $cid_type = shift;
  my $id = shift;
  my $referralId = shift;
  my $file = shift;

  if (exists $self->{notSupportedContent}->{$cid_type}) {
    return $self->_addTar( $cid_type, $self->{packer}->getPhostingBackupPath($id, $file ), @_);
  }

  return if $self->_isDomainPhParentContent( $self->{packer}->getPhostingParentId( $referralId ) );
  _addContent($self, $cid_type, $id, $file, @_);
}

sub addSubDomainContent{
  _addContent(@_);
}

sub addSubDomainPhostingContent{
  my $self = shift;
  my $cid_type = shift;
  my $id = shift;
  my $webspaceId = shift;
  my $file = shift;

  if (exists $self->{notSupportedContent}->{$cid_type}) {
    return $self->_addTar( $cid_type, $self->{packer}->getSubdomainsBackupPath($id, $file ), @_);
  }

  _addContent($self, $cid_type, $id, $file, @_);
}

sub addMailnameContent{
  my $self = shift;
  my $cid_type = shift;
  my $id = shift;
  return if $self->_isDomainMailParentContent( $self->{packer}->getMailNameParentId( $id ) );
  _addContent($self, $cid_type, $id, @_);
}

sub addDbContent {
  my $self = shift;
  my $cid_type = shift;
  my $id = shift;
  return $self->_addDb( $cid_type, $self->{packer}->getDatabasesBackupPath( $id, '' ), @_);
}

sub addSubdomainDbContent {
  my $self = shift;
  my $cid_type = shift;
  my $subdomainId = shift;
  my $id = shift;
  return $self->_addDb( $cid_type, $self->{packer}->getSubdomainsBackupPath( $subdomainId, "db$id" ), @_);
}

sub getContentTransportDescription {
  my $self = shift;
  my $descriptionNode = XmlNode->new( 'rsync' );
  $descriptionNode->setAttribute( 'host', $self->{options}->{'host'} );
  $descriptionNode->setAttribute( 'port', $self->{options}->{'port'} ) if exists ( $self->{options}->{'port'} );
  $descriptionNode->setAttribute( 'login', $self->{options}->{'login'} );
  $descriptionNode->setAttribute( 'password', $self->{options}->{'passwd'} );
  return $descriptionNode;
}

sub hasLink {
  my ($self, $linkpath, $targetpath, %options) = @_;

  Logging::debug("Check link '$linkpath' to '$targetpath'.");
  my $result = $self->_hasLocalLink($linkpath, $targetpath);
  Logging::debug("Check link result: $result");
  return $result;
}

### Private routines ###

# check full vhost content is dumped for domainId
sub _isDomainPhParentContent{
  my $self = shift;
  my $domainId = shift;
  return 1 if exists $self->{packer}->{domainVhost}->{$domainId};
}

sub _isDomainMlParentContent{
  my $self = shift;
  my $domainId = shift;
  return 1 if exists $self->{packer}->{domainMailListContent}->{$domainId};
}

sub _isDomainMailParentContent{
  my $self = shift;
  my $domainId = shift;
  return 1 if exists $self->{packer}->{domainMailContent}->{$domainId};
}

sub _addContent{
  my $self = shift;
  my $cid_type = shift;
  my $id = shift;
  my $file = shift;
  return $self->_addRsyncContent( $cid_type, @_);
}

sub _addRsyncContent {
  my $self = shift;
  my $cid_type = shift;
  my %options = @_;
  return if $self->_skipContent(%options);

  if ( $options{'checkEmptyDir'} ) {
    return unless $self->{storage}->checkDirForArchive($options{'directory'}, $options{'exclude'}, $options{'include_hidden_files'});
  }

  return $self->_makeCidRsyncNode( $cid_type, $options{'directory'},
                                              $options{'include'},
                                              $options{'exclude'},
                                              $options{'no_recursion'} );
}

sub _addDb {
  my $self = shift;
  my $cid_type = shift;
  my $dstFile = shift;
  my %options = @_;
  return if $self->_skipContent(%options);

  my $id = $self->{storage}->addDb( $dstFile, @_);

  return unless defined $id;

  return $self->_makeCidNode($id, $cid_type, @_);
}

sub _addTar {
  my $self = shift;
  my $cid_type = shift;
  my $dstFile = shift;
  my %options = @_;
  return if $self->_skipContent(%options);

  my $id = $self->{storage}->addTar( $dstFile, @_);

  return unless defined $id;

  return $self->_makeCidNode($id, $cid_type, @_);
}


sub _makeCidNode{
  my ($self, $id, $cid_type, %options) = @_;
  my $cid = XmlNode->new( 'cid' );
  $cid->setAttribute( 'type', $cid_type );
  $cid->setAttribute( 'unpacksize', $self->{storage}->getFilesUnpackSizeFromId($id) );
  $cid->setAttribute( 'path', $self->{storage}->getFilePathFromId( $id ) );

  my $files = $self->{storage}->getFilesFromId( $id );
  foreach my $filedata( @{$files} ){
    $cid->addChild( XmlNode->new( 'content-file', 'content' => $filedata->[0], 'attributes' => {'size' => $filedata->[1] } ) );
  }

  if (defined($options{'include'})) {
    my @includes = @{$options{'include'}};
    foreach my $inc (@includes) {
      $cid->addChild(XmlNode->new('include-item', 'content' => $inc));
    }
  }

  if (defined($options{'exclude'})) {
    my @excludes = @{$options{'exclude'}};
    foreach my $exc (@excludes) {
      $cid->addChild(XmlNode->new('exclude-item', 'content' => $exc));
    }
  }

  return $cid;
}

sub _makeCidRsyncNode{
  my ($self, $cid_type, $directory, $include, $exclude, $no_recursion) = @_;
  # '$include' could contain filename inside '$directory'.

  return unless(-d $directory);

  my $cid = XmlNode->new( 'cid-rsync' );
  $cid->setAttribute( 'type', $cid_type );

  my $path = $directory;
  if ( defined $include ) {
    my @includes = @{$include};
    foreach my $inc (@includes) {
      $cid->addChild(XmlNode->new('include-item', 'content' => $inc));
    }
  }

  if ( defined $exclude ) {
    my @excludes = @{$exclude};
    foreach my $inc (@excludes) {
      $cid->addChild(XmlNode->new('exclude-item', 'content' => $inc));
    }
  }
  my $cmd = "du -sb";
  if ( defined $no_recursion ) {
    $cmd .= " -S";
  }
  $cmd .= " $path";
  my $sizes = `$cmd`;

  my $unpacksize = 0;
  for(split /[\r\n]+/,$sizes) {
    my ($number, $file) = split /\t/;
    $unpacksize += $number;
  }

  if ( -d $path and substr( $path, -1, 1 ) ne '/') {
    $path .= '/';
  }

  $cid->setAttribute( 'unpacksize', $unpacksize );
  $cid->setAttribute( 'path', $path );
  return $cid;
}

sub _hasLocalLink {
  my ($self, $linkpath, $targetpath) = @_;

  if (! -l $linkpath) {
    return 'false';
  }
  return (Cwd::realpath($linkpath) eq $targetpath) ? 'true' : 'false';
}

sub _skipContent {
    my ($self, %options) = @_;

  if (exists($options{'skip_content'})) {
    return $options{'skip_content'} ? 1 : 0;
  } else {
    return exists($self->{packer}->{skip_content}) ? 1 : 0;
  }
}

1;
