# Copyright 1999-2014. Parallels IP Holdings GmbH. All Rights Reserved.
package Agent;

use Logging;
use XmlNode;
use Transformer;

use AgentConfig;
use Dumper; # used for getCpanelVersion only
use ContentDumper;
use Storage::Storage;


my $agentWorkDir;

#
# Agent.pm module implements Agent interface
#

my $contentDumper = ContentDumper->new( Storage::Storage::createFileStorage($do_gzip, getWorkDir()) );
#
# Begin interface methods
#
sub setWorkDir {
  my $workDir = shift;
  $agentWorkDir = $workDir;
  return $agentWorkDir;
}

sub setContentTransport{
  my $contentTransportType = shift;
  my $transportOpt = shift;
  $contentDumper->setContentTransport($contentTransportType, $transportOpt);
}

sub getContentTransportDescription{
  return $contentDumper->getContentTransportDescription();
}

sub getWorkDir {
  return $agentWorkDir || AgentConfig::cwd();;
}

sub getAgentName {
  return 'cPanel';
}

sub getAgentVersion {
  return '10.2.0';
}

sub getAgentStatus {
  my $statusNode = XmlNode->new('agent-status');

  my $wrongPlatformItem;
  if ( !defined AgentConfig::iconvBin() ) {
    $item = XmlNode->new( 'wrong-platform' );
    $item->setText( 'no iconv found on the source host' );
  }
  elsif ( !defined Dumper::getCpanelVersion() ) {
    $item = XmlNode->new( 'wrong-platform' );
    $item->setText( '' );
  }

  $statusNode->addChild($item) if defined $wrongPlatformItem;
  return $statusNode;
}

sub getResellers {
  # resellers migration is not supported now

  # should return an array of reseller identifiers, that could be a number, name or guid. Should be unique through migration dump.
  return Transformer::getResellers();
}

sub getClients {
  my $owner = shift; # reseller that owns the clients returned. Could be 'undef' for default reseller ('root' or 'admin')

  # clients migration is not supported now

  # should return an array of clients identifiers, that could be a number, name or guid. Should be unique through migration dump.
  return Transformer::getClients($owner);
}

sub getDomains {
  my $owner = shift; # reseller or client that owns the domains returned. Could be 'undef' for default reseller or client ('root' or 'admin')

  # Should return an array of clients identifiers, that could be a number, name or guid. Should be unique through migration dump.

  # The current implementation of cPanel migration deals with domains only. Will return all the domains for 'undef' passed.

  return Transformer::getDomains($owner);
}

sub getDomain {
  my ($id, $recursive, $content, $do_gzip, $contentOptions) = @_; # domain identifier from 'getDomains' result, flag to return node with children (for optimization purporses), flag to dump content, flag to compress content, content options

  my $clientNode = Transformer::getClientNode4Domain( $id );

  my $myDomainNode;

  if ( $clientNode ) {
    my $domainsNode = $clientNode->getChild( 'domains' );
    if ( defined $domainsNode ) {
      my @domainNodes = $domainsNode->getChildren( 'domain' );
      foreach my $domainNode (@domainNodes) {
        if ($domainNode->getAttribute( 'name' ) eq $id ) {
          $myDomainNode = $domainNode;
          last;
        }
      }

      if ( defined $myDomainNode ) {
        if( $recursive ){
          unless ( $content ) {
            # Return from hash. Do not copy()
            return $myDomainNode;
          }
          else {
            return addContent2DomainNode( $myDomainNode->copy(), $do_gzip, $contentOptions );
          }
        }
        return $myDomainNode->copy('no_child');
      }
    }
  }
  return;
}

sub _getDomainChild {
  my $id = shift; # domain identifier from 'getDomains' result
  my $child = shift;

  my $domainNode = getDomain( $id, 'recursive');
  return unless ( defined ( $domainNode ) ); 

  my $childNode = $domainNode->getChild( $child );
  return unless ( defined ( $childNode ) ); 
  
  return $childNode->copy();
}

