<?php
/**
 * Plugin Name: Foxblock
 * Plugin URI: https://foxplugin.com
 * Description: Real-time traffic monitoring and bot blocking system for WordPress.
 * Version: 2.1.1
 * Author: Foxtheme
 * Author URI: https://foxplugin.com
 * Text Domain: foxblock
 */

if (!defined('ABSPATH')) {
    exit; // Exit if accessed directly
}

define('FOXBLOCK_VERSION', '2.1.1');
define('FOXBLOCK_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('FOXBLOCK_PLUGIN_URL', plugin_dir_url(__FILE__));

function foxblock_get_known_bots()
{
    return array(
        'GPTBot',
        'OAI-SearchBot',
        'ChatGPT-User',
        'ClaudeBot',
        'Claude-SearchBot',
        'Claude-User',
        'anthropic-ai',
        'PerplexityBot',
        'Perplexity-User',
        'Google-Extended',
        'GoogleOther',
        'GoogleOther-Image',
        'Google-CloudVertexBot',
        'CCBot',
        'Amazonbot',
        'Applebot',
        'Applebot-Extended',
        'Bytespider',
        'TikTokSpider',
        'meta-externalagent',
        'meta-externalfetcher',
        'FacebookBot',
        'Cohere-ai',
        'Diffbot',
        'OmgiliBot',
        'DuckAssistBot',
        'DuckDuckBot',
        'AhrefsBot',
        'SemrushBot',
        'MJ12bot',
        'DotBot',
        'BLEXBot',
        'PetalBot',
        'Barkrowler',
        'MegaIndex',
        'MauiBot',
        'Sogou_spider',
        'YisouSpider',
    );
}

function foxblock_sync_blocked_bots_catalog()
{
    $catalog_version = '2026-03-18';
    $current_version = get_option('foxblock_bots_catalog_version', '');
    if ($current_version === $catalog_version) {
        return;
    }

    $known_bots = foxblock_get_known_bots();
    $blocked_bots = get_option('foxblock_blocked_bots', false);

    if ($blocked_bots === false) {
        update_option('foxblock_blocked_bots', $known_bots);
    } elseif (is_array($blocked_bots)) {
        update_option('foxblock_blocked_bots', array_values(array_unique(array_merge($blocked_bots, $known_bots))));
    }

    update_option('foxblock_bots_catalog_version', $catalog_version);
}

// 1. Setup Database Table on Activation
register_activation_hook(__FILE__, 'foxblock_activate_plugin');
function foxblock_activate_plugin()
{
    global $wpdb;
    $charset_collate = $wpdb->get_charset_collate();

    // Table for traffic logs
    $table_name = $wpdb->prefix . 'foxblock_log';
    $sql = "CREATE TABLE $table_name (
        id bigint(20) NOT NULL AUTO_INCREMENT,
        time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
        ip varchar(100) NOT NULL,
        url varchar(255) NOT NULL,
        user_agent text NOT NULL,
        is_bot boolean DEFAULT false NOT NULL,
        PRIMARY KEY  (id)
    ) $charset_collate;";

    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
    dbDelta($sql);

    // Table for malware scan results
    $scan_table = $wpdb->prefix . 'foxblock_malware_scans';
    $sql_scan = "CREATE TABLE $scan_table (
        id bigint(20) NOT NULL AUTO_INCREMENT,
        scan_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
        total_files int NOT NULL DEFAULT 0,
        total_findings int NOT NULL DEFAULT 0,
        is_baseline tinyint(1) NOT NULL DEFAULT 0,
        PRIMARY KEY  (id),
        KEY scan_time (scan_time),
        KEY is_baseline (is_baseline)
    ) $charset_collate;";
    dbDelta($sql_scan);

    // Table for malware findings
    $findings_table = $wpdb->prefix . 'foxblock_malware_findings';
    $sql_findings = "CREATE TABLE $findings_table (
        id bigint(20) NOT NULL AUTO_INCREMENT,
        scan_id bigint(20) NOT NULL,
        file_path varchar(500) NOT NULL,
        line_number int NOT NULL,
        code_snippet text NOT NULL,
        issue_type varchar(200) NOT NULL,
        risk_level varchar(20) NOT NULL,
        score int NOT NULL DEFAULT 0,
        PRIMARY KEY  (id),
        KEY scan_id (scan_id),
        KEY file_path (file_path(255))
    ) $charset_collate;";
    dbDelta($sql_findings);

    // Set default settings
    if (get_option('foxblock_blocked_bots') === false) {
        update_option('foxblock_blocked_bots', foxblock_get_known_bots());
    }
    if (get_option('foxblock_block_all_non_google') === false) {
        update_option('foxblock_block_all_non_google', 0);
    }
    update_option('foxblock_bots_catalog_version', '2026-03-18');
}

add_action('init', 'foxblock_sync_blocked_bots_catalog', 0);

