Refusing to install over plain HTTP (your key would travel unencrypted). ' . 'Use https, or set $ALLOW_HTTP = true if this is an internal test box.

'); exit; } /* ── preflight checks ────────────────────────────────────────────────── */ $errors = []; if (!is_file($AGENT_FILE)) { $errors[] = 'wordfort-agent.php is not in this directory. Upload it next to install.php.'; } if (!is_writable(__DIR__)) { $errors[] = 'This directory is not writable by PHP (' . __DIR__ . '). ' . 'The installer needs to write .user.ini here.'; } // .user.ini only works under PHP-FPM / CGI, not mod_php. $sapi = php_sapi_name(); if (stripos($sapi, 'fpm') === false && stripos($sapi, 'cgi') === false) { $errors[] = 'This host runs PHP as "' . htmlspecialchars($sapi) . '", not FPM/CGI. ' . '.user.ini auto_prepend_file only works on PHP-FPM/CGI. ' . 'Use the .htaccess or FPM-pool method instead (see docs).'; } if (!function_exists('curl_init')) { $errors[] = 'PHP cURL extension is not available — needed for verification and reporting.'; } if ($errors) { render('Cannot install yet', '

Fix the above and reload this page.

'); exit; } /* ── collect the WordFort key from the form ──────────────────────────── */ $submittedKey = trim($_POST['wf_key'] ?? $_GET['wf_key'] ?? ''); /* ── ACTION: only proceed on explicit confirm with a key ─────────────── */ $doInstall = (($_POST['go'] ?? $_GET['go'] ?? '') === '1'); $keyLooksValid = (bool)preg_match('/^(wf|akg)_[A-Za-z0-9_\-]{8,}$/', $submittedKey); if (!$doInstall || !$keyLooksValid) { $abs = $AGENT_FILE; $keyErr = ($doInstall && !$keyLooksValid) ? '

That doesn\'t look like an ACKgeo key (it should start with ' . 'akg_). Copy it from your ACKgeo dashboard and try again.

' : ''; render('WordFort — install', '

Paste your ACKgeo key (from the WordFort page in your dashboard), then install. This is the only thing you need — the key both configures the agent and authorizes this install.

' . $keyErr . '

This creates .user.ini with auto_prepend_file=' . htmlspecialchars($abs) . ', verifies the agent loads, and deletes this installer on success.

SAPI: ' . htmlspecialchars($sapi) . ' · Dir writable: yes · HTTPS: ' . ($isHttps ? 'yes' : 'no (allowed)') . '

'); exit; } $WORDFORT_KEY = $submittedKey; // validated above /* ── STEP 1 — inject key into the agent ──────────────────────────────── */ $changed = []; $agentSrc = file_get_contents($AGENT_FILE); if (strpos($agentSrc, 'wf_live_REPLACE_ME') !== false) { $agentSrc = str_replace('wf_live_REPLACE_ME', addslashes($WORDFORT_KEY), $agentSrc); file_put_contents($AGENT_FILE, $agentSrc); $changed[] = 'Wrote key into wordfort-agent.php'; } else { $changed[] = 'Agent already had a key (left as-is)'; } /* ── STEP 2 — health token ───────────────────────────────────────────── */ $healthToken = bin2hex(random_bytes(16)); file_put_contents($TOKENFILE, $healthToken); @chmod($TOKENFILE, 0640); $changed[] = 'Created health token'; /* ── STEP 3 — .user.ini ──────────────────────────────────────────────── */ $abs = str_replace('\\', '/', $AGENT_FILE); $iniLine = 'auto_prepend_file=' . $abs; $existing = is_file($USERINI) ? file_get_contents($USERINI) : ''; if (strpos($existing, 'auto_prepend_file') === false) { $out = rtrim($existing) . (strlen($existing) ? "\n" : '') . "; Added by WordFort installer\n" . $iniLine . "\n"; file_put_contents($USERINI, $out); $changed[] = 'Wrote auto_prepend_file into .user.ini'; } else { $changed[] = '.user.ini already had auto_prepend_file (left as-is)'; } /* ── STEP 4 — wait for .user.ini cache, then verify by loopback ──────── */ // PHP caches .user.ini for user_ini.cache_ttl seconds (default 300). We can't // force a flush, but a freshly-created .user.ini in a dir with no prior cache // entry is usually picked up on the next request. We poll for up to ~8s; if it // hasn't taken effect we tell the user it may take up to 5 min (the TTL). $ttl = (int) ini_get('user_ini.cache_ttl'); $selfUrl = build_self_url($healthToken); $verified = false; $lastBody = ''; for ($i = 0; $i < 8; $i++) { [$code, $body, $hdr] = http_get($selfUrl); $lastBody = $body; if ($code === 200 && stripos($hdr, 'x-wordfort: active') !== false) { $verified = true; break; } sleep(1); } /* ── STEP 5 — result ─────────────────────────────────────────────────── */ if ($verified) { // Success — clean up: remove health token + self-delete. @unlink($TOKENFILE); $selfGone = @unlink($SELF); render('WordFort is protecting this site ✓', '

The agent is loaded and running on live requests.

' . ($selfGone ? 'This installer has deleted itself. Nothing else to do.' : 'Could not auto-delete install.php — please delete it manually now.') . '

Blocked requests will start appearing in your ACKgeo dashboard. To uninstall later: remove the auto_prepend_file line from .user.ini and delete wordfort-agent.php.

'); exit; } // Not verified yet — do NOT self-delete. Likely the .user.ini cache TTL. render('Installed — waiting for PHP to pick it up', '

Files are in place, but the agent has not answered a live request yet.

PHP caches .user.ini for ' . ($ttl ?: 300) . 's. Wait that long, then reload:
Re-check now →

The installer was kept (not deleted) so you can retry. If it never verifies, confirm this host uses PHP-FPM and that ' . htmlspecialchars($abs) . ' is readable.

'); exit; /* ───────────────────────── helpers ─────────────────────────────────── */ function self_base() { $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || ($_SERVER['SERVER_PORT'] ?? '') == 443 || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https'); $scheme = $https ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST'] ?? 'localhost'; $path = strtok($_SERVER['REQUEST_URI'] ?? '/install.php', '?'); return $scheme . '://' . $host . $path; } function build_self_url($healthToken) { $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || ($_SERVER['SERVER_PORT'] ?? '') == 443 || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https'); $scheme = $https ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST'] ?? 'localhost'; // Hit a PHP endpoint (this installer script itself) with the health param. // The agent prepends onto every PHP request, so it answers here and exits // BEFORE install.php's own logic runs. We must NOT hit '/' — on a static-HTML // site the root serves index.html without invoking PHP, so the prepend never // fires and verification would fail even when the agent is correctly installed. $self = basename($_SERVER['SCRIPT_NAME'] ?? '/install.php'); return $scheme . '://' . $host . '/' . $self . '?__wordfort_health=' . urlencode($healthToken); } function http_get($url) { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_TIMEOUT => 4, CURLOPT_CONNECTTIMEOUT => 3, CURLOPT_SSL_VERIFYPEER => false, // loopback to self; cert may be internal CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_FOLLOWLOCATION => false, ]); $resp = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $hsz = curl_getinfo($ch, CURLINFO_HEADER_SIZE); curl_close($ch); $hdr = substr((string)$resp, 0, $hsz); $body = substr((string)$resp, $hsz); return [$code, $body, $hdr]; } function render($title, $bodyHtml) { echo '' . '' . '' . '' . htmlspecialchars($title) . ' — WordFort

' . htmlspecialchars($title) . '

' . $bodyHtml . '

WordFort · part of ACKgeo

' . ''; }