envExists()) { render_page('Already installed', '

Cypht is already configured. Delete .env first if you really want to run the installer again.

', 'locked'); exit; } $web_installer = new Hm_Web_Installer($installer, APP_PATH.'.install_token'); session_set_cookie_params(['httponly' => true, 'samesite' => 'Lax', 'secure' => Hm_Web_Installer::isHttps($_SERVER)]); session_start(); if (!$web_installer->tokenExists()) { $web_installer->generateToken(); } $given_token = $_GET['token'] ?? ($_POST['token'] ?? ''); if (!$web_installer->tokenMatches($given_token)) { render_page('Installer locked', '

Missing or invalid access token.

'. '

Retrieve it from .install_token next to this script (over FTP, SSH, or your host\'s file manager; enable "show hidden files" if needed), then reopen this page with ?token= appended.

', 'locked'); exit; } // A valid token proves filesystem access, promoting this session from // anonymous to installer-operator: regenerate the id so a session fixed // before that point (e.g. via a cookie set on a shared subdomain) can't // be reused to submit the form on the operator's behalf. if (empty($_SESSION['token_verified'])) { session_regenerate_id(true); $_SESSION['token_verified'] = true; } $missing = array_keys(array_filter($installer->checkRequirements(), fn($ok) => !$ok)); if (!empty($missing)) { render_page('Missing requirements', '

The server is missing: '.htmlspecialchars(implode(', ', $missing)).'

', 'error'); exit; } if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(24)); } $csrf_token = $_SESSION['csrf_token']; $ajax_action = $_POST['ajax_action'] ?? ''; if ($_SERVER['REQUEST_METHOD'] === 'POST' && in_array($ajax_action, ['test_auth_connection', 'test_storage_dirs'], true)) { header('Content-Type: application/json'); if (!Hm_Web_Installer::secretMatches($csrf_token, $_POST['csrf_token'] ?? '')) { echo json_encode(['success' => false, 'error' => 'Invalid session, reload the form and try again.']); exit; } $values = Hm_Web_Installer::collectFormValues($_POST); if ($ajax_action === 'test_auth_connection') { $errors = Hm_Web_Installer::validate($values, APP_PATH, ['AUTH']); if (!empty($errors)) { echo json_encode(['success' => false, 'error' => implode(' ', $errors)]); exit; } $result = $web_installer->testAuthConnection($values); echo json_encode(['success' => $result['success'], 'error' => $result['error']]); exit; } $errors = Hm_Web_Installer::validate($values, APP_PATH, ['DIRS']); if (!empty($errors)) { echo json_encode(['success' => false, 'error' => implode(' ', $errors)]); exit; } $result = Hm_Web_Installer::checkStorageWritable($values); echo json_encode(['success' => $result['success'], 'error' => $result['error']]); exit; } if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!Hm_Web_Installer::secretMatches($csrf_token, $_POST['csrf_token'] ?? '')) { render_page('Installer locked', '

Invalid session, reload the form and try again.

', 'locked'); exit; } $values = Hm_Web_Installer::collectFormValues($_POST); $errors = Hm_Web_Installer::validate($values, APP_PATH); if (!empty($errors)) { render_page('Installation failed', '', 'error'); exit; } try { $result = $web_installer->install($values, $_POST['admin_user'] ?? '', $_POST['admin_pass'] ?? ''); $body = '
'.htmlspecialchars($result['output']).'
'; if ($result['success']) { $web_installer->consumeToken(); render_page('Installation complete', $body.'

Delete or rename install.php now.

', 'success'); } else { $hint = $result['env_written'] ? '

.env was written before this step failed. If you retry, delete .env first or the installer will report itself as already configured.

' : ''; render_page('Installation failed', $body.$hint, 'error'); } } catch (Throwable $e) { error_log('Cypht installer error: '.$e->getMessage()); render_page('Installation failed', '

An unexpected error occurred. Check the server error log.

', 'error'); } exit; } render_form($csrf_token, $given_token); function field($name, $label, $type = 'text', $hint = '', $required = false) { $hint_html = $hint ? ''.htmlspecialchars($hint).'' : ''; $input = ''; if ($type === 'checkbox') { return ''; } return ''; } function driver_select() { $opts = ''; foreach (['mysql', 'pgsql', 'sqlite'] as $driver) { $opts .= ''; } return ''; } function auth_type_select() { $opts = ''; foreach (['DB' => 'Database', 'IMAP' => 'IMAP server', 'LDAP' => 'LDAP server'] as $value => $label) { $opts .= ''; } return ''; } function auth_fields_group($auth_type, $fields, $visible_by_default = false) { return '
'.$fields.'
'; } function render_form($csrf_token, $token) { $steps = [ ['title' => 'Authentication', 'validate' => 'auth', 'only_for' => '', 'fields' => auth_type_select(). auth_fields_group('DB', driver_select(). field('DB_HOST', 'Host', 'text', '', true). field('DB_NAME', 'Name', 'text', '', true). field('DB_USER', 'User', 'text', '', true). field('DB_PASS', 'Password', 'password'), true). auth_fields_group('IMAP', field('IMAP_AUTH_SERVER', 'Server', 'text', '', true). field('IMAP_AUTH_PORT', 'Port', 'text', '', true). field('IMAP_AUTH_TLS', 'Use TLS', 'checkbox'). field('IMAP_AUTH_NAME', 'Display name', 'text', 'optional, defaults to the server address'). field('IMAP_AUTH_SIEVE_CONF_HOST', 'Sieve filter server', 'text', 'optional')). auth_fields_group('LDAP', field('LDAP_AUTH_SERVER', 'Server', 'text', '', true). field('LDAP_AUTH_PORT', 'Port', 'text', '', true). field('LDAP_AUTH_BASE_DN', 'Base DN', 'text', '', true). field('LDAP_AUTH_UID_ATTR', 'UID attribute', 'text', '', true). field('LDAP_AUTH_TLS', 'Use TLS', 'checkbox'))], ['title' => 'Storage', 'validate' => 'dirs', 'only_for' => '', 'fields' => field('USER_SETTINGS_DIR', 'User settings directory', 'text', 'outside the web root', true). field('ATTACHMENT_DIR', 'Attachment directory', 'text', 'outside the web root', true)], ['title' => 'Admin account (optional)', 'validate' => '', 'only_for' => 'DB', 'fields' => field('admin_user', 'Username'). field('admin_pass', 'Password', 'password')], ]; $panels = ''; $dots = ''; $i = 0; foreach ($steps as $step) { $i++; $panels .= '
'. '

'.htmlspecialchars($step['title']).'

'. ''. $step['fields'].'
'; $dots .= ''; } $body = '
    '.$dots.'
'. '
'. ''. ''. $panels. ''. '
'. step_script(); render_page('Cypht installer', $body); } function step_script() { return <<<'HTML' HTML; } function render_page($title, $body, $variant = 'info') { echo ''. ''. ''.htmlspecialchars($title).''. page_style(). '
'. '

'.htmlspecialchars($title).'

'. $body. '
'; } function page_style() { return <<<'CSS' CSS; }