// Check and create malware scan tables if not exist
add_action('admin_init', 'foxblock_check_malware_tables');
function foxblock_check_malware_tables()
{
    global $wpdb;
    $scan_table = $wpdb->prefix . 'foxblock_malware_scans';

    // Check if table exists
    $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$scan_table'") === $scan_table;

    if (!$table_exists) {
        // Tables don't exist, create them
        $charset_collate = $wpdb->get_charset_collate();

        $sql_scan = "CREATE TABLE $scan_table (
            id bigint(20) NOT NULL AUTO_INCREMENT,
            scan_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
            total_files int NOT NULL DEFAULT 0,
            total_findings int NOT NULL DEFAULT 0,
            is_baseline tinyint(1) NOT NULL DEFAULT 0,
            PRIMARY KEY  (id),
            KEY scan_time (scan_time),
            KEY is_baseline (is_baseline)
        ) $charset_collate;";

        $findings_table = $wpdb->prefix . 'foxblock_malware_findings';
        $sql_findings = "CREATE TABLE $findings_table (
            id bigint(20) NOT NULL AUTO_INCREMENT,
            scan_id bigint(20) NOT NULL,
            file_path varchar(500) NOT NULL,
            line_number int NOT NULL,
            code_snippet text NOT NULL,
            issue_type varchar(200) NOT NULL,
            risk_level varchar(20) NOT NULL,
            score int NOT NULL DEFAULT 0,
            PRIMARY KEY  (id),
            KEY scan_id (scan_id),
            KEY file_path (file_path(255))
        ) $charset_collate;";

        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql_scan);
        dbDelta($sql_findings);
    }
}

