#!/usr/bin/perl

# Name: ALT Mirror Switcher
# Autor: Aleksandr Shamaraev <shad@altlinux.org>
# License: GPLv2+
# URL: https://altlinux.space/shad

# Inspired by the apt-repo package and uses a small part of its code.

use strict;
use warnings;
use String::Util 'trim';
use Date::Calc qw(check_date);

my $version = '0.10.0';

my $dir;
my $cmd = '';
my $active_mirror = '';
my $conf_list = '/etc/apt/sources.list.d/*.list';
my $ams_path = "/etc/apt/sources.list.d/ams.list";
my $exclude_list = "/etc/apt/sources.list.d/heanet.list";
my $archive_link = "rpm [alt] https://ftp.altlinux.org/pub/distributions/archive/sisyphus/date/";
my @archive_end = (" x86_64 classic", " x86_64-i586 classic", " noarch classic");

$dir = $conf_list;
$dir =~ s/\*\.list$//;

$cmd = $ARGV[0] if scalar @ARGV > 0;

# show local mirrors and switch if ARGV[2] = switch
sub show_mirror{
    if (!defined $ARGV[1]){
        opendir( DIR, $dir );
        print "Local mirrors:\n";
        foreach my $name (sort readdir( DIR )) {
        if( $name =~ /\.list$/)
            {
                if ((trim($dir . $name) ne trim($ams_path)) && (trim($dir . $name) ne trim($exclude_list)))
                {
                    open(my $fl, "<", $dir . $name);
                    while (my $line = <$fl>) {
                        if (($line =~ /\[p1/) or ($line =~ /\[alt\]/)) {
                        $line =~ s/(.*\s+)(http|https|ftp|ftps|rsync|file):\/\///;
                        $line =~ s/\/.*//;
                        print $line;                  
                        last;
                        }
                    }
                    close($fl);
                }
            }
        }
        closedir( DIR );
    }
    else {
        print "Error: Unnecessary parameter.\nRun `ams --help` or `ams -h` for supported commands.\n";
    }
}

# Switch local mirror
sub switch_mirror {
    if (!defined $ARGV[1]) { print "Error: Mirror not specified.\nRun `ams --help` or `ams -h` for supported commands.\n"; return 0; }
    #definition of an active mirror
    my $active_protocol = '';
    my $line2 = '';
    opendir( DIR, $dir );
    foreach my $name (sort readdir( DIR )) {
        if( $name =~ /\.list$/)
        {
            if ((trim($dir . $name) ne trim($ams_path)) && (trim($dir . $name) ne trim($exclude_list)))
            {
                open(my $fl, "<", $dir . $name);
                while (my $line = <$fl>) {
                    if (($line =~ /\[p1/) or ($line =~ /\[alt\]/)) {
                    my $t2 = substr($line, 0, 3);
                    if ($t2 eq'rpm') {
                        $active_mirror = $dir . $name; 
                        if ($line =~ 'http:') {$active_protocol = 'http:';}
                        if ($line =~ 'https:') {$active_protocol = 'https:';}
                        if ($line =~ 'ftp:') {$active_protocol = 'ftp:';}
                        if ($line =~ 'ftps:') {$active_protocol = 'ftps:';}
                        if ($line =~ 'rsync:') {$active_protocol = 'rsync:';}
                        if ($line =~ 'file:') {$active_protocol = 'file:';}
                        last;
                    }
                    }
                }
                close($fl);
            }
        }
    }
    closedir( DIR );

    #definition of an active mirror
    if ( (!defined $ARGV[2]) || (! $ARGV[2] =~ /^(http|https|ftp|ftps|rsync|file):\//) ) { print "Error: Protocol not specified. Set default: http\n"; $line2 = 'http:'; }
    if ($line2 eq '') {$line2 = $ARGV[2].':';}

    #find new mirror and check protocol
    my $new_mirror = '';
    my $flag_protocol = 0;
    $dir = $conf_list;
    $dir =~ s/\*\.list$//;
    opendir( DIR, $dir );
    foreach my $name (sort readdir( DIR )) {
        if( $name =~ /\.list$/)
            {
                if ((trim($dir . $name) ne trim($ams_path)) && (trim($dir . $name) ne trim($exclude_list)))
                {
                    open(my $fl, "<", $dir . $name);
                    while (my $line = <$fl>) {
                        if (($line =~ /\[p1/) or ($line =~ /\[alt\]/)) {
                            if (($line =~ $ARGV[1]) && ($new_mirror eq '')) {$new_mirror = $dir . $name;}
                            if (($line =~ $ARGV[1]) && ($line =~ $line2) && ($new_mirror ne "")) {$flag_protocol = 1; last;}
                        }
                    }
                    close($fl);
                }
            }
    }
    closedir( DIR );

    # ------ block with different checks
    if ($new_mirror eq '') {print "Mirror '" . $ARGV[1] . "' not found.\n"; return 0;}
    if ($flag_protocol==0) {print "Protocol '" . $line2 . "' not found in '" . $ARGV[1] . "'\n"; return 0;}
    if (($new_mirror eq $active_mirror) && ($active_protocol eq $line2)) {print "The mirror and protocol have already been selected\n"; return 0;}  
    # check branch
    my $act_branch = trim(`rpm --eval %_priority_distbranch`);
    my $act_mirror = `rpm -qa | grep apt-conf-`;
    my $check_brach = 0;
    if ($act_mirror =~ $act_branch) {$check_brach = 1;}
    elsif (($act_branch =~ /p1/) && ($act_mirror =~ /branch/)) {$check_brach = 1;}
    else {$check_brach = 0;}
    if ($check_brach==0) {print "A difference in branches was detected. If you are using Sisyphus, install apt-conf-sisyphus.\n"; return 0;}
    # check arch
    my $arch = trim(`uname -m`);
    open(ARCH,$new_mirror);
    my @lines = <ARCH>;
    close ARCH;
    my $check_arch = 0;
    foreach my $arch_line (@lines) { if ($arch_line =~ $arch) {$check_arch = 1; last;} }
    if ($check_arch==0) {print "This mirror (" . $ARGV[1] . ") does not contain the required architecture.\n"; return 0;}
    # check ams lists
    my $ams_list_package = trim(`rpm -qa | grep switcher-lists-`);
    my $check_ams_list;
    if (!defined $ams_list_package) {$check_ams_list = 1;}
    elsif ($ams_list_package =~ $act_branch) {$check_ams_list = 1;}
    elsif (($act_branch =~ /p1/) && ($ams_list_package =~ /branch/)) {$check_ams_list = 1;}
    else {$check_ams_list = 0;}
    if ($check_ams_list==0) {print "A branch difference was detected. You are using additional mirrors from a different branch.\n"; return 0;}
    # ------
    
    my $fl;
    opendir( DIR, $dir );

    if ($active_mirror ne '') {
        rename $active_mirror, $active_mirror . '.tmp' or die "Cannot switch mirror: $!";
        open($fl, "<", $active_mirror . '.tmp');
        open ACTIVE_MIRROR, '>', $active_mirror or die "Can't create $active_mirror: $!";
        while (my $line = <$fl>) {
            my $t2 = substr($line, 0, 3);
            if ($t2 eq'rpm') {
                print ACTIVE_MIRROR "#$line";
            }
            else {
                print ACTIVE_MIRROR $line;
            }
        }
        close($fl);
        close ACTIVE_MIRROR;
    }

    rename $new_mirror, $new_mirror . '.tmp' or die "Cannot switch mirror: $!";
    open($fl, "<", $new_mirror . '.tmp');
    open NEW_MIRROR, '>', $new_mirror or die "Can't create $new_mirror: $!";
    while (my $line = <$fl>) {
        if ($line =~ $line2){
            print NEW_MIRROR substr($line, 1);
        }
        else {
            print NEW_MIRROR $line;
        }
    }
    close($fl);
    close NEW_MIRROR;
    unlink( $active_mirror . '.tmp' ) if -e $active_mirror . '.tmp';
    unlink( $new_mirror . '.tmp' ) if -e $new_mirror . '.tmp';
    unlink( $ams_path ) if -e $ams_path;
    closedir( DIR );

    print "Disabled: /etc/apt/sources.list\n";
    opendir( DIR, "/etc/apt/" );
    rename '/etc/apt/sources.list', '/etc/apt/sources.list.tmp' or die "Cannot switch mirror: $!";
    open($fl, "<", '/etc/apt/sources.list.tmp');
    open ACTIVE_MIRROR, '>', '/etc/apt/sources.list' or die "Can't create /etc/apt/sources.list: $!";
    while (my $line = <$fl>) {
        my $t2 = substr($line, 0, 3);
        if ($t2 eq'rpm') {
            print ACTIVE_MIRROR "#$line";
        }
        else {
            print ACTIVE_MIRROR $line;
        }
    }
    close($fl);
    close ACTIVE_MIRROR;
    unlink( '/etc/apt/sources.list.tmp' ) if -e '/etc/apt/sources.list.tmp';
    closedir( DIR );

    print "\nDone.\n";
           
    return 0;
}

# Set Sisyphus archive date
sub set_archive{
    my $act_branch = trim(`rpm --eval %_priority_distbranch`);
    if ($act_branch =~ /sisyphus/) {
        if ( (defined $ARGV[1]) && ($ARGV[1] =~ m/^[+-]?\d+$/) && (length($ARGV[1]) == 8) ) {
            my $st_temp = $ARGV[1];
            my $myr = substr($st_temp, 0, 4);
            my $mmm = substr($st_temp, 4, 2);
            my $mda = substr($st_temp, 6, 2);
            print("year: $myr, month: $mmm, day: $mda\n");
            # Date check
            if (check_date($myr, $mmm, $mda)){
                substr($st_temp,4,0) = '/';
                substr($st_temp,7,0) = '/';
                opendir( DIR, $dir );
                # Find active mirror
                print("Find active mirror...\n");
                foreach my $name (sort readdir( DIR )) {
                if( $name =~ /\.list$/)
                {
                    if ((trim($dir . $name) ne trim($ams_path)) && (trim($dir . $name) ne trim($exclude_list)))
                    {
                        open(my $fl, "<", $dir . $name);
                        while (my $line = <$fl>) {
                            if (($line =~ /\[p1/) or ($line =~ /\[alt\]/)) {
                            my $t2 = substr($line, 0, 3);
                            if ($t2 eq'rpm') {
                                $active_mirror = $dir . $name;
                                last;
                            }
                            }
                        }
                       close($fl);
                    }
                }
                }
                print("Disable active mirror: $active_mirror\n");
                if ($active_mirror ne '') {
                    rename $active_mirror, $active_mirror . '.tmp' or die "Cannot switch mirror: $!";
                    open(my $fl, "<", $active_mirror . '.tmp');
                    open ACTIVE_MIRROR, '>', $active_mirror or die "Can't create $active_mirror: $!";
                    while (my $line = <$fl>) {
                        my $t2 = substr($line, 0, 3);
                        if ($t2 eq'rpm') {
                            print ACTIVE_MIRROR "#$line";
                        }
                        else {
                            print ACTIVE_MIRROR $line;
                        }
                    }
                    close($fl);
                    close ACTIVE_MIRROR;
                }
                unlink( $active_mirror . '.tmp' ) if -e $active_mirror . '.tmp';
                unlink( $ams_path ) if -e $ams_path;
                print("Set the date of the archive `Sisyphus`: $st_temp\n");
                open(my $fl, "<", $ams_path);
                open AMS_MIRROR, '>', $ams_path or die "Can't create $ams_path: $!";
                print AMS_MIRROR "# Sisyphus archive: $st_temp\n\n";
                print AMS_MIRROR "$archive_link$st_temp$archive_end[0]\n";
                print AMS_MIRROR "$archive_link$st_temp$archive_end[1]\n";
                print AMS_MIRROR "$archive_link$st_temp$archive_end[2]\n";
                close($fl);
                close AMS_MIRROR;
                closedir( DIR );
                print "Disabled: /etc/apt/sources.list\n";
                opendir( DIR, "/etc/apt/" );
                rename '/etc/apt/sources.list', '/etc/apt/sources.list.tmp' or die "Cannot switch mirror: $!";
                open($fl, "<", '/etc/apt/sources.list.tmp');
                open ACTIVE_MIRROR, '>', '/etc/apt/sources.list' or die "Can't create /etc/apt/sources.list: $!";
                while (my $line = <$fl>) {
                    my $t2 = substr($line, 0, 3);
                    if ($t2 eq'rpm') {
                        print ACTIVE_MIRROR "#$line";
                    }
                    else {
                        print ACTIVE_MIRROR $line;
                    }
                }
                close($fl);
                close ACTIVE_MIRROR;
                unlink( '/etc/apt/sources.list.tmp' ) if -e '/etc/apt/sources.list.tmp';
                closedir( DIR );
                print "\nDone.\n";
                return 0;
            }
            else{
                print "Error: Incorrect date.\n";
            }
        }
        else
        {
         print "Error: `$ARGV[1]` is not int(8).\n"; return 0;
        }
    }
    else{
         print "Error: You don`t use Sisyphus.\n"; return 0; }
}

# Show usage information
sub show_usage {
    print <<"HELP";
Usage: ams mirror
       ams switch <mirror> <http|https|ftp|ftps|rsync|file>
       ams set <yyyymmdd>

COMMANDS:
  mirror - Showing local update mirrors
  switch <mirror> <http|https|ftp|ftps|rsync|file> - Switching local update mirrors with needs protocols (default: http)
  set <yyyymmdd> - Set Sisyphus archive date, for example: `ams set 20260509`
  -h, --help - Show help and exit
  -v, --version - Show version number
HELP
    exit 0;
}

# Functions with return
if( $cmd =~ /^(mirror)$/ ) {
    show_mirror( @ARGV ) if $cmd eq 'mirror';
}elsif( $cmd =~ /^(switch)$/ ) {
    switch_mirror( @ARGV ) if $cmd eq 'switch';
}elsif( $cmd =~ /^(set)$/ ) {
    set_archive( @ARGV ) if $cmd eq 'set';
}elsif( $cmd =~ /-(h|-help)$/ ) {
    show_usage();
}elsif( $cmd =~ /-(v|-version)$/ ) {
    die "Version - $version\n";
}elsif( scalar @ARGV == 0 ) {
    die "Run `ams --help` or `ams -h` for supported commands.\n";
}else {
    die "Unknown command `$cmd`.\nRun `ams --help` or `ams -h` for supported commands.\n";
}

__END__
