#!/usr/local/cpanel/3rdparty/bin/perl

package autofixer2::mysql97_lts_repo_disable;

#                                      Copyright 2026 WebPros International, LLC
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited.

use strict;
use warnings;

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

my $SCRIPT = 'mysql97_lts_repo_disable';
my $PKG    = 'mysql*-community-release';
my $PKG_RE = qr/mysql[^-]*-community-release/;
my $TAG    = 'cpanel:mysql84-versionlock';

exit run() unless caller;

# Exclude mysql*-community-release from dnf/yum updates so the release RPM
# cannot be upgraded to a version that ships unwanted repo stanzas (e.g. the
# mysql-9.7-lts-community repo that caused unplanned MySQL 8.4 -> 9.7 upgrades).
# MySQL upstream bug: https://bugs.mysql.com/bug.php?id=120315
# If the repo file already contains a 9.7 stanza the user has intentionally
# upgraded, so we leave it alone.

sub run {
    return 0 unless supported_on_this_major( 118, 136 );

    # Only applies to RPM-based systems.
    return 0 unless -e '/usr/bin/rpm' || -e '/bin/rpm';

    # Check if the package is installed.
    return 0 unless _is_pkg_installed($PKG);

    # If the repo file already contains the 9.7 stanza, the user has a newer
    # mysql-community-release RPM that we should not lock.
    if ( _repo_has_97() ) {
        print "$SCRIPT: mysql-community.repo contains 9.7 repo; skipping exclude.\n";
        return 0;
    }

    # Already excluded in dnf.conf or yum.conf?
    if ( _is_excluded_in_conf($PKG) ) {
        print "$SCRIPT: $PKG is already excluded in dnf/yum configuration.\n";
        return 0;
    }

    return _apply_exclude_in_conf($PKG);
}

sub _is_pkg_installed {
    my ($pkg) = @_;

    # rpm -q does not support globs; use rpm -qa which does.
    my @matches = `rpm -qa '$pkg' 2>/dev/null`;
    return scalar @matches ? 1 : 0;
}

sub _repo_has_97 {
    my $repo_file = '/etc/yum.repos.d/mysql-community.repo';
    return 0 unless -f $repo_file;

    open( my $fh, '<', $repo_file ) or return 0;
    while ( my $line = <$fh> ) {
        # Match stanza headers like [mysql-9.7-lts-community] or [mysql97-community].
        if ( $line =~ /^\s*\[\s*mysql[^]]*9[\.\-]?7[^]]*\]/i ) {
            close $fh;
            return 1;
        }
    }
    close $fh;
    return 0;
}

sub _is_excluded_in_conf {
    my ($pkg) = @_;

    for my $conf ( '/etc/dnf/dnf.conf', '/etc/yum.conf' ) {
        next unless -f $conf;
        open( my $fh, '<', $conf ) or next;
        while ( my $line = <$fh> ) {
            chomp $line;

            # Strip inline comments before checking.
            ( my $clean = $line ) =~ s/\s*#.*//;
            next unless $clean =~ /^\s*exclude\s*=/i;
            return 1 if $clean =~ /$PKG_RE/;
        }
        close $fh;
    }
    return 0;
}

sub _apply_exclude_in_conf {
    my ($pkg) = @_;

    for my $conf ( '/etc/dnf/dnf.conf', '/etc/yum.conf' ) {
        open( my $fh, '<', $conf ) or next;
        my @lines = <$fh>;
        close $fh;

        # DNF only honors the last exclude= in a section, so we must
        # append to an existing line rather than adding a second one.
        # The $TAG comment marks our addition for future removal.
        my $found_main = 0;
        my $modified   = 0;

        for my $i ( 0 .. $#lines ) {
            if ( $lines[$i] =~ /^\s*\[main\]/i ) {
                $found_main = 1;
                next;
            }

            # Another section starts — stop looking in [main].
            last if $found_main && $lines[$i] =~ /^\s*\[/;

            next unless $found_main;

            ( my $clean = $lines[$i] ) =~ s/\s*#.*//;
            next unless $clean =~ /^\s*exclude\s*=/i;

            # Append our package to the existing exclude line.
            chomp $lines[$i];
            $lines[$i] =~ s/\s*#.*//;    # strip any trailing comment
            $lines[$i] .= " $pkg # $TAG\n";
            $modified = 1;
            last;
        }

        # No exclude= line found in [main] — insert one after [main].
        if ( $found_main && !$modified ) {
            for my $i ( 0 .. $#lines ) {
                if ( $lines[$i] =~ /^\s*\[main\]/i ) {
                    splice( @lines, $i + 1, 0, "exclude=$pkg # $TAG\n" );
                    $modified = 1;
                    last;
                }
            }
        }

        if ($modified) {
            open( my $wfh, '>', $conf ) or do {
                warn "$SCRIPT: Cannot write $conf: $!\n";
                return 1;
            };
            print $wfh @lines;
            close $wfh;
            print "$SCRIPT: Added exclude for $pkg in $conf. (marker: $TAG)\n";
            return 0;
        }
    }

    warn "$SCRIPT: Could not find a writable dnf.conf or yum.conf to add exclude.\n";
    return 1;
}

# ---- Standard autofixer helpers (from template_autofixer) ----

sub supported_on_this_major {
    my ( $min, $max ) = @_;

    my $major = get_major_version();
    return 0 if !$major;
    return 0 if $major < $min || $major > $max;
    return 1;
}

sub get_major_version {
    open( my $fh, '<', '/usr/local/cpanel/version' ) or return 0;
    my $version = readline($fh);
    close $fh;
    chomp $version if $version;

    my ($major) = $version =~ m/^11\.(\d+)\./a;
    return $major || 0;
}

1;