// 2. Core Protection & Tracking Logic
add_action('init', 'foxblock_track_and_protect', 1);
function foxblock_track_and_protect()
{
    // Skip tracking for the WordPress admin dashboard to save resources
    if (is_admin() && !wp_doing_ajax()) {
        return;
    }

    // Skip admin-ajax requests entirely because many plugins poll here frequently.
    if (wp_doing_ajax()) {
        return;
    }

    // Skip if it is a WP Cron request
    if (defined('DOING_CRON') && DOING_CRON) {
        return;
    }

    $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field($_SERVER['HTTP_USER_AGENT']) : '';
    $query_string = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
    // Use x-forwarded-for if behind proxy like Cloudflare
    $ip = isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0] : (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1'));
    $ip = sanitize_text_field(trim($ip));

    $url = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field($_SERVER['REQUEST_URI']) : '';

    $is_bot = 0;

    // Check if it's a known bot generally (for red display)
    if (preg_match('/bot|crawl|slurp|spider|mediapartners|agent/i', $user_agent)) {
        $is_bot = 1;
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // WHITELIST CHECK - MUST BE FIRST! Skip all blocking if IP is whitelisted
    // ═══════════════════════════════════════════════════════════════════════════
    $whitelist_ips = get_option('foxblock_whitelist_ips', array());
    if (!empty($whitelist_ips) && is_array($whitelist_ips)) {
        foreach ($whitelist_ips as $whitelist_ip) {
            if (strpos($whitelist_ip, '/') !== false) {
                // CIDR whitelist check
                list($subnet, $bits) = explode('/', $whitelist_ip);
                $ip_long = ip2long($ip);
                $subnet_long = ip2long($subnet);
                $mask = -1 << (32 - $bits);
                $subnet_long &= $mask;
                if (($ip_long & $mask) == $subnet_long) {
                    // IP is whitelisted - skip all blocking and just log
                    foxblock_insert_log($ip, $url, $user_agent, $is_bot);
                    return;
                }
            } else {
                // Exact IP whitelist match
                if ($ip === $whitelist_ip) {
                    // IP is whitelisted - skip all blocking and just log
                    foxblock_insert_log($ip, $url, $user_agent, $is_bot);
                    return;
                }
            }
        }
    }

    // 2.1 Check Custom IP Blocklist first (CIDR support)
    $blocked_ips = get_option('foxblock_blocked_ips', array());
    if (!empty($blocked_ips) && is_array($blocked_ips)) {
        foreach ($blocked_ips as $block_ip) {
            if (strpos($block_ip, '/') !== false) {
                // CIDR block check
                list($subnet, $bits) = explode('/', $block_ip);
                $ip_long = ip2long($ip);
                $subnet_long = ip2long($subnet);
                $mask = -1 << (32 - $bits);
                $subnet_long &= $mask;
                if (($ip_long & $mask) == $subnet_long) {
                    foxblock_block_request('Blocked IP Range');
                }
            } else {
                // Exact IP match
                if ($ip === $block_ip) {
                    foxblock_block_request('Blocked IP Address');
                }
            }
        }
    }

    // 2.1.1 Auto-block by Country
    $blocked_countries = get_option('foxblock_blocked_countries', array());
    if (!empty($blocked_countries) && is_array($blocked_countries)) {
        $country_code = foxblock_get_country_from_ip($ip);
        if ($country_code && in_array($country_code, $blocked_countries)) {
            // Auto-add to IP blocklist
            if (!in_array($ip, $blocked_ips)) {
                $blocked_ips[] = $ip;
                update_option('foxblock_blocked_ips', $blocked_ips);
            }
            foxblock_block_request('Blocked Country: ' . $country_code);
        }
    }

    // 2.1.2 Auto-block by Behavior
    $blocked_behaviors = get_option('foxblock_blocked_behaviors', array());
    if (!empty($blocked_behaviors) && is_array($blocked_behaviors)) {
        $should_block = false;
        $block_reason = '';

        // Rate limiting check
        if (in_array('rate_limit', $blocked_behaviors)) {
            $rate_key = 'foxblock_rate_' . md5($ip);
            $requests = get_transient($rate_key);
            if ($requests === false) {
                set_transient($rate_key, 1, 60);
            } else {
                $requests++;
                set_transient($rate_key, $requests, 60);
                if ($requests > 30) {
                    $should_block = true;
                    $block_reason = 'Rate Limit Exceeded (>30 req/min)';
                }
            }
        }

        // Empty User-Agent
        if (in_array('user_agent_empty', $blocked_behaviors) && empty($user_agent)) {
            $should_block = true;
            $block_reason = 'Empty User-Agent';
        }

        // Suspicious parameters (SQL/XSS)
        if (in_array('suspicious_params', $blocked_behaviors)) {
            if (preg_match('/(union|select|insert|update|delete|drop|script|javascript|onerror|onload)/i', $url)) {
                $should_block = true;
                $block_reason = 'Suspicious URL Parameters';
            }
        }

        // Scanner patterns
        if (in_array('scanner_pattern', $blocked_behaviors)) {
            if (preg_match('/(nmap|nikto|sqlmap|acunetix|nessus|openvas)/i', $user_agent)) {
                $should_block = true;
                $block_reason = 'Scanner Pattern Detected';
            }
        }

        // No referrer on POST
        if (in_array('no_referrer', $blocked_behaviors) && $_SERVER['REQUEST_METHOD'] === 'POST') {
            if (empty($_SERVER['HTTP_REFERER'])) {
                $should_block = true;
                $block_reason = 'POST without Referrer';
            }
        }

        if ($should_block) {
            // Auto-add to IP blocklist
            if (!in_array($ip, $blocked_ips)) {
                $blocked_ips[] = $ip;
                update_option('foxblock_blocked_ips', $blocked_ips);
            }
            foxblock_block_request($block_reason);
        }
    }

    // 2.1.3 Auto-block by Endpoint
    $blocked_endpoints = get_option('foxblock_blocked_endpoints', array());
    if (!empty($blocked_endpoints) && is_array($blocked_endpoints)) {
        $should_block = false;
        $block_reason = '';

        foreach ($blocked_endpoints as $endpoint) {
            switch ($endpoint) {
                case 'xmlrpc':
                    if (strpos($url, '/xmlrpc.php') !== false) {
                        $should_block = true;
                        $block_reason = 'XML-RPC Access Blocked';
                    }
                    break;
                case 'wp-json-users':
                    if (strpos($url, '/wp-json/wp/v2/users') !== false) {
                        $should_block = true;
                        $block_reason = 'User Enumeration Blocked';
                    }
                    break;
                case 'author-enum':
                    if (preg_match('/\?author=\d+/', $url)) {
                        $should_block = true;
                        $block_reason = 'Author Enumeration Blocked';
                    }
                    break;
                case 'env-file':
                    if (strpos($url, '/.env') !== false) {
                        $should_block = true;
                        $block_reason = 'Environment File Access Blocked';
                    }
                    break;
                case 'git-folder':
                    if (strpos($url, '/.git') !== false) {
                        $should_block = true;
                        $block_reason = 'Git Repository Access Blocked';
                    }
                    break;
                case 'svn-folder':
                    if (strpos($url, '/.svn') !== false) {
                        $should_block = true;
                        $block_reason = 'SVN Repository Access Blocked';
                    }
                    break;
                case 'phpmyadmin':
                    if (stripos($url, '/phpmyadmin') !== false) {
                        $should_block = true;
                        $block_reason = 'phpMyAdmin Access Blocked';
                    }
                    break;
                case 'adminer':
                    if (stripos($url, '/adminer') !== false) {
                        $should_block = true;
                        $block_reason = 'Adminer Access Blocked';
                    }
                    break;
                case 'config-php':
                    if (strpos($url, '/wp-config.php') !== false) {
                        $should_block = true;
                        $block_reason = 'Config File Access Blocked';
                    }
                    break;
                case 'readme':
                    if (strpos($url, '/readme.html') !== false) {
                        $should_block = true;
                        $block_reason = 'Readme Access Blocked';
                    }
                    break;
                case 'sql-backup':
                    if (preg_match('/\.sql$/i', $url)) {
                        $should_block = true;
                        $block_reason = 'SQL Backup Access Blocked';
                    }
                    break;
                case 'zip-backup':
                    if (preg_match('/\.zip$/i', $url)) {
                        $should_block = true;
                        $block_reason = 'Backup Archive Access Blocked';
                    }
                    break;
                case 'log-files':
                    if (preg_match('/\.log$/i', $url)) {
                        $should_block = true;
                        $block_reason = 'Log File Access Blocked';
                    }
                    break;
                case 'debug-log':
                    if (strpos($url, '/debug.log') !== false) {
                        $should_block = true;
                        $block_reason = 'Debug Log Access Blocked';
                    }
                    break;
                case 'shell-php':
                    if (preg_match('/shell.*\.php/i', $url)) {
                        $should_block = true;
                        $block_reason = 'Shell Script Access Blocked';
                    }
                    break;
                case 'sql-injection':
                    if (preg_match('/(union|select|insert|update|delete|drop|create|alter|exec|execute)/i', $url)) {
                        $should_block = true;
                        $block_reason = 'SQL Injection Pattern Blocked';
                    }
                    break;
                case 'xss-patterns':
                    if (preg_match('/(<script|javascript:|onerror=|onload=|<iframe)/i', urldecode($url))) {
                        $should_block = true;
                        $block_reason = 'XSS Pattern Blocked';
                    }
                    break;
                case 'path-traversal':
                    if (preg_match('/(\.\.\/|\.\.\\\\)/i', $url)) {
                        $should_block = true;
                        $block_reason = 'Path Traversal Blocked';
                    }
                    break;
                case 'null-byte':
                    if (strpos($url, '%00') !== false) {
                        $should_block = true;
                        $block_reason = 'Null Byte Injection Blocked';
                    }
                    break;
            }

            if ($should_block) {
                break;
            }
        }

        if ($should_block) {
            foxblock_block_request($block_reason);
        }
    }

    // 2.2 Check Query String for malicious intent (SQLi etc.)
    if (preg_match('/(union|select|sleep|benchmark|base64_)/i', $query_string) || preg_match('/(union|select|sleep|benchmark|base64_)/i', urldecode($url))) {
        foxblock_block_request('Malicious Query String Detected');
    }

    // 2.3 Check Bot Blocking Settings
    $blocked_bots = get_option('foxblock_blocked_bots', array());
    $block_all_non_google = get_option('foxblock_block_all_non_google', 0);

    if ($block_all_non_google && $is_bot) {
        // Allow Google bots
        if (!preg_match('/Googlebot/i', $user_agent)) {
            foxblock_block_request('Blocked non-Google Bot');
        }
    } else {
        // Specific block list
        if (!empty($blocked_bots) && is_array($blocked_bots)) {
            $bot_pattern = implode('|', array_map('preg_quote', $blocked_bots));
            if (!empty($bot_pattern) && preg_match("/($bot_pattern)/i", $user_agent)) {
                foxblock_block_request('Blocked by Bot List');
            }
        }
    }

    // 2.4 Log the request
    foxblock_insert_log($ip, $url, $user_agent, $is_bot);
}

// Helper function to get country from IP
function foxblock_get_country_from_ip($ip)
{
    return Foxblock_GeoIP::get_country_code($ip);
}

function foxblock_block_request($reason = '')
{
    // Send Telegram notification if enabled
    $telegram_enabled = get_option('foxblock_telegram_enabled', 0);
    if ($telegram_enabled) {
        $token = get_option('foxblock_telegram_token', '');
        $chatid = get_option('foxblock_telegram_chatid', '');

        if (!empty($token) && !empty($chatid)) {
            $ip = isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] :
                (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0] :
                    (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'Unknown'));
            $ip = sanitize_text_field(trim($ip));

            $url = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field($_SERVER['REQUEST_URI']) : '/';
            $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field($_SERVER['HTTP_USER_AGENT']) : 'Unknown';
            $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';

            $message = "🚨 <b>Foxblock Security Alert</b>\n\n";
            $message .= "❌ <b>IP đã bị chặn</b>\n";
            $message .= "━━━━━━━━━━━━━━━━━━━━\n";
            $message .= "🌐 <b>IP:</b> <code>" . esc_html($ip) . "</code>\n";
            $message .= "⚠️ <b>Lý do:</b> " . esc_html($reason) . "\n";
            $message .= "🔗 <b>URL:</b> <code>" . esc_html($url) . "</code>\n";
            $message .= "📱 <b>Method:</b> " . esc_html($method) . "\n";
            $message .= "🤖 <b>User-Agent:</b>\n<code>" . esc_html(substr($user_agent, 0, 100)) . "</code>\n";
            $message .= "━━━━━━━━━━━━━━━━━━━━\n";
            $message .= "📅 <b>Thời gian:</b> " . current_time('Y-m-d H:i:s') . "\n";
            $message .= "🏠 <b>Website:</b> " . get_bloginfo('name');

            // Send async (non-blocking)
            wp_remote_post('https://api.telegram.org/bot' . $token . '/sendMessage', array(
                'timeout' => 5,
                'blocking' => false, // Non-blocking to avoid delays
                'body' => array(
                    'chat_id' => $chatid,
                    'text' => $message,
                    'parse_mode' => 'HTML'
                )
            ));
        }
    }

    header('HTTP/1.1 403 Forbidden');
    echo '<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>403 Forbidden</title>