sub getDomainContent {
  return;
}

sub getDomainPreferences {
  my ($id, $content, $do_gzip) = @_; # domain identifier from 'getDomains' result, flag to dump content, flag to compress content

  my $childNode = _getDomainChild( $id, 'preferences');
  if ( $content ) {
    return addContent2DomainPreferencesNode( $childNode, $do_gzip );
  }
  return $childNode;
}

sub getDomainProperties {
  _getDomainChild( shift, 'properties');
}

sub getDomainLimitsAndPermissions {
  _getDomainChild( shift, 'limits-and-permissions');
}

sub getDomainMailsystem {
  my ($id, $content, $do_gzip) = @_; # domain identifier from 'getDomains' result, flag to dump content, flag to compress content

  my $childNode = _getDomainChild( $id, 'mailsystem');
  if ( $content ) {
    return addContent2MailsystemNode( $childNode, $do_gzip );
  }
  return $childNode;
}

sub getDomainDatabases {
  my ($id, $content, $do_gzip) = @_; # domain identifier from 'getDomains' result, flag to dump content, flag to compress content

  my $childNode = _getDomainChild( $id, 'databases');
  if ( $content ) {
    return addContent2DatabasesNode( $childNode, $do_gzip );
  }
  return $childNode;
}

sub getDomainOdbcDsn {
  return;
}

sub getDomainColdfusionDsn {
  return;
}

sub getDomainMaillists {
  my ($id, $content, $do_gzip) = @_; # domain identifier from 'getDomains' result, flag to dump content, flag to compress content

  my $childNode = _getDomainChild( $id, 'maillists');
  if ( $content ) {
    return addContent2MaillistsNode( $childNode, $do_gzip );
  }
  return $childNode;
}

sub getDomainTraffic {
  _getDomainChild( shift, 'traffic');
}

sub getDomainCertificates {
  _getDomainChild( shift, 'certificates');
}

sub getDomainTomcat {
  my ($id, $content, $do_gzip) = @_; # domain identifier from 'getDomains' result, flag to dump content, flag to compress content

  my $childNode = _getDomainChild( $id, 'tomcat');
  if ( $content ) {
    return addContent2TomcatNode( $childNode, $do_gzip );
  }
  return $childNode;
}

sub getDomainDomainuser {
  _getDomainChild( shift, 'domainuser');
}

sub getDomainShosting {
  _getDomainChild( shift, 'shosting');
}

sub getDomainFhosting {
  _getDomainChild( shift, 'fhosting');
}

sub getDomainPhosting {
  my ($id, $content, $do_gzip) = @_; # domain identifier from 'getDomains' result, flag to dump content, flag to compress content

  my $childNode = _getDomainChild( $id, 'phosting');
  if ( $content ) {
    return addContent2PhostingNode( $childNode, $do_gzip );
  }
  return $childNode;
}
#
# End of interface methods
#



