0) {
$should_skip_to_user = true;
$resume_step = 'user';
$user_count_result = mysqli_query($mysqli, "SELECT COUNT(*) AS user_count FROM users");
if ($user_count_result) {
$user_count_row = mysqli_fetch_assoc($user_count_result);
if (intval($user_count_row['user_count']) > 0) {
$install_is_live = true;
}
} else {
// Cannot prove the install is empty, so treat it as live
$install_is_live = true;
}
}
if ($install_is_live) {
$resume_step = 'company';
$company_result = mysqli_query($mysqli, "SELECT company_locale FROM companies WHERE company_id = 1");
if (!$company_result) {
// Cannot prove either step is outstanding, so treat both as done
$company_exists = true;
$localization_done = true;
$resume_step = 'telemetry';
} elseif ($company_row = mysqli_fetch_assoc($company_result)) {
$company_exists = true;
$resume_step = 'localization';
if (trim($company_row['company_locale'] ?? '') !== '') {
$localization_done = true;
$resume_step = 'telemetry';
}
}
}
// Restore needs a database connection and an empty install. A populated one restores
// from the command line instead - scripts/restore_cli.php.
if (!$install_is_live) {
$all_tables = mysqli_query($mysqli, "SHOW TABLES");
if ($all_tables && mysqli_num_rows($all_tables) > 0) {
$can_show_restore = true;
}
}
}
/*
* config.php is written when the database step completes, but $config_enable_setup is only
* appended to it by the LAST step, so the flag is absent for the whole middle of an install
* and the wizard has to stay open across that gap or it cannot be finished.
*
* Deriving the flag from the database instead - closing setup as soon as the install looked
* "live" - is what stranded people: the first user made it live, three steps before there
* were companies or settings rows, and /setup and /login.php then redirected at each other
* until the browser gave up. Deriving it from any later step has the same shape, because the
* step that writes the flag is behind the gate that reads it.
*
* So the page stays open until the flag says otherwise, and each handler below refuses to run
* a second time on its own. That keeps the reason the derived flag was added in the first
* place - the restore handler drops every table, imports whatever archive it is handed and
* rewrites the uploads directory - without the page-level gate that came with it.
*/
if (!isset($config_enable_setup)) {
$config_enable_setup = 1;
}
if ($config_enable_setup == 0) {
header("Location: /login.php");
exit;
}
include_once "../includes/settings_localization_array.php";
$errorLog = ini_get('error_log') ?: "Debian/Ubuntu default is usually /var/log/apache2/error.log";
// Get a list of all available timezones
$timezones = DateTimeZone::listIdentifiers();
if (isset($_POST['add_database'])) {
// Check if database has been set up already. If it has, direct user to edit directly instead.
if (file_exists('../config.php')) {
$_SESSION['alert_message'] = "Database already configured. Any further changes should be made by editing the config.php file.";
header("Location: ?user");
exit;
}
$host = filter_var(trim($_POST['host']), FILTER_SANITIZE_STRING);
$database = filter_var(trim($_POST['database']), FILTER_SANITIZE_STRING);
$username = filter_var(trim($_POST['username']), FILTER_SANITIZE_STRING);
$password = filter_var(trim($_POST['password']), FILTER_SANITIZE_STRING);
$config_base_url = $_SERVER['HTTP_HOST'];
$installation_id = randomString(32);
// Ensure variables meet specific criteria (very basic examples)
if (!preg_match('/^[a-zA-Z0-9.-]+$/', $host)) {
die('Invalid host format.');
}
// Test database connection before writing it to config.php
$conn = mysqli_connect($host, $username, $password, $database);
if (!$conn) {
exit("Database connection failed - please check and try again
" . mysqli_connect_error());
}
$new_config = " 0 && $max_upload_bytes > 0 && $content_length > $max_upload_bytes) {
$too_large = true;
} elseif (isset($_FILES['backup_zip']) && in_array($_FILES['backup_zip']['error'], [UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE], true)) {
$too_large = true;
}
if ($too_large) {
$_SESSION['alert_message'] = "That backup is too large to upload through a browser (this server accepts up to "
. backupFormatBytes($max_upload_bytes)
. "). Restore it from the command line instead - there is no size limit there. Copy the backup onto this server and run: php "
. dirname(__DIR__) . "/scripts/restore_cli.php --file=/path/to/backup.zip";
header("Location: ?restore");
exit;
}
if (!isset($_FILES['backup_zip']) || $_FILES['backup_zip']['error'] !== UPLOAD_ERR_OK) {
$_SESSION['alert_message'] = "No backup file was uploaded, or the upload failed.";
header("Location: ?restore");
exit;
}
if (strtolower(pathinfo($_FILES['backup_zip']['name'], PATHINFO_EXTENSION)) !== 'zip') {
$_SESSION['alert_message'] = "Only .zip backup archives can be restored.";
header("Location: ?restore");
exit;
}
// The key belongs to the install that MADE the backup, which on a rebuilt server is not
// this one, so it is asked for rather than read from config.php.
$restore_key = trim($_POST['backup_key'] ?? '');
if ($restore_key === '') {
$restore_key = $config_backup_key ?? '';
}
if ($restore_key === '') {
$_SESSION['alert_message'] = "Enter the backup encryption key. It is shown in Maintenance > Backup on the install that made this archive.";
header("Location: ?restore");
exit;
}
$temp_zip = tempnam(sys_get_temp_dir(), "itflow_restore_upload_");
if (!move_uploaded_file($_FILES['backup_zip']['tmp_name'], $temp_zip)) {
@unlink($temp_zip);
$_SESSION['alert_message'] = "Could not save the uploaded backup file.";
header("Location: ?restore");
exit;
}
@chmod($temp_zip, 0600);
$restore_error = null;
$restored = backupRestoreArchive($mysqli, $temp_zip, $restore_key, $restore_error);
@unlink($temp_zip);
if (!$restored) {
$_SESSION['alert_message'] = $restore_error;
header("Location: ?restore");
exit;
}
// Close setup behind us. The gate above would now do this on its own because the
// restored database has users, but the flag is what stops the wizard being reachable
// at all.
$config_path = __DIR__ . "/../config.php";
if (@file_put_contents($config_path, "\n\$config_enable_setup = 0;\n\n", FILE_APPEND | LOCK_EX) === false) {
$_SESSION['alert_message'] = "Backup restored, but config.php could not be updated - please set \$config_enable_setup = 0 in it by hand.";
} else {
$_SESSION['alert_message'] = "Backup restored. Log in with the credentials that were in use when the backup was taken.";
}
header("Location: ../login.php");
exit;
}
if (isset($_POST['add_user'])) {
// SELECT COUNT(*) returns exactly one row whatever the count is, so the mysqli_num_rows()
// test this replaces was always 1 and never fired: a resubmitted form created a second
// user and then died on the duplicate user_settings row. $install_is_live is the same
// count, taken at the top of the file, and it fails closed.
if ($install_is_live) {
$_SESSION['alert_message'] = "Users already exist in the database. Clear them to reconfigure here.";
header("Location: ?company");
exit;
}
$name = escapeSql($_POST['name']);
$email = escapeSql($_POST['email']);
$password = password_hash(trim($_POST['password']), PASSWORD_DEFAULT);
//Generate master encryption key
$site_encryption_master_key = randomString();
//Generate user specific key
$user_specific_encryption_ciphertext = setupFirstUserSpecificKey(trim($_POST['password']), $site_encryption_master_key);
mysqli_query($mysqli,"INSERT INTO users SET user_name = '$name', user_email = '$email', user_password = '$password', user_specific_encryption_ciphertext = '$user_specific_encryption_ciphertext', user_role_id = 3");
// Normally 1, but the table's AUTO_INCREMENT can already have moved on, so ask for it.
$user_id = intval(mysqli_insert_id($mysqli));
mkdirMissing("../uploads/users/$user_id");
//Check to see if a file is attached
if ($_FILES['file']['tmp_name'] != '') {
// get details of the uploaded file
$file_error = 0;
$file_tmp_path = $_FILES['file']['tmp_name'];
$file_name = $_FILES['file']['name'];
$file_size = $_FILES['file']['size'];
$file_type = $_FILES['file']['type'];
$file_extension = strtolower(end(explode('.',$_FILES['file']['name'])));
// sanitize file-name
$new_file_name = md5(time() . $file_name) . '.' . $file_extension;
// check if file has one of the following extensions
$allowed_file_extensions = array('jpg', 'jpeg', 'gif', 'png', 'webp');
if (in_array($file_extension,$allowed_file_extensions) === false) {
$file_error = 1;
}
//Check File Size
if ($file_size > 2097152) {
$file_error = 1;
}
if ($file_error == 0) {
// directory in which the uploaded file will be moved
$upload_file_dir = "../uploads/users/$user_id/";
$dest_path = $upload_file_dir . $new_file_name;
move_uploaded_file($file_tmp_path, $dest_path);
//Set Avatar
mysqli_query($mysqli,"UPDATE users SET user_avatar = '$new_file_name' WHERE user_id = $user_id");
$_SESSION['alert_message'] = 'File successfully uploaded.';
} else {
$_SESSION['alert_message'] = 'There was an error moving the file to upload directory. Please make sure the upload directory is writable by web server.';
}
}
//Create Settings
mysqli_query($mysqli,"INSERT INTO user_settings SET user_id = $user_id");
$_SESSION['alert_message'] = "User $name created";
header("Location: ?company");
exit;
}
if (isset($_POST['add_company_settings'])) {
// Run once. A second pass would add a second companies row and re-seed the defaults.
if ($company_exists) {
$_SESSION['alert_message'] = "Company details have already been saved.";
header("Location: ?localization");
exit;
}
$name = escapeSql($_POST['name']);
$country = escapeSql($_POST['country']);
$address = escapeSql($_POST['address']);
$city = escapeSql($_POST['city']);
$state = escapeSql($_POST['state']);
$zip = escapeSql($_POST['zip']);
$phone = preg_replace("/[^0-9]/", '',$_POST['phone']);
$email = escapeSql($_POST['email']);
$website = escapeSql($_POST['website']);
$tax_id = escapeSql($_POST['tax_id']);
mysqli_query($mysqli,"INSERT INTO companies SET company_name = '$name', company_address = '$address', company_city = '$city', company_state = '$state', company_zip = '$zip', company_country = '$country', company_phone = '$phone', company_email = '$email', company_website = '$website', company_tax_id = '$tax_id'");
//Check to see if a file is attached
if ($_FILES['file']['tmp_name'] != '') {
// get details of the uploaded file
$file_error = 0;
$file_tmp_path = $_FILES['file']['tmp_name'];
$file_name = $_FILES['file']['name'];
$file_size = $_FILES['file']['size'];
$file_type = $_FILES['file']['type'];
$file_extension = strtolower(end(explode('.',$_FILES['file']['name'])));
// sanitize file-name
$new_file_name = md5(time() . $file_name) . '.' . $file_extension;
// check if file has one of the following extensions
$allowed_file_extensions = array('jpg', 'jpeg', 'png');
if (in_array($file_extension,$allowed_file_extensions) === false) {
$file_error = 1;
}
//Check File Size
if ($file_size > 2097152) {
$file_error = 1;
}
if ($file_error == 0) {
// directory in which the uploaded file will be moved
$upload_file_dir = "../uploads/settings/";
$dest_path = $upload_file_dir . $new_file_name;
move_uploaded_file($file_tmp_path, $dest_path);
mysqli_query($mysqli,"UPDATE companies SET company_logo = '$new_file_name' WHERE company_id = 1");
$_SESSION['alert_message'] = 'File successfully uploaded.';
} else {
$_SESSION['alert_message'] = 'There was an error moving the file to upload directory. Please make sure the upload directory is writable by web server.';
}
}
// Seed the defaults shared with the CLI installer
seedDefaultData($mysqli);
$_SESSION['alert_message'] = "Company $name created";
header("Location: ?localization");
}
if (isset($_POST['add_localization_settings'])) {
// Run once. A second pass would add a second Cash account.
if ($localization_done) {
$_SESSION['alert_message'] = "Localization has already been saved.";
header("Location: ?telemetry");
exit;
}
$locale = escapeSql($_POST['locale']);
$currency_code = escapeSql($_POST['currency_code']);
$timezone = escapeSql($_POST['timezone']);
mysqli_query($mysqli,"UPDATE companies SET company_locale = '$locale', company_currency = '$currency_code' WHERE company_id = 1");
mysqli_query($mysqli,"UPDATE settings SET config_timezone = '$timezone' WHERE company_id = 1");
// Create Default Cash Account
mysqli_query($mysqli,"INSERT INTO accounts SET account_name = 'Cash', account_currency_code = '$currency_code'");
$_SESSION['alert_message'] = "Localization Info saved";
header("Location: ?telemetry");
}
if (isset($_POST['add_telemetry'])) {
if (isset($_POST['share_data']) && $_POST['share_data'] == 1) {
mysqli_query($mysqli,"UPDATE settings SET config_telemetry = 2");
$comments = escapeSql($_POST['comments']);
$sql = mysqli_query($mysqli,"SELECT company_city, company_country, company_currency, company_name, company_state,
company_website FROM companies WHERE company_id = 1");
$row = mysqli_fetch_assoc($sql);
$company_name = $row['company_name'];
$website = $row['company_website'];
$city = $row['company_city'];
$state = $row['company_state'];
$country = $row['company_country'];
$currency = $row['company_currency'];
$postdata = http_build_query(
array(
'installation_id' => "$installation_id",
'company_name' => "$company_name",
'website' => "$website",
'city' => "$city",
'state' => "$state",
'country' => "$country",
'currency' => "$currency",
'comments' => "$comments",
'collection_method' => 1
)
);
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
$context = stream_context_create($opts);
$result = file_get_contents('https://telemetry.itflow.org', false, $context);
echo $result;
}
//final setup stages
$myfile = fopen("../config.php", "a");
$txt = "\$config_enable_setup = 0;\n\n";
fwrite($myfile, $txt);
fclose($myfile);
header("Location: ../login.php");
exit;
}
?>
| PHP Extensions | ||
|---|---|---|
| = htmlspecialchars($check['name']); ?> | = htmlspecialchars($check['value']); ?> | |
| PHP Configuration | ||
| = htmlspecialchars($check['name']); ?> | = htmlspecialchars($check['value']); ?> | |
| Shell Commands | ||
| = htmlspecialchars($check['name']); ?> | = htmlspecialchars($check['value']); ?> | |
| SSL Checks | ||
| = htmlspecialchars($check['name']); ?> | = htmlspecialchars($check['value']); ?> | |
| Domain Checks | ||
| = htmlspecialchars($check['name']); ?> | = htmlspecialchars($check['value']); ?> | |
| File Permissions | ||
| = htmlspecialchars($check['name']); ?> | = htmlspecialchars($check['value']); ?> | |
config.php file.";
if (@$mysqli) {
echo "Next Step (User Setup) ";
} else {
echo "config.php.You must configure the database before restoring a backup.
Go to Database SetupThis install already has a user - the rest of your team is added from Admin > Users once you are logged in.
Company details have already been saved - they can be changed later from Admin > Settings.
This is the start of your journey towards amazing client management
A few tips:
This install was left part-way through setup - click on the button below to pick up where it stopped.
A database must be created before proceeding - click on the button below to get started.
ITFlow is free software: you can redistribute and/or modify it under the terms of the GNU General Public License.
It is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.