<?php

use CleantalkSP\Variables\Get;
use CleantalkSP\Variables\Post;
use CleantalkSP\Variables\Request;
use CleantalkSP\Variables\Server;
use CleantalkSP\SpbctWP\DB;
use CleantalkSP\SpbctWP\Firewall;
use CleantalkSP\SpbctWP\Firewall\BFP;
use CleantalkSP\SpbctWP\Firewall\FW;
use CleantalkSP\SpbctWP\Firewall\TC;
use CleantalkSP\SpbctWP\Firewall\WAF;
use CleantalkSP\SpbctWP\Firewall\WafBlocker;
use CleantalkSP\SpbctWP\Firewall\UploadChecker;
use CleantalkSP\SpbctWP\Helpers\IP;
use CleantalkSP\SpbctWP\Variables\Cookie;

// Prevent direct call
if ( ! defined('ABSPATH') ) {
    die('Not allowed!');
}

function spbc_firewall__check()
{
    global $spbc;

    $firewall = new Firewall();

    $secfw_enabled_on_main_site = false;
    if (!is_main_site() && $spbc->network_settings['ms__work_mode'] == 2) {
        $spbc_settings_main_site = get_blog_option(1, 'spbc_settings');
        if ($spbc_settings_main_site['secfw__enabled']) {
            $secfw_enabled_on_main_site = true;
        }
    }

    if ( (int) $spbc->settings['secfw__enabled'] || $secfw_enabled_on_main_site ) {
        $firewall->loadFwModule(
            new FW(
                array(
                    'data_table__personal_countries' => SPBC_TBL_FIREWALL_DATA__COUNTRIES,
                    'log_table'                      => SPBC_TBL_FIREWALL_LOG,
                    'state'                          => $spbc,
                    'api_key'                        => $spbc->api_key,
                )
            )
        );
    }

    spbc_firewall_check_waf($firewall);

    if ( class_exists('Poppyz_Core') ) { //fix poppyz plugin early start conflict
        if ( empty($GLOBALS['wp_rewrite']) || ! ($GLOBALS['wp_rewrite'] instanceof WP_Rewrite) ) {
            $GLOBALS['wp_rewrite'] = new WP_Rewrite(); // Fix for early load WP_Rewrite
        }
    }

     /**
     * Constant kept for backward compatibility; may be overridden in wp-config.php
     * @psalm-suppress RedundantCondition
     */
    if (!empty($spbc->settings['bfp__enabled']) && !SPBC_BFP_DISABLE) {
        $firewall->loadFwModule(
            new BFP(
                array(
                'api_key'       => $spbc->api_key,
                'state'         => $spbc,
                'is_login_page' => spbc_is_login_page_request(),
                'is_logged_in'  => Cookie::getString('spbc_is_logged_in') === md5($spbc->data['salt'] . get_option('home')),
                'bf_limit'      => $spbc->settings['bfp__allowed_wrong_auths'],
                'block_period'  => $spbc->settings['bfp__block_period__5_fails'],
                'count_period'  => $spbc->settings['bfp__count_interval'], // Counting login attempts in this interval
                )
            )
        );
    }

    if (
        $spbc->settings['traffic_control__enabled'] &&
            (
                //run if in not in admin area
                ! is_admin() ||
                //run if in admin area and user not logged in
                ! spbc_is_user_logged_in()
            )
    ) {
        $firewall->loadFwModule(
            new TC(
                array(
                'data_table'   => SPBC_TBL_FIREWALL_DATA,
                'log_table'    => SPBC_TBL_TC_LOG,
                'state'        => $spbc,
                'api_key'      => $spbc->api_key,
                'is_logged_in' => Cookie::getString('spbc_is_logged_in') === md5($spbc->data['salt'] . get_option('home')),
                'store_interval' => $spbc->settings['traffic_control__autoblock_timeframe'],
                'tc_limit'     => $spbc->settings['traffic_control__autoblock_amount'],
                'block_period' => $spbc->settings['traffic_control__autoblock_period'],
                )
            )
        );
    }

    $firewall->run();
}