<style>
	:root {
		--foxhome-bg: #111;
		--foxhome-surface: #222;
		--foxhome-primary: var(--texta, #e50914);
		--foxhome-primary-hover: var(--texta, #b91c1c);
		--foxhome-text: #f8fafc;
		--foxhome-text-muted: var(--texta, #ff9797);
		--foxhome-border: var(--texta, #333);
		--foxhome-error: #ef4444;
	}
	* {
		box-sizing: border-box;
		margin: 0;
		padding: 0;
	}
	body {
		font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
		background-color: var(--foxhome-bg);
		color: var(--foxhome-text);
		display: flex;
		align-items: center;
		justify-content: center;
		height: 100vh;
		width: 100vw;
		overflow: hidden;
		padding: 20px;
		margin: 0;
	}
	#foxhome-matrix-bg {
		position: fixed;
		top: 0;
		left: 0;
		width: 100vw;
		height: 100vh;
		z-index: 0;
		opacity: 0.15;
		pointer-events: none;
	}

	#foxhome-login-container {
		background-color: var(--foxhome-surface);
		border: 1px solid var(--foxhome-border);
		border-radius: 16px;
		padding: 40px;
		width: 100%;
		max-width: 420px;
		box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.05);
		text-align: center;
		animation: foxhome-fade-in 0.6s cubic-bezier(0.16, 1, 0.3, 1);
		position: relative;
		z-index: 10;
	}
	@keyframes foxhome-fade-in {
		from { opacity: 0; transform: translateY(-40px); }
		to { opacity: 1; transform: translateY(0); }
	}
	#foxhome-icon-wrap {
		background-color: rgba(239, 68, 68, 0.15); /* Red tinted background */
		width: 72px;
		height: 72px;
		border-radius: 50%;
		display: flex;
		align-items: center;
		justify-content: center;
		margin: 0 auto 24px;
		color: var(--foxhome-error); /* Red icon color */
		box-shadow: 0 0 20px rgba(239, 68, 68, 0.3);
		animation: foxhome-sos-pulse 1.2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
	}
	@keyframes foxhome-sos-pulse {
		0%, 100% {
			opacity: 1;
			transform: scale(1);
			box-shadow: 0 0 20px rgba(239, 68, 68, 0.3);
		}
		50% {
			opacity: 0.5;
			transform: scale(0.95);
			box-shadow: 0 0 40px rgba(239, 68, 68, 0.6);
		}
	}

	#foxhome-title {
		font-size: 28px;
		font-weight: 700;
		margin-bottom: 12px;
		letter-spacing: -0.025em;
	}

	#foxhome-desc {
		color: var(--foxhome-text-muted);
		font-size: 15px;
		line-height: 1.5;
	}
	.foxhome-input-icon {
		position: absolute;
		left: 16px;
		top: 50%;
		transform: translateY(-50%);
		color: var(--foxhome-text-muted);
		width: 20px;
		height: 20px;
	}
	/* Background decoration */
	.foxhome-bg-blob {
		position: absolute;
		filter: blur(80px);
		z-index: 1;
		opacity: 0.5;
	}
	.foxhome-bg-blob.blob-1 {
		top: -10%;
		left: -10%;
		width: 300px;
		height: 300px;
		background: color-mix(in srgb, var(--foxhome-primary) 20%, transparent);
		border-radius: 50%;
	}
	.foxhome-bg-blob.blob-2 {
		bottom: -10%;
		right: -10%;
		width: 400px;
		height: 400px;
		background: color-mix(in srgb, var(--foxhome-primary) 20%, transparent);
		border-radius: 50%;
	}
