<?php
/*
Urmi you happy me happy licence

Copyright (c) 2026 shreebhattji

License text:
https://github.com/shreebhattji/Urmi/blob/main/licence.md
*/

exec("sudo chmod 444 /sys/class/dmi/id/product_uuid");
$version = "12.32";

function fail(string $msg): never
{
    error_log("ERROR: $msg");
    exit(1);
}

function download(string $url, string $dest): void
{
    $fp = fopen($dest, 'wb');
    if (!$fp) fail("Cannot write $dest");

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_FILE => $fp,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_FAILONERROR => true,
        CURLOPT_TIMEOUT => 60,
        CURLOPT_SSL_VERIFYPEER => true,
    ]);

    if (!curl_exec($ch)) {
        fail("Download failed: " . curl_error($ch));
    }

    curl_close($ch);
    fclose($fp);
}


$device_id = trim(file_get_contents('/sys/class/dmi/id/product_uuid'));

$publicKey = "-----BEGIN PUBLIC KEY-----
MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEAm6glpTALuc82R+9Mqb5f
HVRC5dScc7USgWoIsYN1tF8YE0d7rhSIFvayVabiyabVmWscjAIlmYf6InSlLsDx
avfmapg32ECd92H49ZbsvXQpLqasyOkN7z6FUcuQ6pEMqfPBmBXKGngHazPp420o
Iki9hLc7IE9EMlDHfozckuJI8mB+bsd2oqua6SSTBYx5HYuSCbootf9GliSd7PVk
H6uir88j49/NFfvmrReicFBiMba959uOdBIhWl9AveZL+iI2NdS5SPw6eWltaMot
PSk7/5Z4Vn5Od7sQA0yUqmCj5XNV5EzRlP1jhP7SDv0D6Mpdf3HuKWdCBqepJBe2
rCHPQ9KrChQau4eEbJb5LIE3gFDpLxTHk9FEp+50evkpFONj0aAjSb3P4wsEGiOk
95Tm56gDRirnbbw/6SzhE7pEvXRUfMl1KO6maYK1z7KNMgEH99C2zCjZhpaXL5Io
rywCw009zoMT2qdKPMGOyQ4KPlCLCJYSF0y/rE07WNgl4BupVZR41B5MbXL5L7+X
OD0jBpbWI7v2ChP9rn5u6Lqpq6ewvc2RJO8lrAyZtzrNJNZNYmUKHrm7qAHJeJZX
Zh7OEf9U/T9JBpzf8l0MzyywlQGUBzb/niG0iZILt3XIpD+Xeyrr6hr+nabeiKXV
jHyUcG84zzLjv7sREzWEGoLBrdztMy69rbfd3d0DpjS90xceKZYBDd3vwjn6h0TN
KssqUb7BMH+zkCe/LQg6EGdXB13+xUSUjFKLLeBKu1VxMPfd/WmV1QumOodidvee
rQAv6yMevq2hVFkiFo7CUpaRv6dvQnQaqX2rHFKZY6zEIzbJXTznl6ZMtCcmcZMk
CYcoWZIAUR5tFP221XzIfJmymVRfJGiKTvt+g/SUUFJt6mq8ettu11XS4KSIxtaA
l8q2SSxpRQa80NUuaBpQc/3eP293wgcf/EOfzhCjxDLjsHSKV1AkSMyjvCzSsCdG
mMEIuT/D7PB7N8vlfhn5qsyt1Sm81/1EZ3u8UqToELhe8j7G26GVl/8ptSxofvZE
X0goYwW18PPhtZvkR8CXpZ7qwjqDcL5cQzcCldufjtqJ5GAwN6SrcmnYjQoo2cu9
XlWo0InPE8BpjR7vJpKLbppQzwUs9GQYx2bMSTbsrduc8zDXlPT5aOfgkJui/NQa
uxttvsXqXd3nNJhbO0BN+wCDT0j4LNRvMlJloWEGrBkY4SA5I1MX8XBL34Csy6Bu
bHWxXNBAGYMchcJKly7XN2hA61V4QCCiFz/MP9l1llw/Mk4D5IUTxcfcEDHx7LO0
To+pc5kuXS6Aps6lKJdwv6h0Bi9SWtBpFi2RtpQpAc+dVPQ9lwq3VTJV5GZz3AgV
KQIDAQAB
-----END PUBLIC KEY-----
";

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if ($_POST['action'] !== "backup")
        include 'header.php';

    switch ($_POST['action']) {
        case 'update':

            $payload['device_id'] = $device_id;
            $payload['project_id'] = "28f27590923d962388f0da125553c5";
            $payload['version'] = $version;
            $payload = json_encode($payload, JSON_UNESCAPED_UNICODE);

            openssl_public_encrypt(
                $payload,
                $encrypted,
                $publicKey,
                OPENSSL_PKCS1_OAEP_PADDING
            );

            $postData = [
                'encrypted' => base64_encode($encrypted)
            ];

            $ch = curl_init('https://account.urmic.org/encoder/update.php');
            curl_setopt_array($ch, [
                CURLOPT_POST           => true,
                CURLOPT_POSTFIELDS     => $postData,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT        => 10,
            ]);

            $response = curl_exec($ch);
            curl_close($ch);

            $data = json_decode($response, true);

            if ($data['status'] != "valid") {
                echo '<script>alert("'
                    . htmlspecialchars($data['message'], ENT_QUOTES)
                    . '");</script>';
            }
            if ($data['status'] == "valid") {
                echo '
<style>
#blocker {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.75);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 999999;
    pointer-events: all;
}

#blockerBox {
    background: #111827;
    color: #ffffff;
    padding: 28px 36px;
    border-radius: 8px;
    text-align: center;
    font-family: Arial, sans-serif;
    min-width: 320px;
    box-shadow: 0 20px 50px rgba(0,0,0,.5);
}

