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

use strict;
use warnings;

use Logging;
use XmlNode;
use ConfixxConfig;
use File::Path qw(mkpath rmtree);
use File::Basename; # for dirname()
use File::Copy::Recursive qw(dircopy);

sub getPHostingContent {
  my ($base, $userName, $domainName, $domainDir, $pDirs_ptr, $domainId) = @_;

  Logging::trace("Getting phosting content for domain '$domainName' ... ");

  return undef; # hosting content transfer is no more done by the agent

  my $topDir = ConfixxConfig::getValue("user_homeDir") . "/$userName/html";

  my @children;
  my (@excludedPaths, @excludedCgiPaths);
  while (my ($path, $info) = each %{$pDirs_ptr}) {
    if ($path =~ /^\/cgi-bin$/) { # cgi directory - /cgi-bin itself
      push @excludedCgiPaths, "./.htaccess";
      push @excludedCgiPaths, "./.htpasswd" if ($info->{'users'});
    } elsif ($path =~ /^\/cgi-bin(\/.*)/) { # cgi directory - a directory under /cgi-bin
      push @excludedCgiPaths, "." . $1 . "/.htaccess";
      push @excludedCgiPaths, "." . $1 . "/.htpasswd" if ($info->{'users'});
    } else { # regular directory under document root
      push @excludedPaths, "." . $path . "/.htaccess";
      push @excludedPaths, "." . $path . "/.htpasswd" if ($info->{'users'});
    }
  }
  push @excludedPaths, "./cgi-bin";

  my $wwwContent = _packUpSinglePhostingDirectory($base, $domainName, $topDir . "/$domainDir", "docroot", \@excludedPaths, undef, undef, $domainId);
  push @children, $wwwContent if (ref($wwwContent) eq 'XmlNode');
  my $cgiContent = _packUpSinglePhostingDirectory($base, $domainName, $topDir . "/cgi-bin", "cgi", \@excludedCgiPaths, undef, undef, $domainId);
  push @children, $cgiContent if (ref($cgiContent) eq 'XmlNode');
  
  my $logsContent = _packUpLogFiles($base, $userName, $domainName, $domainId);
  push @children, $logsContent if (ref($logsContent) eq 'XmlNode');
  
  my $statisticsContent = _packUpStatisticsFiles($base, $userName, $domainName, $domainId);
  push @children, $statisticsContent if (ref($statisticsContent) eq 'XmlNode');

  if ( @children ) {
    return XmlNode->new('content',
      'children' => \@children
    );
  }

  Logging::warning("No phosting content found for domain '$domainName'.");
  return undef;
}

sub _packUpLogFiles {
  my ($base, $userName, $domainName, $domainId) = @_;

  my $logsDir = ConfixxConfig::getValue("user_homeDir") . "/$userName/log";
  return _packUpSinglePhostingDirectory($base, $domainName, $logsDir, 'logs', undef, undef, $domainId);
}

# pack up statistics files in a private dir
# (private dir in Plesk is not accessible via web)
sub _packUpStatisticsFiles {
  my ($base, $userName, $domainName, $domainId) = @_;

  my $tempPrivateDir = '/tmp/private';
  my $tempStatisticsDir = "$tempPrivateDir/confixx-statistics";

  if (-d $tempPrivateDir) {
    unless(rmtree $tempPrivateDir) {
      Logging::warning("Statistics files of user '$userName' were not migrated due to an error removing temporary directory '$tempPrivateDir'.");
      return undef;
    }
  }

  unless (mkpath $tempStatisticsDir) {
    Logging::warning("Statistics files of user '$userName' were not migrated due to an error creating temporary directory '$tempStatisticsDir'.");
    return undef;
  }

  my $awstatsDir = "$ConfixxConfig::confixx_homeDir/awstats/$userName";
  if (-d $awstatsDir) {
    unless (dircopy($awstatsDir, $tempStatisticsDir . '/awstats')) {
      Logging::warning("Failed to copy AWStats files of user '$userName'");
    }
  }

  my $webalizerDir = "$ConfixxConfig::confixx_homeDir/html/webalizer/$userName";
  if (-d $webalizerDir) {
    unless (dircopy($webalizerDir, $tempStatisticsDir . '/webalizer')) {
      Logging::warning("Failed to copy Webalizer files of user '$userName'.");
    }
  }
  
  my $privateContent = _packUpSinglePhostingDirectory($base, $domainName, $tempPrivateDir, 'private', undef, undef, $domainId);
  
  unless (rmtree $tempPrivateDir) {
    Logging::warning("Failed to remove temporary directory '$tempPrivateDir' after migration of statistics files of user '$userName'.");
  }

  return $privateContent;
}

