Reworked the entire debugger, only grather nessesary info and database comparison to existing and db.sql works now

This commit is contained in:
johnnyq 2024-11-05 17:36:50 -05:00
parent 2250269820
commit 351bb484a7
2 changed files with 1025 additions and 286 deletions

View File

@ -5,191 +5,503 @@ require_once "database_version.php";
require_once "config.php"; require_once "config.php";
$folderPath = 'uploads'; $checks = [];
function countFilesInDirectory($dir) { // Section: System Information
$count = 0; $systemInfo = [];
$size = 0;
$files = scandir($dir);
foreach ($files as $file) { // Operating System and Version
if ($file === '.' || $file === '..') { $os = php_uname();
continue; $systemInfo[] = [
} 'name' => 'Operating System',
'value' => $os,
];
$filePath = $dir . '/' . $file; // Web Server and Version
$webServer = $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown';
$systemInfo[] = [
'name' => 'Web Server',
'value' => $webServer,
];
if (is_file($filePath)) { // Kernel and Version
$count++; $kernelVersion = php_uname('r');
$size += filesize($filePath); $systemInfo[] = [
} elseif (is_dir($filePath)) { 'name' => 'Kernel Version',
$result = countFilesInDirectory($filePath); 'value' => $kernelVersion,
$count += $result['count']; ];
$size += $result['size'];
}
}
return [ // Database and Version
'count' => $count, $dbVersion = $mysqli->server_info;
'size' => $size $systemInfo[] = [
'name' => 'Database Version',
'value' => $dbVersion,
];
// Section: PHP Extensions
$phpExtensions = [];
$extensions = [
'php-mailparse' => 'mailparse',
'php-imap' => 'imap',
'php-mysqli' => 'mysqli',
'php-intl' => 'intl',
'php-curl' => 'curl',
'php-mbstring' => 'mbstring',
'php-gd' => 'gd',
];
foreach ($extensions as $name => $ext) {
$loaded = extension_loaded($ext);
$phpExtensions[] = [
'name' => "$name installed",
'passed' => $loaded,
'value' => $loaded ? 'Installed' : 'Not Installed',
]; ];
} }
// Function to compare two arrays recursively and return the differences // Section: PHP Configuration
function arrayDiffRecursive($array1, $array2) { $phpConfig = [];
$diff = array();
foreach ($array1 as $key => $value) { // Check if shell_exec is enabled
if (is_array($value)) { $disabled_functions = explode(',', ini_get('disable_functions'));
if (!isset($array2[$key]) || !is_array($array2[$key])) { $disabled_functions = array_map('trim', $disabled_functions);
$diff[$key] = $value; $shell_exec_enabled = !in_array('shell_exec', $disabled_functions);
} else {
$recursiveDiff = arrayDiffRecursive($value, $array2[$key]); $phpConfig[] = [
if (!empty($recursiveDiff)) { 'name' => 'shell_exec is enabled',
$diff[$key] = $recursiveDiff; 'passed' => $shell_exec_enabled,
'value' => $shell_exec_enabled ? 'Enabled' : 'Disabled',
];
// Check upload_max_filesize and post_max_size >= 500M
function return_bytes($val) {
$val = trim($val);
$unit = strtolower(substr($val, -1));
$num = (float)$val;
switch ($unit) {
case 'g':
$num *= 1024;
case 'm':
$num *= 1024;
case 'k':
$num *= 1024;
} }
return $num;
}
$required_bytes = 500 * 1024 * 1024; // 500M in bytes
$upload_max_filesize = ini_get('upload_max_filesize');
$post_max_size = ini_get('post_max_size');
$upload_passed = return_bytes($upload_max_filesize) >= $required_bytes;
$post_passed = return_bytes($post_max_size) >= $required_bytes;
$phpConfig[] = [
'name' => 'upload_max_filesize >= 500M',
'passed' => $upload_passed,
'value' => $upload_max_filesize,
];
$phpConfig[] = [
'name' => 'post_max_size >= 500M',
'passed' => $post_passed,
'value' => $post_max_size,
];
// PHP Memory Limit >= 128M
$memoryLimit = ini_get('memory_limit');
$memoryLimitBytes = return_bytes($memoryLimit);
$memoryLimitPassed = $memoryLimitBytes >= (128 * 1024 * 1024);
$phpConfig[] = [
'name' => 'PHP Memory Limit >= 128M',
'passed' => $memoryLimitPassed,
'value' => $memoryLimit,
];
// Max Execution Time >= 300 seconds
$maxExecutionTime = ini_get('max_execution_time');
$maxExecutionTimePassed = $maxExecutionTime >= 300;
$phpConfig[] = [
'name' => 'Max Execution Time >= 300 seconds',
'passed' => $maxExecutionTimePassed,
'value' => $maxExecutionTime . ' seconds',
];
// Check PHP version >= 8.2.0
$php_version = PHP_VERSION;
$php_passed = version_compare($php_version, '8.2.0', '>=');
$phpConfig[] = [
'name' => 'PHP version >= 8.2.0',
'passed' => $php_passed,
'value' => $php_version,
];
// Section: Shell Commands
$shellCommands = [];
if ($shell_exec_enabled) {
$commands = ['whois', 'dig', 'git'];
foreach ($commands as $command) {
$which = trim(shell_exec("which $command 2>/dev/null"));
$exists = !empty($which);
$shellCommands[] = [
'name' => "Command '$command' available",
'passed' => $exists,
'value' => $exists ? $which : 'Not Found',
];
} }
} else { } else {
if (!isset($array2[$key]) || $array2[$key] !== $value) { // If shell_exec is disabled, mark commands as unavailable
$diff[$key] = $value; foreach (['whois', 'dig', 'git'] as $command) {
} $shellCommands[] = [
} 'name' => "Command '$command' available",
} 'passed' => false,
'value' => 'shell_exec Disabled',
return $diff; ];
}
// Function to load the table structures from an SQL dump file URL
function loadTableStructuresFromSQLDumpURL($fileURL) {
$context = stream_context_create(array('http' => array('header' => 'Accept: application/octet-stream')));
$fileContent = file_get_contents($fileURL, false, $context);
if ($fileContent === false) {
return null;
}
$structure = array();
$queries = explode(";", $fileContent);
foreach ($queries as $query) {
$query = trim($query);
if (!empty($query)) {
if (preg_match("/^CREATE TABLE `(.*)` \((.*)\)$/s", $query, $matches)) {
$tableName = $matches[1];
$tableStructure = $matches[2];
$structure[$tableName] = array('structure' => $tableStructure);
}
}
}
return $structure;
}
// Function to fetch the database structure from the MySQL server
function fetchDatabaseStructureFromServer() {
global $mysqli;
$tables = array();
// Fetch table names
$result = $mysqli->query("SHOW TABLES");
if ($result->num_rows > 0) {
while ($row = $result->fetch_row()) {
$tableName = $row[0];
$tables[$tableName] = array();
} }
} }
// Fetch table structures // Section: SSL Checks
foreach ($tables as $tableName => &$table) { $sslChecks = [];
$result = $mysqli->query("SHOW CREATE TABLE `$tableName`");
if ($result->num_rows > 0) { // Check if accessing via HTTPS
$row = $result->fetch_row(); $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443;
$table['structure'] = $row[1]; $sslChecks[] = [
'name' => 'Accessing via HTTPS',
'passed' => $https,
'value' => $https ? 'Yes' : 'No',
];
// SSL Certificate Validity Check
if ($https) {
$streamContext = stream_context_create(["ssl" => ["capture_peer_cert" => true]]);
$socket = @stream_socket_client("ssl://{$_SERVER['HTTP_HOST']}:443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $streamContext);
if ($socket) {
$params = stream_context_get_params($socket);
$cert = $params['options']['ssl']['peer_certificate'];
$certInfo = openssl_x509_parse($cert);
$validFrom = $certInfo['validFrom_time_t'];
$validTo = $certInfo['validTo_time_t'];
$currentTime = time();
$certValid = ($currentTime >= $validFrom && $currentTime <= $validTo);
$sslChecks[] = [
'name' => 'SSL Certificate is valid',
'passed' => $certValid,
'value' => $certValid ? 'Valid' : 'Invalid or Expired',
];
} else {
$sslChecks[] = [
'name' => 'SSL Certificate is valid',
'passed' => false,
'value' => 'Unable to retrieve certificate',
];
}
} else {
$sslChecks[] = [
'name' => 'SSL Certificate is valid',
'passed' => false,
'value' => 'Not using HTTPS',
];
}
// Section: Domain Checks
$domainChecks = [];
// Check if the site has a valid FQDN
$fqdn = $_SERVER['HTTP_HOST'];
$isValidFqdn = (bool) filter_var('http://' . $fqdn, FILTER_VALIDATE_URL) && preg_match('/^[a-z0-9.-]+\.[a-z]{2,}$/i', $fqdn);
$domainChecks[] = [
'name' => 'Site has a valid FQDN',
'passed' => $isValidFqdn,
'value' => $fqdn,
];
// Section: File Permissions
$filePermissions = [];
// Check if web user has write access to webroot directory
$webroot = $_SERVER['DOCUMENT_ROOT'];
$writable = is_writable($webroot);
$filePermissions[] = [
'name' => 'Web user has write access to webroot directory',
'passed' => $writable,
'value' => $webroot,
];
// Section: Uploads Directory Stats
$uploadsStats = [];
// Define the uploads directory path
$uploadsDir = __DIR__ . '/uploads'; // Adjust the path if needed
if (is_dir($uploadsDir)) {
// Function to recursively count files and calculate total size
function getDirStats($dir) {
$files = 0;
$size = 0;
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
foreach ($iterator as $file) {
if ($file->isFile()) {
$files++;
$size += $file->getSize();
}
}
return ['files' => $files, 'size' => $size];
}
$stats = getDirStats($uploadsDir);
$sizeInMB = round($stats['size'] / (1024 * 1024), 2);
$uploadsStats[] = [
'name' => 'Number of files in uploads directory',
'value' => $stats['files'],
];
$uploadsStats[] = [
'name' => 'Total size of uploads directory (MB)',
'value' => $sizeInMB . ' MB',
];
} else {
$uploadsStats[] = [
'name' => 'Uploads directory exists',
'value' => 'Directory not found',
];
}
// Section: Database Stats
$databaseStats = [];
// Get list of tables
$tablesResult = $mysqli->query("SHOW TABLE STATUS");
if ($tablesResult) {
$totalTables = 0;
$totalFields = 0;
$totalRows = 0;
$totalSize = 0;
$tableDetails = [];
while ($table = $tablesResult->fetch_assoc()) {
$tableName = $table['Name'];
$tableRows = $table['Rows'];
$dataLength = $table['Data_length'];
$indexLength = $table['Index_length'];
$tableSize = ($dataLength + $indexLength) / (1024 * 1024); // Size in MB
// Get number of fields
$fieldsResult = $mysqli->query("SHOW COLUMNS FROM `$tableName`");
$numFields = $fieldsResult->num_rows;
$fieldsResult->free();
$totalTables++;
$totalFields += $numFields;
$totalRows += $tableRows;
$totalSize += $tableSize;
$tableDetails[] = [
'name' => $tableName,
'fields' => $numFields,
'rows' => $tableRows,
'size' => round($tableSize, 2),
];
}
$tablesResult->free();
$databaseStats[] = [
'name' => 'Current Database Version',
'value' => CURRENT_DATABASE_VERSION,
];
$databaseStats[] = [
'name' => 'Total number of tables',
'value' => $totalTables,
];
$databaseStats[] = [
'name' => 'Total number of fields',
'value' => $totalFields,
];
$databaseStats[] = [
'name' => 'Total number of rows',
'value' => $totalRows,
];
$databaseStats[] = [
'name' => 'Total database size (MB)',
'value' => round($totalSize, 2) . ' MB',
];
} else {
$databaseStats[] = [
'name' => 'Database connection error',
'value' => $mysqli->error,
];
}
// Section: Database Structure Comparison
$dbComparison = [];
// Path to the db.sql file
$dbSqlFile = __DIR__ . '/db.sql';
if (file_exists($dbSqlFile)) {
// Read the db.sql file
$sqlContent = file_get_contents($dbSqlFile);
// Remove comments and empty lines
$lines = explode("\n", $sqlContent);
$sqlStatements = [];
$statement = '';
foreach ($lines as $line) {
// Remove single-line comments
$line = preg_replace('/--.*$/', '', $line);
$line = preg_replace('/\/\*.*?\*\//', '', $line);
// Skip empty lines
if (trim($line) == '') {
continue;
}
// Append line to the current statement
$statement .= $line . "\n";
// Check if the statement ends with a semicolon
if (preg_match('/;\s*$/', $line)) {
$sqlStatements[] = $statement;
$statement = '';
} }
} }
return $tables; // Parse the CREATE TABLE statements
$sqlTables = [];
foreach ($sqlStatements as $sql) {
if (preg_match('/CREATE TABLE\s+`?([^` ]+)`?\s*\((.*)\)(.*?);/msi', $sql, $match)) {
$tableName = $match[1];
$columnsDefinition = $match[2];
// Extract column names and data types
$columns = [];
$columnLines = explode("\n", $columnsDefinition);
foreach ($columnLines as $line) {
$line = trim($line);
// Skip empty lines and lines that do not define columns
if ($line == '' || strpos($line, 'PRIMARY KEY') !== false || strpos($line, 'UNIQUE KEY') !== false || strpos($line, 'KEY') === 0 || strpos($line, 'CONSTRAINT') === 0 || strpos($line, ')') === 0) {
continue;
} }
//function to get current crontab and return it as an array // Remove trailing comma if present
function get_crontab() { $line = rtrim($line, ',');
$crontab = shell_exec('crontab -l');
$crontab = explode(PHP_EOL, $crontab); // Match column definition
return $crontab; if (preg_match('/^`([^`]+)`\s+(.+)/', $line, $colMatch)) {
$colName = $colMatch[1];
$colDefinition = $colMatch[2];
// Extract the data type from the column definition
$tokens = preg_split('/\s+/', $colDefinition);
$colType = $tokens[0];
// Handle data types with parentheses (e.g., varchar(255), decimal(15,2))
if (preg_match('/^([a-zA-Z]+)\(([^)]+)\)/', $colType, $typeMatch)) {
$colType = $typeMatch[1] . '(' . $typeMatch[2] . ')';
} }
// URL to the SQL dump file $columns[$colName] = $colType;
$fileURL = "https://raw.githubusercontent.com/itflow-org/itflow/master/db.sql"; }
}
// Load the desired table structures from the SQL dump file URL $sqlTables[$tableName] = $columns;
$desiredStructure = loadTableStructuresFromSQLDumpURL($fileURL); }
if ($desiredStructure === null) {
die("Failed to load the desired table structures from the SQL dump file URL.");
} }
// Fetch the current database structure from the MySQL server // Get current database table structures
$currentStructure = fetchDatabaseStructureFromServer(); $dbTables = [];
$tablesResult = $mysqli->query("SHOW TABLES");
if ($currentStructure === null) {
die("Failed to fetch the current database structure from the server.");
}
// Compare the structures and display the differences
$differences = arrayDiffRecursive($desiredStructure, $currentStructure);
//DB Stats
// Query to fetch the number of tables
$tablesQuery = "SHOW TABLES";
$tablesResult = $mysqli->query($tablesQuery);
$numTables = $tablesResult->num_rows;
$numFields = 0;
$numRows = 0;
// Loop through each table
while ($row = $tablesResult->fetch_row()) { while ($row = $tablesResult->fetch_row()) {
$tableName = $row[0]; $tableName = $row[0];
$columnsResult = $mysqli->query("SHOW COLUMNS FROM `$tableName`");
$columns = [];
while ($col = $columnsResult->fetch_assoc()) {
$columns[$col['Field']] = $col['Type'];
}
$columnsResult->free();
$dbTables[$tableName] = $columns;
}
$tablesResult->free();
// Query to fetch the number of fields // Compare the structures
$fieldsQuery = "DESCRIBE `$tableName`"; foreach ($sqlTables as $tableName => $sqlColumns) {
$fieldsResult = $mysqli->query($fieldsQuery); if (!isset($dbTables[$tableName])) {
$dbComparison[] = [
'name' => "Table `$tableName` missing in database",
'status' => 'Missing Table',
];
continue;
}
// Check if the query was successful // Compare columns
if ($fieldsResult) { $dbColumns = $dbTables[$tableName];
$numFields += $fieldsResult->num_rows; foreach ($sqlColumns as $colName => $colType) {
if (!isset($dbColumns[$colName])) {
// Query to fetch the number of rows $dbComparison[] = [
$rowsQuery = "SELECT COUNT(*) FROM `$tableName`"; 'name' => "Column `$colName` missing in table `$tableName`",
$rowsResult = $mysqli->query($rowsQuery); 'status' => 'Missing Column',
];
// Check if the query was successful
if ($rowsResult) {
$numRows += $rowsResult->fetch_row()[0];
} else { } else {
echo "Error executing query: " . $mysqli->error; // Normalize data types for comparison
$sqlColType = strtolower($colType);
$dbColType = strtolower($dbColumns[$colName]);
// Remove attributes and constraints
$sqlColType = preg_replace('/\s+.*$/', '', $sqlColType);
$dbColType = preg_replace('/\s+.*$/', '', $dbColType);
// Remove additional attributes like unsigned, zerofill, etc.
$sqlColType = preg_replace('/\s+unsigned|\s+zerofill|\s+binary/', '', $sqlColType);
$dbColType = preg_replace('/\s+unsigned|\s+zerofill|\s+binary/', '', $dbColType);
if ($sqlColType != $dbColType) {
$dbComparison[] = [
'name' => "Data type mismatch for `$colName` in table `$tableName`",
'status' => "Expected: $colType, Found: {$dbColumns[$colName]}",
];
}
}
}
// Check for extra columns in the database that are not in the SQL file
foreach ($dbColumns as $colName => $colType) {
if (!isset($sqlColumns[$colName])) {
$dbComparison[] = [
'name' => "Extra column `$colName` in table `$tableName` not present in db.sql",
'status' => 'Extra Column',
];
}
}
}
// Check for tables in the database not present in the db.sql file
foreach ($dbTables as $tableName => $dbColumns) {
if (!isset($sqlTables[$tableName])) {
$dbComparison[] = [
'name' => "Extra table `$tableName` in database not present in db.sql",
'status' => 'Extra Table',
];
}
} }
} else { } else {
echo "Error executing query: " . $mysqli->error; $dbComparison[] = [
} 'name' => 'db.sql file not found',
'status' => 'File Missing',
];
} }
//Get loaded PHP modules $mysqli->close();
$loadedModules = get_loaded_extensions();
//Get Server Info / Service versions
$phpVersion = phpversion();
$databaseInfo = mysqli_get_server_info($mysqli) . " / " . $mysqli->server_version;
$operatingSystem = php_uname();
$webServer = $_SERVER['SERVER_SOFTWARE'];
$errorLog = ini_get('error_log') ?: "Debian/Ubuntu default is usually /var/log/apache2/error.log";
$updates = fetchUpdates();
?> ?>
<div class="card card-dark"> <div class="card card-dark">
@ -205,128 +517,220 @@ $updates = fetchUpdates();
<li><a class="text-danger text-bold">Caution:</a> Be careful when sharing the full debug output - it contains your PHP session variables/cookies ("PHPSESSID") which could allow anyone to login to your ITFlow instance</li> <li><a class="text-danger text-bold">Caution:</a> Be careful when sharing the full debug output - it contains your PHP session variables/cookies ("PHPSESSID") which could allow anyone to login to your ITFlow instance</li>
<li>Note: Sometimes you might need to gather <a href="https://docs.itflow.org/gathering_logs#error_logs">PHP error logs</a> as well</li> <li>Note: Sometimes you might need to gather <a href="https://docs.itflow.org/gathering_logs#error_logs">PHP error logs</a> as well</li>
</ul> </ul>
<br>
<h3>Server Info</h3>
<?php
echo "PHP version: " . $phpVersion . "<br>";
echo "Database Version: " . $databaseInfo . "<br>";
echo "Operating System: " . $operatingSystem . "<br>";
echo "Web Server: " . $webServer . "<br>";
echo "Apache/PHP Error Log: " . $errorLog
?>
<hr> <hr>
<h3>File System</h3> <!-- System Information Table -->
<?php <h3>System Information</h3>
$result = countFilesInDirectory($folderPath); <table class="table table-sm table-bordered">
<tbody>
$totalFiles = $result['count']; <?php foreach ($systemInfo as $info): ?>
$totalSizeMB = round($result['size'] / (1024 * 1024), 2);
echo "Total number of files in $folderPath and its subdirectories: " . $totalFiles . "<br>";
echo "Total size of files in $folderPath and its subdirectories: " . $totalSizeMB . " MB";
?>
<hr>
<h3>ITFlow app</h3>
<?php
echo "App Version: " . $updates->current_version . "<br>";
echo "Cron enabled: " . $config_enable_cron . "<br>";
echo "App Timezone: " . $config_timezone;
?>
<hr>
<h3>Database Structure Check</h3>
<h4>Database stats</h4>
<?php
echo "Number of tables: " . $numTables . "<br>";
echo "Total number of fields: " . $numFields . "<br>";
echo "Total number of rows: " . $numRows . "<br>";
echo "Current Database Version: " . CURRENT_DATABASE_VERSION . "<br>";
?>
<hr>
<h4>Table Stats</h4>
<?php
// Fetch all table names from the database
$tables = array();
$result = mysqli_query($mysqli, "SHOW TABLES");
while ($row = mysqli_fetch_array($result)) {
$tables[] = $row[0];
}
// Generate an HTML table to display the results
?>
<table class="table table-sm">
<tr> <tr>
<th>Table Name</th> <td><?= htmlspecialchars($info['name']); ?></td>
<th>Number of Fields</th> <td><?= htmlspecialchars($info['value']); ?></td>
<th>Number of Rows</th>
</tr> </tr>
<?php endforeach; ?>
<?php </tbody>
foreach ($tables as $table) {
// Count the number of fields and rows for each table
$columns_result = mysqli_query($mysqli, "SHOW COLUMNS FROM `$table`");
$columns = mysqli_num_rows($columns_result);
$rows_result = mysqli_query($mysqli, "SELECT COUNT(*) FROM `$table`");
$rows = mysqli_fetch_array($rows_result)[0];
?>
<tr>
<td><?php echo $table; ?></td>
<td><?php echo $columns; ?></td>
<td><?php echo $rows; ?></td>
</tr>
<?php
}
?>
</table> </table>
<hr> <!-- PHP Extensions and Configuration Table -->
<h3 class="mt-3">PHP Extensions and Configuration</h3>
<table class="table table-sm table-bordered">
<!-- PHP Extensions Section -->
<thead>
<tr class="table-secondary">
<th colspan="3">PHP Extensions</th>
</tr>
</thead>
<tbody>
<?php foreach ($phpExtensions as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<!-- PHP Configuration Section -->
<thead>
<tr class="table-secondary">
<th colspan="3">PHP Configuration</th>
</tr>
</thead>
<tbody>
<?php foreach ($phpConfig as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<thead>
<tr class="table-secondary">
<th colspan="3">Shell Commands</th>
</tr>
</thead>
<tbody>
<?php foreach ($shellCommands as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<thead>
<tr class="table-secondary">
<th colspan="3">SSL Checks</th>
</tr>
</thead>
<tbody>
<?php foreach ($sslChecks as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<thead>
<tr class="table-secondary">
<th colspan="3">Domain Checks</th>
</tr>
</thead>
<tbody>
<?php foreach ($domainChecks as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<h3>PHP Modules Installed</h3> <!-- File Permissions Table -->
<thead>
<tr class="table-secondary">
<th colspan="3">File Permissions</th>
</tr>
</thead>
<tbody>
<?php foreach ($filePermissions as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php
foreach ($loadedModules as $module) {
echo $module . "<br>";
}
?>
<hr>
<h3>PHP Info</h3> <!-- Database Structure Comparison Table -->
<?php <h3 class="mt-3">Database Structure Comparison</h3>
//Output phpinfo, but in a way that doesnt mess up the page <table class="table table-sm table-bordered">
ob_start(); <tbody>
phpinfo(); <?php if (!empty($dbComparison)): ?>
$phpinfo = ob_get_contents(); <?php foreach ($dbComparison as $issue): ?>
ob_end_clean(); <tr>
<td><?= htmlspecialchars($issue['name']); ?></td>
<td colspan="2"><?= htmlspecialchars($issue['status']); ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="3">No discrepancies found between the database and db.sql file.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
//Remove everything before the body tag <!-- Uploads Directory Stats Table -->
$phpinfo = preg_replace('%^.*<body>(.*)</body>.*$%ms', '$1', $phpinfo); <h3 class="mt-3">Uploads Directory Stats</h3>
<table class="table table-sm table-bordered">
<tbody>
<?php foreach ($uploadsStats as $stat): ?>
<tr>
<td><?= htmlspecialchars($stat['name']); ?></td>
<td colspan="2"><?= htmlspecialchars($stat['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
//Remove everything after the body tag <!-- Database Stats Table -->
$phpinfo = preg_replace('%^(.*)</body>.*$%ms', '$1', $phpinfo); <h3 class="mt-3">Database Stats</h3>
<table class="table table-sm table-bordered">
<tbody>
<?php foreach ($databaseStats as $stat): ?>
<tr>
<td><?= htmlspecialchars($stat['name']); ?></td>
<td colspan="2"><?= htmlspecialchars($stat['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
//Remove the body tag itself <!-- Table Stats Table -->
$phpinfo = preg_replace('%^<body>(.*)$%ms', '$1', $phpinfo); <h3 class="mt-3">Table Stats</h3>
<table class="table table-sm table-bordered">
<thead>
<tr>
<th>Table Name</th>
<th>Fields / Rows</th>
<th>Size (MB)</th>
</tr>
</thead>
<tbody>
<?php foreach ($tableDetails as $table): ?>
<tr>
<td><?= htmlspecialchars($table['name']); ?></td>
<td><?= htmlspecialchars("Fields: {$table['fields']}, Rows: {$table['rows']}"); ?></td>
<td><?= htmlspecialchars($table['size'] . ' MB'); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
//Output the result
echo $phpinfo;
?>
<hr>
</div> </div>
</div> </div>
<?php <?php

335
admin_legacy_debug.php Normal file
View File

@ -0,0 +1,335 @@
<?php
require_once "inc_all_admin.php";
require_once "database_version.php";
require_once "config.php";
$folderPath = 'uploads';
function countFilesInDirectory($dir) {
$count = 0;
$size = 0;
$files = scandir($dir);
foreach ($files as $file) {
if ($file === '.' || $file === '..') {
continue;
}
$filePath = $dir . '/' . $file;
if (is_file($filePath)) {
$count++;
$size += filesize($filePath);
} elseif (is_dir($filePath)) {
$result = countFilesInDirectory($filePath);
$count += $result['count'];
$size += $result['size'];
}
}
return [
'count' => $count,
'size' => $size
];
}
// Function to compare two arrays recursively and return the differences
function arrayDiffRecursive($array1, $array2) {
$diff = array();
foreach ($array1 as $key => $value) {
if (is_array($value)) {
if (!isset($array2[$key]) || !is_array($array2[$key])) {
$diff[$key] = $value;
} else {
$recursiveDiff = arrayDiffRecursive($value, $array2[$key]);
if (!empty($recursiveDiff)) {
$diff[$key] = $recursiveDiff;
}
}
} else {
if (!isset($array2[$key]) || $array2[$key] !== $value) {
$diff[$key] = $value;
}
}
}
return $diff;
}
// Function to load the table structures from an SQL dump file URL
function loadTableStructuresFromSQLDumpURL($fileURL) {
$context = stream_context_create(array('http' => array('header' => 'Accept: application/octet-stream')));
$fileContent = file_get_contents($fileURL, false, $context);
if ($fileContent === false) {
return null;
}
$structure = array();
$queries = explode(";", $fileContent);
foreach ($queries as $query) {
$query = trim($query);
if (!empty($query)) {
if (preg_match("/^CREATE TABLE `(.*)` \((.*)\)$/s", $query, $matches)) {
$tableName = $matches[1];
$tableStructure = $matches[2];
$structure[$tableName] = array('structure' => $tableStructure);
}
}
}
return $structure;
}
// Function to fetch the database structure from the MySQL server
function fetchDatabaseStructureFromServer() {
global $mysqli;
$tables = array();
// Fetch table names
$result = $mysqli->query("SHOW TABLES");
if ($result->num_rows > 0) {
while ($row = $result->fetch_row()) {
$tableName = $row[0];
$tables[$tableName] = array();
}
}
// Fetch table structures
foreach ($tables as $tableName => &$table) {
$result = $mysqli->query("SHOW CREATE TABLE `$tableName`");
if ($result->num_rows > 0) {
$row = $result->fetch_row();
$table['structure'] = $row[1];
}
}
return $tables;
}
//function to get current crontab and return it as an array
function get_crontab() {
$crontab = shell_exec('crontab -l');
$crontab = explode(PHP_EOL, $crontab);
return $crontab;
}
// URL to the SQL dump file
$fileURL = "https://raw.githubusercontent.com/itflow-org/itflow/master/db.sql";
// Load the desired table structures from the SQL dump file URL
$desiredStructure = loadTableStructuresFromSQLDumpURL($fileURL);
if ($desiredStructure === null) {
die("Failed to load the desired table structures from the SQL dump file URL.");
}
// Fetch the current database structure from the MySQL server
$currentStructure = fetchDatabaseStructureFromServer();
if ($currentStructure === null) {
die("Failed to fetch the current database structure from the server.");
}
// Compare the structures and display the differences
$differences = arrayDiffRecursive($desiredStructure, $currentStructure);
//DB Stats
// Query to fetch the number of tables
$tablesQuery = "SHOW TABLES";
$tablesResult = $mysqli->query($tablesQuery);
$numTables = $tablesResult->num_rows;
$numFields = 0;
$numRows = 0;
// Loop through each table
while ($row = $tablesResult->fetch_row()) {
$tableName = $row[0];
// Query to fetch the number of fields
$fieldsQuery = "DESCRIBE `$tableName`";
$fieldsResult = $mysqli->query($fieldsQuery);
// Check if the query was successful
if ($fieldsResult) {
$numFields += $fieldsResult->num_rows;
// Query to fetch the number of rows
$rowsQuery = "SELECT COUNT(*) FROM `$tableName`";
$rowsResult = $mysqli->query($rowsQuery);
// Check if the query was successful
if ($rowsResult) {
$numRows += $rowsResult->fetch_row()[0];
} else {
echo "Error executing query: " . $mysqli->error;
}
} else {
echo "Error executing query: " . $mysqli->error;
}
}
//Get loaded PHP modules
$loadedModules = get_loaded_extensions();
//Get Server Info / Service versions
$phpVersion = phpversion();
$databaseInfo = mysqli_get_server_info($mysqli) . " / " . $mysqli->server_version;
$operatingSystem = php_uname();
$webServer = $_SERVER['SERVER_SOFTWARE'];
$errorLog = ini_get('error_log') ?: "Debian/Ubuntu default is usually /var/log/apache2/error.log";
$updates = fetchUpdates();
?>
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-bug mr-2"></i>Debug</h3>
</div>
<div class="card-body">
<h2>Debugging</h2>
<ul>
<li>If you are experiencing a problem with ITFlow you may be directed to this page to gather server/app info.</li>
<li>When creating forum posts / support requests ensure you share the information under <i>Server Info</i>, <i>ITFlow app</i> and <i>Database stats</i>.</li>
<li><a class="text-danger text-bold">Caution:</a> Be careful when sharing the full debug output - it contains your PHP session variables/cookies ("PHPSESSID") which could allow anyone to login to your ITFlow instance</li>
<li>Note: Sometimes you might need to gather <a href="https://docs.itflow.org/gathering_logs#error_logs">PHP error logs</a> as well</li>
</ul>
<br>
<h3>Server Info</h3>
<?php
echo "PHP version: " . $phpVersion . "<br>";
echo "Database Version: " . $databaseInfo . "<br>";
echo "Operating System: " . $operatingSystem . "<br>";
echo "Web Server: " . $webServer . "<br>";
echo "Apache/PHP Error Log: " . $errorLog
?>
<hr>
<h3>File System</h3>
<?php
$result = countFilesInDirectory($folderPath);
$totalFiles = $result['count'];
$totalSizeMB = round($result['size'] / (1024 * 1024), 2);
echo "Total number of files in $folderPath and its subdirectories: " . $totalFiles . "<br>";
echo "Total size of files in $folderPath and its subdirectories: " . $totalSizeMB . " MB";
?>
<hr>
<h3>ITFlow app</h3>
<?php
echo "App Version: " . $updates->current_version . "<br>";
echo "Cron enabled: " . $config_enable_cron . "<br>";
echo "App Timezone: " . $config_timezone;
?>
<hr>
<h3>Database Structure Check</h3>
<h4>Database stats</h4>
<?php
echo "Number of tables: " . $numTables . "<br>";
echo "Total number of fields: " . $numFields . "<br>";
echo "Total number of rows: " . $numRows . "<br>";
echo "Current Database Version: " . CURRENT_DATABASE_VERSION . "<br>";
?>
<hr>
<h4>Table Stats</h4>
<?php
// Fetch all table names from the database
$tables = array();
$result = mysqli_query($mysqli, "SHOW TABLES");
while ($row = mysqli_fetch_array($result)) {
$tables[] = $row[0];
}
// Generate an HTML table to display the results
?>
<table class="table table-sm">
<tr>
<th>Table Name</th>
<th>Number of Fields</th>
<th>Number of Rows</th>
</tr>
<?php
foreach ($tables as $table) {
// Count the number of fields and rows for each table
$columns_result = mysqli_query($mysqli, "SHOW COLUMNS FROM `$table`");
$columns = mysqli_num_rows($columns_result);
$rows_result = mysqli_query($mysqli, "SELECT COUNT(*) FROM `$table`");
$rows = mysqli_fetch_array($rows_result)[0];
?>
<tr>
<td><?php echo $table; ?></td>
<td><?php echo $columns; ?></td>
<td><?php echo $rows; ?></td>
</tr>
<?php
}
?>
</table>
<hr>
<h3>PHP Modules Installed</h3>
<?php
foreach ($loadedModules as $module) {
echo $module . "<br>";
}
?>
<hr>
<h3>PHP Info</h3>
<?php
//Output phpinfo, but in a way that doesnt mess up the page
ob_start();
phpinfo();
$phpinfo = ob_get_contents();
ob_end_clean();
//Remove everything before the body tag
$phpinfo = preg_replace('%^.*<body>(.*)</body>.*$%ms', '$1', $phpinfo);
//Remove everything after the body tag
$phpinfo = preg_replace('%^(.*)</body>.*$%ms', '$1', $phpinfo);
//Remove the body tag itself
$phpinfo = preg_replace('%^<body>(.*)$%ms', '$1', $phpinfo);
//Output the result
echo $phpinfo;
?>
<hr>
</div>
</div>
<?php
require_once "footer.php";