#
# Authentic Theme (https://github.com/authentic-theme/authentic-theme)
# Copyright Ilia Rostovtsev <ilia@virtualmin.com>
# Licensed under MIT (https://github.com/authentic-theme/authentic-theme/blob/master/LICENSE)
#
use strict;

use Fcntl qw( :flock );

our (%gconfig, %in, $get_user_level, $current_theme, $config_directory, $theme_webprefix, %theme_text, %theme_config);

&webmin_user_is_admin() || &error($theme_text{'theme_error_access_dir_not_allowed'});

my $csf_conf     = "/etc/csf";
my $csf_lib      = "/var/lib/csf";
my $header       = "csf.header";
my $body         = "csf.body";
my $footer       = "csf.footer";
my $htmltag      = "csf.htmltag";
my $bodytag      = "csf.bodytag";
my $csf_header   = "$csf_conf/$header";
my $csf_body     = "$csf_conf/$body";
my $csf_footer   = "$csf_conf/$footer";
my $csf_html_tag = "$csf_conf/$htmltag";
my $csf_body_tag = "$csf_conf/$bodytag";
my $csf_ui_patch = "$config_directory/$current_theme/csf.ui";

sub csf_clear
{
    unlink_file($csf_header);
    unlink_file($csf_body);
    unlink_file($csf_footer);
    unlink_file($csf_html_tag);
    unlink_file($csf_body_tag);
    unlink_file($csf_ui_patch);
}

sub csf_strings
{

    # Define CSF installed version
    my $error;
    my $csf_update_required;
    my $csf_title;
    my $csf_data;
    my $csf_remote_version;
    my $csf_installed_version = read_file_lines("$csf_conf/version.txt", 1);
    $csf_installed_version = $csf_installed_version->[0];

    # Define CSF actual version if allowed
    if ($theme_config{'settings_sysinfo_csf_updates'} eq 'true' &&
        &webmin_user_is_admin() &&
        $in{'xhr-info'} eq '1')
    {
        my $cache_id = 'version-csf';
        $csf_remote_version = theme_cache_read($cache_id);
        my $download_server = $theme_config{'settings_csf_download_domain_privileged'};
        # If download server is set to discontinued "download.configserver.com",
        # update cache with existing without trying to download anything
        if ($download_server eq 'download.configserver.com') {
            theme_cache_write($cache_id, $csf_remote_version);    
            $csf_remote_version = '0';   
        } else {
            my $download_port   = $theme_config{'settings_csf_download_port_privileged'};
            my $download_path   = $theme_config{'settings_csf_download_path_privileged'};
            my $ssl = ($download_port =~ /^\d+$/)
                    ? ($download_port == 80 || $download_port == 8080 ? 0 : 1)
                    : 0;
            if (!$csf_remote_version ||
                ($csf_remote_version && !theme_cache_is_fresh($cache_id))) {
                http_download($download_server, $download_port, $download_path,
                            \$csf_remote_version, \$error,
                            undef, $ssl, undef, undef, 30);
                theme_cache_write($cache_id, $csf_remote_version) if ($csf_remote_version && !$error);
            }
        }
        # Trim versions' number
        $csf_installed_version = &trim($csf_installed_version) if $csf_installed_version;
        $csf_remote_version    = &trim($csf_remote_version) if $csf_remote_version;

    } else {
        $csf_remote_version = '0';
    }

    if ($csf_remote_version <= $csf_installed_version) {
        $csf_update_required = '0';
    } else {
        $csf_update_required = '1';
    }

    $csf_title = $theme_text{'body_firewall'} . ' '
      .
      ( `pgrep lfd` ? '' :
' &nbsp;&nbsp;&nbsp;&nbsp;<a class="label label-danger csf-submit" data-id="csf_lfdstatus" class="label label-danger">Stopped</a> '
      );
    $csf_data = (
        '<a href=\'' . $theme_webprefix . '/csf/index.cgi\' data-id="csf_link_open">' .
          $theme_text{'theme_xhred_csf'} . '</a> ' . product_version_update($csf_installed_version, 'f') . ''

          . ($csf_update_required eq '1' ?
               '. ' . $theme_text{'theme_update_available'} . ' ' . $csf_remote_version . '&nbsp;&nbsp;&nbsp;' :
               '&nbsp;&nbsp;&nbsp;'
          ) .
          '
              <form action="/csf/index.cgi" method="post" class="hidden" id="csf_lfdstatus">
                  <input type="hidden" name="action" value="lfdstatus">
              </form>
              <form action="/csf/index.cgi" method="post" class="hidden" id="csf_upgrade">
                  <input type="hidden" name="action" value="upgrade">
              </form>
              <form action="/csf/index.cgi" method="post" class="hidden" id="csf_temporary_ip_entries">
                  <input type="hidden" name="action" value="temp">
              </form>
              <form action="/csf/index.cgi" method="post" class="hidden" id="csf_search_system_log">
                  <input type="hidden" name="action" value="loggrep">
              </form>
              <form action="/csf/index.cgi" method="post" class="hidden" id="csf_denyf">
                  <input type="hidden" name="action" value="denyf">
              </form>
          '
          . (
            $csf_update_required eq '1' ?
              '<div class="btn-group">
              <a class="btn btn-xxs btn-success csf csf-submit" data-id="csf_upgrade"><i class="fa fa-fw fa-refresh">&nbsp;</i>'
              . $theme_text{'theme_update'} . '</a>
              <a class="btn btn-xxs btn-info csf" target="_blank" href="https://'.
                $theme_config{'settings_csf_download_domain_privileged'}
                .'/csf/changelog.txt"><i class="fa fa-fw fa-pencil-square-o">&nbsp;</i>'
              . $theme_text{'theme_changelog'} . '</a>
              <a class="btn btn-xxs btn-warning csf" target="_blank" href="https://'.
                $theme_config{'settings_csf_download_domain_privileged'}.'/csf.tgz"><i class="fa fa-fw fa-download">&nbsp;</i>'
              . $theme_text{'theme_download'} . '</a>
          </div>'
            :
              ''
          ) .
          '');
    return ($csf_title, $csf_data, $csf_remote_version);
}

