add installation page

This commit is contained in:
hanyixuanten
2026-08-10 13:17:42 +08:00
parent ea431ae124
commit 778e1b7e26
5 changed files with 691 additions and 1 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteRule ^(?:index|manage)\.php$ - [L]
RewriteRule ^(?:index|manage|install)\.php$ - [L]
RewriteRule ^ index.php [L,QSA]
</IfModule>
+5
View File
@@ -1,5 +1,10 @@
<?php
if (!file_exists(__DIR__.'/config.php')) {
require(__DIR__.'/lib/install_redirect.php');
redirect_to_installer();
}
require(__DIR__.'/config.php');
require(__DIR__.'/lib/http.php');
require(__DIR__.'/lib/auth.php');
+655
View File
@@ -0,0 +1,655 @@
<?php
/* First-run installer. It provisions the database, imports schema.mysql.sql and
writes config.php. The missing config.php is the only thing gating this
endpoint, so it refuses to run once that file exists. */
require(__DIR__.'/lib/http.php');
function install_escape($value) {
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
function install_config_path() {
return __DIR__.DIRECTORY_SEPARATOR.'config.php';
}
function install_is_complete() {
return file_exists(install_config_path());
}
function install_page_url() {
$script = isset($_SERVER['SCRIPT_NAME']) ? (string) $_SERVER['SCRIPT_NAME'] : '';
return $script === '' ? '/install.php' : $script;
}
function install_home_url() {
$base = rtrim(str_replace('\\', '/', dirname(install_page_url())), '/');
return $base === '' ? '/' : $base.'/';
}
function install_request_is_https() {
$https = isset($_SERVER['HTTPS']) ? strtolower((string) $_SERVER['HTTPS']) : '';
return $https === 'on' || $https === '1'
|| (isset($_SERVER['SERVER_PORT']) && (string) $_SERVER['SERVER_PORT'] === '443');
}
function install_start_session() {
if (session_status() === PHP_SESSION_ACTIVE) {
return TRUE;
}
if (session_status() === PHP_SESSION_DISABLED || headers_sent()) {
return FALSE;
}
session_name('PHPGITSERVERINSTALL');
session_set_cookie_params(array(
'lifetime' => 0,
'path' => install_home_url(),
'secure' => install_request_is_https(),
'httponly' => TRUE,
'samesite' => 'Strict'));
return @session_start();
}
function install_csrf_token() {
if (!install_start_session()) {
return FALSE;
}
if (!isset($_SESSION['install_csrf_token'])
|| !is_string($_SESSION['install_csrf_token'])
|| strlen($_SESSION['install_csrf_token']) !== 64) {
try {
$_SESSION['install_csrf_token'] = bin2hex(random_bytes(32));
} catch (Exception $exception) {
return FALSE;
}
}
return $_SESSION['install_csrf_token'];
}
function install_post_value($name) {
return isset($_POST[$name]) && is_string($_POST[$name]) ? $_POST[$name] : '';
}
function install_post_checked($name) {
return isset($_POST[$name]) && $_POST[$name] === '1';
}
/* MySQL identifiers cannot be bound as parameters, so they are restricted to a
conservative character set before any interpolation. */
function install_identifier_is_valid($value) {
return is_string($value) && preg_match('~^[A-Za-z0-9_]{1,64}$~D', $value) === 1;
}
function install_host_is_valid($value) {
return is_string($value) && preg_match('~^[A-Za-z0-9._%-]{1,255}$~D', $value) === 1;
}
function install_quote_identifier($value) {
return '`'.$value.'`';
}
function install_schema_statements() {
$buffer = @file_get_contents(__DIR__.DIRECTORY_SEPARATOR.'schema.mysql.sql');
if ($buffer === FALSE) {
return FALSE;
}
$statements = array();
foreach (preg_split('~;\s*(?:\r?\n|$)~', $buffer) as $statement) {
$statement = trim($statement);
if ($statement !== '') {
$statements[] = $statement;
}
}
return $statements;
}
function install_validate($input) {
$errors = array();
if ($input['url_base'] !== ''
&& !preg_match('~^/[A-Za-z0-9._~/-]*[A-Za-z0-9._~-]$~D', $input['url_base'])) {
$errors[] = '基础路径必须以 / 开头,且不能以 / 结尾;部署在域名根路径时请留空。';
}
if ($input['git_executable'] === '' || strlen($input['git_executable']) > 255
|| preg_match('~[\x00-\x1F\x7F]~', $input['git_executable'])) {
$errors[] = 'Git 可执行文件路径无效。';
}
if (!install_host_is_valid($input['db_host'])) {
$errors[] = '数据库主机名无效。';
}
if (!preg_match('~^[1-9][0-9]{0,4}$~D', $input['db_port']) || (int) $input['db_port'] > 65535) {
$errors[] = '数据库端口无效。';
}
if (!install_identifier_is_valid($input['db_name'])) {
$errors[] = '数据库名只能包含字母、数字和下划线,最多 64 个字符。';
}
if (!install_identifier_is_valid($input['db_user'])) {
$errors[] = '数据库用户名只能包含字母、数字和下划线,最多 64 个字符。';
}
if ($input['db_password'] === '' || strlen($input['db_password']) > 255) {
$errors[] = '数据库密码不能为空,且最多 255 个字符。';
}
if (!install_host_is_valid($input['db_user_host'])) {
$errors[] = '数据库账号来源主机无效。';
}
if ($input['provision'] && $input['root_user'] === '') {
$errors[] = '选择自动创建数据库时必须填写管理账号名。';
}
if ($input['provision'] && !install_host_is_valid($input['root_user'])) {
$errors[] = '数据库管理账号名无效。';
}
if (auth_normalize_username($input['admin_username']) === FALSE) {
$errors[] = '管理员用户名须为 3 至 64 个字母、数字、点、短横线或下划线。';
}
if (!auth_password_is_valid($input['admin_password'])) {
$errors[] = '管理员密码长度须为 12 至 72 个字符,且不能超过 72 字节。';
} else if (!hash_equals($input['admin_password'], $input['admin_password_confirmation'])) {
$errors[] = '两次输入的管理员密码不一致。';
}
return $errors;
}
function install_connect($dsn, $username, $password) {
return new PDO($dsn, $username, $password, array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => FALSE));
}
function install_server_dsn($input) {
return 'mysql:host='.$input['db_host'].';port='.$input['db_port'].';charset=utf8mb4';
}
function install_database_dsn($input) {
return 'mysql:host='.$input['db_host'].';port='.$input['db_port']
.';dbname='.$input['db_name'].';charset=utf8mb4';
}
function install_provision_database($input) {
$connection = install_connect(
install_server_dsn($input), $input['root_user'], $input['root_password']);
$connection->exec(
'CREATE DATABASE IF NOT EXISTS '.install_quote_identifier($input['db_name'])
.' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci');
/* CREATE/ALTER USER reject bound parameters, so the password is quoted by the
driver and inlined instead. */
$account = $connection->quote($input['db_user']).'@'.$connection->quote($input['db_user_host']);
$password = $connection->quote($input['db_password']);
$connection->exec('CREATE USER IF NOT EXISTS '.$account.' IDENTIFIED BY '.$password);
$connection->exec('ALTER USER '.$account.' IDENTIFIED BY '.$password);
$connection->exec(
'GRANT SELECT, INSERT, UPDATE, DELETE ON '
.install_quote_identifier($input['db_name']).'.* TO '.$account);
$connection->exec('FLUSH PRIVILEGES');
}
function install_import_schema($connection) {
$statements = install_schema_statements();
if ($statements === FALSE) {
return array('status' => 'schema_unreadable');
}
$existing = $connection->query('SHOW TABLES LIKE \'pgit_%\'')->fetchAll(PDO::FETCH_COLUMN);
if (!empty($existing)) {
return array('status' => 'schema_present', 'tables' => count($existing));
}
try {
foreach ($statements as $statement) {
$connection->exec($statement);
}
} catch (PDOException $exception) {
$driver_code = isset($exception->errorInfo[1]) ? (int) $exception->errorInfo[1] : 0;
if ($driver_code === 1142 || $driver_code === 1044) {
return array('status' => 'schema_denied');
}
throw $exception;
}
return array('status' => 'schema_imported', 'tables' => count($statements));
}
/* SHOW GRANTS output is awkward to parse reliably, so DELETE is confirmed with a
statement that matches no rows but still requires the privilege. */
function install_verify_delete_privilege($connection) {
try {
$connection->exec('DELETE FROM pgit_users WHERE 1 = 0');
return TRUE;
} catch (PDOException $exception) {
return FALSE;
}
}
function install_create_administrator($connection, $username, $password) {
$statement = $connection->prepare(
'SELECT id FROM pgit_users WHERE username = ? LIMIT 1');
$statement->execute(array($username));
if ($statement->fetch() !== FALSE) {
return 'administrator_exists';
}
$statement = $connection->prepare(
'INSERT INTO pgit_users (username, password_hash) VALUES (?, ?)');
$statement->execute(array($username, password_hash($password, PASSWORD_DEFAULT)));
return 'administrator_created';
}
function install_config_contents($input) {
$administrators = " ".var_export($input['admin_username'], TRUE).",\n";
$secure_cookie = $input['session_cookie_secure']
? " 'session_cookie_secure' => TRUE,\n" : '';
return "<?php\n\n"
."/* Generated by install.php. See config.php.sample for every option. */\n\n"
."\$url_base = ".var_export($input['url_base'], TRUE).";\n"
."\$git_executable = ".var_export($input['git_executable'], TRUE).";\n\n"
."\$auth = array(\n"
." 'enabled' => TRUE,\n"
." 'registration_enabled' => ".($input['registration_enabled'] ? 'TRUE' : 'FALSE').",\n"
.$secure_cookie
." 'administrators' => array(\n"
.$administrators
." ),\n"
." 'database' => array(\n"
." 'dsn' => ".var_export(install_database_dsn($input), TRUE).",\n"
." 'username' => ".var_export($input['db_user'], TRUE).",\n"
." 'password' => ".var_export($input['db_password'], TRUE)."));\n\n"
."\$managed_repositories = array(\n"
." 'options' => array(\n"
." 'read' => TRUE,\n"
." 'push' => TRUE,\n"
." 'branches' => TRUE,\n"
." 'tags' => TRUE,\n"
." 'other_refs' => FALSE,\n"
." 'allow_non_fast_forward' => FALSE,\n"
." 'max_object_bytes' => 268435456,\n"
." 'max_pack_objects' => 100000,\n"
." 'max_request_bytes' => 0));\n\n"
."\$repos = array();\n";
}
/* Exclusive creation keeps a second concurrent installer from overwriting a
configuration that was just written. */
function install_write_config($input) {
$path = install_config_path();
$handle = @fopen($path, 'xb');
if ($handle === FALSE) {
return FALSE;
}
$contents = install_config_contents($input);
$written = fwrite($handle, $contents);
fclose($handle);
if ($written !== strlen($contents)) {
@unlink($path);
return FALSE;
}
@chmod($path, 0600);
return TRUE;
}
function install_run($input) {
$steps = array();
try {
if ($input['provision']) {
install_provision_database($input);
$steps[] = array('success', '数据库与应用账号已创建或更新,并已授予所需权限。');
}
/* The application account deliberately has no CREATE privilege, so the
schema is imported over the administrative connection when available. */
$schema_connection = $input['provision']
? install_connect(
install_database_dsn($input), $input['root_user'], $input['root_password'])
: install_connect(
install_database_dsn($input), $input['db_user'], $input['db_password']);
$import = install_import_schema($schema_connection);
if ($import['status'] === 'schema_unreadable') {
return array('errors' => array('无法读取 schema.mysql.sql。'), 'steps' => $steps);
}
if ($import['status'] === 'schema_denied') {
return array(
'errors' => array(
'应用账号没有建表权限,无法导入表结构。请先手动导入 schema.mysql.sql'
.'或勾选“自动创建数据库”并填写管理账号。'),
'steps' => $steps);
}
$steps[] = $import['status'] === 'schema_imported'
? array('success', '已导入 '.$import['tables'].' 张表。')
: array('success', '检测到已存在的 pgit_ 表,跳过导入。');
$connection = install_connect(
install_database_dsn($input), $input['db_user'], $input['db_password']);
$steps[] = array('success', '已使用应用账号连接数据库。');
if (!install_verify_delete_privilege($connection)) {
return array(
'errors' => array(
'应用账号缺少 DELETE 权限,删除仓库和用户会失败。请授予 DELETE 后重试。'),
'steps' => $steps);
}
$steps[] = array('success', '已确认应用账号具备 SELECT、INSERT、UPDATE 和 DELETE 权限。');
$administrator = install_create_administrator(
$connection, $input['admin_username'], $input['admin_password']);
$steps[] = $administrator === 'administrator_created'
? array('success', '管理员账号 '.$input['admin_username'].' 已创建。')
: array('success', '账号 '.$input['admin_username'].' 已存在,将其设为管理员。');
} catch (PDOException $exception) {
return array(
'errors' => array('数据库操作失败:'.$exception->getMessage()),
'steps' => $steps);
}
if (!install_write_config($input)) {
return array(
'errors' => array(
'config.php 写入失败。请确认项目目录可写,或该文件是否已存在。'),
'steps' => $steps);
}
$steps[] = array('success', 'config.php 已写入,权限设为 0600。');
return array('errors' => array(), 'steps' => $steps, 'complete' => TRUE);
}
function install_send_head() {
echo <<<'HTML'
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>安装 - PHP Git 服务器</title>
<style>
:root { color-scheme: light dark; --ink: #1f2530; --muted: #5b6472; --line: #d5dae1;
--accent: #176b43; --danger: #a43a3a; }
* { box-sizing: border-box; }
body { max-width: 52rem; margin: 0 auto; padding: 2.5rem 1.25rem; line-height: 1.6;
color: var(--ink); background: #fff;
font-family: system-ui, -apple-system, "Segoe UI", "Noto Sans CJK SC", sans-serif; }
h1 { margin: 0 0 .25rem; font-size: 1.5rem; }
h2 { margin: 2rem 0 .5rem; font-size: 1.05rem; }
p { margin: 0 0 1rem; }
.lead { color: var(--muted); }
fieldset { margin: 0 0 1.5rem; padding: 1rem 1.1rem 1.2rem; border: 1px solid var(--line);
border-radius: .4rem; }
legend { padding: 0 .4rem; font-weight: 600; }
.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .85rem; }
label { display: block; margin-bottom: .3rem; font-weight: 600; font-size: .9rem; }
input[type="text"], input[type="password"], input[type="number"] { width: 100%;
min-height: 2.5rem; padding: .45rem .6rem; border: 1px solid #9ba4b0;
border-radius: .35rem; background: #fff; color: var(--ink); font: inherit; }
input:focus { border-color: #1769aa; outline: 2px solid #1769aa; outline-offset: 1px; }
.check { display: flex; align-items: flex-start; gap: .5rem; margin-top: .85rem; }
.check input { margin-top: .35rem; }
.check label { margin: 0; font-weight: 500; }
.hint { margin: .3rem 0 0; color: var(--muted); font-size: .85rem; }
button { min-height: 2.7rem; padding: .5rem 1.4rem; border: 1px solid #175b3a;
border-radius: .35rem; color: #fff; background: var(--accent); font: inherit;
font-weight: 600; cursor: pointer; }
button:hover { background: #125635; }
.notice { margin: 0 0 1rem; padding: .65rem .85rem; border-left: .25rem solid; }
.notice-success { border-color: var(--accent); background: #edf8f1; color: #155735; }
.notice-error { border-color: var(--danger); background: #fff0f0; color: #842828; }
.notice-warning { border-color: #8a6116; background: #fdf6e6; color: #6d4c11; }
ul { margin: 0 0 1rem; padding-left: 1.2rem; }
code { font-size: .95em; word-break: break-all;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
pre { padding: .7rem .9rem; overflow-x: auto; border: 1px solid var(--line);
border-radius: .4rem; background: #f6f7f9; }
@media (max-width: 40rem) { .grid { grid-template-columns: 1fr; } }
@media (prefers-color-scheme: dark) {
:root { --ink: #e6e9ef; --muted: #9aa4b2; --line: #2b323d; }
body { background: #12161c; }
input[type="text"], input[type="password"], input[type="number"] {
border-color: #596474; background: #1a1f27; color: var(--ink); }
pre { background: #1a1f27; }
.notice-success { background: #152b20; color: #95dab1; }
.notice-error { background: #331c1c; color: #f0abab; }
.notice-warning { background: #2e2513; color: #e6c886; }
}
</style>
</head>
<body>
<h1>PHP Git 服务器安装</h1>
HTML;
}
function install_send_form($input, $errors) {
$token = install_csrf_token();
if ($token === FALSE) {
echo '<p class="notice notice-error">当前无法初始化安全会话,无法继续安装。</p>' ."\n";
return;
}
echo '<p class="lead">检测到项目根目录没有 <code>config.php</code>。填写以下信息即可创建'
.'数据库表、首个管理员账号并生成配置文件。</p>' ."\n";
if (!install_request_is_https()) {
echo '<p class="notice notice-warning">当前不是 HTTPS 连接。密码将以明文传输,'
.'建议改用 HTTPS 或仅从本机访问安装页面。</p>' ."\n";
}
if (!extension_loaded('pdo_mysql')) {
echo '<p class="notice notice-error">PHP 未启用 <code>pdo_mysql</code> 扩展,'
.'安装无法继续。请先安装该扩展并重启 PHP。</p>' ."\n";
}
if (!is_writable(__DIR__)) {
echo '<p class="notice notice-error">项目目录不可写,无法生成 <code>config.php</code>。'
.'请调整目录权限后刷新。</p>' ."\n";
}
if (!empty($errors)) {
echo '<div class="notice notice-error"><ul>' ."\n";
foreach ($errors as $error) {
echo '<li>'.install_escape($error).'</li>' ."\n";
}
echo '</ul></div>' ."\n";
}
echo '<form method="post" action="'.install_escape(install_page_url()).'">' ."\n";
echo '<input type="hidden" name="csrf_token" value="'.install_escape($token).'">' ."\n";
echo '<fieldset><legend>应用</legend><div class="grid">' ."\n";
echo '<div><label for="url_base">基础路径</label>'
.'<input id="url_base" name="url_base" type="text" value="'
.install_escape($input['url_base']).'" placeholder="/php-git-server">'
.'<p class="hint">部署在域名根路径时留空。</p></div>' ."\n";
echo '<div><label for="git_executable">Git 可执行文件</label>'
.'<input id="git_executable" name="git_executable" type="text" value="'
.install_escape($input['git_executable']).'" required>'
.'<p class="hint">不可用时会回退到纯 PHP 实现。</p></div>' ."\n";
echo '</div><div class="check"><input id="registration_enabled" name="registration_enabled" '
.'type="checkbox" value="1"'.($input['registration_enabled'] ? ' checked' : '').'>'
.'<label for="registration_enabled">允许公开注册新账号</label></div>' ."\n";
echo '<div class="check"><input id="session_cookie_secure" name="session_cookie_secure" '
.'type="checkbox" value="1"'.($input['session_cookie_secure'] ? ' checked' : '').'>'
.'<label for="session_cookie_secure">HTTPS 在可信反向代理终止(设置 Secure Cookie</label>'
.'</div></fieldset>' ."\n";
echo '<fieldset><legend>数据库</legend><div class="grid">' ."\n";
echo '<div><label for="db_host">主机</label><input id="db_host" name="db_host" type="text" '
.'value="'.install_escape($input['db_host']).'" required></div>' ."\n";
echo '<div><label for="db_port">端口</label><input id="db_port" name="db_port" '
.'type="number" min="1" max="65535" value="'.install_escape($input['db_port'])
.'" required></div>' ."\n";
echo '<div><label for="db_name">数据库名</label><input id="db_name" name="db_name" '
.'type="text" value="'.install_escape($input['db_name']).'" required></div>' ."\n";
echo '<div><label for="db_user">应用账号</label><input id="db_user" name="db_user" '
.'type="text" value="'.install_escape($input['db_user']).'" required></div>' ."\n";
echo '<div><label for="db_password">应用账号密码</label><input id="db_password" '
.'name="db_password" type="password" autocomplete="new-password" required></div>' ."\n";
echo '<div><label for="db_user_host">账号来源主机</label><input id="db_user_host" '
.'name="db_user_host" type="text" value="'.install_escape($input['db_user_host']).'">'
.'<p class="hint">MySQL 账号的 host 部分,需与连接方式一致。</p></div>' ."\n";
echo '</div></fieldset>' ."\n";
echo '<fieldset><legend>自动创建数据库(可选)</legend>' ."\n";
echo '<div class="check"><input id="provision" name="provision" type="checkbox" value="1"'
.($input['provision'] ? ' checked' : '').'>'
.'<label for="provision">使用管理账号创建数据库与应用账号,并授予所需权限</label></div>' ."\n";
echo '<p class="hint">不勾选时,请先手动创建数据库和账号;安装仍会校验 DELETE 权限。</p>' ."\n";
echo '<div class="grid">' ."\n";
echo '<div><label for="root_user">管理账号</label><input id="root_user" name="root_user" '
.'type="text" value="'.install_escape($input['root_user']).'"></div>' ."\n";
echo '<div><label for="root_password">管理账号密码</label><input id="root_password" '
.'name="root_password" type="password" autocomplete="off"></div>' ."\n";
echo '</div></fieldset>' ."\n";
echo '<fieldset><legend>管理员账号</legend>' ."\n";
echo '<p class="hint">该账号会写入 <code>$auth[\'administrators\']</code>'
.'安装完成后即可访问管理界面。</p>' ."\n";
echo '<div class="grid">' ."\n";
echo '<div><label for="admin_username">用户名</label><input id="admin_username" '
.'name="admin_username" type="text" minlength="3" maxlength="64" value="'
.install_escape($input['admin_username']).'" autocomplete="off" required></div>' ."\n";
echo '<div><label for="admin_password">密码</label><input id="admin_password" '
.'name="admin_password" type="password" minlength="12" maxlength="72" '
.'autocomplete="new-password" required></div>' ."\n";
echo '<div><label for="admin_password_confirmation">确认密码</label>'
.'<input id="admin_password_confirmation" name="admin_password_confirmation" '
.'type="password" minlength="12" maxlength="72" autocomplete="new-password" required>'
.'</div>' ."\n";
echo '</div></fieldset>' ."\n";
echo '<button type="submit">开始安装</button>' ."\n";
echo '</form>' ."\n";
}
function install_send_result($steps) {
echo '<p class="notice notice-success">安装完成。</p>' ."\n";
foreach ($steps as $step) {
echo '<p class="notice notice-'.install_escape($step[0]).'">'
.install_escape($step[1]).'</p>' ."\n";
}
echo '<h2>请立即完成以下操作</h2>' ."\n";
echo '<p>删除或禁止访问安装脚本,避免它在配置被移除后再次可用:</p>' ."\n";
echo '<pre><code>rm install.php</code></pre>' ."\n";
echo '<p>确认 <code>config.php</code> 仅应用进程可读,并检查 <code>repos</code> 目录权限。'
.'详细说明见 <code>usage.md</code>。</p>' ."\n";
echo '<p><a href="'.install_escape(install_home_url()).'">进入首页并登录</a></p>' ."\n";
}
function install_send_foot() {
echo '</body>' ."\n".'</html>' ."\n";
}
if (install_is_complete()) {
send_error(
403,
'Forbidden',
'Installation is already complete. Remove install.php, or delete config.php to reinstall.');
}
require(__DIR__.'/lib/auth.php');
/* Session must be started before any output so the cookie can be set. */
install_start_session();
header_nocache();
header('Content-Type: text/html; charset=utf-8');
header('X-Content-Type-Options: nosniff');
header('Content-Security-Policy: default-src \'none\'; style-src \'unsafe-inline\'; '
.'form-action \'self\'; base-uri \'none\'; frame-ancestors \'none\'');
$method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
if ($method !== 'GET' && $method !== 'HEAD' && $method !== 'POST') {
send_status(405, 'Method Not Allowed');
header('Allow: GET, HEAD, POST');
header('Content-Type: text/plain; charset=utf-8');
echo 'Method Not Allowed';
die();
}
$input = array(
'url_base' => rtrim(str_replace('\\', '/', dirname(install_page_url())), '/'),
'git_executable' => 'git',
'registration_enabled' => TRUE,
'session_cookie_secure' => FALSE,
'db_host' => '127.0.0.1',
'db_port' => '3306',
'db_name' => 'php_git_server',
'db_user' => 'php_git_server',
'db_password' => '',
'db_user_host' => '127.0.0.1',
'provision' => FALSE,
'root_user' => 'root',
'root_password' => '',
'admin_username' => '',
'admin_password' => '',
'admin_password_confirmation' => '');
if ($method !== 'POST') {
install_send_head();
install_send_form($input, array());
install_send_foot();
die();
}
if (!request_content_type_is(
get_request_header('Content-Type'), 'application/x-www-form-urlencoded')) {
send_error(415, 'Unsupported Media Type', 'Expected a form-encoded request.');
}
$expected_token = install_csrf_token();
if ($expected_token === FALSE
|| !hash_equals($expected_token, install_post_value('csrf_token'))) {
send_error(403, 'Forbidden', 'The form security token is invalid.');
}
$input = array(
'url_base' => rtrim(trim(install_post_value('url_base')), '/'),
'git_executable' => trim(install_post_value('git_executable')),
'registration_enabled' => install_post_checked('registration_enabled'),
'session_cookie_secure' => install_post_checked('session_cookie_secure'),
'db_host' => trim(install_post_value('db_host')),
'db_port' => trim(install_post_value('db_port')),
'db_name' => trim(install_post_value('db_name')),
'db_user' => trim(install_post_value('db_user')),
'db_password' => install_post_value('db_password'),
'db_user_host' => trim(install_post_value('db_user_host')),
'provision' => install_post_checked('provision'),
'root_user' => trim(install_post_value('root_user')),
'root_password' => install_post_value('root_password'),
'admin_username' => trim(install_post_value('admin_username')),
'admin_password' => install_post_value('admin_password'),
'admin_password_confirmation' => install_post_value('admin_password_confirmation'));
if ($input['db_user_host'] === '') {
$input['db_user_host'] = $input['db_host'];
}
$errors = install_validate($input);
if (!extension_loaded('pdo_mysql')) {
$errors[] = 'PHP 未启用 pdo_mysql 扩展。';
}
install_send_head();
if (!empty($errors)) {
install_send_form($input, $errors);
install_send_foot();
die();
}
$result = install_run($input);
if (empty($result['errors'])) {
install_send_result($result['steps']);
} else {
foreach ($result['steps'] as $step) {
echo '<p class="notice notice-'.install_escape($step[0]).'">'
.install_escape($step[1]).'</p>' ."\n";
}
install_send_form($input, $result['errors']);
}
install_send_foot();
+25
View File
@@ -0,0 +1,25 @@
<?php
/* Sends a first-run visitor to install.php. The target is derived from the
current script path, so it is always a fixed same-directory location. */
function redirect_to_installer() {
$script = isset($_SERVER['SCRIPT_NAME']) ? (string) $_SERVER['SCRIPT_NAME'] : '';
$base = rtrim(str_replace('\\', '/', dirname($script)), '/');
$target = $base.'/install.php';
if (!file_exists(__DIR__.'/../install.php')) {
$protocol = isset($_SERVER['SERVER_PROTOCOL'])
? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
header($protocol.' 500 Internal Server Error');
header('Content-Type: text/plain; charset=utf-8');
header('X-Content-Type-Options: nosniff');
echo 'config.php is missing and install.php is not available.';
die();
}
$protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
header($protocol.' 303 See Other');
header('Location: '.$target);
header('Cache-Control: no-store');
die();
}
+5
View File
@@ -1,5 +1,10 @@
<?php
if (!file_exists(__DIR__.'/config.php')) {
require(__DIR__.'/lib/install_redirect.php');
redirect_to_installer();
}
require(__DIR__.'/config.php');
require(__DIR__.'/lib/http.php');
require(__DIR__.'/lib/auth.php');