function spbc_firewall_check_admin_area()
{
    if (spbc_user_is_admin()) {
        return;
    }

    // Flow for non-admin users
    $firewall = new Firewall();

    spbc_firewall_check_waf($firewall);

    $firewall->run();
}

function spbc_firewall_check_waf($firewall)
{
    global $spbc;

    if ( $spbc->settings['waf__enabled'] ) {
        $waf_params = [
            'api_key'                           => $spbc->api_key,
            'log_table'                         => SPBC_TBL_TC_LOG,
            'state'                             => $spbc,
            'waf__xss_check'                    => $spbc->settings['waf__xss_check'],
            'waf__sql_check'                    => $spbc->settings['waf__sql_check'],
            'waf__exploit_check'                => $spbc->settings['waf__exploit_check']
        ];
        if ( $spbc->settings['waf_blocker__enabled'] ) {
            $waf_blocker_params = [
                'is_logged_in' => Cookie::getString('spbc_is_logged_in') === md5($spbc->data['salt'] . get_option('home')),
                'db' => DB::getInstance(),
                'ip_array' => $firewall->ip_array
            ];
            $waf_blocker = new WafBlocker($waf_blocker_params);
            $waf_params['waf_blocker'] = $waf_blocker;
            $firewall->loadFwModule($waf_blocker);
        }
        $firewall->loadFwModule(new WAF($waf_params));
    }
}

/**
 * Wrapper to call UploadChecker logic.
 * @return void
 */
function spbc_upload_checker__check()
{
    global $spbc;
    if ( $spbc->settings['upload_checker__file_check'] && !empty($_FILES) && UploadChecker::isUploadingFilesExists($_FILES) ) {
        /** @var WP_Error|null $run_checker_error */
        $run_checker_error = null;
        if (is_user_logged_in()) {
            if (is_admin()) {
                $action = Post::getString('action') ?: Get::getString('action') ?: '';
                if ($action === 'upload-plugin' || $action === 'upload-theme') {
                    if ($action === 'upload-plugin') {
                        if (!wp_verify_nonce(Request::getString('_wpnonce') ?: '', 'plugin-upload')) {
                            // Install plugins interface - exit if nonce is wrong
                            $run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
                        }
                        if (!current_user_can('install_plugins')) {
                            // Install plugins interface - exit if no permission to do that
                            $run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
                        }
                    }
                    if ($action === 'upload-theme') {
                        if (!wp_verify_nonce(Request::getString('_wpnonce') ?: '', 'theme-upload')) {
                            // Install themes interface - exit if nonce is wrong
                            $run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
                        }
                        if (!current_user_can('install_themes')) {
                            // Install themes interface - exit if no permission to do that
                            $run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
                        }
                    }
                    if (!current_user_can('install_plugins')) {
                        // Install plugins/themes interface - exit if no permission to do that
                        $run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
                    }
                } elseif (!current_user_can('upload_files')) {
                    // Media interface - exit if no permission to uploading
                    $run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files: Not allowed upload files', 'security-malware-firewall'));
                } elseif (
                    //Nonce can be different here
                    !(
                        wp_verify_nonce(Request::getString('_wpnonce') ?: '', 'media-form') ||
                        wp_verify_nonce(Request::getString('emr_nonce') ?: '', 'media_replace_upload') ||
                        // ACF Tools > Import Field Groups (JSON)
                        wp_verify_nonce(Request::getString('_acf_nonce') ?: '', 'import')
                    )
                ) {
                    $run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files. Upload files request wrong', 'security-malware-firewall'));
                }
            } elseif (!current_user_can('upload_files')) {
                // Not admin area - exit if no permission to uploading
                $run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
            }
        }

        // RateLimit for all uploads, but permission check only for logged-in users
        if ( ! $run_checker_error && UploadChecker::hasRateOverlimit() ) {
            $run_checker_error = new WP_Error(429, __('You have exceeded the upload limit. Please try again later.', 'security-malware-firewall'));
        }

        if ( $run_checker_error ) {
            wp_die(
                $run_checker_error->get_error_message(),
                __('Upload Checker Exceeded', 'security-malware-firewall'),
                array('response' => $run_checker_error->get_error_code())
            );
        }

        $upload_checker = new UploadChecker(array(
            'upload_checker__do_check_wordpress_modules' => $spbc->settings['upload_checker__do_check_wordpress_modules'],
            'api_key'                    => $spbc->api_key,
        ));
        $firewall = new Firewall();
        $firewall->loadFwModule($upload_checker);
        $firewall->run();
    }
}

