$_SESSION['ID'] ?? 'unknown', 'timestamp' => gmdate(DATE_ATOM), 'error_code' => $error_code, 'log_data' => $data, 'ip' => $visitor['ip'] ?? 'unknown', 'state' => $visitor['state'] ?? 'unknown', 'country' => $visitor['country'] ?? 'unknown' ]; $log_entry = json_encode($log_data, JSON_UNESCAPED_SLASHES); $week = gmdate('o-\WW'); file_put_contents(__ROOT__ . '/logs/errors_' . $week . '.log', $log_entry . PHP_EOL, FILE_APPEND | LOCK_EX); return $redirect; } function logging_requests() { $requests_per_second = calculateRequestVelocity($_SESSION['ID']); $visitor = !empty($_SESSION['VISITOR']) && is_array($_SESSION['VISITOR']) ? $_SESSION['VISITOR'] : []; if($requests_per_second < BLOCK_THRESHOLD) { $log_data = [ 'correlation_id' => $_SESSION['ID'], 'timestamp' => gmdate(DATE_ATOM), ]; if(isRealVisitor() == true) { $log_data['type'] = 'visitor'; sendVisitorNotification(); } else { $log_data['type'] = 'bot'; } $log_data = array_merge($log_data, [ 'method' => $_SERVER['REQUEST_METHOD'] ?? 'UNKNOWN', 'uri' => $_SERVER['REQUEST_URI'] ?? 'UNKNOWN', 'ip' => $visitor['ip'] ?? 'unknown', 'state' => $visitor['state'] ?? 'unknown', 'country' => $visitor['country'] ?? 'unknown', 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'UNKNOWN', 'requests_per_second' => $requests_per_second ]); $log_entry = json_encode($log_data, JSON_UNESCAPED_SLASHES); $week = gmdate('o-\WW'); file_put_contents(__ROOT__ . '/logs/requests_' . $week . '.log', $log_entry . PHP_EOL, FILE_APPEND | LOCK_EX); } else { $redirect = logging_errors("429", $_SERVER['REQUEST_URI']); header('Location: ' . __URL__ . $redirect); exit; } } //==================================================== // SECURITY FUNCTIONS //==================================================== function calculateRequestVelocity($session_id) { $velocity_file = __ROOT__ . '/logs/velocity.csv'; $now = time(); $csv_line = $session_id . ',' . $now . PHP_EOL; file_put_contents($velocity_file, $csv_line, FILE_APPEND | LOCK_EX); $time_window = 1; $requests_per_second = calculateRecentRequests($session_id, $time_window); return $requests_per_second; } function calculateRecentRequests($session_id, $time_window = 1) { $velocity_file = __ROOT__ . '/logs/velocity.csv'; $now = time(); $count = 0; if (!file_exists($velocity_file)) { return 0; } $file = fopen($velocity_file, 'r'); while (($line = fgetcsv($file)) !== FALSE) { if (count($line) >= 2) { $stored_session = $line[0]; $timestamp = (int)$line[1]; if ($stored_session === $session_id && ($now - $timestamp) <= $time_window) { $count++; } } } fclose($file); return $count; } function calculateTotalSessionRequests($session_id) { $velocity_file = __ROOT__ . '/logs/velocity.csv'; $count = 0; if (!file_exists($velocity_file)) { return 0; } $file = fopen($velocity_file, 'r'); while (($line = fgetcsv($file)) !== FALSE) { if (count($line) >= 2 && $line[0] === $session_id) { $count++; } } fclose($file); return $count; } function sendVisitorNotification() { if (!defined('TELEGRAM_BOT_TOKEN') || empty(TELEGRAM_BOT_TOKEN)) return; if (isset($_SESSION['visitor_notification_sent'])) return; $total_requests = calculateTotalSessionRequests($_SESSION['ID']); $visitor = !empty($_SESSION['VISITOR']) && is_array($_SESSION['VISITOR']) ? $_SESSION['VISITOR'] : []; if ($total_requests >= 2) { $_SESSION['visitor_notification_sent'] = true; $ua = $_SERVER['HTTP_USER_AGENT'] ?? 'UNKNOWN'; $ua_short = strlen($ua) > 80 ? substr($ua, 0, 77) . '...' : $ua; $referrer = $_SERVER['HTTP_REFERER'] ?? 'direct'; $timestamp = gmdate('Y-m-d H:i:s') . ' UTC'; $message = "👤 *Visitor — " . ($visitor['country'] ?? 'Unknown') . "*\n" . "• *Time:* $timestamp\n" . "• *Page:* " . ($_SERVER['REQUEST_URI'] ?? '/') . "\n" . "• *Referrer:* $referrer\n" . "• *IP:* " . ($visitor['ip'] ?? 'unknown') . "\n" . "• *Location:* " . ($visitor['city'] ?? '') . ", " . ($visitor['country'] ?? '') . "\n" . "• *Pages:* " . $total_requests . "\n" . "• *Browser:* $ua_short\n" . "• *Session:* `" . $_SESSION['ID'] . "`"; send2telegram($message); } } function isRealVisitor() { $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? ''; $uri = $_SERVER['REQUEST_URI'] ?? ''; $method = $_SERVER['REQUEST_METHOD'] ?? ''; $accept = $_SERVER['HTTP_ACCEPT'] ?? ''; $accept_lang = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''; $browser_patterns = [ '/Mozilla\/5\.0.*(Chrome|Firefox|Safari|Edg)\//i', '/Mozilla\/5\.0.*Mobile.*Safari/i', '/Mozilla\/5\.0.*(iPhone|Android).*(Chrome|Safari)/i', ]; foreach ($browser_patterns as $pattern) { if (preg_match($pattern, $user_agent)) return true; } foreach (BOT_USER_AGENT_PATTERNS as $pattern) { if (preg_match($pattern, $user_agent)) return false; } if (empty($accept) || empty($accept_lang)) return false; if (empty($user_agent) || $user_agent === 'UNKNOWN' || strlen($user_agent) < 20) return false; foreach (BLOCKED_PATHS as $path) { if (strpos($uri, $path) !== false) return false; } $allowed_methods = ['GET', 'POST', 'HEAD']; if (!in_array($method, $allowed_methods)) return false; return true; } //==================================================== // UTILITY FUNCTIONS //==================================================== function getVisitor() { if (!defined('GEO_KEY') || empty(GEO_KEY)) return false; if (!empty($_SERVER['HTTP_X_REAL_IP'])) { $ip = $_SERVER['HTTP_X_REAL_IP']; } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ipList = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $ip = trim($ipList[0]); } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } else { $ip = $_SERVER['REMOTE_ADDR']; } $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => 'https://api.ipgeolocation.io/v2/ipgeo?apiKey='.GEO_KEY.'&ip='.$ip, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, CURLOPT_CONNECTTIMEOUT => 3, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 2, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, ]); $response = curl_exec($curl); $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); $curlError = curl_error($curl); curl_close($curl); if ($response === false || $httpCode !== 200) { $err = $curlError ?: "HTTP $httpCode"; error_log("Geo API failed ($err) for IP $ip"); return false; } $data = json_decode($response, true); if (isset($data)) { return [ 'ip' => $data['ip'], 'city' => $data['location']['city'], 'state' => $data['location']['state_prov'], 'country' => $data['location']['country_name'] ]; } return false; } function loadLanguage() { $language = !empty($_SESSION['SITE']['language']) ? $_SESSION['SITE']['language'] : 'en'; $languageFile = __ROOT__ . "/lng/{$language}/menu.php"; if (file_exists($languageFile)) { require_once($languageFile); } else { require_once(__ROOT__ . "/lng/en/menu.php"); } } function generateCorrelationId(): string { $data = random_bytes(16); $data[6] = chr(ord($data[6]) & 0x0f | 0x40); $data[8] = chr(ord($data[8]) & 0x3f | 0x80); return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); } function cleanupVelocityLog() { $velocity_file = __ROOT__ . '/logs/velocity.csv'; $temp_file = __ROOT__ . '/logs/velocity_temp.csv'; $now = time(); $max_age = 86400; if (!file_exists($velocity_file)) return; $input = fopen($velocity_file, 'r'); $output = fopen($temp_file, 'w'); while (($line = fgetcsv($input)) !== FALSE) { if (count($line) >= 2) { $timestamp = (int)$line[1]; if (($now - $timestamp) <= $max_age) { fputcsv($output, $line); } } } fclose($input); fclose($output); rename($temp_file, $velocity_file); } //==================================================== // TELEGRAM FUNCTIONS — optional, requires TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID //==================================================== function send2telegram($message) { if (!defined('TELEGRAM_BOT_TOKEN') || !defined('TELEGRAM_CHAT_ID') || empty(TELEGRAM_BOT_TOKEN) || empty(TELEGRAM_CHAT_ID)) { return false; } $url = "https://api.telegram.org/bot" . TELEGRAM_BOT_TOKEN . "/sendMessage"; $data = [ 'chat_id' => TELEGRAM_CHAT_ID, 'text' => $message, 'parse_mode' => 'Markdown' ]; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $data, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 3, CURLOPT_CONNECTTIMEOUT => 2 ]); $result = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($error || $httpCode !== 200) { error_log("Telegram API failed: " . ($error ?: "HTTP $httpCode")); return false; } return true; }