<?php
// PractiPrint 2.6 installer. Upload beside iprint(v2.6).zip in the website root.
ini_set('display_errors', '0');
ini_set('log_errors', '1');
header('Content-Type: text/html; charset=UTF-8');
header('Cache-Control: no-store, max-age=0');
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');

$version = '2.6';
$archiveName = 'iprint(v2.6).zip';
$archiveHash = '1aafdf66cc23ec609e7ddd812bb9bb2be250d73ff2d1dd7629476a8fb99d2a0b';
$manifest = array(
    'iprint/README_BG.txt' => '4d3867cea7fae0a53439d928a10ff277a4ddba358a121b191dfe93c06c9524d5',
    'iprint/VERSION.txt' => '8b6f2a818985188f55a3e4e26e239c69a21b70e1306b5231e65694ea4a547128',
    'iprint/api_iprint.php' => '8b9cbab8f528713b353fc17062f107bcdbf456990bd29c59df35fb03795cfa8d',
    'iprint/api_iprint_80.php' => '7201aba80f8a198b967d3d76b1be6579e9732d2bfa4933215c3dde1437b484aa',
    'iprint/api_iprint_a4.php' => 'b51abcd8575d0a0391d872cc34bf93e7699be22407b4afbaba361c556c59f966',
    'iprint/iprint_cleanup.php' => '509a12049d86be0baf0cc78466f48761404052976d06b4e76edc65207c93e0be',
    'iprint/iprint_common.php' => '528f8640b1dbe66f597ed90ed31e7ddf8c276ed57d96accc518bb67fdae4b8f5',
    'iprint/iprint_done.php' => 'd79dc3716051974c166d660921b06bc9908fdb46a5aa40c16ec415515fad0709',
    'iprint/iprint_image.php' => 'bffaf0564d512d76dfeb32b5ad733e44b6fc8e25323ea3aa09639e76c7dd32f1',
    'iprint/iprint_jobs/.htaccess' => 'fe9f46eded9a0d417fd56f73bd7f0f9a651e252f47426202c20cbb89a968a1f3',
    'iprint/iprint_jobs/index.php' => '173fa45cbe78f410d31acb40bcafdc0971996768d82eb2a108662ffce62e08d4',
    'iprint/iprint_js/2-4-1-html2canvas.min.js' => 'e87e550794322e574a1fda0c1549a3c70dae5a93d9113417a429016838eab8cb',
    'iprint/iprint_js/html2canvas.min.js' => 'bacbbb275f41a08e6eb4db0c5b44d9477546186d3078757577f4205147d1814f',
    'iprint/iprint_upload.php' => '496529ca7bf3c2553c1b8db4cb4dbeaf1ce9724f49687d37ec3d3c31883d78e3',
);
$root = __DIR__;
$target = $root . '/iprint';
$marker = $target . '/.installed-v2.6';
$errors = array();
$messages = array();
$installed = false;
$locked = is_file($marker);