sub csf_mod
{
    my $ext = (theme_debug_mode() ? 'src' : 'min');

    my $csf_header_mod  = "$config_directory/$current_theme/$header";
    my $csf_body_mod    = "$config_directory/$current_theme/$body";
    my $csf_footer_mod  = "$config_directory/$current_theme/$footer";
    my $csf_htmltag_mod = "$config_directory/$current_theme/$htmltag";
    my $csf_bodytag_mod = "$config_directory/$current_theme/$bodytag";

    open(my $fh, '>', $csf_header_mod) or die $!;

    print $fh '<link data-hostname="' . &get_display_hostname() . '" data-version="' .
      (theme_version('timestamped')) . '" rel="shortcut icon" href="' . $theme_webprefix . '/images/favicons/webmin/favicon.ico">' . "\n";
    print $fh '<link type="text/css" href="' .
      $theme_webprefix . '/unauthenticated/css/bundle.min.css?' . theme_version('timestamped') . '" rel="stylesheet">' . "\n";
    print $fh '<link type="text/css" href="' . $theme_webprefix .
      '/unauthenticated/css/palettes/nightrider.' . $ext . '.css?' . theme_version('timestamped') . '" rel="stylesheet">' . "\n";

    print $fh '<link type="text/css" href="' . $theme_webprefix .
      '/unauthenticated/css/fonts-roboto.' . $ext . '.css?' . theme_version('timestamped') . '" rel="stylesheet">' . "\n";
    
    # Print default options
    print $fh " <script type='application/javascript' src=\"$theme_webprefix/unauthenticated/js/defaults.js?" . theme_version('timestamped') . "\"></script>\n";

    # Print object with language strings
    print $fh ' <script type="application/javascript">';
    print $fh 'var v___theme_language = ' . get_theme_language();
    print $fh "</script>\n";

    print $fh '<script type="application/javascript" src="' .
      $theme_webprefix . '/unauthenticated/js/bundle.min.js?' . theme_version('timestamped') . '"></script>' . "\n";

    print $fh '<link type="text/css" href="' .
      $theme_webprefix . '/extensions/csf/csf.' . $ext . '.css?' . theme_version('timestamped') . '" rel="stylesheet">' . "\n";
    print $fh '<script type="application/javascript" src="' .
      $theme_webprefix . '/extensions/csf/csf.' . $ext . '.js?' . theme_version('timestamped') . '"></script>' . "\n";

    close $fh;

    open(my $fh2, '>', $csf_body_mod) or die $!;
    print $fh2 '<div class="container-fluid col-lg-10 col-lg-offset-1 csf-container" data-dcontainer="1">' . "\n";
    close $fh2;

    open(my $fh3, '>', $csf_footer_mod) or die $!;
    print $fh3 '</div><script type="application/javascript">!$.support.spa && csf_init()</script>' . "\n";
    close $fh3;

    open(my $fh4, '>', $csf_htmltag_mod) or die $!;
    print $fh4 ' '
      .
      replace("\"", "'",
              header_html_data('csf', '1', ($theme_text{'theme_xhred_csf'} . " — " . get_html_framed_title(), 0, 0, '1')))
      .
      '';
    close $fh4;

    open(my $fh5, '>', $csf_bodytag_mod) or die $!;
    print $fh5 ' ' . replace("\"", "'", header_body_data('csf')) . '';
    close $fh5;

    if (-e $csf_conf && -d $csf_conf) {
        copy_source_dest($csf_header_mod,  $csf_conf);
        copy_source_dest($csf_body_mod,    $csf_conf);
        copy_source_dest($csf_footer_mod,  $csf_conf);
        copy_source_dest($csf_htmltag_mod, $csf_conf);
        copy_source_dest($csf_bodytag_mod, $csf_conf);
    }
    my $csf_ui   = uc('style' . '_' . 'custom');
    my $csf_conf = ("$csf_conf/csf.conf");

    if (-f $csf_conf && !-f $csf_ui_patch) {
        (my $fc = read_file_contents($csf_conf)) =~ s/$csf_ui = "0"/$csf_ui = "1"/g;
        write_file_contents($csf_conf,     $fc);
        write_file_contents($csf_ui_patch, "\n");
    }
}