</style>
</head>
<body>
<div class="foxhome-bg-blob blob-1"></div>
<div class="foxhome-bg-blob blob-2"></div>
<div id="foxhome-login-container">
	<div id="foxhome-icon-wrap">
		<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="foxhome-icon">
			<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
			<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
		</svg>
	</div>
	<h1 id="foxhome-title">403 Forbidden</h1>
	<div id="foxhome-desc">Access to the website is not available right now</div>    
</div>
</body>
</html>';
    exit;
}

function foxblock_insert_log($ip, $url, $user_agent, $is_bot)
{
    global $wpdb;
    $table_name = $wpdb->prefix . 'foxblock_log';

    $wpdb->insert(
        $table_name,
        array(
            'time' => current_time('mysql'),
            'ip' => substr($ip, 0, 100),
            'url' => substr($url, 0, 255),
            'user_agent' => substr($user_agent, 0, 500),
            'is_bot' => $is_bot
        ),
        array('%s', '%s', '%s', '%s', '%d')
    );

    // Limit to 300 rows optimally without locking large parts of DB.
    // If the difference is big, delete old ones
    $count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
    if ($count > 300) {
        $excess = intval($count - 300);
        $wpdb->query("DELETE FROM $table_name ORDER BY id ASC LIMIT " . $excess);
    }
}

