package Cpanel::Easy::Utils::Targz;

# cpanel - Cpanel/Easy/Utils/Targz.pm             Copyright(c) 2015 cPanel, Inc.
#                                                           All rights Reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cpanel license. Unauthorized copying is prohibited

use YAML::Syck ();

use strict;
use warnings;
no warnings qw(redefine);

{

    sub untar_targz {
        my ( $self, $targz, $into ) = @_;

        my $start = $self->cwd();

        if ($into) {
            chdir $into or return ( 0, q{Could not chdir into '[_1]': [_2]}, $into, $! );
        }

        my @no_owner_flags = $self->get_targz_no_owner_flag();

        my $old_umask = umask oct(22);

        my ( $rc, $exit ) = $self->run_system_cmd( [ 'tar', @no_owner_flags, '-x', '-z', '-f', $targz ], 1 );

        umask $old_umask;

        if ($into) {
            chdir $start or return ( 0, q{Could not chdir back into '[_1]': [_2]}, $start, $! );
        }

        return ( 1, 'ok' ) if $rc;

        # lets log some info about the file
        $self->output_system_cmd( [ 'ls', '-l', $targz ] );
        $self->output_system_cmd( [ 'lsattr', $targz ] );
        return ( 0, q{tar xzf of '[_1]' exited with '[_2]'}, $targz, $exit );

    }

    sub get_targz_no_owner_flag {
        my ($self) = @_;
        my @flags = ();
        require Cpanel::SafeRun;
        my @tar_help = Cpanel::SafeRun::saferunallerrors( 'tar', '--help' );
        if ( grep( /--no-same-owner/i, @tar_help ) ) {
            push @flags, '--no-same-owner';
        }
        else {
            push @flags, '-o';
        }
        if ( grep( /--no-same-permissions/i, @tar_help ) ) {
            push @flags, '--no-same-permissions';
        }
        return @flags;
    }

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

        return $self->{'get_targz_md5sums_hashref_has_run'} if $self->{'get_targz_md5sums_hashref_has_run'};

        my $yaml = $self->{'opt_mod_src_dir'} . '/targz.yaml';
        unlink $yaml;

        if ( !$self->get_param('no-targz-yaml') && !-e '/var/cpanel/easy_no_targz_yaml' ) {
            return {} if !$self->fetch_from_httpupdate_silent(
                'host'     => $self->get_easources_host(),
                'url'      => $self->{'httpupdate_uri'} . '/targz.yaml',
                'destfile' => $yaml,
                'signed'   => 1,
            );
        }

        my @targz_hashes = ();
        eval { @targz_hashes = YAML::Syck::LoadFile($yaml) };

        # SHA512 hashes will be in the second yaml document in the file.
        # If they exist we will always use them, even if $self->{'use_md5'} is set.
        if ( 2 == scalar @targz_hashes && 'HASH' eq ref $targz_hashes[1] ) {

            # alert if /var/cpanel/allow_weak_checksums exists, but targz.yaml contains SHA512 checksums
            if ( $self->{'use_md5'} ) {
                $self->print_alert('SHA512 tarball checksums detected. Ignoring intent to use weak checksums.');
                $self->{'use_md5'} = undef;
            }
            $self->{'use_sha'}                           = 1;
            $self->{'get_targz_md5sums_hashref_has_run'} = $targz_hashes[1];
        }

        # fallback to md5 as a last resort only if $self->{'use_md5'} is set.
        elsif ( $self->{'use_md5'} && 1 == scalar @targz_hashes && 'HASH' eq ref $targz_hashes[0] ) {
            $self->print_alert('Using weak checksums to verify tarballs.');
            $self->{'get_targz_md5sums_hashref_has_run'} = $targz_hashes[0];
        }
        else {
            $self->{'get_targz_md5sums_hashref_has_run'} = {};
        }

        return $self->{'get_targz_md5sums_hashref_has_run'};
    }

    sub fetch_tarball_if_needed {
        my ( $self, $tarball, $targz_md5sums, $ns ) = @_;

        my $needs_downloaded = 0;
        my $is_custom        = 0;

        if ( !-e $tarball ) {
            $needs_downloaded = 1;
        }
        else {
            my $tarball_md5 = $self->file_md5_hex($tarball)       || -1;
            my $saved_md5   = $self->get_saved_path_md5($tarball) || -2;
            if ( $tarball_md5 ne $saved_md5 ) {
                $needs_downloaded = 1;
            }

            if ( exists $targz_md5sums->{$tarball} ) {
                if ( $tarball_md5 ne $targz_md5sums->{$tarball} ) {
                    $needs_downloaded = 1;
                }
            }
        }

        my $tarball_urlrelative = $tarball;
        $tarball_urlrelative =~ s/^$self->{'opt_mod_dir'}//;
        if ( $tarball =~ m/^$self->{'opt_mod_custom_dir'}/ ) {
            $needs_downloaded = 0;
            return ( 1, q{skipping custom opt mod tarball '[_1]'}, $tarball ) if $self->get_param('skip-custom-optmods');
            $is_custom = 1;
            $self->print_alert( q{Custom opt mod '[_1]' is using custom opt mod tarball '[_2]'}, $ns, $tarball );
        }

        my $host = $self->get_easources_host();

        if ($needs_downloaded) {
            unlink $tarball;
            $self->log_warn( [ q{unlink '[_1]' failed: [_2]}, $tarball, $! ] )
              if -e $tarball;

            $self->{'ui_obj'}->alert_over_last_twiddle( $self, q{Downloading '[_1]'}, $tarball );

            $self->fetch_from_httpupdate_silent(
                'host'     => $host,
                'url'      => "$self->{'httpupdate_uri'}/targz/$tarball_urlrelative",
                'destfile' => $tarball,
            );

            if ( ( $self->get_param('no-targz-yaml') || -e '/var/cpanel/easy_no_targz_yaml' ) || ( $self->get_digest($tarball) eq $targz_md5sums->{$tarball} ) && -e $tarball ) {
                $self->set_path_md5($tarball);
            }
            else {
                unlink($tarball) if -e $tarball;
                my $digest = ( $self->{'use_sha'} ) ? 'SHA512' : 'MD5';
                return ( 0, "$digest checksum mismatch for: $tarball" );
            }
        }

        if ( -e $tarball ) {
            my $path = $tarball =~ m{\.patch\.tar\.gz$} ? $self->{'patch_src_dir'} : '';
            return $self->untar_targz( $tarball, $path );
        }
        else {
            my @maketext =
              $is_custom
              ? ( q{Custom opt mod tarball '[_1]' does not exist}, $tarball )
              : ( q{Could not download '[_1]' from [_2]}, $tarball, $host . " $self->{'httpupdate_uri'}/targz/$tarball_urlrelative" );

            $self->print_to_log_and_screen( $self->maketext(@maketext) );
            $self->add_error_detail( $ns, 'fetch_tarball_if_needed()', 0, @maketext );

            return ( 0, @maketext );
        }
    }

}

1;