sub csf_temporary_list
{
    my $let = "$csf_conf/csf.allow";
    my $ban = "$csf_conf/csf.deny";
    my $cnf = "$csf_conf/csf.conf";
    my $tmp = "$csf_lib/csf.tempban";
    my $log = "$csf_lib/stats/iptables_log";

    my @p;
    my @t;
    my @l;

    if (-e $cnf && !-z $cnf) {
        my @q;
        my $x = read_file_contents($let) . read_file_contents($ban);
        my $z = read_file_contents($cnf);
        (@p) = $z =~
/(?:TCP_IN|UDP_IN|TCP6_IN|UDP6_IN|PORTS_TCP|PORTS_UDP|CT_PORTS|CLUSTER_PORT|PORTS_webmin|PORTS_sshd).*=\s*"([\d+,]+)"/g;
        (@q) = $x =~ /^(?:(?!#).).*\|(?:d|s)=([\d+,]+)\|/gm;
        if (@p || @q) {
            @p = array_unique(split(",", join(",", (@p, @q))));
        }

    }

    if (-e $tmp && !-z $tmp) {
        open(my $IN, "<", $tmp) or die $!;
        @t = <$IN>;
        chomp @t;
        close($IN);
    }

    if (@t && -e $log) {
        open(my $IN, "<", $log) or die $!;
        flock($IN, LOCK_SH);
        my @i = <$IN>;
        close($IN);
        chomp @i;
        @i = reverse @i;
        my $c = 0;

        my $s = scalar @i;
        my @g;

        foreach my $h (reverse @t) {
            if ($h) {
                my ($a, $b, $d, $e, $f, $g) = split(/\|/, $h);
                my ($ll, $dl) = (undef, '|');
                for (my $x = 0; $x < $s; $x++) {
                    $c++;
                    my $u = $i[$x];
                    my ($o, $l) = split(/\|/, $u);
                    my ($r, $w, $k);
                    if ($l =~ /SRC=(\S+)/) {$r = $1}
                    if ($l =~ /DST=(\S+)/) {$w = $1}
                    if ($l =~ /DPT=(\d+)/) {$k = $1}
                    if (($r eq $b && array_contains(\@p, $k)) || $d =~ /\d/g || $g =~ /failed|\(CT\)/gi) {
                        $ll = ($a . $dl . $b . $dl . $w . $dl . $k . $dl . $d . $dl . $e . $dl . $f . $dl . $g);
                        if (!array_contains(\@g, $g) && !array_contains(\@l, $ll)) {
                            push @g, $g;
                            push @l, $ll;
                        }
                    }
                }
            }
        }
    }
    convert_to_json(\@l);
}

1;