// 3. Include Admin Files
if (is_admin() || (function_exists('wp_doing_cron') && wp_doing_cron()) || (defined('DOING_CRON') && DOING_CRON)) {
    require_once FOXBLOCK_PLUGIN_DIR . 'admin/admin.php';
}

// 4. Include GeoIP Handler
require_once FOXBLOCK_PLUGIN_DIR . 'includes/GeoIP/GeoIP.php';

// 5. Include Custom Login Engine
require_once FOXBLOCK_PLUGIN_DIR . 'includes/CustomLogin.php';
new Foxblock_CustomLogin();

// ─────────────────────────────────────────────────────────────────────────────
// 6. Apply Disabled Security Features
// ─────────────────────────────────────────────────────────────────────────────
add_action('init', 'foxblock_apply_disabled_features', 5);
function foxblock_apply_disabled_features()
{
    $disabled = get_option('foxblock_disabled_features', array());
    if (empty($disabled) || !is_array($disabled)) {
        return;
    }

    // --- Disable REST API for non-logged-in users ---
    if (in_array('disable_rest_api', $disabled)) {
        add_filter('rest_authentication_errors', function ($result) {
            if (!is_user_logged_in()) {
                return new WP_Error('rest_not_logged_in', 'REST API chỉ khả dụng cho người dùng đã đăng nhập.', array('status' => 401));
            }
            return $result;
        });
        // Also remove REST API links from HTML head
        remove_action('wp_head', 'rest_output_link_wp_head', 10);
        remove_action('template_redirect', 'rest_output_link_header', 11);
        remove_action('xmlrpc_rsd_apis', 'rest_output_rsd');
    }

    // --- Disable XML-RPC completely ---
    if (in_array('disable_xmlrpc', $disabled)) {
        add_filter('xmlrpc_enabled', '__return_false');
        // Block xmlrpc.php requests at WP level
        add_action('xmlrpc_call', function () {
            wp_die(__('XML-RPC services are disabled on this site.', 'foxblock'), 'XML-RPC Disabled', array('response' => 403));
        });
    }

    // --- Disable WP Embed / oEmbed ---
    if (in_array('disable_wp_embed', $disabled)) {
        // Remove oEmbed discovery links from <head>
        remove_action('wp_head', 'wp_oembed_add_discovery_links');
        remove_action('wp_head', 'wp_oembed_add_host_js');
        // Disable oEmbed REST endpoint
        add_filter('rest_endpoints', function ($endpoints) {
            if (isset($endpoints['/oembed/1.0/embed'])) {
                unset($endpoints['/oembed/1.0/embed']);
            }
            if (isset($endpoints['/oembed/1.0/proxy'])) {
                unset($endpoints['/oembed/1.0/proxy']);
            }
            return $endpoints;
        });
        // Prevent WP from embedding into external sites
        remove_action('init', 'wp_oembed_register_route');
        add_filter('embed_oembed_discover', '__return_false');
    }

    // --- Disable RSS / Atom Feeds ---
    if (in_array('disable_rss_feed', $disabled)) {
        add_action('do_feed', 'foxblock_disable_feed', 1);
        add_action('do_feed_rdf', 'foxblock_disable_feed', 1);
        add_action('do_feed_rss', 'foxblock_disable_feed', 1);
        add_action('do_feed_rss2', 'foxblock_disable_feed', 1);
        add_action('do_feed_atom', 'foxblock_disable_feed', 1);
        add_action('do_feed_rss2_comments', 'foxblock_disable_feed', 1);
        add_action('do_feed_atom_comments', 'foxblock_disable_feed', 1);
        remove_action('wp_head', 'feed_links', 2);
        remove_action('wp_head', 'feed_links_extra', 3);
    }

    // --- Disable Author Enumeration (?author=N) ---
    if (in_array('disable_author_enum', $disabled)) {
        add_action('template_redirect', function () {
            if (!is_admin() && isset($_REQUEST['author']) && !is_user_logged_in()) {
                wp_redirect(home_url('/'), 301);
                exit;
            }
        });
    }

    // --- Hide WordPress Version ---
    if (in_array('disable_wp_version', $disabled)) {
        // Remove generator meta tag
        remove_action('wp_head', 'wp_generator');
        // Strip version from scripts and styles
        add_filter('style_loader_src', 'foxblock_remove_wp_version_from_src', 9999);
        add_filter('script_loader_src', 'foxblock_remove_wp_version_from_src', 9999);
        // Remove version from RSS
        add_filter('the_generator', '__return_empty_string');
    }

    // --- Disable File Editor in Admin ---
    if (in_array('disable_file_edit', $disabled)) {
        if (!defined('DISALLOW_FILE_EDIT')) {
            define('DISALLOW_FILE_EDIT', true);
        }
    }

    // --- Disable Pingback & Trackback ---
    if (in_array('disable_pingback', $disabled)) {
        add_filter('wp_headers', function ($headers) {
            unset($headers['X-Pingback']);
            return $headers;
        });
        add_filter('bloginfo_url', function ($output, $property) {
            return $property === 'pingback_url' ? '' : $output;
        }, 11, 2);
        add_action('pre_ping', function (&$links) {
            $links = array();
        });
        add_filter('pings_open', '__return_false', 20, 2);
    }

    // --- Disable Self-Pingback ---
    if (in_array('disable_self_pingback', $disabled)) {
        add_action('pre_ping', function (&$links) {
            $home = get_option('home');
            foreach ($links as $key => $link) {
                if (strpos($link, $home) === 0) {
                    unset($links[$key]);
                }
            }
        });
    }

    // --- Block readme.html and license.txt ---
    if (in_array('disable_readme', $disabled)) {
        add_action('template_redirect', function () {
            $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
            if (preg_match('#/(readme\.html|license\.txt|wp-config-sample\.php)#i', $uri)) {
                wp_die('Access Denied', 'Forbidden', array('response' => 403));
            }
        });
    }

    // --- Disable Script Concatenation in Admin ---
    if (in_array('disable_script_concat', $disabled)) {
        add_action('admin_init', function () {
            global $concatenate_scripts;
            $concatenate_scripts = false;
        });
    }

    // --- Disable Application Passwords ---
    if (in_array('disable_application_passwords', $disabled)) {
        add_filter('wp_is_application_passwords_available', '__return_false');
    }
}