function iprint_h($value) {
    return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
function iprint_remove_tree($path) {
    if (!is_dir($path) || is_link($path)) {
        return;
    }
    foreach (new DirectoryIterator($path) as $entry) {
        if ($entry->isDot()) { continue; }
        if ($entry->isDir() && !$entry->isLink()) {
            iprint_remove_tree($entry->getPathname());
        } else {
            @unlink($entry->getPathname());
        }
    }
    @rmdir($path);
}
function iprint_check_target($path) {
    if (is_link($path)) {
        throw new RuntimeException('Не се допуска символна връзка: ' . basename($path));
    }
}

if (PHP_VERSION_ID < 70200) {
    $errors[] = 'Необходим е PHP 7.2 или по-нов.';
}
if (!class_exists('ZipArchive')) {
    $errors[] = 'Липсва PHP разширението ZipArchive (zip). Активирайте го от хостинг панела.';
}
if (!is_writable($root)) {
    $errors[] = 'PHP няма права за запис в основната директория на сайта.';
}
$documentRoot = isset($_SERVER['DOCUMENT_ROOT']) ? realpath($_SERVER['DOCUMENT_ROOT']) : false;
if ($documentRoot !== false && $documentRoot !== realpath($root)) {
    $errors[] = 'Качете install.php и ZIP архива в корена на сайта, а не в поддиректория.';
}
if (!$locked && !is_file($root . '/' . $archiveName)) {
    $errors[] = 'Липсва ' . $archiveName . ' до install.php.';
}
if (session_status() !== PHP_SESSION_ACTIVE && !@session_start()) {
    $errors[] = 'PHP не може да стартира сесия за инсталацията.';
}
if (session_status() === PHP_SESSION_ACTIVE && empty($_SESSION['iprint_install_token'])) {
    $_SESSION['iprint_install_token'] = bin2hex(random_bytes(24));
}
$token = isset($_SESSION['iprint_install_token']) ? $_SESSION['iprint_install_token'] : '';

if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && !$locked && !$errors) {
    $submitted = isset($_POST['token']) && is_string($_POST['token']) ? $_POST['token'] : '';
    if ($token === '' || !hash_equals($token, $submitted)) {
        $errors[] = 'Невалидна сесия. Презаредете страницата и опитайте отново.';
    } else {
        $stage = null;
        $lock = null;
        $changed = array();
        $newDirs = array();
        $rollbackOK = true;
        $zip = new ZipArchive();
        $zipOpen = false;
        try {
            iprint_check_target($target);
            // A separate lock serializes installers opened in different browser sessions.
            $lockPath = $root . '/.iprint-install.lock';
            iprint_check_target($lockPath);
            $lock = @fopen($lockPath, 'c');
            if ($lock === false || !flock($lock, LOCK_EX | LOCK_NB)) {
                throw new RuntimeException('Друга инсталация е активна или не може да се създаде заключване.');
            }
            clearstatcache();
            if (is_file($marker)) {
                throw new RuntimeException('Версия 2.6 вече е инсталирана.');
            }
            $archive = $root . '/' . $archiveName;
            iprint_check_target($archive);
            if (!hash_equals($archiveHash, (string) @hash_file('sha256', $archive))) {
                throw new RuntimeException('ZIP архивът не съвпада с този инсталатор или е повреден. Качете отново оригиналната двойка файлове.');
            }
            if ($zip->open($archive) !== true) {
                throw new RuntimeException('ZIP архивът не може да бъде отворен.');
            }
            $zipOpen = true;
            $stage = tempnam(sys_get_temp_dir(), 'iprint26-');
            if ($stage === false || !@unlink($stage) || !@mkdir($stage, 0700)) {
                throw new RuntimeException('Не може да се създаде временна директория.');
            }
            $seen = array();
            // Extract only the exact files shipped with this installer; no extractTo().
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $name = $zip->getNameIndex($i);
                if (in_array($name, array('iprint/', 'iprint/iprint_jobs/', 'iprint/iprint_js/'), true)) {
                    continue;
                }
                if (!isset($manifest[$name]) || isset($seen[$name])) {
                    throw new RuntimeException('Неочакван или дублиран файл в ZIP архива.');
                }
                $seen[$name] = true;
                $data = $zip->getFromIndex($i);
                if ($data === false || !hash_equals($manifest[$name], hash('sha256', $data))) {
                    throw new RuntimeException('Невалидно съдържание: ' . $name);
                }
                $stagedFile = $stage . '/new/' . $name;
                if (!is_dir(dirname($stagedFile)) && !@mkdir(dirname($stagedFile), 0700, true)) {
                    throw new RuntimeException('Не може да се подготви ' . $name);
                }
                if (@file_put_contents($stagedFile, $data) !== strlen($data)) {
                    throw new RuntimeException('Не може да се запише ' . $name);
                }
            }
            $zip->close();
            $zipOpen = false;
            if (count($seen) !== count($manifest)) {
                throw new RuntimeException('В архива липсват необходими файлове.');
            }
            foreach (array($target, $target . '/iprint_js', $target . '/iprint_jobs') as $dir) {
                iprint_check_target($dir);
                if (!is_dir($dir)) {
                    if (!@mkdir($dir, 0755)) {
                        throw new RuntimeException('Не може да се създаде ' . basename($dir));
                    }
                    $newDirs[] = $dir;
                }
                if (!is_writable($dir)) {
                    throw new RuntimeException('Няма права за запис в ' . basename($dir));
                }
            }
            // Prepare all backups before changing any existing package files.
            foreach ($manifest as $name => $hash) {
                $destination = $root . '/' . $name;
                iprint_check_target($destination);
                if (file_exists($destination)) {
                    if (!is_file($destination) || !is_readable($destination)) {
                        throw new RuntimeException('Не може да се архивира текущият файл ' . $name);
                    }
                    $backup = $stage . '/old/' . $name;
                    if (!is_dir(dirname($backup)) && !@mkdir(dirname($backup), 0700, true)) {
                        throw new RuntimeException('Не може да се създаде временно резервно копие.');
                    }
                    if (!@copy($destination, $backup)) {
                        throw new RuntimeException('Не може да се архивира ' . $name);
                    }
                }
            }
            foreach ($manifest as $name => $hash) {
                $destination = $root . '/' . $name;
                $temporary = $destination . '.new-' . bin2hex(random_bytes(6));
                if (!@copy($stage . '/new/' . $name, $temporary)) {
                    @unlink($temporary);
                    throw new RuntimeException('Не може да се копира ' . $name);
                }
                if (!@chmod($temporary, 0644) || !@rename($temporary, $destination)) {
                    @unlink($temporary);
                    throw new RuntimeException('Не може да се инсталира ' . $name);
                }
                $changed[] = $name;
                if (function_exists('opcache_invalidate')) { @opcache_invalidate($destination, true); }
            }
            // Test actual job-directory writes, not only permission flags.
            $probe = $target . '/iprint_jobs/.write-test-' . bin2hex(random_bytes(6));
            if (@file_put_contents($probe, 'ok') !== 2) {
                @unlink($probe);
                throw new RuntimeException('Проверката за запис на изображения е неуспешна.');
            }
            @unlink($probe);
            iprint_check_target($marker);
            if (@file_put_contents($marker, date('c') . "\n", LOCK_EX) === false) {
                throw new RuntimeException('Не може да се запише потвърждение за инсталацията.');
            }
            $installed = true;
            $locked = true;
            unset($_SESSION['iprint_install_token']);
            $messages[] = 'PractiPrint iPrint 2.6 е инсталиран успешно в /iprint/.';
            $messages[] = 'Проверката за запис в iprint_jobs е успешна. Съществуващите изображения са запазени.';
            foreach (array($archive, __FILE__) as $remove) {
                if (!@unlink($remove)) {
                    $messages[] = 'Изтрийте ръчно ' . basename($remove) . ' от корена на сайта.';
                }
            }
        } catch (Throwable $error) {
            // Restore every changed package file. Unrelated website files are untouched.
            foreach (array_reverse($changed) as $name) {
                $destination = $root . '/' . $name;
                $backup = $stage . '/old/' . $name;
                $restored = is_file($backup) ? @copy($backup, $destination) : @unlink($destination);
                if (!$restored) { $rollbackOK = false; }
                if (function_exists('opcache_invalidate')) { @opcache_invalidate($destination, true); }
            }
            foreach (array_reverse($newDirs) as $dir) { @rmdir($dir); }
            $errors[] = $error->getMessage();
            if (!$rollbackOK) {
                $errors[] = 'Част от файловете не можаха да бъдат възстановени. Резервните копия са в ' . $stage;
            }
        } finally {
            if ($zipOpen) { $zip->close(); }
            if ($stage && $rollbackOK) { iprint_remove_tree($stage); }
            if (is_resource($lock)) {
                flock($lock, LOCK_UN);
                fclose($lock);
            }
        }
    }
}
?>
<!doctype html>
<html lang="bg">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>PractiPrint 2.6 — инсталиране</title>
<style>
body{margin:0;background:#f3f5f8;color:#182437;font:16px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;padding:32px 16px}
main{max-width:680px;margin:24px auto;background:white;border-radius:18px;padding:32px;box-shadow:0 8px 35px #17243b12}
h1{font-size:28px;margin-top:0}code{background:#f0f3f8;padding:2px 5px;border-radius:4px;overflow-wrap:anywhere}
.error{background:#fff0ef;border-left:4px solid #c63328;padding:12px}.success{background:#eaf8f0;padding:12px;border-left:4px solid #178d4e}
button{font:inherit;font-weight:600;background:#0866ff;color:white;border:0;border-radius:9px;padding:13px 22px;cursor:pointer}li{margin:8px 0}
</style>
</head>
<body><main>
<h1>PractiPrint 2.6</h1>
<?php foreach ($errors as $error): ?><p class="error"><?= iprint_h($error) ?></p><?php endforeach; ?>
<?php foreach ($messages as $message): ?><p class="success"><?= iprint_h($message) ?></p><?php endforeach; ?>
<?php if ($installed): ?>
<p>Включете желания API файл в страницата с документа. За 58 и 80 mm документът трябва да е в един елемент <code>.receipt-wrap</code>. Отделният A4 API също използва този елемент.</p>
<p><code>&lt;?php require $_SERVER['DOCUMENT_ROOT'] . '/iprint/api_iprint.php'; ?&gt;</code></p>
<p>За другите формати: <code>api_iprint_80.php</code> или <code>api_iprint_a4.php</code>. Тествайте от HTTPS страница с iOS PractiPrint 2.6.</p>
<?php elseif ($locked): ?>
<p>Версия 2.6 вече е инсталирана. Изтрийте останалите <code>install.php</code> и <code>iprint(v2.6).zip</code>, ако все още са в корена.</p>
<?php else: ?>
<p>Инсталира или обновява iPrint в <code>/iprint/</code> на този сайт.</p>
<ul><li>Поддържа 58 mm, 80 mm и A4.</li><li>Адресът на сайта се подава автоматично към приложението.</li><li>Изображенията остават на този сайт и са достъпни за печат 15 минути.</li></ul>
<p>При обновяване се заменят файловете от пакета. Ако сте ги редактирали, запазете свое копие преди инсталиране.</p>
<?php if (!$errors): ?>
<form method="post"><input type="hidden" name="token" value="<?= iprint_h($token) ?>"><button type="submit">Инсталирай iPrint 2.6</button></form>
<?php endif; ?>
<?php endif; ?>
</main></body></html>