sub getSitePHostingContent {
  my ($base, $domainName, $vhostFileContent, $systemDomainName, $domainId) = @_;

  unless (length($vhostFileContent) > 0) {
    return undef;
  }

  my $tempDir = '/tmp/migration_vhost';
  my $vhostFileName = "$tempDir/vhost.conf";
  mkdir $tempDir;
  DumperUtils::writeFile($vhostFileName, $vhostFileContent);

  my $confContent = _packUpSinglePhostingDirectory($base, $domainName, $tempDir, "conf", undef, $systemDomainName, $domainId); 

  rmtree($tempDir);

  unless (ref($confContent) eq 'XmlNode') {
    return undef;
  }
  
  return XmlNode->new('content',
    'children' => [ $confContent ]
  );
}

# make virtual host template content (the content that is put into user's home directory for each new user)
# the main goal of this function is to migrate reseller's and admin's index pages
# @param $resellerName reseller name, or undef if admin
sub getResellerOrAdminVirtualHostTemplateContent {
  my ($base, $resellerName, $indexPageName, $indexPageContent) = @_;

  my $tempVirtualHostTemplateDir = '/tmp/virtual_host_template';
  my $tempHttpdocsDir = "$tempVirtualHostTemplateDir/httpdocs";
  my $messagesUserName = defined($resellerName)? "reseller '$resellerName'" : "admin";

  if (-d $tempVirtualHostTemplateDir) {
    unless (rmtree $tempVirtualHostTemplateDir) {
      Logging::warning("Virtual host template of $messagesUserName was not migrated due to an error removing temporary directory '$tempVirtualHostTemplateDir'.");
      return undef;
    }
  }

  unless (mkpath $tempHttpdocsDir) {
    Logging::warning("Virtual host template of $messagesUserName was not migrated due to an error creating temporary directory '$tempHttpdocsDir'.");
    return undef;
  }

  DumperUtils::writeFile("$tempHttpdocsDir/$indexPageName", $indexPageContent);

  my $filename = $base->{namecreator}->{fnamecreator}->getFileName(defined($resellerName)? "resellers/$resellerName" : '', '', '', 'skeleton');

  my %options = (
    'directory' => $tempVirtualHostTemplateDir,
    'include_hidden_files' => 1, # in case if index file page is a hidden file (however probability of this is low) 
  );

  my $contentNode = $base->{content_transport}->addContent('skeleton', $filename, %options);
  
  unless (rmtree $tempVirtualHostTemplateDir) {
    Logging::warning("Failed to remove temporary dir '$tempVirtualHostTemplateDir' after migration of virtual host template of $messagesUserName.");
  }

  return $contentNode;
}

sub _packUpSinglePhostingDirectory {
  my ($base, $domainName, $rootPath, $contentType, $excludedPaths, $systemDomainName, $domainId) = @_;

  return undef; # XXX content transfer is not done by the agent
  $domainName =~ s/\*/_/; # for wildcard subdomains
  my $contentStorageDomainName = defined $systemDomainName ? $systemDomainName : $domainName;
  my $contentFileName = $base->{namecreator}->getPhostingDstFile($contentType, $contentStorageDomainName, $domainId);
  return _packUpSingleDirectory($base, $rootPath, $contentType, $contentFileName, $excludedPaths, $systemDomainName);
}

