<?php
/**
 * backup-dis-preserve.php
 *
 * One-click backup for KPPRSAJ DIS with permission/owner preservation + passworded ZIP:
 *  - mysqldump -> /tmp/dis-YYYYmmdd-HHMMSS.sql
 *  - tar (preserve uid/gid, perms, xattrs, selinux if supported) -> /tmp/backup-dis-YYYYmmdd-HHMMSS.tar.gz
 *  - Pack tar.gz into password-protected ZIP (7z AES-256 preferred; fallback to zip -P)
 *  - Stream ZIP to user
 *
 * EDIT CONFIG below before use.
 */

@ini_set('memory_limit', '1024M');
@set_time_limit(0);
date_default_timezone_set('Asia/Kuala_Lumpur');

/* ====== CONFIG (EDIT AS NEEDED) ====== */
$BASIC_USER   = 'vizone';
$BASIC_PASS   = 'V1z0ne1234';

$SITE_DIR     = '/var/www/html/dis';   // directory to backup
$DB_HOST      = '82.112.230.79';
$DB_NAME      = 'dis';
$DB_USER      = 'root';
$DB_PASS      = 'SAJD3v-w4q1s';

$ZIP_PASSWORD = 'V1z0ne1234';      // <-- password required to unzip the downloaded ZIP

$MYSQLDUMP_BIN = trim(shell_exec('command -v mysqldump 2>/dev/null')) ?: '/usr/bin/mysqldump';
$TAR_BIN       = trim(shell_exec('command -v tar 2>/dev/null')) ?: '/bin/tar';
$SEVENZ_BIN    = trim(shell_exec('command -v 7z 2>/dev/null')) ?: null;
$ZIP_BIN       = trim(shell_exec('command -v zip 2>/dev/null')) ?: null;
/* ====================================== */

/* ===== Basic HTTP Auth ===== */
if (!isset($_SERVER['PHP_AUTH_USER'])) {
    header('WWW-Authenticate: Basic realm="Backup DIS (preserve perms + zip pwd)"');
    header('HTTP/1.0 401 Unauthorized');
    echo "Authentication required.\n";
    exit;
}
$user = $_SERVER['PHP_AUTH_USER'];
$pass = $_SERVER['PHP_AUTH_PW'] ?? '';
if (!hash_equals((string)$BASIC_USER, (string)$user) || !hash_equals((string)$BASIC_PASS, (string)$pass)) {
    header('HTTP/1.0 403 Forbidden');
    echo "Forbidden: invalid credentials.\n";
    exit;
}

/* ===== Helpers ===== */
function abort_msg($code, $msg) {
    http_response_code($code);
    echo $msg;
    exit;
}
function run_cmd_string($cmd) : array {
    $out = []; $ret = 0;
    exec($cmd . ' 2>&1', $out, $ret);
    return [$ret, $out, $cmd];
}

/* ===== Run condition ===== */
$shouldRun = ($_SERVER['REQUEST_METHOD'] === 'POST') || (isset($_GET['run']) && $_GET['run'] === '1');