#
# Begin subs for content linking
#
sub addContent2DomainNode {
  my ($domainNode, $do_gzip, $contentOptions) = @_;

  return unless ( defined ( $domainNode ) );

  my $onlyMail    = exists ( $contentOptions->{'only-mail'} )    || undef;
  my $onlyHosting = exists ( $contentOptions->{'only-hosting'} ) || undef;

  my $domainPreferencesNode = $domainNode->getChild( 'preferences' );
  addContent2DomainPreferencesNode( $domainPreferencesNode, $do_gzip);

  unless ( defined ( $onlyHosting ) ) {
    my $domainMailsystemNode = $domainNode->getChild( 'mailsystem' );
    addContent2MailsystemNode( $domainMailsystemNode, $do_gzip) if ( defined ( $domainMailsystemNode ) );
    my $domainPhostingNode = $domainNode->getChild( 'phosting' );
    if ( defined ( $domainPhostingNode ) ) {
      my $sitesNode = $domainPhostingNode->getChild( 'sites' );
      if ( defined ( $sitesNode ) ) {
        my @siteNodes = $sitesNode->getChildren( 'site' );
        if ( @siteNodes ) {
          foreach my $siteNode ( @siteNodes ) {
            my $siteMailsystemNode = $siteNode->getChild( 'mailsystem' );
            addContent2MailsystemNode( $siteMailsystemNode, $do_gzip) if ( defined ( $siteMailsystemNode ) );
          }
        }
      }
    }
  }

  unless ( defined ( $onlyMail ) ) {
    my $domainDatabasesNode = $domainNode->getChild( 'databases' );
    addContent2DatabasesNode( $domainDatabasesNode, $do_gzip) if ( defined ( $domainDatabasesNode ) );
  }

  unless ( defined ( $onlyHosting ) ) {
    my $domainMaillistsNode = $domainNode->getChild( 'maillists' );
    addContent2MaillistsNode( $domainMaillistsNode, $do_gzip) if ( defined ( $domainMaillistsNode ) );
  }

  unless ( defined ( $onlyMail ) ) {
    my $domainTomcatNode = $domainNode->getChild( 'tomcat' );
    addContent2TomcatNode( $domainTomcatNode, $do_gzip) if ( defined ( $domainTomcatNode ) );
  }

  unless ( defined ( $onlyMail ) ) {
    my $domainPhostingNode = $domainNode->getChild( 'phosting' );
    addContent2PhostingNode( $domainPhostingNode, $do_gzip) if ( defined ( $domainPhostingNode ) );
  }

  return $domainNode;
}

sub addContent2DomainPreferencesNode {
  my ($domainPreferencesNode, $do_gzip) = @_;

  return unless ( defined ( $domainPreferencesNode ) );

  return $domainPreferencesNode;
}

sub addContent2MailsystemNode {
  my ($mailsystemNode, $do_gzip) = @_;

  return unless ( defined ( $mailsystemNode ) );

  my $mailusersNode = $mailsystemNode->getChild( 'mailusers' );

  if ( defined ( $mailusersNode ) ) {
    my @mailuserNodes = $mailusersNode->getChildren( 'mailuser' );
    foreach my $mailuserNode ( @mailuserNodes ) {
      my $mailuserMetadata = $mailuserNode->getMetadata();
      my $mailuserPreferencesNode = $mailuserNode->getChild( 'preferences' );
      my $mailboxNode = $mailuserPreferencesNode->getChild( 'mailbox' );
      if ( defined ( $mailboxNode ) ) {
        my $mailboxContentNode = $contentDumper->getMailnameContent( $mailuserMetadata );
        $mailboxNode->addChild( $mailboxContentNode, 'first' ) if ( defined ( $mailboxContentNode ) );
      }
    }
  

  }

  return $mailsystemNode;
}

sub addContent2DatabasesNode {
  my ($databasesNode, $do_gzip) = @_;

  return unless ( defined ( $databasesNode ) );
  
  my %dbCreds = ('mysql'      => Dumper::getMySQLDBCredentials(),
                 'postgresql' => {'user' => 'postgres', 'password' => undef});

  my $databasesMetadata = $databasesNode->getMetadata();
  my $domain = $databasesMetadata->{'domain'};

  my @databaseNodes = $databasesNode->getChildren( 'database' );
  if ( @databaseNodes ) {
    foreach my $databaseNode ( @databaseNodes ) {
      my $dbName = $databaseNode->getAttribute( 'name' );
      my $dbType = $databaseNode->getAttribute( 'type' );
      my $contentNode = $contentDumper->getDatabaseContent($dbName, $dbType, $dbCreds{$dbType}->{'user'}, $domain, $dbCreds{$dbType}->{'password'} );
      $databaseNode->addChild( $contentNode, 'first' ) if ( defined ( $contentNode ) );
    }
  }

  return $databasesNode;
}

