# Copyright 1999-2020. Plesk International GmbH. All rights reserved.
package Storage::MimeStorage;
use AgentConfig;
use POSIX;
use File::Temp qw/tmpnam/;
use MIME::Lite;
use IPC::Run;
use Storage::Splitter;

use strict;
use Storage::Storage;

use vars qw|@ISA|;

@ISA = qw|Storage::Storage|;

sub _init {
  my ($self, %options) = @_;
  $self->SUPER::_init(%options);

  $self->{output_file} = $options{output_file};
  $self->{gzip} = $options{gzip};
  $self->{split_size} = $options{split_size};

  $self->{not_compatible_mysql_323} = 1;

  $self->{bundles} = [];

  Logging::debug("-" x 60);
  Logging::debug("MIME storage initialized.");
  Logging::debug("Destination file: $self->{output_file}");
  Logging::debug("Gzip: " . ($self->{gzip} ? "yes" : "no"));
  Logging::debug("Backup split size: " . ($self->{split_size} || "do not split"));
  Logging::debug("-" x 60);
}

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

  $options{not_compatible_mysql_323} = $self->{not_compatible_mysql_323};
  
  my $bundle = Storage::Bundle::createDbBundle(%options);

  push @{$self->{bundles}}, [$proposedId, $bundle] if $bundle;

  return $proposedId;
}

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

  return unless -d $options{'directory'};

  my $bundle = Storage::Bundle::createTarBundle(%options);

  push @{$self->{bundles}}, [$proposedId, $bundle] if $bundle;

  return $proposedId;
}

sub _getMimeWriter {
  my ($self, $descriptor) = @_;

  return sub {
    my $msg = MIME::Lite->new(Type => 'multipart/related');
    $msg->preamble('');

    my $dump_dir = AgentConfig::get("DUMP_D");
    my $tmp_dir = (AgentConfig::get("os") eq "FreeBSD") ? "$dump_dir/var/tmp" : "$dump_dir/tmp";

    if (! -d $tmp_dir) {
      system("mkdir -p ".$tmp_dir);
      chown 0 + getpwnam("psaadm"), 0 + getgrnam("psaadm"), $tmp_dir;
      chmod S_IRUSR|S_IWUSR|S_IXUSR, $tmp_dir;
    }

    my $fileName = $dump_dir . tmpnam();

    open DESCRIPTOR, ">$fileName";
    $descriptor->{'PRINT_TREE'}->(\*DESCRIPTOR);
    close DESCRIPTOR;

    $msg->attach('Type' => 'text/xml', Path => $fileName, Filename => 'dump.xml');

    foreach my $ptrRow (@{$self->{bundles}}) {
      my $id = $ptrRow->[0];
      my $bundle = $ptrRow->[1];

      Logging::debug("Dumping MIME part $id");

      $msg->attach(Type => 'application/octet-stream',
                   Encoding => "binary",
                   Id => $id,
                   Filename => $id,
                   GenerateFH => sub { return $bundle->run() },
                   CloseFH => sub { close $_[0]; $bundle->cleanup(); },
                   TmpFile => $fileName);
    }

    binmode STDOUT;
    $msg->print(\*STDOUT);

    unlink($fileName);
  }
}

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

  Logging::debug("Writing MIME file");

  my @cmd;
  push @cmd, $self->_getMimeWriter($descriptor);

  if ($self->{gzip}) {
    push @cmd, "|", ["gzip"];
  }

  my $files;

  #allocating filehandle for creating pipe from subprocess
  my $newhandle = POSIX::open("/dev/null", O_RDWR, 0666);

  push @cmd, "|", \&Storage::Splitter::run, "$newhandle>", \$files,
    init => sub {Storage::Splitter::init_process($newhandle, $self->{split_size}, $self->{output_file})};

  my $h = IPC::Run::harness(@cmd);
  if (!$h->run()) {
    POSIX::close($newhandle);
    Logging::error("Failed to create MIME message");
    return 1;
  }

  POSIX::close($newhandle);

  if ($h->result()) {
    Logging::error("Unable to create backup");
    return $h->result();
  }

  Logging::debug("MIME writing finished");
  return 0;
}

1;

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