if ($shouldRun) {
    // validations
    if (!is_dir($SITE_DIR)) abort_msg(500, "Source directory not found: {$SITE_DIR}");
    if (!is_executable($MYSQLDUMP_BIN)) abort_msg(500, "mysqldump not found at: {$MYSQLDUMP_BIN}");
    if (!is_executable($TAR_BIN)) abort_msg(500, "tar not found at: {$TAR_BIN}");
    if (empty($SEVENZ_BIN) && empty($ZIP_BIN)) abort_msg(500, "Neither '7z' nor 'zip' found on server. Install one of them.");

    $ts = date('Ymd-His');
    $tmpSql      = sys_get_temp_dir() . DIRECTORY_SEPARATOR . "dis-{$ts}.sql";
    $plainTar    = sys_get_temp_dir() . DIRECTORY_SEPARATOR . "backup-dis-{$ts}.tar.gz";
    $finalZip    = sys_get_temp_dir() . DIRECTORY_SEPARATOR . "backup-dis-{$ts}.zip";

    /* ---------- 1) mysqldump ---------- */
    $dumpCmd = sprintf(
        '%s --user=%s --password=%s --host=%s --single-transaction --quick --routines --events --add-drop-table %s > %s',
        escapeshellcmd($MYSQLDUMP_BIN),
        escapeshellarg($DB_USER),
        escapeshellarg($DB_PASS),
        escapeshellarg($DB_HOST),
        escapeshellarg($DB_NAME),
        escapeshellarg($tmpSql)
    );
    [$retDump, $outDump, $cmdDump] = run_cmd_string($dumpCmd);
    if ($retDump !== 0 || !file_exists($tmpSql) || filesize($tmpSql) === 0) {
        @unlink($tmpSql);
        abort_msg(500, "mysqldump failed (ret={$retDump}). Output:\n" . htmlspecialchars(implode("\n", $outDump)));
    }

    /* ---------- 2) create tar.gz (preserve perms/owner/xattrs/selinux if possible) ---------- */
    $site_base = dirname($SITE_DIR);
    $site_dir  = basename($SITE_DIR);
    $tmpSql_dir = dirname($tmpSql);
    $tmpSql_base = basename($tmpSql);

    // try several variants (selinux/xattrs may not be supported)
    $tarAttempts = [];
    $tarAttempts[] = sprintf(
        '%s --numeric-owner --xattrs --selinux -C %s -czf %s %s -C %s %s',
        escapeshellcmd($TAR_BIN),
        escapeshellarg($site_base),
        escapeshellarg($plainTar),
        escapeshellarg($site_dir),
        escapeshellarg($tmpSql_dir),
        escapeshellarg($tmpSql_base)
    );
    $tarAttempts[] = sprintf(
        '%s --numeric-owner --xattrs -C %s -czf %s %s -C %s %s',
        escapeshellcmd($TAR_BIN),
        escapeshellarg($site_base),
        escapeshellarg($plainTar),
        escapeshellarg($site_dir),
        escapeshellarg($tmpSql_dir),
        escapeshellarg($tmpSql_base)
    );
    $tarAttempts[] = sprintf(
        '%s --numeric-owner -C %s -czf %s %s -C %s %s',
        escapeshellcmd($TAR_BIN),
        escapeshellarg($site_base),
        escapeshellarg($plainTar),
        escapeshellarg($site_dir),
        escapeshellarg($tmpSql_dir),
        escapeshellarg($tmpSql_base)
    );

    $tarOk = false;
    $tarDebug = [];
    foreach ($tarAttempts as $tcmd) {
        if (file_exists($plainTar)) @unlink($plainTar);
        [$retTar, $outTar, $cmdTar] = run_cmd_string($tcmd);
        $tarDebug[] = ['cmd'=>$cmdTar,'ret'=>$retTar,'out'=>$outTar];
        if ($retTar === 0 && file_exists($plainTar) && filesize($plainTar) > 0) {
            $tarOk = true;
            break;
        }
    }
    if (!$tarOk) {
        @unlink($tmpSql);
        $msg = "Failed to create tar archive. Attempts/debug:\n";
        foreach ($tarDebug as $d) {
            $msg .= "Cmd: {$d['cmd']}\nOutput:\n" . implode("\n", $d['out']) . "\n\n";
        }
        abort_msg(500, $msg);
    }

    /* ---------- 3) create password-protected ZIP (7z AES-256 preferred) ---------- */
    // Remove finalZip if exists
    if (file_exists($finalZip)) @unlink($finalZip);

    if (!empty($SEVENZ_BIN)) {
        // Build: 7z a -tzip -p'PASSWORD' -mem=AES256 finalZip plainTar
        // We wrap password with escapeshellarg so it is safe; 7z accepts -p'pass'
        $passArg = '-p' . escapeshellarg($ZIP_PASSWORD);
        $cmd = escapeshellcmd($SEVENZ_BIN) . ' a -tzip ' . $passArg . ' -mem=AES256 ' . escapeshellarg($finalZip) . ' ' . escapeshellarg($plainTar);
        [$r7, $out7, $c7] = run_cmd_string($cmd);
        if ($r7 !== 0 || !file_exists($finalZip)) {
            @unlink($tmpSql); @unlink($plainTar);
            abort_msg(500, "7z failed to create encrypted zip. Output:\n" . htmlspecialchars(implode("\n", $out7)));
        }
        $zipToolUsed = '7z (AES-256)';
    } else {
        // fallback: zip -j -P 'PASSWORD' finalZip plainTar
        // Note: zip -P is less secure (legacy). We use escapeshellarg for password.
        if (empty($ZIP_BIN)) {
            @unlink($tmpSql); @unlink($plainTar);
            abort_msg(500, "No zip tool available to create passworded archive.");
        }
        $cmd = escapeshellcmd($ZIP_BIN) . ' -j -P ' . escapeshellarg($ZIP_PASSWORD) . ' ' . escapeshellarg($finalZip) . ' ' . escapeshellarg($plainTar);
        [$rz, $outz, $cz] = run_cmd_string($cmd);
        if ($rz !== 0 || !file_exists($finalZip)) {
            @unlink($tmpSql); @unlink($plainTar);
            abort_msg(500, "zip failed to create password-protected zip. Output:\n" . htmlspecialchars(implode("\n", $outz)));
        }
        $zipToolUsed = 'zip -P (legacy)';
    }

    /* ---------- 4) stream final ZIP to user ---------- */
    while (ob_get_level()) ob_end_clean();

    header('Content-Type: application/zip');
    header('Content-Disposition: attachment; filename="'.basename($finalZip).'"');
    header('Content-Length: ' . filesize($finalZip));
    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');

    $fp = fopen($finalZip, 'rb');
    if ($fp === false) {
        @unlink($finalZip);
        @unlink($plainTar);
        @unlink($tmpSql);
        abort_msg(500, "Failed to open final zip for streaming.");
    }
    while (!feof($fp)) {
        echo fread($fp, 8192);
        flush();
    }
    fclose($fp);

    /* ---------- cleanup ---------- */
    @unlink($finalZip);
    @unlink($plainTar);
    @unlink($tmpSql);
    exit;
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Backup KPPRSAJ DIS — Preserve Permissions + ZIP password</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
  body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;padding:20px;background:#fafafa;color:#111}
  .card{max-width:760px;margin:18px auto;padding:18px;border-radius:10px;border:1px solid #ddd;background:#fff;box-shadow:0 6px 18px rgba(0,0,0,.04)}
  button{background:#0b74de;color:#fff;border:0;padding:10px 14px;border-radius:8px;font-weight:600;cursor:pointer}
  .warn{color:#b33}
  pre.code{background:#f7f7f7;padding:10px;border-radius:6px;overflow:auto}
</style>
</head>
<body>
  <div class="card">
    <h1>Backup KPPRSAJ DIS — Preserve Permissions + ZIP password</h1>
    <p>Tekan butang di bawah untuk buat backup. Browser akan minta username/password (Basic Auth).</p>

    <form method="post" onsubmit="return confirm('Teruskan buat backup sekarang?');">
      <button type="submit">📦 Generate & Download ZIP (passworded)</button>
    </form>

    <p style="margin-top:14px;"><strong>Direct link:</strong> <code>?run=1</code></p>

    <hr>

    <h4>Current config (edit file to change):</h4>
    <pre class="code">
SITE_DIR: <?=htmlspecialchars($SITE_DIR) . "\n"?>
DB_HOST: <?=htmlspecialchars($DB_HOST) . "\n"?>
DB_NAME: <?=htmlspecialchars($DB_NAME) . "\n"?>
DB_USER: <?=htmlspecialchars($DB_USER) . "\n"?>
MYSQLDUMP: <?=htmlspecialchars($MYSQLDUMP_BIN) . "\n"?>
TAR: <?=htmlspecialchars($TAR_BIN) . "\n"?>
7z: <?=htmlspecialchars($SEVENZ_BIN ?: 'not found') . "\n"?>
zip: <?=htmlspecialchars($ZIP_BIN ?: 'not found') . "\n"?>
Basic auth user: <?=htmlspecialchars($BASIC_USER) . "\n"?>
ZIP password: <?=htmlspecialchars($ZIP_PASSWORD) . "\n"?>
    </pre>

    <p class="warn"><strong>Security reminder:</strong> Skrip ini mengandungi DB creds & Basic-Auth & ZIP password. Sila simpan fail ini di lokasi terhad dan jangan dedahkan secara awam.</p>

    <h4>How to extract (client):</h4>
    <ol>
      <li>Run <code>unzip backup-dis-...zip</code> — masukkan ZIP password ketika diminta.</li>
      <li>You will get <code>backup-dis-...tar.gz</code>. Then extract as root to restore owner & SELinux contexts:<br>
        <code>sudo tar -xzf backup-dis-...tar.gz -C /restore/path</code>
      </li>
    </ol>
  </div>
</body>
</html>
