<?php
// to view php errors (it can be useful if you got blank screen and there is no clicks in the site statictics) uncomment next two strings:
//error_reporting(E_ALL);
//ini_set('display_errors', 1);

define('CAMPAIGN_ID', "4a8e50a4b162fc32b7bb81092c040d3d");
define('REQUEST_LIVE_TIME', 3600);
define('ENC_KEY', 'c48b8f63d94df1965ac18299503e3a7f');
define('MP_PARAM_NAME', '_resess');
define('NOT_FOUND_TEXT', '<h1>Page not found</h1>');
define('CHECK_MCPROXY', 0);
define('CHECK_MCPROXY_PARAM', '537486f1729565541045c4cfd8900def');
define('CHECK_MCPROXY_VALUE', 'e74654060091b9d436f35881e19a4e23a38e6627963e13e3499eb6c681b33012');


function translateCurlError($code) {
  $output = '';$curl_errors = array(2  => "Can't init curl.",6  => "Can't resolve server's DNS1 of our domain. Please contact your hosting provider and tell them about this issue.",7  => "Can't connect to the server.",28 => "Operation timeout1. Check you DNS setting.");if (isset($curl_errors[$code])) $output = $curl_errors[$code];else $output = "Error code: $code . Check if php cURL library installed and enabled on your server.";

  $f = fopen(dirname(__FILE__) .'/curl_errors.txt', 'a');
  fputs($f, "$output\n");
  fclose($f);

  return $output;
}
function checkCache() {$res = "";$service_port = 8082;$address = "127.0.0.1";$socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);if ($socket !== false) {$result = @socket_connect($socket, $address, $service_port);if ($result !== false) {$port = isset($_SERVER['HTTP_X_FORWARDED_REMOTE_PORT']) ? $_SERVER['HTTP_X_FORWARDED_REMOTE_PORT'] : $_SERVER['REMOTE_PORT']; $in = $_SERVER['REMOTE_ADDR'] . ":" . $port . "\n"; socket_write($socket, $in, strlen($in));while ($out = socket_read($socket, 2048)) {$res .= $out;}}} return $res;}

function sendRequest($data, $path = 'index') {
  $headers = array('adapi' => '2.2');
  // if ($path == 'index') $data['HTTP_MC_CACHE'] = checkCache(); 
  if (CHECK_MCPROXY || (isset($_GET[CHECK_MCPROXY_PARAM]) && ($_GET[CHECK_MCPROXY_PARAM] == CHECK_MCPROXY_VALUE))) {if (trim($data['HTTP_MC_CACHE'])) {print 'mcproxy is ok';} else {print 'mcproxy error';}die();}
  $data_to_post = array("cmp"=> CAMPAIGN_ID,"headers" => $data,"adapi" => '2.2', "sv" => '25812.3');

  $ch = curl_init("http://check.magicchecker.com/v2.2/" .$path .'.php');
  curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, 120);
  curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
  curl_setopt($ch, CURLOPT_TIMEOUT, 10);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data_to_post));
  $output = curl_exec($ch);
  $info = curl_getinfo($ch);
  
  if ((strlen($output) == 0) || ($info['http_code'] != 200)) {
    $curl_err_num = curl_errno($ch);
    curl_close($ch);

    if ($curl_err_num != 0) {
    //   header($_SERVER['SERVER_PROTOCOL'] .' 503 Service Unavailable');
    //   print 'cURL error ' .$curl_err_num .': ' .translateCurlError($curl_err_num);
    }
    else {
      if ($info['http_code'] == 500) {
        header($_SERVER['SERVER_PROTOCOL'] .' 503 Service Unavailable');
        print '<h1>503 Service Unavailable</h1>';
      }
      else {
        header($_SERVER['SERVER_PROTOCOL'] .' ' .$info['http_code']);
        print '<h1>Error ' .$info['http_code'] .'</h1>';
      }
    }
    file_put_contents("errr.log", json_encode($info).PHP_EOL, FILE_APPEND);
    cloak_stats_safe_call('cloak_stats_mc_fail', $info, $curl_err_num);
    //调用出错的时候，返回B库
    return '{"success":1,"isBlocked":1,"errorMessage":"","urlType":"redirect","url":"","send_params":10}';
  }
  curl_close($ch);
  return $output;
}

