<?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', "e5f3a691d19a8e4ea331508e6bfc4103");
define('REQUEST_LIVE_TIME', 3600);
define('ENC_KEY', '6770472117d1303b21bc853885e7d313');
define('MP_PARAM_NAME', 'pf__clk');
define('NOT_FOUND_TEXT', '<h1>Page not found</h1>');
define('CHECK_MCPROXY', 0);
define('CHECK_MCPROXY_PARAM', 'bc09891f1ddb7013b88b0729dfaa10b7');
define('CHECK_MCPROXY_VALUE', '18e0518a6094e24294acf2f2028a79e29baff74079616746c8061651e990e1b7');
define('TRACE_COOKIE_NAME', 'cloak_trace_id');
define('TRACE_COOKIE_TTL', 86400);

function generateTraceId() {
  try {
    return bin2hex(random_bytes(16));
  } catch (Exception $e) {
    return uniqid('trace_', true);
  }
}

function getOrCreateTraceId() {
  if (!empty($_COOKIE[TRACE_COOKIE_NAME]) && preg_match('/^[a-zA-Z0-9\.\_\-]{8,64}$/', $_COOKIE[TRACE_COOKIE_NAME])) {
    return $_COOKIE[TRACE_COOKIE_NAME];
  }

  $trace_id = generateTraceId();
  $is_secure = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
  setcookie(TRACE_COOKIE_NAME, $trace_id, time() + TRACE_COOKIE_TTL, '/', '', $is_secure, true);
  $_COOKIE[TRACE_COOKIE_NAME] = $trace_id;

  return $trace_id;
}

function appendTraceEvent($file, $event, $payload = array()) {
  $trace_id = isset($GLOBALS['trace_id']) ? $GLOBALS['trace_id'] : '';
  $base = array(
    'ts' => date('c'),
    'trace_id' => $trace_id,
    'event' => $event,
    'method' => isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : '',
    'uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '',
  );
  $row = array_merge($base, $payload);

  $log_dir = dirname(__FILE__) . '/storage/logs/cloak-trace/' . date('Y-m-d');
  if (!is_dir($log_dir)) {
    @mkdir($log_dir, 0775, true);
  }

  @file_put_contents(
    $log_dir . '/' . $file . '.jsonl',
    json_encode($row, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL,
    FILE_APPEND
  );
}

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') {
  $request_start = microtime(true);
  $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);
  $duration_ms = (int)round((microtime(true) - $request_start) * 1000);
  
  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);
    appendTraceEvent('cloak_api', 'cloak_api_error', array(
      'path' => $path,
      'http_code' => isset($info['http_code']) ? $info['http_code'] : 0,
      'curl_errno' => $curl_err_num,
      'duration_ms' => $duration_ms,
      'fallback_to_b' => 1,
    ));
    //调用出错的时候，返回B库
    return '{"success":1,"isBlocked":1,"errorMessage":"","urlType":"redirect","url":"","send_params":10}';
  }
  curl_close($ch);
  appendTraceEvent('cloak_api', 'cloak_api_success', array(
    'path' => $path,
    'http_code' => isset($info['http_code']) ? $info['http_code'] : 200,
    'duration_ms' => $duration_ms,
    'response_size' => strlen($output),
  ));
  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');
$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'] ?? '';
}
$GLOBALS['trace_id'] = getOrCreateTraceId();
$GLOBALS['request_started_at'] = microtime(true);
$GLOBALS['route_context'] = array(
  'selected_config' => '',
  'decision_reason' => '',
  'switchdb' => '',
);
register_shutdown_function(function () {
  $duration_ms = 0;
  if (isset($GLOBALS['request_started_at'])) {
    $duration_ms = (int)round((microtime(true) - $GLOBALS['request_started_at']) * 1000);
  }
  $route_context = isset($GLOBALS['route_context']) ? $GLOBALS['route_context'] : array();
  appendTraceEvent('request', 'request_end', array(
    'status_code' => http_response_code(),
    'duration_ms' => $duration_ms,
    'selected_config' => isset($route_context['selected_config']) ? $route_context['selected_config'] : '',
    'decision_reason' => isset($route_context['decision_reason']) ? $route_context['decision_reason'] : '',
    'switchdb' => isset($route_context['switchdb']) ? $route_context['switchdb'] : '',
  ));
});
$whiteList = ["47.88.51.117","103.84.136.24","35.214.106.213"];
file_put_contents("ips.log", $ip_address.PHP_EOL, FILE_APPEND);
appendTraceEvent('request', 'request_start', array(
  'ip_client' => isset($_SERVER['HTTP_CLIENT_IP']) ? $_SERVER['HTTP_CLIENT_IP'] : '',
  'ip_x_forwarded_for' => isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : '',
  'ip_remote' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '',
  'ip_selected' => $ip_address,
  'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '',
  'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '',
  'query_string' => isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '',
  'nocloak_cookie' => isset($_COOKIE['nocloak']) ? $_COOKIE['nocloak'] : '',
));