// Helper: block RSS feed with 403
function foxblock_disable_feed()
{
    wp_die(
        '<p>RSS feed bị vô hiệu hoá trên trang này. <a href="' . esc_url(home_url('/')) . '">Về trang chủ</a></p>',
        'Feed Disabled',
        array('response' => 403)
    );
}

// Helper: remove ?ver= from asset URLs
function foxblock_remove_wp_version_from_src($src)
{
    if ($src && strpos($src, '?') !== false) {
        $src = remove_query_arg('ver', $src);
    }
    return $src;
}

// ─────────────────────────────────────────────────────────────────────────────
// 7. Media Filter — Upload Security Scanner
// ─────────────────────────────────────────────────────────────────────────────
add_filter('wp_handle_upload_prefilter', 'foxblock_media_filter_prefilter');
function foxblock_media_filter_prefilter($file)
{
    $opts = get_option('foxblock_media_filter', array());
    if (empty($opts['enabled'])) {
        return $file; // Feature disabled — pass through
    }

    $tmp_path = isset($file['tmp_name']) ? $file['tmp_name'] : '';
    $filename = isset($file['name']) ? strtolower($file['name']) : '';

    // ── A: Always-blocked extensions (regardless of sub-settings) ────────────
    $always_blocked = array(
        'php',
        'phtml',
        'phar',
        'php3',
        'php4',
        'php5',
        'php7',
        'asp',
        'aspx',
        'cgi',
        'pl',
        'exe',
        'sh',
        'shtml',
        'htaccess',
        'htpasswd',
    );
    // Get primary extension
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    if (in_array($ext, $always_blocked, true)) {
        $file['error'] = 'Foxblock: Loại file này bị chặn vì lý do bảo mật (.' . esc_attr($ext) . ').';
        return $file;
    }
    // Block double-extension (e.g. image.jpg.php)
    if (preg_match('/\.(php\d?|phtml|phar|asp|cgi|sh)\b/i', $filename)) {
        $file['error'] = 'Foxblock: File có double-extension nguy hiểm bị chặn.';
        return $file;
    }

    // ── B: Fake image / MIME mismatch ────────────────────────────────────────
    if (!empty($opts['block_fake_image'])) {
        $image_extensions = array('jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'tiff', 'ico');

        if (in_array($ext, $image_extensions, true) && file_exists($tmp_path)) {
            // Attempt MIME detection
            $real_mime = '';
            if (function_exists('finfo_open')) {
                $finfo = finfo_open(FILEINFO_MIME_TYPE);
                $real_mime = finfo_file($finfo, $tmp_path);
                finfo_close($finfo);
            } elseif (function_exists('mime_content_type')) {
                $real_mime = mime_content_type($tmp_path);
            }

            if ($real_mime) {
                $safe_image_mimes = array(
                    'image/jpeg',
                    'image/png',
                    'image/gif',
                    'image/webp',
                    'image/bmp',
                    'image/tiff',
                    'image/x-icon',
                    'image/vnd.microsoft.icon',
                );
                if (!in_array($real_mime, $safe_image_mimes, true)) {
                    $file['error'] = 'Foxblock: File giả ảnh bị chặn. Extension: .' . esc_attr($ext) . ' nhưng MIME thật là: ' . esc_attr($real_mime) . '.';
                    return $file;
                }
            }
        }
    }

    // ── C: SVG malware scan ───────────────────────────────────────────────────
    if (!empty($opts['block_svg_malware']) && $ext === 'svg' && file_exists($tmp_path)) {
        $svg_content = @file_get_contents($tmp_path, false, null, 0, 524288); // Read up to 512KB
        if ($svg_content !== false) {
            $svg_dangerous_patterns = array(
                '/<script[\s>]/i',
                '/on\w+\s*=/i',        // onload=, onerror=, onclick=, etc.
                '/javascript\s*:/i',
                '/\beval\s*\(/i',
                '/\bdata\s*:\s*text\/html/i',
                '/<iframe[\s>]/i',
                '/<object[\s>]/i',
                '/<embed[\s>]/i',
                '/href\s*=\s*["\']javascript/i',
                '/\bxlink:href\s*=\s*["\']javascript/i',
                '/expression\s*\(/i',
            );
            foreach ($svg_dangerous_patterns as $pattern) {
                if (preg_match($pattern, $svg_content)) {
                    $file['error'] = 'Foxblock: File SVG chứa mã độc/script nguy hiểm bị chặn.';
                    return $file;
                }
            }
        }
    }

    // ── D: Archive payload scan (ZIP/RAR) ────────────────────────────────────
    if (!empty($opts['block_malware_archive']) && in_array($ext, array('zip', 'rar', '7z', 'tar', 'gz'), true) && file_exists($tmp_path)) {
        // Read the first 500KB as text to look for embedded patterns
        $archive_content = @file_get_contents($tmp_path, false, null, 0, 524288);
        if ($archive_content !== false) {
            $archive_dangerous = array(
                '/<\?php/i',
                '/eval\s*\(/i',
                '/base64_decode\s*\(/i',
                '/shell_exec\s*\(/i',
                '/system\s*\(/i',
                '/passthru\s*\(/i',
                '/exec\s*\(/i',
                '/preg_replace.*\/e/i',
                '/\$_POST\s*\[/i',
                '/\$_GET\s*\[/i',
                '/\$_REQUEST\s*\[/i',
            );
            foreach ($archive_dangerous as $pattern) {
                if (preg_match($pattern, $archive_content)) {
                    $file['error'] = 'Foxblock: File nén chứa code nguy hiểm bị chặn.';
                    return $file;
                }
            }
        }
    }

    // ── E: Code payload scan (all files, max 2MB) ────────────────────────────
    if (!empty($opts['block_code_payload']) && file_exists($tmp_path)) {
        $max_size = 2097152; // 2MB
        if (filesize($tmp_path) <= $max_size) {
            $content = @file_get_contents($tmp_path);
            if ($content !== false) {
                $code_patterns = array(
                    array('/<\?php/i', '<?php tag'),
                    array('/eval\s*\(/i', 'eval()'),
                    array('/base64_decode\s*\(/i', 'base64_decode()'),
                    array('/shell_exec\s*\(/i', 'shell_exec()'),
                    array('/system\s*\(\s*[\$_]/i', 'system() with variable'),
                    array('/passthru\s*\(\s*[\$_]/i', 'passthru() with variable'),
                    array('/exec\s*\(\s*[\$_]/i', 'exec() with variable'),
                    array('/<script[\s>]/i', '<script>'),
                    array('/javascript\s*:\s*void/i', 'javascript:void'),
                );
                // Only scan non-binary files (check for printable characters ratio)
                $printable = preg_match_all('/[\x20-\x7e]/', substr($content, 0, 1024), $m);
                $is_text_like = ($printable / max(1, min(1024, strlen($content)))) > 0.7;

                if ($is_text_like) {
                    foreach ($code_patterns as [$pattern, $label]) {
                        if (preg_match($pattern, $content)) {
                            $file['error'] = 'Foxblock: File chứa code nguy hiểm (' . esc_attr($label) . ') bị chặn.';
                            return $file;
                        }
                    }
                }
            }
        }
    }

    return $file; // All checks passed
}

// ─────────────────────────────────────────────────────────────────────────────
// Two-Factor Authentication (2FA) Module
// ─────────────────────────────────────────────────────────────────────────────
require_once FOXBLOCK_PLUGIN_DIR . 'includes/TwoFactorAuth.php';
add_action('plugins_loaded', function() {
    new Foxblock_TwoFactorAuth();
});