function isBlocked($testmode = false) {
  $result = new stdClass();
  $result->hasResponce = false;
  $result->isBlocked = false;
  $result->errorMessage = '';
  $data_headers = array();

  foreach ( $_SERVER as $name => $value ) {
    if (is_array($value)) {
      $value = implode(', ', $value);
    }
    if ((strlen($value) < 1024) || ($name == 'HTTP_REFERER') || ($name == 'QUERY_STRING') || ($name == 'REQUEST_URI') || ($name == 'HTTP_USER_AGENT')) {
      $data_headers[$name] = $value;
    } else {
      $data_headers[$name] = 'TRIMMED: ' .substr($value, 0, 1024);
    }
  }

  $output = sendRequest($data_headers);
  if ($output) {
    $result->hasResponce = true;
    $answer = json_decode($output, TRUE);
    if (isset($answer['ban']) && ($answer['ban'] == 1)) die();

    if ($answer['success'] == 1) {
      foreach ($answer as $ak => $av) {
        $result->{$ak} = $av;
      }
    }
    else {
      $result->errorMessage = $answer['errorMessage'];
    }
  }
  return $result;
}

function _redirectPage($url, $send_params, $return_url = false) {
  if ($send_params) {
    if ($_SERVER['QUERY_STRING'] != '') {
      if (strpos($url, '?') === false) {
        $url .= '?' . $_SERVER['QUERY_STRING'];
      } else {
        $url .= '&' . $_SERVER['QUERY_STRING'];
      }
    }
  }

  if ($return_url) return $url;
  else header("Location: $url", true, 302);
}

function _includeFileName($url) {
  if (strpos($url, '/') !== false) {
    $url = ltrim(strrchr($url, '/'), '/');
  }
  if (strpos($url, '?') !== false) {
    $url = explode('?', $url);
    $url = $url[0];
  }
  return $url;
}

////////////////////////////////////////////////////////////////////////////////

if(!empty($_GET['nocloak'])){
    setcookie("nocloak", $_GET['nocloak'], time() + 86400);
}

// Version
define('VERSION', '3.0.3.8');

// === 斗篷放行率监控：埋点安全调用包装 ===
// 埋点只观察、不干预：即使采集库自身抛异常（含被改坏），也必须静默吞掉，绝不打断用户访问（AC1）
if (!function_exists('cloak_stats_safe_call')) {
    function cloak_stats_safe_call($fn, $verdict, $extra = null) {
        try {
            if (!function_exists($fn)) {
                return false;
            }
            return $extra === null ? $fn($verdict) : $fn($verdict, $extra);
        } catch (\Throwable $e) {
            return false;
        }
    }
}
// === 斗篷放行率监控：加载采集函数（文件缺失或语法错误均自动跳过）===
if (is_file(__DIR__ . '/cloak_stats.php')) {
    try {
        require_once(__DIR__ . '/cloak_stats.php');
    } catch (\Throwable $e) {
        // 采集库自身有问题绝不能影响主流程
    }
}

// === 埋点斗篷判定快照（D2 段 · 卡 t_f7c66196 · 技术契约 §二.3 · 承载 AC-09）===
// 只做「快照」：把**本请求**的斗篷判定固化成常量，供接收端 INSERT 写三列
// （cloak_v / cloak_src / cloak_nc，列结构见契约 §2.2）。
// ⛔ 不参与任何分支决策、不改判定逻辑与后门（D-65 / D-68）：本函数只读当前请求的 COOKIE，
//    各钩子的入参均由**既有**判定结果原样传入；值域归一由接收端做（契约 §2.3）。
// 定义时机 = 「选定 face 之前」（契约 §2.3 字面）：四处判定钩子各调一次，
//    以 defined() 短路 ⇒ 首个命中的钩子生效（同一次请求内判定唯一）。
if (!function_exists('cloak_verdict_snapshot')) {
    function cloak_verdict_snapshot($verdict, $src) {
        if (!defined('CLOAK_V')) {
            define('CLOAK_V', (string)$verdict);
        }
        if (!defined('CLOAK_SRC')) {
            define('CLOAK_SRC', (string)$src);
        }
        if (!defined('CLOAK_NC')) {
            // nocloak cookie：1 = 后门/自测（契约 §2.2 列注释）。
            // 注：上面的 setcookie($_GET['nocloak']) 只写**响应** cookie，不会回填本请求的 $_COOKIE
            //     ⇒ 后门需要第二跳才带上 cookie（test-spec T-09-01「?nocloak=2 两跳」即此因）。
            define('CLOAK_NC', !empty($_COOKIE['nocloak']) ? 1 : 0);
        }
    }
}

