diff --git a/application/controllers/Update.php b/application/controllers/Update.php index 5c6937735..b3b3ef253 100644 --- a/application/controllers/Update.php +++ b/application/controllers/Update.php @@ -211,7 +211,7 @@ class Update extends CI_Controller { $gz = gzopen($url, 'r'); if ($gz === FALSE) { $this->update_status("FAILED: Could not download from clublog.org"); - log_message('error', 'FAILED: Could not download exceptions from clublog.org'); + log_message('error', 'FAILED: Could not download data from clublog.org'); exit(); } @@ -223,6 +223,7 @@ class Update extends CI_Controller { if (file_put_contents($this->paths->make_update_path("cty.xml"), $data) === FALSE) { $this->update_status("FAILED: Could not write to cty.xml file"); + log_message('error', 'DXCC UPDATE FAILED: Could not write to cty.xml file'); exit(); } diff --git a/install/includes/core/core_class.php b/install/includes/core/core_class.php index a62ecce27..a9c80a215 100644 --- a/install/includes/core/core_class.php +++ b/install/includes/core/core_class.php @@ -8,22 +8,34 @@ class Core { // Counter variable $counter = 0; + $errors = []; // Validate the hostname if (isset($data['db_hostname']) and !empty($data['db_hostname'])) { $counter++; + } else { + $errors[] = "DB Hostname is missing."; } + // Validate the username if (isset($data['db_username']) and !empty($data['db_username'])) { $counter++; + } else { + $errors[] = "DB Username is missing."; } + // Validate the password if (isset($data['db_password']) and !empty($data['db_password'])) { - // pass + $counter++; + } else { + $errors[] = "DB Password is missing."; } + // Validate the database if (isset($data['db_name']) and !empty($data['db_name'])) { $counter++; + } else { + $errors[] = "DB Name is missing."; } if ($data['directory'] ?? '' != "") { @@ -31,36 +43,46 @@ class Core //pass folders real $counter++; } else { - echo "Directory " . $data['directory'] . " cannot be found"; - exit; + $errors[] = "Directory " . $data['directory'] . " does not exist."; } } else { + // directory is not set so nothing to check here $counter++; } // Validate First Name if (isset($data['firstname']) && !empty($data['firstname'])) { $counter++; + } else { + $errors[] = "First Name is missing."; } // Validate Last Name if (isset($data['lastname']) && !empty($data['lastname'])) { $counter++; + } else { + $errors[] = "Last Name is missing."; } // Validate Username if (isset($data['username']) && !empty($data['username'])) { $counter++; + } else { + $errors[] = "Username is missing."; } // Validate Callsign if (isset($data['callsign']) && !empty($data['callsign'])) { $counter++; + } else { + $errors[] = "Callsign is missing."; } // Validate Password if (isset($data['password']) && !empty($data['password'])) { $counter++; + } else { + $errors[] = "User Password is missing."; } // Validate Locator @@ -72,12 +94,14 @@ class Core $errors[] = "Invalid Maidenhead Locator format."; } } else { - $errors[] = "Locator is required."; + $errors[] = "Locator is missing."; } // Validate Confirm Password if (isset($data['cnfm_password']) && !empty($data['cnfm_password'])) { $counter++; + } else { + $errors[] = "Confirm Password is missing."; } // Validate Email Address @@ -90,58 +114,81 @@ class Core // Validate Timezone if (isset($data['timezone']) && is_numeric($data['timezone'])) { $counter++; + } else { + $errors[] = "Invalid Timezone."; } // Check if all the required fields have been entered - if ($counter == '13') { + if ($counter == '14') { + log_message('info', 'Data validation passed.'); return true; } else { - log_message('error', 'Failed to validate POST data'); + log_message('error', 'Data validation failed.'); + foreach ($errors as $error) { + log_message('error', $error); + } return false; } } - // Function to write the config file + // Function to write the database config file function write_config($data) { $template_path = 'config/database.php'; $output_path = $_SERVER['DOCUMENT_ROOT'] . '/' . $data['directory'] . '/application/config/database.php'; if (isset($_ENV['CI_ENV'])) { $output_path = $_SERVER['DOCUMENT_ROOT'] . '/' . $data['directory'] . '/application/config/'.$_ENV['CI_ENV'].'/database.php'; + log_message('info', 'CI_ENV is set to ' . $_ENV['CI_ENV'] . '. Using ' . $_ENV['CI_ENV'] . ' database.php config path.'); + } else { + log_message('info', 'CI_ENV is not set. Using default database.php config path.'); + } + + if (!file_exists($template_path)) { + log_message('error', 'database.php template file not found.'); + return false; } // Open the file $database_file = file_get_contents($template_path); + if ($database_file === false) { + log_message('error', 'Failed to read database.php template file.'); + return false; + } + log_message('info', 'database.php template file read successfully.'); // Sanitize DB Password from single quotes - $sanitized_db_pwd = preg_replace("/\\\\/i",'\\\\\\\\',$data['db_password']); // Escape the Escape char ( '\' becomes '\\' ) - $sanitized_db_pwd = preg_replace("/\'/i",'\\\\\'',$sanitized_db_pwd); // Escape the ' ( ' becomes \' ) + $sanitized_db_pwd = preg_replace("/\\\\/i",'\\\\\\\\',$data['db_password']); // Escape the Escape char ( '\' becomes '\\' ) + $sanitized_db_pwd = preg_replace("/\'/i",'\\\\\'',$sanitized_db_pwd); // Escape the ' ( ' becomes \' ) $new = str_replace("%HOSTNAME%", $data['db_hostname'], $database_file); $new = str_replace("%USERNAME%", $data['db_username'], $new); $new = str_replace("%PASSWORD%", $sanitized_db_pwd, $new); $new = str_replace("%DATABASE%", $data['db_name'], $new); + log_message('info', 'Database config file prepared successfully. Writing to file...'); // Write the new database.php file $handle = fopen($output_path, 'w+'); - - // Chmod the file, in case the user forgot - @chmod($output_path, 0777); + if ($handle === false) { + log_message('error', 'Failed to open target path for writing the database.php file.'); + return false; + } // Verify file permissions if (is_writable($output_path)) { - // Write the file if (fwrite($handle, $new)) { if(file_exists($output_path)) { + log_message('info', 'database.php file written successfully.'); return true; } else { + log_message('error', 'database.php file not found after writing.'); return false; } } else { return false; } } else { + log_message('error', 'database.php path is not writable.'); return false; } } @@ -153,15 +200,23 @@ class Core $output_path = '../application/config/config.php'; if (isset($_ENV['CI_ENV'])) { $output_path = '../application/config/'.$_ENV['CI_ENV'].'/config.php'; + log_message('info', 'CI_ENV is set to ' . $_ENV['CI_ENV'] . '. Using ' . $_ENV['CI_ENV'] . ' config.php config path.'); + } else { + log_message('info', 'CI_ENV is not set. Using default config.php config path.'); } // Open the file - $database_file = file_get_contents($template_path); + $config_file = file_get_contents($template_path); + if ($config_file === false) { + log_message('error', 'Failed to read config.php template file.'); + return false; + } + log_message('info', 'config.php template file read successfully.'); // creating a unique encryption key $encryptionkey = uniqid(bin2hex(random_bytes(8)), false); - $new = str_replace("%baselocator%", strtoupper($data['userlocator']), $database_file); + $new = str_replace("%baselocator%", strtoupper($data['userlocator']), $config_file); $new = str_replace("%websiteurl%", $data['websiteurl'], $new); $new = str_replace("%directory%", $data['directory'], $new); $new = str_replace("%callbook%", $data['global_call_lookup'], $new); @@ -190,24 +245,31 @@ class Core $new = str_replace("%encryptionkey%", $encryptionkey, $new); $new = str_replace("'%log_threshold%'", $data['log_threshold'], $new); + log_message('info', 'Config.php file prepared successfully. Writing to file...'); // Write the new config.php file $handle = fopen($output_path, 'w+'); + if ($handle === false) { + log_message('error', 'Failed to open target path for writing the config.php file.'); + return false; + } // Verify file permissions if (is_writable($output_path)) { - // Write the file if (fwrite($handle, $new)) { if(file_exists($output_path)) { + log_message('info', 'config.php file written successfully.'); return true; } else { + log_message('error', 'config.php file not found after writing.'); return false; } } else { return false; } } else { + log_message('error', 'config.php path is not writable.'); return false; } } diff --git a/install/includes/core/database_class.php b/install/includes/core/database_class.php index fe37dd9f2..5795b19cc 100644 --- a/install/includes/core/database_class.php +++ b/install/includes/core/database_class.php @@ -8,11 +8,16 @@ class Database { $mysqli = new mysqli($data['db_hostname'], $data['db_username'], $data['db_password'], $data['db_name']); // Check for errors - if (mysqli_connect_errno()) + if (mysqli_connect_errno()) { + log_message('error', 'Database connection error: ' . mysqli_connect_error()); return false; + } // Open the default SQL file - $query = file_get_contents('assets/install.sql'); + if (!$query = file_get_contents('assets/install.sql')) { + log_message('error', 'Failed to read install.sql file.'); + return false; + } $newpw = password_hash($data['password'], PASSWORD_DEFAULT); $newquery = str_replace("%%FIRSTUSER_NAME%%", str_replace("'", "\\'", $data['username']), $query); @@ -26,24 +31,37 @@ class Database { $newquery = str_replace("%%FIRSTUSER_DXCC%%", $data['dxcc'], $newquery); $newquery = str_replace("%%FIRSTUSER_CITY%%", str_replace("'", "\\'", $data['city']), $newquery); $newquery = str_replace("%%FIRSTUSER_USERLANGUAGE%%", $data['userlanguage'], $newquery); + log_message('info', 'SQL queries prepared successfully. Writing to database...'); mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); - // Execute a multi query - $mysqli->multi_query($newquery); + try { + // Execute a multi query + $mysqli->multi_query($newquery); - // MultiQuery is NON-Blocking,so wait until everything is done - do { - null; - } while ($mysqli->next_result()); + // MultiQuery is NON-Blocking,so wait until everything is done + do { + null; + } while ($mysqli->next_result()); - $result = $mysqli->store_result(); + $mysqli->store_result(); - // Close the connection - $mysqli->close(); + // Close the connection + $mysqli->close(); - return true; + log_message('info', 'Database tables created successfully.'); + return true; + + } catch (mysqli_sql_exception $e) { + log_message('error', 'Database Error: ' . $e->getMessage()); + + if ($mysqli->ping()) { + $mysqli->close(); + } + + return false; + } } function database_check($data) { @@ -63,7 +81,6 @@ class Database { throw new Exception(__("Unable to create database: ") . $link->error); } - // Wählen Sie die Datenbank aus if (!$link->select_db($data['db_name'])) { throw new Exception(__("Unable to select database: ") . $link->error); } @@ -74,12 +91,30 @@ class Database { throw new Exception(__("Database is not empty.")); } - $mysql_version = $link->server_info; - + $version_query = $link->query("SELECT VERSION() as version")->fetch_assoc(); // $link->server_info sometimes returns wrong version or additional (in this case unnecessary) information, e.g. 5.5.5-10.3.29-MariaDB-0+deb10u1 + if (!$version_query) { + throw new Exception(__("Unable to get Database version: ") . $link->error); + } + if (!isset($version_query['version'])) { + throw new Exception(__("Database version could not be retrieved.")); + } + $mysql_version = $version_query['version']; + + // in case of a previous failed installation it can happen that still the migration lockfile is existent + // this would prevent the migration from running or at least would cause a unnecessary delay + // so we delete it here + $lockfile = sys_get_temp_dir() . '/.migration_running'; + if (file_exists($lockfile)) { + log_message('info', 'Removing migration lockfile. Not expected to be present at this point.'); + unlink($lockfile); + } + $link->close(); return $mysql_version; + } catch (Exception $e) { + log_message('error', 'Database Check Error: ' . $e->getMessage()); return 'Error: ' . $e->getMessage(); } } diff --git a/install/index.php b/install/index.php index 52384055f..ae4b6917f 100644 --- a/install/index.php +++ b/install/index.php @@ -1557,7 +1557,7 @@ if (!file_exists('.lock')) { } else { db_connection_results.addClass('alert-warning'); $('#db_connection_test_button').html(originalButtonText).prop('disabled', false); - db_connection_results.html(" " + "

' . $mysql_version . '', '' . $mariadb_version . ''); ?>"); + db_connection_results.html(" " + "

' . $mysql_version . '', '' . $mariadb_version . ''); ?>"); } resolve(true); } @@ -1857,7 +1857,7 @@ if (!file_exists('.lock')) { if ((checklistPrechecks.hasClass('fa-check-circle') || checklistPrechecks.hasClass('fa-exclamation-triangle')) && checklistConfiguration.hasClass('fa-check-circle') && - checklistDatabase.hasClass('fa-check-circle') && + (checklistDatabase.hasClass('fa-check-circle') || checklistDatabase.hasClass('fa-exclamation-triangle')) && (checklistFirstUser.hasClass('fa-check-circle') || checklistFirstUser.hasClass('fa-exclamation-triangle'))) { install_possible = true; } diff --git a/install/run.php b/install/run.php index 339e08ec8..6cb1879d3 100644 --- a/install/run.php +++ b/install/run.php @@ -148,7 +148,7 @@ } }, error: async function(error) { - await log_message('error', "Install Lock Check went wrong..."); + await log_message('error', "Install Lock Check went wrong... Ajax failed. Error: " + error.status); reject(error); window.location.href = "" + "index.php/user/login"; } @@ -183,7 +183,7 @@ } }, error: async function(error) { - await log_message('error', 'File: Could not write file. Ajax failed.'); + await log_message('error', 'File: Could not write file. Ajax failed. Status: ' + error.status + ' Status Text: ' + error.statusText); running(field, false, true); reject(error); } @@ -218,7 +218,7 @@ } }, error: async function(error) { - await log_message('error', 'File: Could not write file. Ajax failed.'); + await log_message('error', 'File: Could not write file. Ajax failed. Status: ' + error.status + ' Status Text: ' + error.statusText); running(field, false, true); reject(error); } @@ -253,7 +253,7 @@ }, error: async function(error) { running(field, false, true); - await log_message('error', 'Creating database tables failed. Ajax crashed.'); + await log_message('error', 'Creating database tables failed. Ajax crashed. Status: ' + error.status + ' Status Text: ' + error.statusText); reject(error); } }); @@ -283,7 +283,7 @@ }, error: async function(error) { running(field, false, true); - await log_message('error', 'Could not migrate database. Ajax crashed.'); + await log_message('error', 'Could not migrate database. Ajax crashed. Status: ' + error.status + ' Status Text: ' + error.statusText); reject(error); } }); @@ -305,13 +305,13 @@ resolve(); } else { running(field, false, true); - await log_message('error', 'Could not update DXCC data.'); + await log_message('error', 'Could not update DXCC data. Check application/logs for any error messages.'); reject(""); } }, error: async function(error) { running(field, false, true); - await log_message('error', 'Could not update DXCC data. Ajax crashed.'); + await log_message('error', 'Could not update DXCC data. Ajax crashed. Status: ' + error.status + ' Status Text: ' + error.statusText); reject(error); } }); @@ -343,7 +343,7 @@ }, error: async function(error) { running(field, false, true); - await log_message('error', 'Could not create .lock file. Ajax crashed'); + await log_message('error', 'Could not create .lock file. Ajax crashed. Status: ' + error.status + ' Status Text: ' + error.statusText); reject(error); } }); diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php index 256748747..e27ade116 100644 --- a/system/libraries/Migration.php +++ b/system/libraries/Migration.php @@ -348,8 +348,10 @@ class CI_Migration { log_message('debug', 'Finished migrating to '.$current_version); // After the migrations we can remove the lockfile - unlink($this->_migration_lockfile); - log_message('debug', 'Deleted migration lockfile'); + if (file_exists($this->_migration_lockfile)) { + unlink($this->_migration_lockfile); + log_message('debug', 'Deleted migration lockfile'); + } } else { log_message('error', 'Failed to create Migration Lockfile. Check directory permissions.');