sub addContent2MaillistsNode {
  my ($maillistsNode, $do_gzip) = @_;

  return unless ( defined ( $maillistsNode ) );

  my @maillistNodes = $maillistsNode->getChildren( 'maillist' );
  foreach my $maillistNode ( @maillistNodes ) {
    my $maillistMetadata = $maillistNode->getMetadata();
    my $domain = $maillistMetadata->{'domain'};
    my $listname = $maillistMetadata->{'listname'};
  
    next unless ( defined ( $domain ) and defined ( $listname ) );

    my $maillistContentNode = $contentDumper->getMaillistContent($listname,$domain);

    $maillistNode->addChild( $maillistContentNode, 'first' ) if ( defined ( $maillistContentNode ) );
  }

  return $maillistsNode;
}

sub addContent2TomcatNode {
  my ($tomcatNode, $do_gzip) = @_;

  return unless ( defined ( $tomcatNode ) );

  return $tomcatNode;
}

sub addContent2PhostingNode {
  my ($phostingNode, $do_gzip) = @_;

  return unless ( defined ( $phostingNode ) );

  my $phostingMetadata = $phostingNode->getMetadata();
  my $domain = $phostingMetadata->{'domain'};
  my $account = $phostingMetadata->{'account'};
  my $from_subdomain = $phostingMetadata->{'from_subdomain'};
  my $to_domain = $phostingMetadata->{'to_domain'};

  my $contentNode;
  if ( defined ( $from_subdomain ) and defined ( $to_domain ) ) {
    $contentNode = $contentDumper->getSubdomainAsDomainContent($from_subdomain, $domain, $to_domain, $account, $phostingMetadata);
  }
  else {
    $contentNode = $contentDumper->getPhostingContent($domain, $account, $phostingMetadata);
  }

  $phostingNode->addChild( $contentNode, 'first' ) if ( defined ( $contentNode ) );

  my $preferencesNode = $phostingNode->getChild( 'preferences' );
  # $preferencesNode is always defined here

  my $anonftpNode = $preferencesNode->getChild( 'anonftp' );
  if ( defined ( $anonftpNode ) ) {
    my $anonftpContentNode = $contentDumper->getAnonFtpContent($domain, $account);
    $anonftpNode->addChild( $anonftpContentNode, 'first' ) if ( defined ( $anonftpContentNode ) );
  }

  my $webusersNode = $phostingNode->getChild( 'webusers' );
  if ( defined ( $webusersNode ) ) {
    my @webuserNodes = $webusersNode->getChildren( 'webuser' );
    foreach my $webuserNode ( @webuserNodes ) {
      # It is better to extract webuser name from metadata, because 'name' is not required attribute for 'webuser'
      my $webuserMetadata = $webuserNode->getMetadata();
      my $webuserName = $webuserMetadata->{'name'};

      my $webuserContentNode = $contentDumper->getWebUserContent($webuserName, $domain, $account);
      $webuserNode->addChild( $webuserContentNode, 'first' ) if ( defined ( $webuserContentNode ) );
    }
  }

  my $subdomainsNode = $phostingNode->getChild( 'subdomains' );
  if ( defined ( $subdomainsNode ) ) {
    my @subdomainNodes = $subdomainsNode->getChildren( 'subdomain' );
    foreach my $subdomainNode ( @subdomainNodes ) {
      my $subdomainMetadata = $subdomainNode->getMetadata();
      my $subdomainName = $subdomainNode->getAttribute( 'name' );
      my $subdomainContentNode = $contentDumper->getSubdomainContent($subdomainName, $domain, $account, $subdomainMetadata);
      $subdomainNode->addChild( $subdomainContentNode, 'first' ) if ( defined ( $subdomainContentNode ) );
    }
  }

  my $sitesNode = $phostingNode->getChild( 'sites' );
  if ( defined ( $sitesNode ) ) {
    my @siteNodes = $sitesNode->getChildren( 'site' );
    foreach my $siteNode ( @siteNodes ) {
      my $sitePhostingNode = $siteNode->getChild( 'phosting' );
      addContent2PhostingNode( $sitePhostingNode, $do_gzip) if ( defined ( $sitePhostingNode ) );
    }
  }

  return $phostingNode;
}
#
# End of subs for content linking
#


1;