# $excludedPaths must be specified relative to $rootPath, and start with "./",
# otherwise tar won't match them and will include them into tarball.
# Examples of $contentFileName:
#   * $base->{namecreator}->getPhostingDstFile('cgi', $domainName)
#   * $base->{namecreator}->getSubdomainDstFile('docroot', $domainName, $subdomainName )
# full list of content filename creator functions can see at common/.../shared/Storage/ContentNameCreator.pm
sub _packUpSingleDirectory {
  my ($base, $rootPath, $contentType, $contentFileName, $excludedPaths, $systemDomainName) = @_;

  my $contentNode;
  if (-d $rootPath) {
    my %options;
    $options{'checkEmptyDir'} = 1;
    $options{'directory'} = $rootPath;
    $options{'include_hidden_files'} = 1;
    $options{'exclude'} = $excludedPaths if ( ref($excludedPaths) eq 'ARRAY' );
    $contentNode = $base->{content_transport}->addContent($contentType, $contentFileName, %options);
  }
  return $contentNode;
}

sub getMailBoxContent {
  my ($base, $mboxName, $domainName, $systemDomainName) = @_;
  my $mailboxNode;
  my %options;
  my $convertedMailDir;

  return undef; # XXX Mail content transfer will be refactored

  $options{'checkEmptyDir'} = 1;
  $options{'include_hidden_files'} = 1;
  $options{'follow_symlinks'} = 0;

  # Syntax of confixx_main.conf:
  # - maildrop can be : /var/spool/mail/USER | HOMEDIR/Mailbox | HOMEDIR/Maildir/
  # - pop_homeDir is location of mail users homedir. Example: $pop_homeDir = '/home/email';
  # - mailBoxName is name of Mailbox file. Example: $mailBoxName = 'Maildir';
  # - mailSpool is location of mailspool directory. Example: $mailSpool = '/var/spool/mail';
  #
  if (ConfixxConfig::getValue('maildrop') eq "HOMEDIR/Maildir/") {
    $options{'directory'} = ConfixxConfig::getValue("pop_homeDir") . "/$mboxName/" . ConfixxConfig::getValue('mailBoxName');
  } else {
    #
    #   Convert from mbox format to mdir.
    #   Attention: in this case will migrate only Inbox, because:
    #       - old migrator did the same
    #       - Confixx dont known about mail storage (use IMAP protocol via dovecot for access to mailboxes dirs)
    #       - for access to other maildirs require parse /etc/dovecot/dovecot.conf (mail_location parameter)
    my $inbox;
    $convertedMailDir = "/tmp/migration_dump_mail";       # temporary dir

    if (ConfixxConfig::getValue('maildrop') eq "HOMEDIR/Mailbox") {
      $inbox = ConfixxConfig::getValue('pop_homeDir') . "/$mboxName/" . ConfixxConfig::getValue('mailBoxName');
    } elsif (ConfixxConfig::getValue('maildrop') eq "/var/spool/mail/USER") {
      $inbox = ConfixxConfig::getValue('mailSpool') . "/$mboxName";
    } else {
      Logging::warning("Cannot identify format of maildrop in Confixx config: " . ConfixxConfig::getValue('maildrop'));
      return undef;
    }

    unless(-r $inbox) {
      Logging::warning("Cannot read mailbox for user '$mboxName': $inbox\n");
      return undef;
    }

    rmtree($convertedMailDir);
        # we do not use Mbox2Mdir::convert since it contains customizations, which we don't want
    my $mb2mdPath = 'shared_legacy/mb2md.pl';
    `/usr/bin/perl $mb2mdPath -s $inbox -d $convertedMailDir`;

    $options{'directory'} = $convertedMailDir;
  }

  my $isSite = defined $systemDomainName && ! ($domainName eq $systemDomainName);
  my $actualDomainName = $isSite ? $systemDomainName : $domainName;
  my $actualDomainInfo = Dumper::getDomainInfo($actualDomainName);
  my $actualDomainId = $actualDomainInfo->{'id'};

  my $contentFileName = $base->{namecreator}->getMailnameDstFile($mboxName, $actualDomainName, $actualDomainId);

  $mailboxNode = $base->{content_transport}->addContent('mailbox', $contentFileName, %options);

  if (defined($convertedMailDir)) {
    rmtree($convertedMailDir);
  }

  if (ref($mailboxNode) ne 'XmlNode') {
      Logging::warning("No content found for mbox $mboxName .");
      return undef;
  }

  return XmlNode->new('content',
    'children' => [
        $mailboxNode
    ]
  );
}

1;