/**
 * Check if the firewall should be skipped
 * @return bool
 */
function spbc_firewall_skip_check()
{
    global $spbc, $apbct;

    // General skip
    if ( $spbc->fw_stats['is_on_maintenance']
        || ! $spbc->feature_restrictions->getState($spbc, 'firewall_log')->is_active
        || ! isset($spbc->fw_stats['last_updated'], $spbc->fw_stats['entries'])  // Plugin's FW base is updated
        || CleantalkSP\SpbctWP\Firewall::isException()
        || defined('DOING_AJAX')  // Pass AJAX
        || spbc_wp_doing_cron()           // Pass WP cron tasks
        || \CleantalkSP\Variables\Server::inUri('/favicon.ico')  // Exclude favicon.ico requests from the check
        || spbc_mailpoet_doing_cron()
        || spbc_is_cli()
    ) {
        return true;
    }

    // By cookie
    if ( ! empty($_GET['access']) ) {
        $apbct_settings = get_option('cleantalk_settings');
        $apbct_key      = ! empty($apbct_settings['apikey']) ? $apbct_settings['apikey'] : false;
        if ( ( $_GET['access'] === $spbc->settings['spbc_key'] || ( $apbct_key !== false && $_GET['access'] === $apbct_key ) ) ) {
            Cookie::set('spbc_firewall_pass_key', md5($_SERVER['REMOTE_ADDR'] . $spbc->settings['spbc_key']), time() + 1200, '/');
            Cookie::set('ct_sfw_pass_key', md5($_SERVER['REMOTE_ADDR'] . $apbct_key), time() + 1200, '/');

            return true;
        }
    }

    // Turn off the SpamFireWall if Remote Call is in progress
    if ( ( ! empty($apbct) && $apbct->rc_running ) || $spbc->rc_running ) {
        return true;
    }

    // Pass the check if cookie is set.
    $ip_set = IP::get();
    $ip_set = empty($ip_set) ? [] : $ip_set;
    $ip_set = is_array($ip_set) ? $ip_set : [$ip_set];
    foreach ( $ip_set as $spbc_cur_ip ) {
        if ( Cookie::getString('spbc_firewall_pass_key') == md5($spbc_cur_ip . $spbc->settings['spbc_key']) ) {
            return true;
        }
    }

    return false;
}

/**
 * Check if the firewall should be skipped
 * @return bool
 */
function spbc_firewall_skip_check_uploadchecker()
{
    global $spbc, $pagenow;

    if (empty($_FILES)) {
        return true;
    }

    if (!$spbc->feature_restrictions->getState($spbc, 'firewall_log')->is_active
        || CleantalkSP\SpbctWP\Firewall::isException()
        || defined('DOING_AJAX')  // Pass AJAX
        || spbc_wp_doing_cron()           // Pass WP cron tasks
        || \CleantalkSP\Variables\Server::inUri('/favicon.ico')  // Exclude favicon.ico requests from the check
        || spbc_mailpoet_doing_cron()
        || spbc_is_cli()
        || $pagenow === 'upload.php'
        || $pagenow === 'async-upload.php'
    ) {
        return true;
    }

    return false;
}
