#!/usr/bin/perl

use strict;

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

use Cpanel::RcsRecord ();

my $system       = $^O;
my $make_conf    = '/etc/make.conf';
my $exim_options = '/var/db/ports/exim/options';
my $now          = localtime;
my $add_comments = 0;

exit if $system !~ m/freebsd/i;

eval {
    fix_cpanel_scripts();

    my $rebuild;
    ( $rebuild = 1 ) if update_make_conf();
    ( $rebuild = 1 ) if update_exim_options();
    if ($rebuild) {
        print "Rebuilding and reinstalling exim\n";
        system '/scripts/eximup', '--force';
    }
};
if ($@) {
    print STDERR $@, "\n";
    print STDERR "Failed.\n";
    exit 1;
}

print "Done.\n";
exit;

sub modify_file {
    my ( $filename, $changes, $no_rcs ) = @_;
    my $tempfile = $filename . '.tmp.' . time;

    if ( open( my $rh, '<', $filename ) && open( my $wh, '>', $tempfile ) ) {
        my $changed_file = 0;
      LINE: while ( my $line = readline $rh ) {
            chomp $line;
            foreach my $remove_re ( @{ $changes->{'remove'} } ) {
                if ( $line =~ m/$remove_re/ ) {
                    if ($add_comments) {
                        print {$wh} "# Commented out by cPanel $now\n";
                        print {$wh} "#$line\n";
                    }
                    $changed_file = 1;
                    next LINE;
                }
            }
            foreach my $set_line ( @{ $changes->{'append'} } ) {
                if ( $line eq $set_line->{'line'} ) {
                    $set_line->{'already-set'} = 1;
                }
            }
            foreach my $set_line ( @{ $changes->{'replace'} } ) {
                my $pattern = $set_line->{'pattern'};
                if ( $line =~ m/$pattern/ ) {
                    if ($add_comments) {
                        print {$wh} "# Modified by cPanel $now\n";
                        print {$wh} "# Original: $line\n";
                    }
                    $line         = $set_line->{'replacement'};
                    $changed_file = 1;
                }
            }
            foreach my $set_line ( @{ $changes->{'set'} } ) {
                my $pattern = $set_line->{'pattern'};
                my $value   = $set_line->{'value'};
                if ( $line =~ m/$pattern(.*?)$/ ) {
                    if ( $1 =~ m/^\s*\Q$value\E\s*$/ ) {
                        $set_line->{'already-set'} = 1;
                        next;
                    }
                    if ($add_comments) {
                        print {$wh} "# Modified by cPanel $now\n";
                        print {$wh} "# Original: $line\n";
                    }
                    $line =~ s/($pattern).*?$/$1$value/;
                    $set_line->{'already-set'} = 1;
                    $changed_file = 1;
                }
            }
            print {$wh} $line, "\n";
        }
        close $rh;

        my $already_added_comment;
        foreach my $set_line ( @{ $changes->{'set'} } ) {
            next if $set_line->{'already-set'};
            if ( !$already_added_comment ) {
                if ($add_comments) {
                    print {$wh} "# Added by cPanel $now\n";
                }
                $already_added_comment = 1;
            }
            print {$wh} $set_line->{'key'} . '=' . $set_line->{'value'} . "\n";
            $changed_file = 1;
        }
        foreach my $set_line ( @{ $changes->{'append'} } ) {
            next if $set_line->{'already-set'};
            if ( !$already_added_comment ) {
                if ($add_comments) {
                    print {$wh} "# Added by cPanel $now\n";
                }
                $already_added_comment = 1;
            }
            print {$wh} $set_line->{'line'}, "\n";
            $changed_file = 1;
        }
        close $wh;

        if ($changed_file) {
            my $mode = ( stat $filename )[2] & 07777;
            rename( $tempfile, $filename ) or die "Unable to copy $tempfile over $filename";
            chmod( $mode, $filename ) or printf STDERR "Unable to return $filename to mode %04o.\n", $mode;
            if ( !$no_rcs ) {
                Cpanel::RcsRecord::rcsrecord($filename);
            }
        }
        else {
            unlink $tempfile;
            return;
        }
    }
    else {
        die "Unable to open $filename or $tempfile";
    }

    return 1;
}

sub modify_file_without_rcs {
    my ( $filename, $changes ) = @_;
    my $no_rcs = 1;
    my $result;

    eval { $result = modify_file( $filename, $changes, $no_rcs ); };
    if ($@) {
        print STDERR "$@\n";
        print STDERR "Failed to modify $filename.\n";
    }

    return $result;
}

sub fix_cpanel_scripts {
    my %changes = (
        'remove' => [
            qr/^\s*WITHOUT_ALT_CONFIG_PREFIX\s*=/i,
        ],
    );
    modify_file_without_rcs( '/scripts/checkmakeconf', \%changes );
    modify_file_without_rcs( '/scripts/make_config',   \%changes );

    %changes = (
        'replace' => [
            {
                'pattern'     => qr/^\s*system\( 'make', 'WITHOUT_ALT_CONFIG_PREFIX=yes' \);/,
                'replacement' => q{system('make');},
            },
        ],
    );
    modify_file_without_rcs( '/scripts/installport.pl', \%changes );

    return 1;
}

sub update_make_conf {
    return if !-e $make_conf;

    my %changes = (
        'remove' => [
            qr/^\s*WITHOUT_ALT_CONFIG_PREFIX\s*=/i,
        ],
    );

    return modify_file( $make_conf, \%changes );
}

sub deep_create {
    my ($filename) = @_;
    my @parts = split /\//, $filename;
    my $file = pop @parts;
    return if !length $file;
    my $dir = join '/', @parts;
    if ( $filename =~ m/^\// ) {
        $dir = '/' . $dir;
    }
    if ( !-e $dir ) {
        system "mkdir -p $dir";
    }
    if ( !-d $dir ) {
        return;
    }
    if ( open my $fh, '>>', $filename ) {
        close $fh;
        return 1;
    }
    return;
}

sub update_exim_options {
    my %changes = (
        'set' => [
            {
                'pattern'     => qr/^\s*WITH_ALT_CONFIG_PREFIX\s*=/,
                'key'         => 'WITH_ALT_CONFIG_PREFIX',
                'value'       => 'true',
                'already-set' => 0,
            },
        ],
        'append' => [
            {
                'line'        => q{SEDLIST+= -e 's,^(ALT_CONFIG_PREFIX=).*,\1/etc/exim,'},
                'already-set' => 0,
            },
            {
                'line'        => q{SEDLIST+= -e 's,^\# (DISABLE_D_OPTION=),\1,'},
                'already-set' => 0,
            },
        ],
    );

    if ( !-e $exim_options ) {
        deep_create($exim_options) or die "Unable to create missing $exim_options.\n";
    }

    return modify_file( $exim_options, \%changes );
}