#blockerBox .msg {
    font-size: 18px;
    margin-bottom: 12px;
}

#blockerBox .timer {
    font-size: 34px;
    font-weight: bold;
}
</style>

<div id="blocker">
    <div id="blockerBox">
        <div class="msg">'
                    . htmlspecialchars($data['message'], ENT_QUOTES) .
                    '</div>
        <div>Refreshing in</div>
        <div class="timer" id="blockerTimer">100</div>
    </div>
</div>

<script>
let seconds = 100;
const timer = document.getElementById("blockerTimer");

const interval = setInterval(() => {
    seconds--;
    timer.textContent = seconds;

    if (seconds <= 0) {
        clearInterval(interval);
        blocker.remove();
        window.location.href = window.location.pathname;
    }
}, 1000);
</script>
';

                $public_key = "-----BEGIN PUBLIC KEY-----
MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEAm+7Vl0fEgey2tF6v2mTn
3C/FDGn589uY5a9rpDeZLlhjdOdFaTMWL3d8oEhmImCd+aPELpxydQ+xGxVPNOzO
WKbF3V/FymwxyU3yCD8rfCPyd05z9ANeicVEZMO2K0CwjLoM1OFpxoo/GRmetHuY
Yt2WxDWHPN9DjDDkIMrx2PKFHPqJnyWliyFWJ4aaaK174GH+b4rHRkAm31fUhbaG
RBcQWJhWv1gJ+lxz2z3oHi9nI6Q/Hkb+u3B11tcx3j6rScxKXk8T6Bw64vEk3t0l
i1kYgnPI4Eya0BXuROMfn+zGG50TNgq+vWntzBoKaWuPVbvvmzTlHK8My9qZUliy
otDNd340xhBCmIYqkwxiN2w4g+TAM9X3r9/4lgJYx5ezh3Y0uLGf6mHZ5wFyDAhh
uLJxkOCZY0b3zoRW5wqqKR67/FxBCpcLS6Y8wlKSR8UU8y73hr2tGD28JgNr9sjx
reRItpdGhQgO8gLZKLK6LhihTFtbt5tiL1l6Fkc11DSac+N/xFyHfRe6K3lIV+cD
WMx0+6YX3p8i4cmRXGn59Xu1VdZvmB03Dl5YmIb6wBNMCEPWohRz0bGmamXGW1Ze
EZQhGJRUqIFNuTQwc/RI1wPUgefXXXitCOlo52oyahuKWxWuGMN/8Uyw74poK7NK
7Tbu+JLNuqMsuPoVkrl7havRUbwQy7xUt93wFew0GFDaOobZzoGIjp3pWGvZiQ7y
XMyzklS42/ZC7rJAJTyuLTHxMeUMB4Zt7Qmp7GQ3NaOUq4egPQ6KZUO4qDNtAJaK
mvHca0HHmskP20/yb4iVtz65zhj6BWt98SsFuRMrMDDoBDEtcd1T7xIRK4nqfIhX
8Nw8z1+m8TVItJM3XxvLx6eXgtnJ8BqWInjRoFkbpzEON56zA1ZwPCFm7MWACKEs
m4Gul3+liBwDnpaJvHLLs6+9R4T1/d6nrwwRPDBz9AhBZV2Qz0/Z67qAyGvT2Joh
qR6fIHe+jsKlPSW4TBBx8C2H6avKv7W0CH7z4Y9APuDucvMQ2X3CCekTRaejU7nr
JOGs8ALAtsL+eXL+KMvU/16zxzcbT4ZW/6kdRFtwkaWlq07Q1yU13s+JQRzenut5
7j1GMcmtt1K/CSBzhs2d2UTwiO3fRDs4TCUAj/vq2OlfL1UOAZ3ni8QmfA1vD/BD
Xqfivizijmypv83rv8se5b6dr78ti+wiAIEJEDX+/yISmEWuDXGaL+eVATr1Rw+0
8vFY2f7lS2/QsSv+X7B6lOs3L18sG7AAYrkFjrfhQ8RC9Lv62ITUAV6B6G/BJ4o0
UubReGWsYm092Z9SWEB8KBUlwMWjEMl6Q2f3AfkAKR3EMYBqmNfL8teAcb711xA2
EwIDAQAB
-----END PUBLIC KEY-----
";

                $tmpDir = sys_get_temp_dir() . '/payload_' . bin2hex(random_bytes(6));
                $zipFile = $tmpDir . '/payload.zip';
                $sigFile = $tmpDir . '/payload.zip.sig';
                $extractDir = $tmpDir . '/extract';

                mkdir($tmpDir, 0700, true);
                mkdir($extractDir, 0700, true);

                download($data['link'], $zipFile);
                download($data['signature'], $sigFile);

                $publicKey = openssl_pkey_get_public($public_key);
                if (!$publicKey) fail('Invalid public key');

                $data = file_get_contents($zipFile);
                $signature = file_get_contents($sigFile);

                $verified = openssl_verify($data, $signature, $publicKey, OPENSSL_ALGO_SHA256);

                if ($verified !== 1) {
                    fail('Signature verification FAILED');
                }

                $zip = new ZipArchive();
                if ($zip->open($zipFile) !== true) {
                    fail('Unable to open ZIP');
                }
                for ($i = 0; $i < $zip->numFiles; $i++) {
                    $name = $zip->getNameIndex($i);
                    if (str_contains($name, '..') || str_starts_with($name, '/')) {
                        fail('Zip traversal detected');
                    }
                }

                $zip->extractTo($extractDir);
                $zip->close();
                $setup = $extractDir . '/setup.sh';

                if (!is_file($setup)) {
                    fail('setup.sh not found');
                }

                chmod($setup, 0755);

                $descriptorSpec = [
                    1 => ['pipe', 'w'],
                    2 => ['pipe', 'w'],
                ];

                $process = proc_open(
                    ['/bin/bash', $setup],
                    $descriptorSpec,
                    $pipes,
                    $extractDir
                );

                if (!is_resource($process)) {
                    fail('Failed to execute setup.sh');
                }

                $output = stream_get_contents($pipes[1]);
                $error  = stream_get_contents($pipes[2]);

                fclose($pipes[1]);
                fclose($pipes[2]);

                $exitCode = proc_close($process);
            }
            update_service("display");
            update_service("rtmp0");
            update_service("rtmp1");
            update_service("udp0");
            update_service("udp1");
            update_service("udp2");
            update_service("srt");
            update_service("custom");
            update_service("input");
            break;
        case 'reset':
            $files = glob('/var/www/encoder/*.json');
            foreach ($files as $file) {
                if (is_file($file)) {
                    unlink($file);
                }
            }

            exec('sudo cp /var/www/default_nginx.conf /etc/nginx/nginx.conf');
            exec('sudo cp /var/www/default_nginx_site /etc/nginx/sites-available/default');


            update_service("display");
            update_service("rtmp0");
            update_service("rtmp1");
            update_service("udp0");
            update_service("udp1");
            update_service("udp2");
            update_service("srt");
            update_service("custom");
            update_service("input");

            break;
        case 'reboot':
            exec('sudo reboot');
            break;
        case 'backup':
            $jsonFiles = [
                'input.json',
                'output.json',
                'firewall.json',
                'network.json',
                'firmware.json',
            ];

            $tmpZip = sys_get_temp_dir() . '/backup.zip';

            $publicKey = file_get_contents('/var/www/backup_public.pem');

            $zip = new ZipArchive();
            $zip->open($tmpZip, ZipArchive::CREATE | ZipArchive::OVERWRITE);

            foreach ($jsonFiles as $json) {
                if (file_exists($json)) {
                    $zip->addFile($json, basename($json));
                }
            }

            $zip->close();
            $data = file_get_contents($tmpZip);

            $aesKey = random_bytes(32);
            $iv     = random_bytes(16);

            $encryptedData = openssl_encrypt(
                $data,
                'AES-256-CBC',
                $aesKey,
                OPENSSL_RAW_DATA,
                $iv
            );

            openssl_public_encrypt($aesKey, $encryptedKey, $publicKey);

            $binary  = pack('N', strlen($encryptedKey));
            $binary .= $encryptedKey;
            $binary .= $iv;
            $binary .= $encryptedData;

            /* Headers */
            $filename = 'universal_encoder_decoder.bin';

            header('Content-Description: File Transfer');
            header('Content-Type: application/octet-stream');
            header('Content-Disposition: attachment; filename="' . $filename . '"');
            header('Content-Length: ' . strlen($binary));
            header('Cache-Control: no-store, no-cache, must-revalidate');
            header('Pragma: no-cache');
            header('Expires: 0');

            echo $binary;
            flush();

            unlink($tmpZip);
            break;
        case 'restore':
            $jsonFiles = [
                'input.json',
                'output.json',
                'firewall.json',
                'network.json',
                'firmware.json',
            ];

            /* 1. Remove existing config files */
            foreach ($jsonFiles as $json) {
                if (file_exists($json)) {
                    unlink($json);
                }
            }

            /* 2. Validate upload */
            if (!isset($_FILES['shree_bhattji_encoder'])) {
                die('No file uploaded');
            }

            $upload = $_FILES['shree_bhattji_encoder'];

            if ($upload['error'] !== UPLOAD_ERR_OK) {
                die('Upload failed');
            }

            if (strtolower(pathinfo($upload['name'], PATHINFO_EXTENSION)) !== 'bin') {
                die('Invalid file type');
            }

            /* 3. Load private key */
            $privateKeyPem = file_get_contents('/var/www/backup_private.pem');
            if ($privateKeyPem === false) {
                die('Private key not found');
            }

            $privateKey = openssl_pkey_get_private($privateKeyPem);
            if ($privateKey === false) {
                die('Invalid private key');
            }

            /* 4. Read binary backup */
            $payloadRaw = file_get_contents($upload['tmp_name']);
            if ($payloadRaw === false) {
                die('Failed to read backup file');
            }

            $offset = 0;

            /* 5. Read RSA key length (4 bytes, big-endian) */
            if (strlen($payloadRaw) < 4) {
                die('Corrupt backup file');
            }

            $keyLen = unpack('N', substr($payloadRaw, 0, 4))[1];
            $offset += 4;

            if ($keyLen <= 0 || $keyLen > 512) {
                die('Invalid encrypted key length');
            }

            /* 6. Read RSA-encrypted AES key */
            $encryptedKey = substr($payloadRaw, $offset, $keyLen);
            if (strlen($encryptedKey) !== $keyLen) {
                die('Corrupt encrypted key');
            }
            $offset += $keyLen;

            /* 7. Read IV */
            $iv = substr($payloadRaw, $offset, 16);
            if (strlen($iv) !== 16) {
                die('Invalid IV');
            }
            $offset += 16;

            /* 8. Remaining data = AES-encrypted ZIP */
            $encryptedData = substr($payloadRaw, $offset);
            if ($encryptedData === '') {
                die('Missing encrypted payload');
            }

            /* 9. Decrypt AES key */
            if (!openssl_private_decrypt($encryptedKey, $aesKey, $privateKey)) {
                die('Key mismatch or wrong private key');
            }

            if (strlen($aesKey) !== 32) {
                die('Invalid AES key length');
            }

            /* 10. Decrypt ZIP */
            $zipBinary = openssl_decrypt(
                $encryptedData,
                'AES-256-CBC',
                $aesKey,
                OPENSSL_RAW_DATA,
                $iv
            );

            if ($zipBinary === false) {
                die('Failed to decrypt data');
            }

            /* 11. Write ZIP to temp file */
            $tmpZip = sys_get_temp_dir() . '/restore_' . uniqid('', true) . '.zip';
            file_put_contents($tmpZip, $zipBinary);

            /* 12. Extract ZIP safely */
            $zip = new ZipArchive();
            if ($zip->open($tmpZip) !== true) {
                unlink($tmpZip);
                die('Invalid ZIP archive');
            }

            $restoreDir = __DIR__ . '/restore_tmp';
            @mkdir($restoreDir, 0700, true);
            $zip->extractTo($restoreDir);
            $zip->close();

            /* 13. Restore only expected JSON files */
            foreach ($jsonFiles as $file) {
                $src = $restoreDir . '/' . $file;
                if (file_exists($src)) {
                    rename($src, __DIR__ . '/' . $file);
                }
            }

            /* 14. Cleanup */
            unlink($tmpZip);
            update_service("display");
            update_service("rtmp0");
            update_service("rtmp1");
            update_service("udp0");
            update_service("udp1");
            update_service("udp2");
            update_service("srt");
            update_service("custom");
            update_service("input");
            break;
        case 'install_deps':
            exec('sudo apt update');
            exec('sudo apt install -y apache2 php libapache2-mod-php vainfo ufw intel-media-va-driver-non-free libavcodec-extra mesa-utils i965-va-driver libmfx1 certbot intel-gpu-tools python3-certbot-nginx ffmpeg nginx v4l-utils python3-pip mpv libnginx-mod-rtmp alsa-utils vlan git zlib1g-dev php-zip php-curl php-gd');
            break;
        case 'resinatll';
            exec('cd /tmp;git clone --depth 1 https://git.dbhatt.org/hw_partner/urmic_digital_encoder_decoder.git;cd urmic_digital_encoder_decoder;chmod +x install.sh;./install.sh');
            break;
    }
    if ($_POST['action'] === "backup")
        include 'header.php';
} else
    include 'header.php';