// ERP and payment callback endpoints always use A database.
$route = isset($_GET['route']) ? (string)$_GET['route'] : '';
if (in_array($route, array('api/login', 'tool/api/order_list', 'extension/payment/payf/notify'), true)) {
    $decision_reason = 'erp_api_force_a';
    if ($route === 'extension/payment/payf/notify') {
      $decision_reason = 'payf_notify_force_a';
    }

    $GLOBALS['route_context']['selected_config'] = 'config.php';
    $GLOBALS['route_context']['decision_reason'] = $decision_reason;
    appendTraceEvent('route', 'route_decision', array(
      'selected_config' => 'config.php',
      'decision_reason' => $decision_reason,
      'switchdb' => isset($GLOBALS['route_context']['switchdb']) ? $GLOBALS['route_context']['switchdb'] : '',
      'whitelist_hit' => in_array($ip_address, $whiteList) ? 1 : 0,
      'nocloak_cookie' => empty($_COOKIE['nocloak']) ? 0 : 1,
      'route' => $route,
    ));
    if (is_file('config.php')) {
        require_once('config.php');
    }
    appendTraceEvent('route', 'boot_start', array('selected_config' => 'config.php'));
    require_once(DIR_SYSTEM . 'startup.php');
    start('catalog');
    appendTraceEvent('route', 'boot_done', array('selected_config' => 'config.php'));
    exit;
}

// 根据后台设置 判断加载数据库 1--强制A库 2--强制B库 3--斗篷机制A/B库(不做处理，按默认index执行)
// nocloak参数和白名单 强制A库 优先级最高
if (file_exists('switchdb.ini')){
    $db_status = file_get_contents('switchdb.ini');
    $GLOBALS['route_context']['switchdb'] = trim((string)$db_status);
    $db_status = $db_status ?? 3;
    if (in_array($db_status, [1,2])){
//        $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';
        $GLOBALS['route_context']['selected_config'] = $config_file;
        $GLOBALS['route_context']['decision_reason'] = 'switchdb_force';
        appendTraceEvent('route', 'route_decision', array(
          'selected_config' => $config_file,
          'decision_reason' => 'switchdb_force',
          'switchdb' => $GLOBALS['route_context']['switchdb'],
          'whitelist_hit' => in_array($ip_address, $whiteList) ? 1 : 0,
          'nocloak_cookie' => empty($_COOKIE['nocloak']) ? 0 : 1,
        ));
        if (is_file($config_file)) {
            require_once($config_file);
        }
        appendTraceEvent('route', 'boot_start', array('selected_config' => $config_file));
        require_once(DIR_SYSTEM . 'startup.php');
        start('catalog');
        appendTraceEvent('route', 'boot_done', array('selected_config' => $config_file));
        exit;
    }
}

if(in_array($ip_address, $whiteList))
{
    $GLOBALS['route_context']['selected_config'] = 'config.php';
    $GLOBALS['route_context']['decision_reason'] = 'ip_whitelist';
    appendTraceEvent('route', 'route_decision', array(
      'selected_config' => 'config.php',
      'decision_reason' => 'ip_whitelist',
      'switchdb' => isset($GLOBALS['route_context']['switchdb']) ? $GLOBALS['route_context']['switchdb'] : '',
      'whitelist_hit' => 1,
      'nocloak_cookie' => empty($_COOKIE['nocloak']) ? 0 : 1,
    ));
    require_once('config.php');
    appendTraceEvent('route', 'boot_start', array('selected_config' => 'config.php'));
    require_once(DIR_SYSTEM . 'startup.php');
    start('catalog');
    appendTraceEvent('route', 'boot_done', array('selected_config' => 'config.php'));
}else{
    $result = isBlocked();
    if ($result->hasResponce && !isset($result->error_message)) {
        appendTraceEvent('route', 'cloak_result', array(
          'has_response' => 1,
          'isBlocked' => isset($result->isBlocked) ? (int)$result->isBlocked : 0,
          'error_message' => isset($result->errorMessage) ? $result->errorMessage : '',
        ));
        if($result->isBlocked == 1 && empty($_COOKIE['nocloak'])){
            $GLOBALS['route_context']['selected_config'] = 'config_b.php';
            $GLOBALS['route_context']['decision_reason'] = 'cloak_blocked';
            appendTraceEvent('route', 'route_decision', array(
              'selected_config' => 'config_b.php',
              'decision_reason' => 'cloak_blocked',
              'switchdb' => isset($GLOBALS['route_context']['switchdb']) ? $GLOBALS['route_context']['switchdb'] : '',
              'whitelist_hit' => 0,
              'nocloak_cookie' => 0,
            ));
            if (is_file('config_b.php')) {
            	require_once('config_b.php');
            }
    
        }else{
            $GLOBALS['route_context']['selected_config'] = 'config.php';
            $GLOBALS['route_context']['decision_reason'] = empty($_COOKIE['nocloak']) ? 'cloak_allowed' : 'nocloak_cookie';
            appendTraceEvent('route', 'route_decision', array(
              'selected_config' => 'config.php',
              'decision_reason' => $GLOBALS['route_context']['decision_reason'],
              'switchdb' => isset($GLOBALS['route_context']['switchdb']) ? $GLOBALS['route_context']['switchdb'] : '',
              'whitelist_hit' => 0,
              'nocloak_cookie' => empty($_COOKIE['nocloak']) ? 0 : 1,
            ));
            if (is_file('config.php')) {
            	require_once('config.php');
            }
        }
        appendTraceEvent('route', 'boot_start', array('selected_config' => $GLOBALS['route_context']['selected_config']));
        require_once(DIR_SYSTEM . 'startup.php');
        start('catalog');
        appendTraceEvent('route', 'boot_done', array('selected_config' => $GLOBALS['route_context']['selected_config']));
    }
    else {
      $GLOBALS['route_context']['decision_reason'] = 'cloak_no_response';
      appendTraceEvent('route', 'cloak_result', array(
        'has_response' => 0,
        'isBlocked' => isset($result->isBlocked) ? (int)$result->isBlocked : 0,
        'error_message' => isset($result->errorMessage) ? $result->errorMessage : '',
      ));
      die('Error: ' .$result->errorMessage);
    }
}


