#!/usr/local/cpanel/3rdparty/bin/perl
#                                      Copyright 2024 WebPros International, LLC
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited.

package autofixer2::centos7_disable_broken_repos;

use strict;
use warnings;

use Config::Tiny            ();
use Cpanel::iContact        ();
use Cpanel::OS              ();
use Cpanel::SafeRun::Object ();
use Cpanel::Version         ();

BEGIN { unshift @INC, '/usr/local/cpanel'; }

exit run() unless caller;

sub run {

    # This is needed for 110 and Cpanel::OS only goes back to 100
    # in its current form
    return 0 unless supported_on_this_major( 100, 108 );

    my $os_major = Cpanel::OS::major();
    my $distro   = Cpanel::OS::distro();

    # This is only needed on CentOS 7 servers
    return 0 unless $os_major == 7 && $distro eq 'centos';

    # Only run if issue is present
    # Note: This will take a while and timeout if problem is present
    my $run_obj = Cpanel::SafeRun::Object->new(
        'program' => '/usr/bin/yum',
        'args'    => ['makecache'],
    );
    return 0 unless $run_obj->error_code();

    my $repos_path = '/etc/yum.repos.d';
    opendir( my $dh, $repos_path ) or die "Can't open /etc/yum.repos.d for reading: $!";
    my @repos2disable;
    my @files = readdir($dh);    # "while (readdir $dh)" doesn't work as documented
    closedir($dh);
    my %files2backup;
    foreach my $file (@files) {
        next if ( substr( $file, 0, 1 ) eq '.' );                     # skip dotfiles
        next if ( rindex( $file, '.repo' ) != length($file) - 5 );    # skip nonrepo

        my $repo_path = "$repos_path/$file";
        my $cfg       = Config::Tiny->read($repo_path);
        my $needs_write;

        # yes, I know it is an object, keys() still works on it
        foreach my $repo ( keys( %{$cfg} ) ) {
            next unless $cfg->{$repo}{'enabled'};
            if ( grep { $_ eq $repo } qw{base updates extras centosplus} ) {

                # We are in the base repo, but a differently named file.
                # Special handling for this, similar to previous c7 autofixer.
                for (qw{baseurl mirrorlist}) { delete $cfg->{$repo}{$_} }
                my $repoid = $repo eq 'base' ? 'os' : $repo;
                $cfg->{$repo}{baseurl} = "https://vault.centos.org/7.9.2009/$repoid/\$basearch";
                $needs_write = 1;
                next;
            }
            foreach my $thing2check (qw{baseurl mirrorlist}) {
                next unless length $cfg->{$repo}{$thing2check};
                next unless $cfg->{$repo}{$thing2check} =~ m{^http[s]?://(?!vault)[a-zA-Z0-9.]*centos\.org};
                push @repos2disable, $repo;
                last;
            }
        }
        if ($needs_write) {

            # Maybe could use File::Copy here instead to avoid ms of "broken"?
            $files2backup{$repo_path} = "$repo_path." . time . ".bak";
            _rename( $repo_path, $files2backup{$repo_path} ) or warn "Failed to create backup of $repo_path: $!";
            $cfg->write($repo_path);
        }
    }

    # Disable all the offending repos, notify the admin
    Cpanel::SafeRun::Object->new_or_die(
        'program' => '/usr/bin/yum-config-manager',
        'args'    => [ '--disable', @repos2disable ],
    ) if @repos2disable;
    if ( @repos2disable || keys(%files2backup) ) {

        # OK, now if makecache *still* fails, let's just re-enable them
        # and throw up our hands.
        my $run_obj = Cpanel::SafeRun::Object->new(
            'program' => '/usr/bin/yum',
            'args'    => ['makecache'],
        );
        if ( $run_obj->error_code() ) {
            Cpanel::SafeRun::Object->new_or_die(
                'program' => '/usr/bin/yum-config-manager',
                'args'    => [ '--enable', @repos2disable ],
            ) if @repos2disable;

            # Restore from backup as well
            foreach my $repo_file ( keys(%files2backup) ) {
                _rename( $files2backup{$repo_file}, $repo_file ) or warn "Failed to restore $repo_file backup: $!";
            }
            return 1;
        }
        return 0 unless @repos2disable;

        my $body = <<~BODY;
        <body style="padding: 10px; background: #F4F4F4;">
            <div style="border: 2px solid #E8E8E8; width: 0 auto; margin: 0 auto; max-width: 680px; padding: 10px 10px 0 10px; background: #FFFFFF; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; border-bottom: 2px solid #FF6C2C;">
                <h2>Invalid YUM Repositories Disabled</h2>
                <p>CentOS 7 is now EOL. YUM no longer uses repositories pointing to invalid mirrors.</p>
                <p>The following repositories have been disabled:</p>
                <ul>
        BODY
        for (@repos2disable) { $body .= "<li>$_</li>" }
        $body .= <<~BODY2;
                </ul>
                <p>Do not reply to this automated message.</p>
            </div>
            <div style="text-align: center; margin: 10px;">
                <p>Copyright©&nbsp;2024 cPanel, L.L.C.<p>
            </div>
        </body>
        BODY2

        Cpanel::iContact::icontact(
            'subject'      => 'Invalid YUM Repositories Disabled',
            'app'          => 'Application',
            'message'      => $body,
            'content-type' => 'text/html',
        );

    }

    return 0;
}

sub supported_on_this_major {
    my ( $min_ver, $max_ver ) = @_;

    my $major = Cpanel::Version::get_short_release_number();

    return 0 if $major < $min_ver;
    return 0 if $major > $max_ver;

    return 1;
}

sub _rename {
    return rename( $_[0], $_[1] );
}

1;