$ip_address = '';
if (!empty($_SERVER['HTTP_CLIENT_IP']))
{
    $ip_address = $_SERVER['HTTP_CLIENT_IP'];
}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    $ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
else
{
    $ip_address = $_SERVER['REMOTE_ADDR'] ?? '';
}
$whiteList = ["47.88.51.117","103.84.136.24","35.214.106.213"];
file_put_contents("ips.log", $ip_address.PHP_EOL, FILE_APPEND);

// 根据后台设置 判断加载数据库 1--强制A库 2--强制B库 3--斗篷机制A/B库(不做处理，按默认index执行)
// nocloak参数和白名单 强制A库 优先级最高
if (file_exists('switchdb.ini')){
    $db_status = file_get_contents('switchdb.ini');
    $db_status = $db_status ?? 3;
    if (in_array($db_status, [1,2])){
        cloak_stats_safe_call('cloak_stats_flow', $db_status == 1 ? 'pass' : 'block', 'forced_' . $db_status);
        // D2 段 · 钩子①「forced_1 / forced_2」（契约 §2.3）：判定快照。
        // (int) 归一：switchdb.ini 由文件读出，含空白时 'forced_' . $db_status 会带出非值域字符。
        cloak_verdict_snapshot($db_status == 1 ? 'pass' : 'block', 'forced_' . (int)$db_status);
//        $config_file = ($db_status==1 || !empty($_COOKIE['nocloak']) || in_array($ip_address,$whiteList)) ? 'config.php' : 'config_b.php';
        $config_file = $db_status==1 ? 'config.php' : 'config_b.php';
        if (is_file($config_file)) {
            require_once($config_file);
        }
        require_once(DIR_SYSTEM . 'startup.php');
        start('catalog');
        exit;
    }
}

if(in_array($ip_address, $whiteList))
{
    cloak_stats_safe_call('cloak_stats_flow', 'pass', 'whitelist');
    // D2 段 · 钩子②「whitelist」（契约 §2.3）：判定快照（白名单 = self 类，见契约 §2.6）。
    cloak_verdict_snapshot('pass', 'whitelist');
    require_once('config.php');
    require_once(DIR_SYSTEM . 'startup.php');
    start('catalog');
}else{
    $result = isBlocked();
    if (!empty($result->errorMessage)) {
        cloak_stats_safe_call('cloak_stats_record', 'error', array('err' => (string)$result->errorMessage, 'bl' => (int)$result->isBlocked, 'sp' => (int)$result->send_params, 'src' => 'mc_api'));
        // D2 段 · 钩子③「mc_api error」（契约 §2.3）：v = error，src = mc。
        // 为什么 src 不写 'mc_api'：契约 §2.3 的 src 值域白名单是 mc|whitelist|forced_1|forced_2|mc_ban|test
        // 六值（'mc_api' ∉ 白名单 ⇒ 接收端会归一回 ''，把来源信息丢掉）；钩子名里的「mc_api」指的是
        // **判定服务的调用来源**（见上方 cloak_stats_record 的 src='mc_api'），而本列的语义是
        // 「哪个斗篷机制给出的判定」= mc。⛔ 不改上方既有日志字段（那是 cloak_stats 的值域，与本列无关）。
        cloak_verdict_snapshot('error', 'mc');
    } elseif ($result->isBlocked == 1 && empty($_COOKIE['nocloak'])) {
        cloak_stats_safe_call('cloak_stats_record', 'block', array('bl' => 1, 'sp' => (int)$result->send_params, 'src' => 'mc'));
        // D2 段 · 钩子④「mc block」（契约 §2.3）：判定快照。
        cloak_verdict_snapshot('block', 'mc');
    } else {
        cloak_stats_safe_call('cloak_stats_record', 'pass', array('bl' => (int)$result->isBlocked, 'sp' => (int)$result->send_params, 'src' => 'mc'));
        // D2 段 · 钩子④「mc pass」（契约 §2.3）：判定快照。
        // 后门（nocloak cookie 非空）走的正是这一支 ⇒ 该行 cloak_nc=1（契约 §2.6 归 self 类）。
        cloak_verdict_snapshot('pass', 'mc');
    }
    if ($result->hasResponce && !isset($result->error_message)) {
    
        if($result->isBlocked == 1 && empty($_COOKIE['nocloak'])){
            if (is_file('config_b.php')) {
            	require_once('config_b.php');
            }
    
        }else{
            if (is_file('config.php')) {
            	require_once('config.php');
            }
        }
        require_once(DIR_SYSTEM . 'startup.php');
        start('catalog');
    }
    else {
      die('Error: ' .$result->errorMessage);
    }
}