?>
<script>
    function confirmReboot() {
        return confirm("Are you sure you want to reboot?");
    }

    function confirmReset() {
        return confirm("All settings will be gone . Are you sure you want to reset ?");
    }

    function confirmUpdate() {
        return confirm("Newer version will be downloaded and installed Do not turn off power .");
    }

    function confirmbackup() {
        return confirm("Are you sure you want to download backup ? ");
    }

    function confirmDeps() {
        return confirm("Are you sure you want to run Installation Dependency ?");
    }

    function confirmReinstall() {
        return confirm("Are you sure you want to Reinstall Software ?");
    }
</script>


<div class="containerindex">
    <div class="grid">
        <div class="card wide">
            Device ID :- <?php echo trim(file_get_contents('/sys/class/dmi/id/product_uuid')); ?><br>
            Project Name :- URMI Universal Encoder / Decoder<br>
            Software Version :- <?php echo $version; ?> <br>
        </div>
        <div class="card wide">
            <form method="post" class="form-center">
                <button type="submit" name="action" value="backup" class="green-btn">Download Backup File</button>
            </form>
        </div>
        <div class="card wide">
            <form method="post" class="form-center" onsubmit="return confirmReboot();">
                <button type="submit" name="action" value="reboot" class="green-btn">Reboot</button>
            </form>
        </div>
        <div class="card wide">
            <form method="post" class="form-center" onsubmit="return confirmReset();">
                <button type="submit" name="action" value="reset" class="red-btn">Reset Settings</button>
            </form>
        </div>
        <div class="card wide">
            <form method="post" class="form-center" onsubmit="return confirmUpdate()">
                <button type="submit" name="action" value="update" class="red-btn">Update Firmware</button>
            </form>
        </div>
        <div class="card wide">
            <form method="post" class="form-center" onsubmit="return confirmDeps()">
                <button type="submit" name="action" value="install_deps" class="blue-btn">Install Dependencies</button>
            </form>
        </div>
        <div class="card wide">
            <form method="post" class="form-center" onsubmit="return confirmReinstall()">
                <button type="submit" name="action" value="resinatll" class="blue-btn">ReInstall Software</button>
            </form>
        </div>
        <div class="card wide">
            <form method="post" class="form-center" enctype="multipart/form-data"
                onsubmit="return confirm('Are you sure you want to restore using this file ? All settings will be restored as per backup file .')">

                <label>Select restore file (.bin only):</label>
                <input type="file"
                    name="shree_bhattji_encoder"
                    accept=".bin"
                    required>
                <button type="submit" name="action" value="restore" class="red-btn">Restore</button>

            </form>
        </div>
    </div>
</div>
<?php include 'footer.php'; ?>