mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-23 03:27:02 +09:00
* chore(test): gate test-only DB short-circuit behind CACTI_TEST_BOOTSTRAP env
The previous `!defined('PHP_TESTING')` guard skipped db_connect_real
whenever PHP_TESTING was set anywhere in the include chain. A stray
define from a CLI tool or third-party script could disable real DB
connection logic in a deployed environment.
Switch the guard to require both `PHP_TESTING` and an explicit
`CACTI_TEST_BOOTSTRAP=1` env var, and replace the silent-no-connect
path with a `Cacti_TestDbSentinel` whose every method throws. Any
production code path that accidentally treats the sentinel as a real
DB handle will fail loudly instead of silently misbehaving.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* fix(test): apply Copilot review feedback for test DB sentinel guard
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* test: collapse remaining test-bootstrap guard to $is_test_bootstrap
The previous review fix introduced a local $is_test_bootstrap flag but left
two call sites still inlining defined('PHP_TESTING') && getenv(...). Replace
the long-form check and guard the single-poller cacti_db_version fetch so the
sentinel branch finishes initialising without invoking the DB.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* chore(test): arm CACTI_TEST_BOOTSTRAP in test bootstraps and fail closed on PHP_TESTING alone
Set the env var where PHP_TESTING is defined so legitimate test runs short-circuit the DB connect again, gate the config.php.dist swap and schema-validation skip on the combined predicate, read it with local_only, and always seed cacti_db_version.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* ci: ignore phpstan function.alreadyNarrowedType identifier
Clears the level 6 alreadyNarrowedType error on lib/data_query.php:2591 that
develop currently emits, matching the sibling narrowing identifiers already ignored.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
---------
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
772 lines
26 KiB
PHP
772 lines
26 KiB
PHP
<?php
|
|
/*
|
|
+-------------------------------------------------------------------------+
|
|
| Copyright (C) 2004-2026 The Cacti Group |
|
|
| |
|
|
| This program is free software; you can redistribute it and/or |
|
|
| modify it under the terms of the GNU General Public License |
|
|
| as published by the Free Software Foundation; either version 2 |
|
|
| of the License, or (at your option) any later version. |
|
|
| |
|
|
| This program 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. See the |
|
|
| GNU General Public License for more details. |
|
|
+-------------------------------------------------------------------------+
|
|
| Cacti: The Complete RRDtool-based Graphing Solution |
|
|
+-------------------------------------------------------------------------+
|
|
| This code is designed, written, and maintained by the Cacti Group. See |
|
|
| about.php and/or the AUTHORS file for specific developer information. |
|
|
+-------------------------------------------------------------------------+
|
|
| http://www.cacti.net/ |
|
|
+-------------------------------------------------------------------------+
|
|
*/
|
|
|
|
/*
|
|
!!! IMPORTANT !!!
|
|
|
|
The following defaults are not to be altered. Please refer to
|
|
include/config.php for user configurable settings.
|
|
|
|
*/
|
|
global $config;
|
|
$config = [];
|
|
|
|
// Workaround for PHPStan scanning
|
|
global $rdatabase_default, $rdatabase_hostname, $rdatabase_password,
|
|
$rdatabase_port, $rdatabase_type, $rdatabase_username;
|
|
// Workaround End
|
|
|
|
// define if cacti is in CLI mode
|
|
define('CACTI_CLI', (php_sapi_name() == 'cli'));
|
|
define('CACTI_WEB', (php_sapi_name() != 'cli'));
|
|
define('CACTI_SERVER_OS', (str_starts_with(PHP_OS, 'WIN')) ? 'win32' : 'unix');
|
|
define('CACTI_ERR_2NULL', (str_starts_with(PHP_OS, 'WIN')) ? ' 2> NULL' : ' 2> /dev/null');
|
|
|
|
if (defined('CACTI_CLI_ONLY') && CACTI_WEB) {
|
|
die('<br><strong>This script is only meant to run at the command line.</strong>');
|
|
}
|
|
|
|
// this should be auto-detected, set it manually if needed
|
|
$config['cacti_server_os'] = (str_starts_with(PHP_OS, 'WIN')) ? 'win32' : 'unix';
|
|
$config['is_web'] = CACTI_WEB;
|
|
|
|
// load cacti version from file
|
|
$cacti_version_file = __DIR__ . '/cacti_version';
|
|
|
|
if (!file_exists($cacti_version_file)) {
|
|
die('ERROR: failed to find cacti version file');
|
|
}
|
|
|
|
$cacti_version = file_get_contents($cacti_version_file, false);
|
|
|
|
if ($cacti_version === false) {
|
|
die('ERROR: failed to load cacti version file');
|
|
}
|
|
$cacti_version = trim($cacti_version);
|
|
|
|
// define documentation table of contents
|
|
define('CACTI_DOCUMENTATION_TOC', 'docs/Table-of-Contents.html');
|
|
|
|
// By default, we assume that it is not
|
|
// an AJAX request.
|
|
global $is_request_ajax;
|
|
$is_request_ajax = false;
|
|
|
|
// If HTTP_X_REQUESTED_WITH is equal to xmlhttprequest
|
|
// We assume this is an ajax call
|
|
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
|
|
strcasecmp($_SERVER['HTTP_X_REQUESTED_WITH'], 'xmlhttprequest') == 0) {
|
|
$is_request_ajax = true;
|
|
} elseif (isset($_REQUEST['header']) && $_REQUEST['header'] == 'false') {
|
|
$is_request_ajax = true;
|
|
} elseif (isset($_REQUEST['headercontent'])) {
|
|
$is_request_ajax = false;
|
|
}
|
|
|
|
// Default database settings
|
|
$database_type = 'mysql';
|
|
$database_default = 'cacti';
|
|
$database_hostname = 'localhost';
|
|
$database_username = 'cactiuser';
|
|
$database_password = 'cactiuser';
|
|
$database_port = 3306;
|
|
$database_retries = 2;
|
|
|
|
$database_ssl = false;
|
|
$database_ssl_key = '';
|
|
$database_ssl_cert = '';
|
|
$database_ssl_ca = '';
|
|
$database_ssl_capath = '';
|
|
$database_ssl_verify_server_cert = true;
|
|
$database_persist = true;
|
|
|
|
// Default session name - Session name must contain alpha characters
|
|
$cacti_session_name = 'Cacti';
|
|
|
|
if (isset($_COOKIE['CactiTab'])) {
|
|
$cacti_session_name = 'CactiTabId:' . $_COOKIE['CactiTab'];
|
|
}
|
|
|
|
// define default url path
|
|
$url_path = '/cacti/';
|
|
|
|
// disable log rotation setting
|
|
$disable_log_rotation = false;
|
|
|
|
// Include configuration, or use the defaults
|
|
if (file_exists(__DIR__ . '/config.php')) {
|
|
if (!is_readable(__DIR__ . '/config.php')) {
|
|
die('Configuration file include/config.php is present, but unreadable.' . PHP_EOL);
|
|
}
|
|
|
|
include(__DIR__ . '/config.php');
|
|
}
|
|
|
|
// Combined test-bootstrap gate: PHP_TESTING alone is not enough. The
|
|
// CACTI_TEST_BOOTSTRAP env var (local_only so per-request SAPI params under
|
|
// FPM/FastCGI cannot set it) must also be present. A stray PHP_TESTING define
|
|
// in a deployed environment therefore cannot swap to the default config, skip
|
|
// schema validation, or disable real DB connection logic. Fail-closed.
|
|
if (!function_exists('cacti_is_test_bootstrap')) {
|
|
function cacti_is_test_bootstrap(): bool {
|
|
return defined('PHP_TESTING') && getenv('CACTI_TEST_BOOTSTRAP', true) === '1';
|
|
}
|
|
}
|
|
|
|
if (cacti_is_test_bootstrap() && file_exists(__DIR__ . '/config.php.dist')) {
|
|
if (!is_readable(__DIR__ . '/config.php.dist')) {
|
|
die('Configuration file include/config.php is present, but unreadable.' . PHP_EOL);
|
|
}
|
|
|
|
include(__DIR__ . '/config.php.dist');
|
|
}
|
|
|
|
if (isset($config['cacti_version'])) { // @phpstan-ignore-line
|
|
die('Invalid include/config.php file detected.' . PHP_EOL);
|
|
}
|
|
|
|
// Define global paths
|
|
require_once(__DIR__ . '/global_path.php');
|
|
|
|
// Should we allow proxy ip headers?
|
|
$config['proxy_headers'] = $proxy_headers ?? [];
|
|
|
|
// Set the poller_id
|
|
if (isset($poller_id)) {
|
|
$config['poller_id'] = $poller_id;
|
|
define('POLLER_ID', $poller_id);
|
|
} else {
|
|
$config['poller_id'] = 1;
|
|
define('POLLER_ID', 1);
|
|
}
|
|
|
|
$db_var_defaults = [
|
|
'database_type' => 'mysql',
|
|
'database_default' => null,
|
|
'database_hostname' => null,
|
|
'database_username' => null,
|
|
'database_password' => null,
|
|
'database_port' => '3306',
|
|
'database_retries' => 2,
|
|
'database_ssl' => false,
|
|
'database_ssl_key' => '',
|
|
'database_ssl_cert' => '',
|
|
'database_ssl_ca' => '',
|
|
'database_ssl_capath' => '',
|
|
'database_ssl_verify_server_cert' => true,
|
|
];
|
|
|
|
$db_var_prefixes = [''];
|
|
|
|
if ($config['poller_id'] > 1 || isset($rdatabase_hostname)) {
|
|
$db_var_prefixes[] = 'r';
|
|
}
|
|
|
|
$db_missing_vars = '';
|
|
|
|
foreach ($db_var_prefixes as $db_var_prefix) {
|
|
foreach ($db_var_defaults as $db_var_name => $db_var_default) {
|
|
$db_var_full = $db_var_prefix . $db_var_name;
|
|
|
|
if (!isset(${$db_var_full})) {
|
|
if ($db_var_default !== null) {
|
|
${$db_var_full} = $db_var_default;
|
|
} else {
|
|
$db_missing_vars .= (($db_missing_vars == '') ? 'missing ' : ', ') . $db_var_full;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($db_missing_vars)) {
|
|
die("config.php is $db_missing_vars" . PHP_EOL);
|
|
}
|
|
|
|
// set the local for international users
|
|
setlocale(LC_CTYPE, 'en_US.UTF-8');
|
|
|
|
$colors = [];
|
|
|
|
// required for Windows
|
|
if ($config['cacti_server_os'] == 'win32') {
|
|
putenv('MIB_DIRS=c:/usr/share/snmp/mibs');
|
|
}
|
|
|
|
if (!empty($path_csrf_secret)) {
|
|
$config['path_csrf_secret'] = $path_csrf_secret;
|
|
} else {
|
|
$config['path_csrf_secret'] = CACTI_PATH_INCLUDE . '/vendor/csrf/csrf-secret.php';
|
|
}
|
|
|
|
define('CACTI_CSRF_SECRET', $config['path_csrf_secret']);
|
|
|
|
// built-in snmp support
|
|
if ((isset($php_snmp_support) && $php_snmp_support == false) || !function_exists('snmpget')) {
|
|
$config['php_snmp_support'] = false;
|
|
} else {
|
|
$config['php_snmp_support'] = class_exists('SNMP');
|
|
}
|
|
|
|
define('CACTI_PHP_SNMP', $config['php_snmp_support']);
|
|
|
|
// PHP binary location
|
|
if (isset($php_path)) {
|
|
$config['php_path'] = $php_path;
|
|
}
|
|
|
|
// Set various debug fields
|
|
$config['DEBUG_READ_CONFIG_OPTION'] = defined('DEBUG_READ_CONFIG_OPTION');
|
|
$config['DEBUG_READ_CONFIG_OPTION_DB_OPEN'] = defined('DEBUG_READ_CONFIG_OPTION_DB_OPEN');
|
|
$config['DEBUG_SQL_CMD'] = defined('DEBUG_SQL_CMD');
|
|
$config['DEBUG_SQL_FLOW'] = defined('DEBUG_SQL_FLOW');
|
|
$config['DEBUG_SQL_CONNECT'] = defined('DEBUG_SQL_CONNECT');
|
|
|
|
// check for an empty database port
|
|
if (empty($database_port)) { // @phpstan-ignore-line
|
|
$database_port = 3306;
|
|
}
|
|
|
|
if (isset($input_whitelist)) {
|
|
$config['input_whitelist'] = $input_whitelist;
|
|
define('CACTI_WHITELIST', $input_whitelist);
|
|
}
|
|
|
|
// define any additional paths as constants
|
|
foreach ($config as $key => $value) {
|
|
if (str_ends_with($key, '_path')) {
|
|
$path_name = substr($key, 0, -5);
|
|
$constant_name = 'CACTI_PATH_' . mb_strtoupper($path_name);
|
|
|
|
if (!defined($constant_name)) {
|
|
define($constant_name, $value);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isset($i18n_handler)) {
|
|
$config['i18n_language_handler'] = $i18n_handler;
|
|
}
|
|
|
|
if (isset($i18n_force_language)) {
|
|
$config['i18n_force_language'] = $i18n_force_language;
|
|
}
|
|
|
|
if (isset($i18n_log)) {
|
|
$config['i18n_log'] = $i18n_log;
|
|
}
|
|
|
|
if (isset($i18n_text_log)) {
|
|
$config['i18n_text_log'] = $i18n_text_log;
|
|
}
|
|
|
|
// include base modules
|
|
require_once(CACTI_PATH_LIBRARY . '/database.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/functions.php');
|
|
require_once(CACTI_PATH_INCLUDE . '/global_constants.php');
|
|
|
|
define('CACTI_VERSION', format_cacti_version($cacti_version, CACTI_VERSION_FORMAT_SHORT));
|
|
define('CACTI_VERSION_FULL', format_cacti_version($cacti_version, CACTI_VERSION_FORMAT_FULL));
|
|
|
|
require_once(CACTI_PATH_LIBRARY . '/html.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/html_utility.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/html_validate.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/html_filter.php');
|
|
|
|
$filename = get_current_page();
|
|
|
|
if (isset($no_http_headers) && $no_http_headers == true) {
|
|
$config['is_web'] = false;
|
|
|
|
if (!str_contains(php_sapi_name(), 'cli') || !isset($_SERVER['argv'])) {
|
|
print 'FATAL: This file can only be called from the command line.' . PHP_EOL;
|
|
|
|
exit;
|
|
}
|
|
}
|
|
|
|
if ($config['is_web'] && ini_get('session.auto_start') == 1) {
|
|
print 'FATAL: PHP setting session.auto_start NOT supported. Disable in your php.ini file and then restart your Web Service' . PHP_EOL;
|
|
|
|
exit;
|
|
}
|
|
|
|
// Test-mode DB sentinel: any method invocation on the sentinel throws,
|
|
// so production-mode code paths can never silently treat a non-handle as
|
|
// a real connection. Gated by the PHP_TESTING constant AND the
|
|
// CACTI_TEST_BOOTSTRAP env var so a stale define alone cannot disable
|
|
// real DB connection logic in a deployed environment.
|
|
// Pass false to class_exists() so this guard never triggers autoload.
|
|
if (!class_exists('Cacti_TestDbSentinel', false)) {
|
|
final class Cacti_TestDbSentinel {
|
|
/**
|
|
* @param string $name
|
|
* @param array<int,mixed> $args
|
|
* @return never
|
|
*/
|
|
public function __call($name, $args) {
|
|
throw new \RuntimeException('PHP_TESTING DB sentinel called: ' . $name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper for "is this a real DB handle" checks that must reject the sentinel.
|
|
// Defined inline so include/global.php remains self-contained.
|
|
if (!function_exists('_cacti_is_real_db_conn')) {
|
|
function _cacti_is_real_db_conn(mixed $x): bool {
|
|
return is_object($x) && !($x instanceof Cacti_TestDbSentinel);
|
|
}
|
|
}
|
|
|
|
// Resolve the test-bootstrap predicate once; both PHP_TESTING and
|
|
// CACTI_TEST_BOOTSTRAP=1 must be set for the sentinel branches to engage.
|
|
$is_test_bootstrap = cacti_is_test_bootstrap();
|
|
|
|
// set poller mode
|
|
global $local_db_cnn_id, $remote_db_cnn_id, $conn_mode;
|
|
|
|
$config['connection'] = 'online';
|
|
|
|
$ps = $config['is_web'] ? '<p>' : '';
|
|
$sp = $config['is_web'] ? '</p>' : PHP_EOL;
|
|
$ul = $config['is_web'] ? '<ul>' : PHP_EOL;
|
|
$li = $config['is_web'] ? '<li>' : PHP_EOL . ' - ';
|
|
$lu = $config['is_web'] ? '</ul>' : '';
|
|
$il = $config['is_web'] ? '</li>' : '';
|
|
|
|
if ($config['poller_id'] > 1 || isset($rdatabase_hostname)) {
|
|
if (!$is_test_bootstrap) {
|
|
$local_db_cnn_id = db_connect_real($database_hostname, $database_username, $database_password, $database_default, $database_type, $database_port, $database_retries, $database_ssl, $database_ssl_key, $database_ssl_cert, $database_ssl_ca, $database_ssl_capath, $database_ssl_verify_server_cert);
|
|
} else {
|
|
$local_db_cnn_id = new Cacti_TestDbSentinel();
|
|
}
|
|
|
|
if (!isset($rdatabase_retries)) {
|
|
$rdatabase_retries = 2;
|
|
}
|
|
|
|
if (!isset($rdatabase_ssl)) {
|
|
$rdatabase_ssl = false;
|
|
}
|
|
|
|
if (!isset($rdatabase_ssl_key)) {
|
|
$rdatabase_ssl_key = false;
|
|
}
|
|
|
|
if (!isset($rdatabase_ssl_cert)) {
|
|
$rdatabase_ssl_cert = false;
|
|
}
|
|
|
|
if (!isset($rdatabase_ssl_ca)) {
|
|
$rdatabase_ssl_ca = false;
|
|
}
|
|
|
|
if (!isset($rdatabase_ssl_capath)) {
|
|
$rdatabase_ssl_capath = false;
|
|
}
|
|
|
|
if (!isset($rdatabase_ssl_verify_server_cert)) {
|
|
$rdatabase_ssl_verify_server_cert = true;
|
|
}
|
|
|
|
// Check for recovery
|
|
if (_cacti_is_real_db_conn($local_db_cnn_id)) {
|
|
$boost_records = db_fetch_cell('SELECT COUNT(*)
|
|
FROM poller_output_boost', '', true, $local_db_cnn_id);
|
|
|
|
if ($boost_records > 0) {
|
|
$config['connection'] = 'recovery';
|
|
}
|
|
}
|
|
|
|
// gather the existing cactidb version (skip when running under the test sentinel).
|
|
// Seed '' first so the key is always set, matching pre-sentinel behavior where a
|
|
// failed db_fetch_cell returned ''; a remote poller with the local DB down then
|
|
// reads a defined value instead of raising an 'Undefined array key' warning.
|
|
$config['cacti_db_version'] = '';
|
|
|
|
if (_cacti_is_real_db_conn($local_db_cnn_id)) {
|
|
$config['cacti_db_version'] = db_fetch_cell('SELECT cacti FROM version LIMIT 1', '', false, $local_db_cnn_id);
|
|
}
|
|
|
|
/**
|
|
* If we have not been forced offline by the $conn_mode global and since we are
|
|
* a remote poller, let's attempt to get back online.
|
|
*/
|
|
if ($conn_mode != 'offline') {
|
|
if (!$is_test_bootstrap) {
|
|
$remote_db_cnn_id = db_connect_real($rdatabase_hostname, $rdatabase_username, $rdatabase_password, $rdatabase_default, $rdatabase_type, $rdatabase_port, $database_retries, $rdatabase_ssl, $rdatabase_ssl_key, $rdatabase_ssl_cert, $rdatabase_ssl_ca, $rdatabase_ssl_capath, $rdatabase_ssl_verify_server_cert);
|
|
} else {
|
|
$remote_db_cnn_id = new Cacti_TestDbSentinel();
|
|
}
|
|
}
|
|
|
|
if ($config['is_web'] && _cacti_is_real_db_conn($remote_db_cnn_id) && $config['connection'] != 'recovery' && $config['cacti_db_version'] != 'new_install' && !defined('IN_CACTI_INSTALL')) {
|
|
// Connection worked, so now override the default settings so that it will always utilize the remote connection
|
|
$database_default = $rdatabase_default;
|
|
$database_hostname = $rdatabase_hostname;
|
|
$database_username = $rdatabase_username;
|
|
$database_password = $rdatabase_password;
|
|
$database_port = $rdatabase_port;
|
|
$database_ssl = $rdatabase_ssl;
|
|
$database_ssl_key = $rdatabase_ssl_key;
|
|
$database_ssl_cert = $rdatabase_ssl_cert;
|
|
$database_ssl_ca = $rdatabase_ssl_ca;
|
|
$database_ssl_capath = $rdatabase_ssl_capath;
|
|
$database_ssl_verify_server_cert = $rdatabase_ssl_verify_server_cert;
|
|
} elseif (_cacti_is_real_db_conn($remote_db_cnn_id)) {
|
|
if ($config['connection'] != 'recovery') {
|
|
$config['connection'] = 'online';
|
|
}
|
|
} else {
|
|
$config['connection'] = 'offline';
|
|
}
|
|
} else {
|
|
if (!$is_test_bootstrap) {
|
|
if (!db_connect_real($database_hostname, $database_username, $database_password, $database_default, $database_type, $database_port, $database_retries, $database_ssl, $database_ssl_key, $database_ssl_cert, $database_ssl_ca, $database_ssl_capath, $database_ssl_verify_server_cert)) {
|
|
print $ps . 'FATAL: Connection to Cacti database failed. Please ensure: ' . $ul;
|
|
print $li . 'the PHP MySQL module is installed and enabled.' . $il;
|
|
print $li . 'the database is running.' . $il;
|
|
print $li . 'the credentials in config.php are valid.' . $il;
|
|
print $lu . $sp;
|
|
|
|
if (isrv('display_db_errors') && !empty($config['DATABASE_ERROR'])) { // @phpstan-ignore-line
|
|
print $ps . 'The following database errors occurred: ' . $ul;
|
|
|
|
foreach ($config['DATABASE_ERROR'] as $e) { // @phpstan-ignore-line
|
|
print $li . $e['Code'] . ': ' . $e['Error'] . $il;
|
|
}
|
|
print $lu . $sp;
|
|
}
|
|
|
|
exit;
|
|
}
|
|
} else {
|
|
$local_db_cnn_id = new Cacti_TestDbSentinel();
|
|
}
|
|
|
|
if (!$is_test_bootstrap) {
|
|
if (!db_table_exists('settings') || !db_table_exists('version')) {
|
|
print $ps . 'FATAL: Connection to Cacti database succeeded but `settings` table not found. Please ensure: ' . $ul;
|
|
print $li . 'the PHP MySQL module is installed and enabled.' . $il;
|
|
print $li . 'the database is running.' . $il;
|
|
print $li . 'the cacti.sql has been imported.' . $il;
|
|
print $li . 'the credentials in config.php are valid and correct.' . $il;
|
|
print $lu . $sp;
|
|
|
|
if (isrv('display_db_errors') && !empty($config['DATABASE_ERROR'])) { // @phpstan-ignore-line
|
|
print $ps . 'The following database errors occurred: ' . $ul;
|
|
|
|
foreach ($config['DATABASE_ERROR'] as $e) { // @phpstan-ignore-line
|
|
print $li . $e['Code'] . ': ' . $e['Error'] . $il;
|
|
}
|
|
print $lu . $sp;
|
|
}
|
|
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// gather the existing cactidb version (skip when running under the test sentinel).
|
|
// Seed '' first so the key is always set even under the test bootstrap.
|
|
$config['cacti_db_version'] = '';
|
|
|
|
if (!$is_test_bootstrap) {
|
|
$config['cacti_db_version'] = db_fetch_cell('SELECT cacti FROM version LIMIT 1');
|
|
}
|
|
}
|
|
|
|
define('CACTI_CONNECTION', $config['connection']);
|
|
|
|
// check cacti log is available
|
|
$log_filename = cacti_log_file();
|
|
|
|
if (!is_resource_writable($log_filename)) {
|
|
print $ps . 'FATAL: System log file is not available for writing. Please ensure: ' . $ul;
|
|
print $li . 'the log folder is correctly set.' . $il;
|
|
|
|
if (CACTI_CLI) {
|
|
print $li . 'the script was run as the website user. ' . $il;
|
|
}
|
|
print $li . 'the log folder is writable by the website user.' . $il;
|
|
print $li . 'there is enough disk space.' . $il;
|
|
print $lu . $sp;
|
|
|
|
if (CACTI_CLI) {
|
|
print $ps . 'To run as the website user, use sudo -u <website user> php -q <script file>' . $sp;
|
|
}
|
|
print $ps . 'Log: ' . $log_filename . $sp;
|
|
}
|
|
|
|
// prime the most popular config settings
|
|
cache_common_config_settings();
|
|
|
|
if ($config['poller_id'] > 1) {
|
|
$timezone = db_fetch_cell_prepared('SELECT timezone
|
|
FROM poller
|
|
WHERE id = ?',
|
|
[$config['poller_id']]);
|
|
|
|
if ($timezone != '') {
|
|
db_execute_prepared('SET time_zone = ?', [$timezone]);
|
|
}
|
|
|
|
if (db_column_exists('poller', 'log_level')) {
|
|
$poller_log_level = db_fetch_cell_prepared('SELECT log_level
|
|
FROM poller
|
|
WHERE id = ?',
|
|
[$config['poller_id']]);
|
|
|
|
if ($poller_log_level > 0) {
|
|
define('POLLER_LOG_LEVEL', $poller_log_level);
|
|
} else {
|
|
define('POLLER_LOG_LEVEL', '-1');
|
|
}
|
|
} else {
|
|
define('POLLER_LOG_LEVEL', '-1');
|
|
}
|
|
} else {
|
|
define('POLLER_LOG_LEVEL', '-1');
|
|
}
|
|
|
|
if (!defined('IN_CACTI_INSTALL')) {
|
|
set_error_handler('CactiErrorHandler');
|
|
register_shutdown_function('CactiShutdownHandler');
|
|
}
|
|
|
|
// verify the cacti database is initialized before moving past here
|
|
db_cacti_initialized($config['is_web']);
|
|
|
|
if ($config['is_web']) {
|
|
if (read_config_option('force_https') == 'on') {
|
|
if (!cacti_is_https() && isset($_SERVER['HTTP_HOST']) && isset($_SERVER['REQUEST_URI'])) {
|
|
header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
|
|
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// set the maximum post size
|
|
ini_set('post_max_size', '8M');
|
|
|
|
// add additional cookie directives
|
|
ini_set('session.cookie_httponly', true);
|
|
ini_set('session.cookie_path', CACTI_PATH_URL);
|
|
ini_set('session.use_strict_mode', true);
|
|
|
|
$options = [
|
|
COOKIE_OPTIONS_HTTPONLY => true,
|
|
COOKIE_OPTIONS_PATH => CACTI_PATH_URL,
|
|
COOKIE_OPTIONS_STRICT => true
|
|
];
|
|
|
|
if (isset($cacti_cookie_domain) && $cacti_cookie_domain != '') {
|
|
ini_set('session.cookie_domain', $cacti_cookie_domain);
|
|
$options[COOKIE_OPTIONS_DOMAIN] = $cacti_cookie_domain;
|
|
}
|
|
|
|
// SameSite php7.3+ behavior
|
|
if (version_compare(PHP_VERSION, '7.3', '>=')) {
|
|
ini_set('session.cookie_samesite', 'Strict');
|
|
$options[COOKIE_OPTIONS_SAMESITE] = 'Strict';
|
|
}
|
|
|
|
if (cacti_is_https()) {
|
|
ini_set('session.cookie_secure', true);
|
|
$options[COOKIE_OPTIONS_SECURE] = true;
|
|
}
|
|
|
|
$config[COOKIE_OPTIONS] = $options;
|
|
$config[CACTI_SESSION_NAME] = $cacti_session_name;
|
|
|
|
if (isset($cacti_db_session) && $cacti_db_session && db_table_exists('sessions') && CACTI_CONNECTION == 'online') {
|
|
include(__DIR__ . '/session.php');
|
|
} else {
|
|
$cacti_db_session = false;
|
|
}
|
|
|
|
// we don't want these pages cached
|
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
|
header('X-Frame-Options: SAMEORIGIN');
|
|
|
|
// increased web hardening
|
|
$script_policy = read_config_option('content_security_policy_script');
|
|
|
|
if ($script_policy == 'unsafe-eval') {
|
|
$script_policy = "'$script_policy'";
|
|
} else {
|
|
$script_policy = '';
|
|
}
|
|
$alternates = htmle(read_config_option('content_security_alternate_sources'));
|
|
|
|
header("Content-Security-Policy: default-src *; img-src 'self' https://api.qrserver.com $alternates data: blob:; style-src 'self' 'unsafe-inline' $alternates; script-src 'self' $script_policy 'unsafe-inline' $alternates; frame-ancestors 'self'; worker-src 'self' $alternates;");
|
|
|
|
// prevent IE from silently rejects cookies sent from third party sites.
|
|
header('P3P: CP="CAO PSA OUR"');
|
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
|
header('Cache-Control: max-age=31536000');
|
|
|
|
cacti_session_start();
|
|
|
|
// make sure to start only Cacti session at a time
|
|
if (!isset($_SESSION[CACTI_CWD])) {
|
|
$_SESSION[CACTI_CWD] = CACTI_PATH_BASE;
|
|
} else {
|
|
if ($_SESSION[CACTI_CWD] != CACTI_PATH_BASE) {
|
|
cacti_session_destroy();
|
|
}
|
|
}
|
|
|
|
// Sanitize the http referer
|
|
if (isset($_SERVER['HTTP_REFERER'])) {
|
|
$_SERVER['HTTP_REFERER'] = sanitize_uri($_SERVER['HTTP_REFERER']);
|
|
}
|
|
}
|
|
|
|
// emulate 'register_globals' = 'off' if turned on
|
|
if ((bool)ini_get('register_globals')) {
|
|
$not_unset = ['_GET', '_POST', '_COOKIE', '_SERVER', '_SESSION', '_ENV', '_FILES', 'database_type', 'database_default', 'database_hostname', 'database_username', 'database_password', 'config', 'colors'];
|
|
|
|
/* Not only will array_merge give a warning if a parameter is not an array, it will
|
|
* actually fail. So we check if HTTP_SESSION_VARS has been initialised. */
|
|
if (!isset($_SESSION)) {
|
|
$_SESSION = [];
|
|
}
|
|
|
|
// Merge all into one extremely huge array; unset this later
|
|
$input = array_merge($_GET, $_POST, $_COOKIE, $_SERVER, $_SESSION, $_ENV, $_FILES);
|
|
|
|
unset($input['input']);
|
|
unset($input['not_unset']);
|
|
|
|
foreach ($input as $var => $val) {
|
|
if (!in_array($var, $not_unset, true)) {
|
|
unset(${$var});
|
|
}
|
|
}
|
|
|
|
unset($input);
|
|
}
|
|
|
|
define('CACTI_DATE_TIME_FORMAT', date_time_format());
|
|
|
|
require_once(CACTI_PATH_INCLUDE . '/global_languages.php');
|
|
|
|
define('CACTI_VERSION_BRIEF', get_cacti_version_text(false, CACTI_VERSION));
|
|
define('CACTI_VERSION_BRIEF_FULL', get_cacti_version_text(false, CACTI_VERSION_FULL));
|
|
define('CACTI_VERSION_TEXT', get_cacti_version_text(true, CACTI_VERSION));
|
|
define('CACTI_VERSION_TEXT_FULL', get_cacti_version_text(true, CACTI_VERSION_FULL));
|
|
define('CACTI_VERSION_TEXT_CLI', get_cacti_cli_version(true, CACTI_VERSION_FULL)); // @phpstan-ignore-line
|
|
|
|
require_once(CACTI_PATH_LIBRARY . '/auth.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/plugins.php');
|
|
require_once(CACTI_PATH_INCLUDE . '/plugins.php');
|
|
require_once(CACTI_PATH_INCLUDE . '/global_arrays.php');
|
|
require_once(CACTI_PATH_INCLUDE . '/global_settings.php');
|
|
require_once(CACTI_PATH_INCLUDE . '/global_form.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/html_form.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/html_filter.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/variables.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/mib_cache.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/poller.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/snmpagent.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/aggregate.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/api_automation.php');
|
|
require_once(CACTI_PATH_INCLUDE . '/csrf.php');
|
|
require_once(CACTI_PATH_INCLUDE . '/vendor/autoload.php');
|
|
|
|
if ($config['is_web']) {
|
|
// raise a message and perform a page refresh if we've changed modes
|
|
if ($config['poller_id'] > 1) {
|
|
if (isset($_SESSION['connection_mode'])) {
|
|
$previous_mode = $_SESSION['connection_mode'];
|
|
$reload = false;
|
|
|
|
cacti_log('Connection: ' . CACTI_CONNECTION . ', Previous Mode: ' . $previous_mode . ', Page: ' . $_SERVER['SCRIPT_NAME'], false, 'WEBUI', POLLER_VERBOSITY_DEBUG);
|
|
|
|
if (CACTI_CONNECTION == 'online' && ($previous_mode != CACTI_CONNECTION)) {
|
|
$reload = true;
|
|
$message = __('The Main Data Collector has returned to an Online Status');
|
|
$level = MESSAGE_LEVEL_INFO;
|
|
} elseif (CACTI_CONNECTION != 'online' && $previous_mode == 'online') {
|
|
$reload = true;
|
|
$message = __('The Main Data Collector has gone to an Offline or Recovering Status');
|
|
$level = MESSAGE_LEVEL_ERROR;
|
|
}
|
|
|
|
if ($reload) {
|
|
$_SESSION['connection_mode'] = CACTI_CONNECTION;
|
|
|
|
raise_message('connection_state', $message, $level);
|
|
|
|
session_destroy();
|
|
|
|
print '<div style="display:none">cactiRemoteState</div>';
|
|
|
|
exit;
|
|
}
|
|
} else {
|
|
cacti_log('Connection: ' . CACTI_CONNECTION . ', Previous Mode: notset', false, 'WEBUI', POLLER_VERBOSITY_DEBUG);
|
|
|
|
$previous_mode = CACTI_CONNECTION;
|
|
|
|
$_SESSION['connection_mode'] = CACTI_CONNECTION;
|
|
}
|
|
}
|
|
|
|
if (isrv('newtheme')) {
|
|
$newtheme = gnrv('newtheme');
|
|
$newtheme_css = __DIR__ . "/themes/$newtheme/main.css";
|
|
|
|
if (is_valid_theme($newtheme)) {
|
|
$_SESSION['selected_theme'] = $newtheme;
|
|
}
|
|
}
|
|
|
|
if (isrv('csrf_timeout')) {
|
|
raise_message('csrf_ptimeout');
|
|
}
|
|
|
|
// check for save actions using GET
|
|
if (isrv('action')) {
|
|
$action = gnrv('action');
|
|
|
|
$bad_actions = ['save', 'update_data', 'changepassword'];
|
|
|
|
foreach ($bad_actions as $bad) {
|
|
if ($action == $bad && !isset($_POST['__csrf_magic'])) {
|
|
cacti_log(sprintf('WARNING: Attempt to use GET method for POST operations in page %s from IP %s', $filename, get_client_addr()), false, 'WEBUI');
|
|
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
cacti_time_zone_set();
|
|
}
|
|
|
|
api_plugin_hook('config_insert');
|
|
|
|
// set config cacti_version for plugins
|
|
$config['cacti_version'] = CACTI_VERSION;
|