Files
cacti/lib/clog_webapi.php
T
TheWitness 41e6d9e033 security: consolidate verified GHSA fixes for develop (batch 4) (#8145)
* security: consolidate verified GHSA fixes for develop (batch 4)

Develop counterparts of the 1.2.x batch-4 fixes in #8144, re-verified against
current develop HEAD.

- GHSA-4rmr-wvjq-qxc2: include/auth.php Basic Auth and guest branches fall
  through to the per-page realm check instead of returning early.
- GHSA-j67j-wpm4-9g3x: the pre-dispatch update_policy branch in user_admin.php
  and user_group_admin.php requires a CSRF-token POST. The denylist -> allowlist
  redesign is tracked for 1.3 in #8143.
- GHSA-2p3x-f5jm-rg6p: the View-Graphs -> poller RCE chain is broken by the
  realm check (4rmr), the update_policy guard (j67j) and shell-free webhits (x5pw).
- GHSA-x5pw-cg7j-mj4r: scripts/webhits.pl runs wc through a shell-free list-form open.
- GHSA-9737-rqh3-h7fh: names interpolated into raise_message() now use __esc()
  across the UI; URL- and HTML-bearing messages are excluded.
- GHSA-9x9h-2577-9w86: the colordropdown input is built as a node and the label
  is no longer round-tripped through parseHTML (the list item already used
  createTextNode on develop).
- GHSA-h96j-8xhc-2p3w: remote_agent.php authorizes on $_SERVER['REMOTE_ADDR'],
  not get_client_addr(), which honours client-settable proxy headers.

Regression guards under tests/Unit/Security/{Auth,Csrf,Xss,ShellInjection}.
php -l, check-changelog.py and the sink/architectural baselines all pass.

* chore: reference PR #8145 on the batch-4 1.3.0-dev CHANGELOG entries

* security: resolve develop merge - keep id-only xmlerror unescaped (matches #8141 h745 test)

* security(GHSA-j67j): use REQUEST_METHOD POST guard instead of raw \ (hardening-pattern-guard)

* security(GHSA-9737): fix review findings - drop double-encoding in __esc, escape missed name sinks, correct plugins format-arg count
2026-10-02 13:10:42 -04:00

1052 lines
32 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/ |
+-------------------------------------------------------------------------+
*/
include_once('functions.php');
/**
* Retrieves a list of graphs associated with a specific data source.
*
* This function queries the database to fetch all distinct graphs that are linked
* to the provided local data ID. The result is returned as an associative array
* where the keys are the graph IDs and the values are the graph names.
*
* @param int $local_data_id The ID of the local data source to fetch graphs for.
*
* @return array An associative array of graphs, where the keys are graph IDs
* and the values are graph names.
*/
function clog_get_graphs_from_datasource(int $local_data_id) : array {
return array_rekey(db_fetch_assoc_prepared('SELECT DISTINCT
gtg.local_graph_id AS id,
gtg.title_cache AS name
FROM graph_templates_graph AS gtg
INNER JOIN graph_templates_item AS gti
ON gtg.local_graph_id=gti.local_graph_id
INNER JOIN data_template_rrd AS dtr
ON gti.task_item_id=dtr.id
WHERE gtg.local_graph_id>0
AND dtr.local_data_id = ?',
[$local_data_id]), 'id', 'name');
}
/**
* Validates and processes a given filename, determining its path and base name.
*
* This function checks if the provided filename matches specific log file patterns
* (e.g., standard log, error log, or boost log) and extracts the corresponding
* file path and base name. Optionally, it can verify if the file exists.
*
* @param string $file The input filename to validate. This will be modified
* to contain only the base name of the file.
* @param string $filepath The output variable that will hold the directory path
* of the validated file.
* @param string $filename The output variable that will hold the base name of
* the validated file.
* @param bool $filecheck If true, the function will check if the
* resolved file exists. Defaults to false.
*
* @return bool Returns true if the file is valid (and exists if $filecheck is true),
* or false otherwise.
*/
function clog_validate_filename(string &$file, string &$filepath = '', string &$filename = '', bool $filecheck = false) : bool {
$logfile = read_config_option('path_cactilog');
if ($logfile == '') {
$logfile = CACTI_PATH_LOG . '/cacti.log';
}
$errfile = read_config_option('path_stderrlog');
$errbase = basename($errfile);
$boostfile = read_config_option('path_boost_log');
$boostbase = basename($boostfile);
$file = basename($file);
$logbase = basename($logfile);
$filepath = '';
$filename = '';
$filefull = '';
if (!empty($errfile) && str_starts_with($file, $errbase)) {
$filepath = dirname($errfile);
$filename = $errbase;
$filefull = $filepath . '/' . $file;
} elseif (!empty($logfile) && str_starts_with($file, $logbase)) {
$filepath = dirname($logfile);
$filename = $logbase;
$filefull = $filepath . '/' . $file;
} elseif (!empty($boostfile) && str_starts_with($file, $boostbase)) {
$filepath = dirname($boostfile);
$filename = $boostbase;
$filefull = $filepath . '/' . $file;
}
return ($filecheck ? file_exists($filefull) : !empty($filefull));
}
/**
* Purges or clears a specified log file within the Cacti application.
*
* @param string $action The action to be taken 'purge' or 'rotate'
*
* @return void
*/
function clog_purge_logfile(string $action = 'purge') : void {
$filename = gnrv('filename');
$logpath = '';
$logname = '';
if (!clog_validate_filename($filename, $logpath, $logname)) {
raise_message('clog_invalid');
header('Location: ' . get_current_page());
exit(0);
}
$purgefile = $logpath . '/' . $filename;
$logfile = $logpath . '/' . $logname;
$log_action = read_config_option('log_action');
// get base filenames for rotate assessment
$cactiLog = basename(read_config_option('path_cactilog'));
$errorLog = basename(read_config_option('path_stderrlog'));
// basic checking
if ($action == 'rotate' && $log_action == LOG_ACTION_PURGE) {
raise_message('rotate_failed', __esc('Cacti Log file rotation failed for Log File \'%s\'. User \'%s\' wished to rotate, but rotating is disabled', basename($purgefile), get_username()), MESSAGE_LEVEL_ERROR);
return;
}
if ($action == 'purge' && $log_action == LOG_ACTION_ROTATE) {
raise_message('purge_failed', __esc('Cacti Log file purging failed for Log File \'%s\'. User \'%s\' wished to purge, but purging is disabled', basename($purgefile), get_username()), MESSAGE_LEVEL_ERROR);
return;
}
if ($filename != $cactiLog && $filename != $errorLog && $action == 'rotate') {
raise_message('rotate_failed', __esc('Cacti Log file rotation failed for Log File \'%s\'. User \'%s\' wished to rotate, but rotating is not allowed on already rotated files', basename($purgefile), get_username()), MESSAGE_LEVEL_ERROR);
return;
}
if ($action == 'purge') {
$imessage = __('The Cacti Log File \'%s\' was Removed by user \'%s\'', basename($purgefile), get_username());
$message = sprintf('The Cacti Log File \'%s\', Removed by user \'%s\'', basename($purgefile), get_username());
} else {
$imessage = __('The Cacti Log File \'%s\' was Rotated by user \'%s\'', basename($purgefile), get_username());
$message = sprintf('The Cacti Log File \'%s\', Rotated by user \'%s\'', basename($purgefile), get_username());
}
if (file_exists($purgefile)) {
if (is_writable($purgefile)) {
if ($logfile != $purgefile) {
if ($action == 'purge') {
unlink($purgefile);
raise_message('clog_removed', $imessage, MESSAGE_LEVEL_INFO);
cacti_log($message, false, 'WEBUI');
} else {
raise_message('clog_removed', __esc('Removal Failed due to the Administrator blocking removal of archived files. The file \'%s\' can not be removed.', basename($purgefile)), MESSAGE_LEVEL_WARN);
}
} else {
if ($action == 'rotate') {
$ext = date('Ymd-His');
rename($logfile, $logfile . '-' . $ext);
}
$log_fh = fopen($logfile, 'w');
if (is_resource($log_fh)) {
fclose($log_fh);
raise_message('clog_removed', $imessage, MESSAGE_LEVEL_INFO);
cacti_log($message, false, 'WEBUI');
} else {
raise_message('clog_removed', __esc('Unable to open log file \'%s\' for writing.', basename($logfile)), MESSAGE_LEVEL_ERROR);
}
}
} else {
raise_message('clog_permissions');
}
} else {
raise_message('clog_missing');
}
}
/**
* Displays the log file viewer for Cacti, allowing users to view, filter, and manage log entries.
*
* @return void
*/
function clog_view_logfile() : void {
global $logfile_actions;
$exclude_reported = false;
$clogAdmin = clog_admin();
// enable page refreshes
kill_session_var('custom');
if (isrv('filename')) {
$logfile = basename(gnrv('filename'));
} elseif (isset($_SESSION['sess_clog']['filename'])) {
$logfile = basename($_SESSION['sess_clog']['filename']);
} else {
$logfile = 'cacti.log';
}
$logname = '';
$logpath = '';
if (!clog_validate_filename($logfile, $logpath, $logname, true)) {
$logfile = read_config_option('path_cactilog');
} else {
$logfile = $logpath . '/' . $logfile;
}
if ($clogAdmin) {
$redirect = false;
if (isrv('purge_continue')) {
$redirect = true;
clog_purge_logfile('purge');
} elseif (isrv('rotate_continue')) {
$redirect = true;
clog_purge_logfile('rotate');
}
if ($redirect) {
$logfile = read_config_option('path_cactilog');
header('Location: clog.php?filename=' . basename($logfile));
exit;
}
}
$page_nr = gnrv('page');
if ($page_nr == '') {
$page_nr = 1;
srv('page', 1);
}
if (get_current_page() == 'clog.php' || get_current_page() == 'clog_user.php') {
general_header();
} else {
top_header();
}
if ($clogAdmin) {
if (gnrv('action') == 'purge' || gnrv('action') == 'rotate') {
// Keep phpstan happy
$action = '';
$title = '';
$message = '';
$header = '';
if (gnrv('action') == 'purge') {
$message = __('Click \'Continue\' to Purge the Log File \'' . htmle(basename($logfile)) . '\'.<br><br><br>Note: If logging is set to both Cacti and Syslog, the log information will remain in Syslog.');
$action = 'purge_continue';
$button = __esc('Purge');
$title = __esc('Purge Log');
$header = $logfile_actions[LOG_ACTION_PURGE];
} elseif (gnrv('action') == 'rotate') {
$message = __('Click \'Continue\' to Rotate the existing Log File \'' . htmle(basename($logfile)) . '\'.<br><br><br>Note: If logging is set to both Cacti and Syslog, the log information will remain in Syslog.');
$action = 'rotate_continue';
$button = __esc('Rotate');
$title = __esc('Rotate Log');
$header = $logfile_actions[LOG_ACTION_ROTATE];
}
form_start(get_current_page());
html_start_box($header, '60%', false, 3, 'center', '');
print "<tr>
<td class='textArea'>
<p>" . $message . "</p>
</td>
</tr>
<tr class='saveRow'>
<td colspan='2' class='right'>
<button type='button' class='ui-button ui-corner-all ui-widget' id='cancel'>" . __esc('Cancel') . "</button>
<button type='button' class='ui-button ui-corner-all ui-widget' id='pc' name='purge_continue' title='$title'>" . __esc('Continue') . "</button>
<script type='text/javascript'>
$('#pc').click(function() {
strURL = location.pathname+'?$action=1&filename=" . basename($logfile) . "';
loadUrl({url:strURL})
});
$('#cancel').click(function() {
strURL = location.pathname;
loadUrl({url:strURL})
});
$(function() {
applySkin();
});
</script>
</td>
</tr>";
html_end_box();
return;
}
}
draw_clog_filter(true, $logfile, $clogAdmin);
// read logfile into an array and display
$total_rows = 0;
$number_of_lines = grv('tail_lines') < 0 ? read_config_option('max_display_rows') : grv('tail_lines');
if (grv('expand') == 2) {
$should_expand = false;
} elseif (grv('expand') == 1) {
$should_expand = true;
} else {
$should_expand = read_config_option('log_expand') != LOG_EXPAND_NONE;
}
$reverse = grv('reverse');
$logcontents = tail_file($logfile, $number_of_lines, grv('message_type'), grv('rfilter'), $page_nr, $total_rows, grv('matches'), $should_expand, $reverse);
if (grv('reverse') == 1) {
$logcontents = array_reverse($logcontents);
}
if (!$clogAdmin) {
$exclude_regex = read_config_option('clog_exclude', true);
if ($exclude_regex != '') {
$ad_filter = __(' - Admin Filter active');
} else {
$ad_filter = __(' - Admin Unfiltered');
}
} else {
$ad_filter = __(' - Admin view');
$exclude_regex = '';
}
if (grv('message_type') > 0 || grv('rfilter') != '') {
$start_string = __('Log [Total Lines: %d %s - Filter active]', $total_rows, $ad_filter);
} else {
$start_string = __('Log [Total Lines: %d %s - Unfiltered]', $total_rows, $ad_filter);
}
$base_url = CACTI_PATH_URL . 'clog.php';
$nav = html_nav_bar($base_url, MAX_DISPLAY_PAGES, $page_nr, $number_of_lines, $total_rows, 1, __('Entries'), 'page', 'main');
print $nav;
html_start_box($start_string, '100%', false, 3, 'center', '');
$linecolor = false;
if (db_column_exists('sites', 'disabled')) {
$sql_where = 'AND IFNULL(s.disabled,"") != "on"';
} else {
$sql_where = '';
}
$hosts = db_fetch_assoc("SELECT h.id, h.description
FROM host h
LEFT JOIN sites s
ON s.id = h.site_id
WHERE IFNULL(TRIM(h.disabled), '') != 'on'
$sql_where
AND deleted = ''");
$hostDescriptions = [];
foreach ($hosts as $host) {
$hostDescriptions[$host['id']] = htmle($host['description']);
}
foreach ($logcontents as $item) {
$new_item = htmle($item);
if ($should_expand) {
$new_item = text_substitute($new_item, isHtml: true);
}
// respect the exclusion filter
if ($exclude_regex != '' && !$clogAdmin) {
if (validate_is_regex($exclude_regex)) {
if (preg_match($exclude_regex, $new_item)) {
continue;
}
} elseif (!$exclude_reported) {
cacti_log('Cacti Log Exclude Regex "' . $exclude_regex . '" is Invalid. Update your Exclude Regex to be valid!');
$exclude_reported = true;
}
}
// get the background color
if (str_contains($new_item, 'ERROR') || str_contains($new_item, 'FATAL')) {
$class = 'clogError';
} elseif (str_contains($new_item, 'WARN')) {
$class = 'clogWarning';
} elseif (str_contains($new_item, ' SQL ')) {
$class = 'clogSQL';
} elseif (str_contains($new_item, 'DEBUG')) {
$class = 'clogDebug';
} elseif (str_contains($new_item, 'STATS')) {
$class = 'clogStats';
} else {
if ($linecolor) {
$class = 'odd';
} else {
$class = 'even';
}
$linecolor = !$linecolor;
}
print "<tr class='$class'><td>$new_item</td></tr>";
}
html_end_box(false);
if ($total_rows) {
print $nav;
}
bottom_footer();
}
/**
* Custom sorting function for log file names.
*
* @param string $a The first file name to compare.
* @param string $b The second file name to compare.
*
* @return int Returns < 0 if $a is less than $b, 0 if they are equal, and > 0 if $a is greater than $b.
*/
function filter_sort(string $a, string $b) : int {
$a_parts = explode('-', $a);
$b_parts = explode('-', $b);
$a_date = '99999999';
if (cacti_count($a_parts) > 1) {
$a_date = $a_parts[1];
}
$b_date = '99999999';
if (cacti_count($b_parts) > 1) {
$b_date = $b_parts[1];
}
// Invert the order, replace _'s with +'s to make them sort after .'s, prefix the date
// This makes cacti_stderr.log appear after cacti.log in date descending order with
// no date files first
return strcmp($b_date . '-' . str_replace('_', '+', $b_parts[0]), $a_date . '-' . str_replace('_', '+', $a_parts[0]));
}
/**
* Retrieves a list of log files from the configured log directories.
*
* @return array An array of log file names, including standard, stderr, and boost logs.
*
* Notes:
* - If the configured log path is not readable, it defaults to 'cacti.log'.
* - The function ensures that archived log files are included in the result.
*/
function clog_get_logfiles() : array {
$stdFileArray = $stdLogFileArray = $stdErrFileArray = $boostFileArray = [];
$configLogPath = read_config_option('path_cactilog');
$configLogBase = basename($configLogPath);
$stderrLogPath = read_config_option('path_stderrlog');
$stderrLogBase = basename($stderrLogPath);
$boostLogPath = read_config_option('path_boost_log');
$boostLogBase = basename($boostLogPath);
if ($configLogPath == '') {
$logPath = CACTI_PATH_LOG . '/';
} else {
$logPath = dirname($configLogPath);
}
if (is_readable($logPath)) {
$files = scandir($logPath);
} else {
$files = ['cacti.log'];
}
$logName = '';
// Defaults go first and second
$stdFileArray[] = basename($configLogPath);
// After Defaults, do Cacti log first (of archived)
if (cacti_sizeof($files)) {
$stdLogFileArray = [];
foreach ($files as $logFile) {
if (in_array($logFile, ['.', '..', '.htaccess', $configLogBase, $stderrLogBase, $boostLogBase], true)) {
continue;
}
$explode = explode('.', $logFile);
if (!str_starts_with($explode[max(array_keys($explode))], 'log')) {
continue;
}
if (!clog_validate_filename($logFile, $logPath, $logName)) {
continue;
}
if (!empty($stderrLogBase) && str_starts_with($logFile, $stderrLogBase)) {
$stdErrFileArray[] = $logFile;
} elseif (!empty($boostLogBase) && str_starts_with($logFile, $boostLogBase)) {
$boostFileArray[] = $logFile;
} else {
$stdLogFileArray[] = $logFile;
}
}
$stdErrFileArray = array_unique($stdErrFileArray);
$stdLogFileArray = array_unique($stdLogFileArray);
$boostFileArray = array_unique($boostFileArray);
}
// Defaults go first and second
if (!empty($stderrLogPath)) {
$stdFileArray[] = basename($stderrLogPath);
// After Defaults, do Cacti StdErr log second (of archived)
if (dirname($stderrLogPath) != $logPath) {
$errFiles = @scandir(dirname($stderrLogPath));
$files = $errFiles;
if (cacti_sizeof($files)) {
$stdErrFileArray = [];
foreach ($files as $logFile) {
if (in_array($logFile, ['.', '..', '.htaccess', $configLogBase, $stderrLogBase], true)) {
continue;
}
$explode = explode('.', $logFile);
if (!str_starts_with($explode[max(array_keys($explode))], 'log')) {
continue;
}
if (!clog_validate_filename($logFile, $logPath, $logName)) {
continue;
}
$stdErrFileArray[] = $logFile;
}
$stdErrFileArray = array_unique($stdErrFileArray);
}
}
}
arsort($stdLogFileArray, SORT_NATURAL);
arsort($stdErrFileArray, SORT_NATURAL);
arsort($boostFileArray, SORT_NATURAL);
return array_unique(array_merge($stdFileArray, $stdLogFileArray, $stdErrFileArray, $boostFileArray));
}
/**
* Creates a filter configuration for the clog (custom log) web API.
*
* @param string $logfile The name of the log file to be filtered.
* @param bool $clogAdmin Indicates whether the user has administrative
* privileges for clog operations.
*
* @return array The filter configuration array, including rows of filter options
* and action buttons.
*/
function create_clog_filter(string $logfile, bool $clogAdmin) : array {
global $log_tail_lines, $page_refresh_interval, $logfile_actions;
$all = ['-1' => __('All')];
$any = ['-1' => __('Any')];
$none = ['0' => __('None')];
$deleted = ['-2' => __('Deleted/Invalid')];
// transform the log directory as required
$logFileArray = clog_get_logfiles();
$newLogArray = [];
$log_action = read_config_option('log_action');
if (cacti_sizeof($logFileArray)) {
foreach ($logFileArray as $logFile) {
$logParts = explode('-', $logFile);
$logDate = cacti_count($logParts) < 2 ? '' : $logParts[1] . (isset($logParts[2]) ? '-' . $logParts[2] : '');
$logName = $logParts[0];
$newLogArray[$logFile] = $logName . ($logDate != '' ? ' [' . substr($logDate,4) . ']' : '');
}
}
$expands = [
'0' => __('System Default'),
'1' => __('Expand Log'),
'2' => __('Raw Log'),
];
$message_types = [
'-1' => __('All'),
'1' => __('Stats'),
'2' => __('Warnings'),
'3' => __('Warnings++'),
'4' => __('Errors'),
'5' => __('Errors++'),
'6' => __('Debug'),
'7' => __('SQL Calls'),
'8' => __('AutoM8'),
'9' => __('Non Stats'),
'10' => __('Boost'),
'11' => __('Device Up/Down'),
'12' => __('Recaches'),
'13' => __('Security Issues'),
];
if (api_plugin_is_enabled('thold')) {
$message_types['99'] = __('Threshold');
}
$reverse = [
'1' => __('Newest First'),
'2' => __('Oldest First')
];
$matches = [
'1' => __('Matches'),
'0' => __('Does Not Match')
];
$filter = [
'rows' => [
[
'filename' => [
'method' => 'drop_array',
'friendly_name' => __('File'),
'filter' => FILTER_DEFAULT,
'default' => 'cacti.log',
'array' => $newLogArray,
'value' => $logfile
],
'tail_lines' => [
'method' => 'drop_array',
'friendly_name' => __('Tail Lines'),
'filter' => FILTER_VALIDATE_INT,
'default' => read_config_option('max_display_rows'),
'pageset' => true,
'array' => $log_tail_lines,
'value' => ''
],
'expand' => [
'method' => 'drop_array',
'friendly_name' => __('Expand Log'),
'filter' => FILTER_VALIDATE_INT,
'default' => '-1',
'pageset' => true,
'array' => $expands,
'value' => '-1'
]
],
[
'message_type' => [
'method' => 'drop_array',
'friendly_name' => __('Type'),
'filter' => FILTER_VALIDATE_INT,
'default' => '-1',
'pageset' => true,
'array' => $message_types,
'value' => '-1'
],
'reverse' => [
'method' => 'drop_array',
'friendly_name' => __('Display'),
'filter' => FILTER_VALIDATE_INT,
'default' => '1',
'pageset' => true,
'array' => $reverse,
'value' => '1'
],
'refresh' => [
'method' => 'drop_array',
'friendly_name' => __('Refresh'),
'filter' => FILTER_VALIDATE_INT,
'default' => '300',
'pageset' => true,
'array' => $page_refresh_interval,
'value' => '300'
]
],
[
'matches' => [
'method' => 'drop_array',
'friendly_name' => __('Search'),
'filter' => FILTER_VALIDATE_INT,
'default' => '1',
'dynamic' => false,
'pageset' => true,
'array' => $matches,
'value' => '1'
],
'rfilter' => [
'method' => 'textbox',
'filter' => FILTER_VALIDATE_IS_REGEX,
'placeholder' => __('Enter a search term'),
'size' => '55',
'default' => '',
'pageset' => true,
'max_length' => '120',
'value' => ''
]
]
],
'buttons' => [
'go' => [
'method' => 'submit',
'display' => __('Go'),
'title' => __('Apply filter to table'),
],
'clear' => [
'method' => 'button',
'display' => __('Clear'),
'title' => __('Reset filter to default values'),
],
'purge' => [
'method' => 'button',
'display' => __('Purge'),
'action' => 'default',
'title' => __('Purge all data from the existing Cacti Log'),
'url' => 'clog.php?action=purge&filename=' . $logfile
],
'rotate' => [
'method' => 'button',
'display' => __('Rotate'),
'action' => 'default',
'title' => __('Rotate the Cacti Log'),
'url' => 'clog.php?action=rotate&filename=' . $logfile
],
]
];
if (!$clogAdmin) {
unset($filter['buttons']['purge']);
unset($filter['buttons']['rotate']);
} else {
$cactiLog = read_config_option('path_cactilog');
$errorLog = read_config_option('path_stderrlog');
$logAction = read_config_option('log_action');
if ($logfile == $cactiLog || $logfile == $errorLog) {
if ($logAction == LOG_ACTION_PURGE) {
unset($filter['buttons']['rotate']);
} elseif ($logAction == LOG_ACTION_ROTATE) {
unset($filter['buttons']['purge']);
}
} else {
if ($logAction == LOG_ACTION_PURGE) {
unset($filter['buttons']['rotate']);
} elseif ($logAction == LOG_ACTION_ROTATE) {
unset($filter['buttons']['rotate']);
unset($filter['buttons']['purge']);
} else {
unset($filter['buttons']['rotate']);
}
}
}
return $filter;
}
/**
* Draws the clog filter for log files and renders or sanitizes it based on the provided parameters.
*
* @param bool $render Determines whether to render the filter or sanitize it. Defaults to false.
* @param bool|string $logfile Specifies the logfile to filter. Defaults to false.
* @param bool $clogAdmin Indicates whether the filter is for an admin user. Defaults to false.
*
* @return void
*/
function draw_clog_filter(bool $render = false, bool|string $logfile = false, bool $clogAdmin = false) : void {
$filters = create_clog_filter($logfile, $clogAdmin);
$page_nr = gnrv('page');
$current_page = get_current_page();
if ($current_page == 'utilities.php') {
$base_page = 'utilities.php?action=view_logfile';
$page = $base_page . '&page=' . $page_nr;
} else {
$base_page = 'clog' . (!$clogAdmin ? '_user' : '') . '.php';
$page = $base_page . '?page=' . $page_nr;
}
// create the page filter
$pageFilter = new CactiTableFilter(__('Log Filters'), $page, 'logfile', 'sess_clog', '', false, false);
$pageFilter->set_filter_array($filters);
if ($render) {
$pageFilter->render();
} else {
$pageFilter->sanitize();
}
}
/**
* Retrieves an array of regular expressions.
*
* @deprecated Use text_get_regex_array() directly instead.
*
* @return array Returns an array of regular expressions.
*/
function clog_get_regex_array() : array {
cacti_depreciated(__FUNCTION__ . '()');
return text_get_regex_array();
}
/**
* Performs a regex replacement on the provided text using the specified parameters.
*
* @deprecated Use `text_regex_replace` directly instead.
*
* @param int $id An identifier for the operation or context.
* @param string $link A link or reference related to the operation.
* @param string $url The URL to be processed.
* @param array $matches An array of matches to be used in the replacement.
* @param mixed $cache Cache data to be used during the operation.
*
* @return mixed The result of the regex replacement operation.
*/
function clog_regex_replace(int $id, string $link, string $url, array $matches, mixed $cache) : mixed {
cacti_depreciated(__FUNCTION__ . '()');
return text_regex_replace($id, $link, $url, $matches, $cache);
}
/**
* Parses HTML content using a regular expression parser.
*
* @deprecated This function is deprecated and should not be used in new code.
*
* @param array $matches An array of matches from a regular expression.
*
* @return mixed The result of the `text_regex_parser()` function.
*/
function clog_regex_parser_html(array $matches) : mixed {
cacti_depreciated(__FUNCTION__ . '()');
return text_regex_parser($matches, true);
}
/**
* Parses the given matches using a regular expression and optionally generates a link.
*
* @deprecated This function is deprecated. Use `text_regex_parser()` directly instead.
*
* @param array $matches The matches to be parsed, typically from a regular expression.
* @param bool $link Optional. Whether to generate a link. Default is false.
*
* @return mixed The result of the `text_regex_parser` function.
*/
function clog_regex_parser(array $matches, bool $link = false) : mixed {
cacti_depreciated(__FUNCTION__ . '()');
return text_regex_parser($matches, $link);
}
/**
* Processes a regex match for a device and optionally generates a link.
*
* @deprecated Use `text_regex_device()` directly instead.
*
* @param array $matches An array of regex matches to process.
* @param bool $link Whether to generate a link. Default is false.
*
* @return mixed The result of the `text_regex_device` function.
*/
function clog_regex_device(array $matches, bool $link = false) : mixed {
cacti_depreciated(__FUNCTION__ . '()');
return text_regex_device($matches, $link);
}
/**
* Processes a regex match for a data source and optionally generates a link.
*
* @deprecated Use `text_regex_datasource()` directly instead.
*
* @param array $matches An array of regex matches to process.
* @param bool $link Whether to generate a link. Default is false.
*
* @return mixed The result of the `text_regex_datasource()` function.
*/
function clog_regex_datasource(array $matches, bool $link = false) : mixed {
cacti_depreciated(__FUNCTION__ . '()');
return text_regex_datasource($matches, $link);
}
/**
* Logs and processes regex matches for a poller.
*
* @deprecated Use `text_regex_poller()` directly instead.
*
* @param array $matches An array of regex matches to be processed.
* @param bool $link Whether to include a link in the processing. Default is false.
*
* @return mixed The result of the `text_regex_poller()` function.
*/
function clog_regex_poller(array $matches, bool $link = false) : mixed {
cacti_depreciated(__FUNCTION__ . '()');
return text_regex_poller($matches, $link);
}
/**
* Processes regex matches for a data query and optionally generates a link.
*
* @deprecated Use `text_regex_dataquery` directly instead.
*
* @param array $matches An array of regex matches to process.
* @param bool $link Whether to generate a link. Default is false.
*
* @return mixed The result of the `text_regex_dataquery` function.
*/
function clog_regex_dataquery(array $matches, bool $link = false) : mixed {
cacti_depreciated(__FUNCTION__ . '()');
return text_regex_dataquery($matches, $link);
}
/**
* Processes regex matches for a RRA and optionally generates a link.
*
* @deprecated Use `text_regex_rra()` directly instead.
*
* @param array $matches An array of regex matches to process.
* @param bool $link Whether to generate a link. Default is false.
*
* @return mixed The result of the `text_regex_rra` function.
*/
function clog_regex_rra(array $matches, bool $link = false) : mixed {
cacti_depreciated(__FUNCTION__ . '()');
return text_regex_rra($matches, $link);
}
/**
* Processes regex matches for graphs and optionally generates a link.
*
* @deprecated Use `text_regex_graphs()` directly instead.
*
* @param array $matches An array of regex matches to process.
* @param bool $link Whether to generate a link. Default is false.
*
* @return mixed The result of the `text_regex_graphs` function.
*/
function clog_regex_graphs(array $matches, bool $link = false) : mixed {
cacti_depreciated(__FUNCTION__ . '()');
return text_regex_graphs($matches, $link);
}
/**
* Processes regex matches for graph templates and optionally generates a link.
*
* @deprecated Use `text_regex_graphtemplates()` directly instead.
*
* @param array $matches An array of regex matches to process.
* @param bool $link Whether to generate a link. Default is false.
*
* @return mixed The result of the `text_regex_graphtemplates` function.
*/
function clog_regex_graphtemplates(array $matches, bool $link = false) : mixed {
cacti_depreciated(__FUNCTION__ . '()');
return text_regex_graphtemplates($matches, $link);
}
/**
* Processes regex matches for users and optionally generates a link.
*
* @deprecated Use `text_regex_users()` directly instead.
*
* @param array $matches An array of regex matches to process.
* @param bool $link Whether to generate a link. Default is false.
*
* @return mixed The result of the `text_regex_users` function.
*/
function clog_regex_users(array $matches, bool $link = false) : mixed {
cacti_depreciated(__FUNCTION__ . '()');
return text_regex_users($matches, $link);
}
/**
* Processes regex matches for rules and optionally generates a link.
*
* @deprecated Use `text_regex_rule()` directly instead.
*
* @param array $matches An array of regex matches to process.
* @param bool $link Whether to generate a link. Default is false.
*
* @return mixed The result of the `text_regex_rule` function.
*/
function clog_regex_rule(array $matches, bool $link = false) : mixed {
cacti_depreciated(__FUNCTION__ . '()');
return text_regex_rule($matches, $link);
}
/**
* Retrieves the titles of data sources based on the provided local data IDs.
*
* @deprecated Use get_data_source_titles() directly instead.
*
* @param array $local_data_ids An array of local data IDs for which to retrieve titles.
*
* @return array An array of data source titles corresponding to the provided IDs.
*/
function clog_get_datasource_titles(array $local_data_ids) : array {
cacti_depreciated(__FUNCTION__ . '()');
return get_data_source_titles($local_data_ids);
}