1<?php 2/* 3 +-------------------------------------------------------------------------+ 4 | Copyright (C) 2004-2021 The Cacti Group | 5 | | 6 | This program is free software; you can redistribute it and/or | 7 | modify it under the terms of the GNU General Public License | 8 | as published by the Free Software Foundation; either version 2 | 9 | of the License, or (at your option) any later version. | 10 | | 11 | This program is distributed in the hope that it will be useful, | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | GNU General Public License for more details. | 15 +-------------------------------------------------------------------------+ 16 | Cacti: The Complete RRDtool-based Graphing Solution | 17 +-------------------------------------------------------------------------+ 18 | This code is designed, written, and maintained by the Cacti Group. See | 19 | about.php and/or the AUTHORS file for specific developer information. | 20 +-------------------------------------------------------------------------+ 21 | http://www.cacti.net/ | 22 +-------------------------------------------------------------------------+ 23*/ 24 25/** 26 * title_trim - takes a string of text, truncates it to $max_length and appends 27 * three periods onto the end 28 * 29 * @param $text - the string to evaluate 30 * @param $max_length - the maximum number of characters the string can contain 31 * before it is truncated 32 * 33 * @return - the truncated string if len($text) is greater than $max_length, else 34 * the original string 35 */ 36function title_trim($text, $max_length) { 37 if (strlen($text) > $max_length) { 38 return mb_substr($text, 0, $max_length) . '...'; 39 } else { 40 return $text; 41 } 42} 43 44/** 45 * filter_value - a quick way to highlight text in a table from general filtering 46 * 47 * @param $text - the string to filter 48 * @param $filter - the search term to filter for 49 * @param $href - the href if you wish to have an anchor returned 50 * 51 * @return - the filtered string 52 */ 53function filter_value($value, $filter, $href = '') { 54 static $charset; 55 56 if ($charset == '') { 57 $charset = ini_get('default_charset'); 58 } 59 60 if ($charset == '') { 61 $charset = 'UTF-8'; 62 } 63 64 $value = htmlspecialchars($value, ENT_QUOTES, $charset, false); 65 // Grave Accent character can lead to xss 66 $value = str_replace('`', '`', $value); 67 68 if ($filter != '') { 69 $value = preg_replace('#(' . preg_quote($filter) . ')#i', "<span class='filteredValue'>\\1</span>", $value); 70 } 71 72 if ($href != '') { 73 $value = '<a class="linkEditMain" href="' . htmlspecialchars($href, ENT_QUOTES, $charset, false) . '">' . $value . '</a>'; 74 } 75 76 return $value; 77} 78 79/** 80 * set_graph_config_option - deprecated - wrapper to set_user_setting(). 81 * 82 * @param $config_name - the name of the configuration setting as specified $settings array 83 * @param $value - the values to be saved 84 * @param $user - the user id, otherwise the session user 85 * 86 * @return - void 87 */ 88function set_graph_config_option($config_name, $value, $user = -1) { 89 set_user_setting($config_name, $value, $user); 90} 91 92/** 93 * graph_config_value_exists - deprecated - wrapper to user_setting_exists 94 * 95 * @param $config_name - the name of the configuration setting as specified $settings_user array 96 * in 'include/global_settings.php' 97 * @param $user_id - the id of the user to check the configuration value for 98 * 99 * @return (bool) - true if a value exists, false if a value does not exist 100 */ 101function graph_config_value_exists($config_name, $user_id) { 102 return user_setting_exists($config_name, $user_id); 103} 104 105/** 106 * read_default_graph_config_option - deprecated - wrapper to read_default_user_setting 107 * 108 * @param $config_name - the name of the configuration setting as specified $settings array 109 * in 'include/global_settings.php' 110 * 111 * @return - the default value of the configuration option 112 */ 113function read_default_graph_config_option($config_name) { 114 return read_default_user_setting($config_name); 115} 116 117/** 118 * read_graph_config_option - deprecated - finds the current value of a graph configuration setting 119 * 120 * @param $config_name - the name of the configuration setting as specified $settings_user array 121 * in 'include/global_settings.php' 122 * 123 * @return - the current value of the graph configuration option 124 */ 125function read_graph_config_option($config_name, $force = false) { 126 return read_user_setting($config_name, false, $force); 127} 128 129/** 130 * save_user_setting - sets/updates aLL user settings 131 * 132 * @param $config_name - the name of the configuration setting as specified $settings array 133 * @param $value - the values to be saved 134 * @param $user - the user id, otherwise the session user 135 * 136 * @return - void 137 */ 138function save_user_settings($user = -1) { 139 global $settings_user; 140 141 if ($user == -1 || empty($user)) { 142 $user = $_SESSION['sess_user_id']; 143 } 144 145 foreach ($settings_user as $tab_short_name => $tab_fields) { 146 foreach ($tab_fields as $field_name => $field_array) { 147 /* Check every field with a numeric default value and reset it to default if the inputted value is not numeric */ 148 if (isset($field_array['default']) && is_numeric($field_array['default']) && !is_numeric(get_nfilter_request_var($field_name))) { 149 set_request_var($field_name, $field_array['default']); 150 } 151 152 if (isset($field_array['method'])) { 153 if ($field_array['method'] == 'checkbox') { 154 set_user_setting($field_name, (isset_request_var($field_name) ? 'on' : ''), $user); 155 } elseif ($field_array['method'] == 'checkbox_group') { 156 foreach ($field_array['items'] as $sub_field_name => $sub_field_array) { 157 set_user_setting($sub_field_name, (isset_request_var($sub_field_name) ? 'on' : ''), $user); 158 } 159 } elseif ($field_array['method'] == 'textbox_password') { 160 if (get_nfilter_request_var($field_name) != get_nfilter_request_var($field_name.'_confirm')) { 161 $_SESSION['sess_error_fields'][$field_name] = $field_name; 162 $_SESSION['sess_field_values'][$field_name] = get_nfilter_request_var($field_name); 163 $errors[4] = 4; 164 } elseif (isset_request_var($field_name)) { 165 set_user_setting($field_name, get_nfilter_request_var($field_name), $user); 166 } 167 } elseif ((isset($field_array['items'])) && (is_array($field_array['items']))) { 168 foreach ($field_array['items'] as $sub_field_name => $sub_field_array) { 169 if (isset_request_var($sub_field_name)) { 170 set_user_setting($sub_field_name, get_nfilter_request_var($sub_field_name), $user); 171 } 172 } 173 } elseif (isset_request_var($field_name)) { 174 set_user_setting($field_name, get_nfilter_request_var($field_name), $user); 175 } 176 } 177 } 178 } 179} 180 181/** 182 * set_user_setting - sets/updates a user setting with the given value. 183 * 184 * @param $config_name - the name of the configuration setting as specified $settings array 185 * @param $value - the values to be saved 186 * @param $user - the user id, otherwise the session user 187 * 188 * @return - void 189 */ 190function set_user_setting($config_name, $value, $user = -1) { 191 global $settings_user; 192 193 if ($user == -1 && isset($_SESSION['sess_user_id'])) { 194 $user = $_SESSION['sess_user_id']; 195 } 196 197 if ($user == -1) { 198 if (isset($_SESSION['sess_user_id'])) { 199 $mode = 'WEBUI'; 200 } else { 201 $mode = 'POLLER'; 202 } 203 204 cacti_log('NOTE: Attempt to set user setting \'' . $config_name . '\', with no user id: ' . cacti_debug_backtrace('', false, false, 0, 1), false, $mode, POLLER_VERBOSITY_MEDIUM); 205 } elseif (db_table_exists('settings_user')) { 206 db_execute_prepared('REPLACE INTO settings_user 207 SET user_id = ?, 208 name = ?, 209 value = ?', 210 array($user, $config_name, $value)); 211 212 unset($_SESSION['sess_user_config_array']); 213 $settings_user[$config_name]['value'] = $value; 214 } 215} 216 217/** 218 * user_setting_exists - determines if a value exists for the current user/setting specified 219 * 220 * @param $config_name - the name of the configuration setting as specified $settings_user array 221 * in 'include/global_settings.php' 222 * @param $user_id - the id of the user to check the configuration value for 223 * 224 * @return (bool) - true if a value exists, false if a value does not exist 225 */ 226function user_setting_exists($config_name, $user_id) { 227 static $user_setting_values = array(); 228 229 if (!isset($user_setting_values[$config_name])) { 230 $value = 0; 231 if (db_table_exists('settings_user')) { 232 $value = db_fetch_cell_prepared('SELECT COUNT(*) 233 FROM settings_user 234 WHERE name = ? 235 AND user_id = ?', 236 array($config_name, $user_id)); 237 } 238 239 if ($value !== false && $value > 0) { 240 $user_setting_values[$config_name] = true; 241 } else { 242 $user_setting_values[$config_name] = false; 243 } 244 } 245 246 return $user_setting_values[$config_name]; 247} 248 249/** 250 * clear_user_setting - if a value exists for the current user/setting specified, removes it 251 * 252 * @param $config_name - the name of the configuration setting as specified $settings_user array 253 * in 'include/global_settings.php' 254 * @param $user_id - the id of the user to remove the configuration value for 255 */ 256function clear_user_setting($config_name, $user = -1) { 257 global $settings_user; 258 259 if ($user == -1) { 260 $user = $_SESSION['sess_user_id']; 261 } 262 263 if (db_table_exists('settings_user')) { 264 db_execute_prepared('DELETE FROM settings_user 265 WHERE name = ? 266 AND user_id = ?', 267 array($config_name, $user)); 268 } 269 270 unset($_SESSION['sess_user_config_array']); 271} 272 273/** 274 * read_default_user_setting - finds the default value of a user configuration setting 275 * 276 * @param $config_name - the name of the configuration setting as specified $settings array 277 * in 'include/global_settings.php' 278 * 279 * @return - the default value of the configuration option 280 */ 281function read_default_user_setting($config_name) { 282 global $config, $settings_user; 283 284 foreach ($settings_user as $tab_array) { 285 if (isset($tab_array[$config_name]) && isset($tab_array[$config_name]['default'])) { 286 return $tab_array[$config_name]['default']; 287 } else { 288 foreach ($tab_array as $field_array) { 289 if (isset($field_array['items']) && isset($field_array['items'][$config_name]) && isset($field_array['items'][$config_name]['default'])) { 290 return $field_array['items'][$config_name]['default']; 291 } 292 } 293 } 294 } 295} 296 297/** 298 * read_user_setting - finds the current value of a graph configuration setting 299 * 300 * @param $config_name - the name of the configuration setting as specified $settings_user array 301 * in 'include/global_settings.php' 302 * @param $default - the default value is none is set 303 * @param $force - pull the data from the database if true ignoring session 304 * @param $user - assume this user's identity 305 * 306 * @return - the current value of the user setting 307 */ 308function read_user_setting($config_name, $default = false, $force = false, $user = 0) { 309 global $config; 310 311 /* users must have cacti user auth turned on to use this, or the guest account must be active */ 312 if ($user == 0 && isset($_SESSION['sess_user_id'])) { 313 $effective_uid = $_SESSION['sess_user_id']; 314 } elseif (read_config_option('auth_method') == 0 || $user > 0) { 315 /* first attempt to get the db setting for guest */ 316 if ($user == 0) { 317 $effective_uid = db_fetch_cell("SELECT user_auth.id 318 FROM settings 319 INNER JOIN user_auth 320 ON user_auth.username = settings.value 321 WHERE settings.name = 'guest_user'"); 322 323 if ($effective_uid == '') { 324 $effective_uid = 0; 325 } 326 } else { 327 $effective_uid = $user; 328 } 329 330 $db_setting = false; 331 if (db_table_exists('settings_user')) { 332 $db_setting = db_fetch_row_prepared('SELECT value 333 FROM settings_user 334 WHERE name = ? 335 AND user_id = ?', 336 array($config_name, $effective_uid)); 337 } 338 339 if (cacti_sizeof($db_setting)) { 340 return $db_setting['value']; 341 } elseif ($default !== false) { 342 return $default; 343 } else { 344 return read_default_user_setting($config_name); 345 } 346 } else { 347 $effective_uid = 0; 348 } 349 350 if (!$force) { 351 if (isset($_SESSION['sess_user_config_array'])) { 352 $user_config_array = $_SESSION['sess_user_config_array']; 353 } 354 } 355 356 if (!isset($user_config_array[$config_name])) { 357 $db_setting = false; 358 if (db_table_exists('settings_user')) { 359 $db_setting = db_fetch_row_prepared('SELECT value 360 FROM settings_user 361 WHERE name = ? 362 AND user_id = ?', 363 array($config_name, $effective_uid)); 364 } 365 366 if (cacti_sizeof($db_setting)) { 367 $user_config_array[$config_name] = $db_setting['value']; 368 } elseif ($default !== false) { 369 $user_config_array[$config_name] = $default; 370 } else { 371 $user_config_array[$config_name] = read_default_user_setting($config_name); 372 } 373 374 if (isset($_SESSION)) { 375 $_SESSION['sess_user_config_array'] = $user_config_array; 376 } else { 377 $config['config_user_settings_array'] = $user_config_array; 378 } 379 } 380 381 return $user_config_array[$config_name]; 382} 383 384/** 385 * set_config_option - sets/updates a cacti config option with the given value. 386 * 387 * @param $config_name - the name of the configuration setting as specified $settings array 388 * @param $value - the values to be saved 389 * 390 * @return - void 391 */ 392function set_config_option($config_name, $value) { 393 global $config; 394 395 db_execute_prepared('REPLACE INTO settings 396 SET name = ?, value = ?', 397 array($config_name, $value)); 398 399 $config_array = array(); 400 if (isset($_SESSION['sess_config_array'])) { 401 $config_array = $_SESSION['sess_config_array']; 402 } elseif (isset($config['config_options_array'])) { 403 $config_array = $config['config_options_array']; 404 } 405 406 $config_array[$config_name] = $value; 407 408 // Store the array back for later retrieval 409 if (isset($_SESSION)) { 410 $_SESSION['sess_config_array'] = $config_array; 411 } else { 412 $config['config_options_array'] = $config_array; 413 } 414 415 if (!empty($config['DEBUG_SET_CONFIG_OPTION'])) { 416 file_put_contents(sys_get_temp_dir() . '/cacti-option.log', get_debug_prefix() . cacti_debug_backtrace($config_name, false, false, 0, 1) . "\n", FILE_APPEND); 417 } 418} 419 420/** 421 * config_value_exists - determines if a value exists for the current user/setting specified 422 * 423 * @param $config_name - the name of the configuration setting as specified $settings array 424 * in 'include/global_settings.php' 425 * 426 * @return - true if a value exists, false if a value does not exist 427 */ 428function config_value_exists($config_name) { 429 static $config_values = array(); 430 431 if (!isset($config_values[$config_name])) { 432 $value = db_fetch_cell_prepared('SELECT COUNT(*) FROM settings WHERE name = ?', array($config_name)); 433 434 if ($value > 0) { 435 $config_values[$config_name] = true; 436 } else { 437 $config_values[$config_name] = false; 438 } 439 } 440 441 return $config_values[$config_name]; 442} 443 444/** 445 * read_default_config_option - finds the default value of a Cacti configuration setting 446 * 447 * @param $config_name - the name of the configuration setting as specified $settings array 448 * in 'include/global_settings.php' 449 * 450 * @return - the default value of the configuration option 451 */ 452function read_default_config_option($config_name) { 453 global $config, $settings; 454 455 if (isset($settings) && is_array($settings)) { 456 foreach ($settings as $tab_array) { 457 if (isset($tab_array[$config_name]) && isset($tab_array[$config_name]['default'])) { 458 return $tab_array[$config_name]['default']; 459 } else { 460 foreach ($tab_array as $field_array) { 461 if (isset($field_array['items']) && isset($field_array['items'][$config_name]) && isset($field_array['items'][$config_name]['default'])) { 462 return $field_array['items'][$config_name]['default']; 463 } 464 } 465 } 466 } 467 } 468} 469 470/** 471 * read_config_option - finds the current value of a Cacti configuration setting 472 * 473 * @param $config_name - the name of the configuration setting as specified $settings array 474 * in 'include/global_settings.php' 475 * 476 * @return - the current value of the configuration option 477 */ 478function read_config_option($config_name, $force = false) { 479 global $config, $database_hostname, $database_default, $database_port, $database_sessions; 480 481 $config_array = array(); 482 if (isset($_SESSION['sess_config_array'])) { 483 $config_array = $_SESSION['sess_config_array']; 484 } elseif (isset($config['config_options_array'])) { 485 $config_array = $config['config_options_array']; 486 } 487 488 if (!empty($config['DEBUG_READ_CONFIG_OPTION'])) { 489 file_put_contents(sys_get_temp_dir() . '/cacti-option.log', get_debug_prefix() . cacti_debug_backtrace($config_name, false, false, 0, 1) . "\n", FILE_APPEND); 490 } 491 492 // Do we have a value already stored in the array, or 493 // do we want to make sure we have the latest value 494 // from the database? 495 if (!isset($config_array[$config_name]) || ($force)) { 496 // We need to check against the DB, but lets assume default value 497 // unless we can actually read the DB 498 $value = read_default_config_option($config_name); 499 500 if (!empty($config['DEBUG_READ_CONFIG_OPTION'])) { 501 file_put_contents(sys_get_temp_dir() . '/cacti-option.log', get_debug_prefix() . 502 " $config_name: " . 503 ' dh: ' . isset($database_hostname) . 504 ' dp: ' . isset($database_port) . 505 ' dd: ' . isset($database_default) . 506 ' ds: ' . isset($database_sessions["$database_hostname:$database_port:$database_default"]) . 507 "\n", FILE_APPEND); 508 509 if (isset($database_hostname) && isset($database_port) && isset($database_default)) { 510 file_put_contents(sys_get_temp_dir() . '/cacti-option.log', get_debug_prefix() . 511 " $config_name: [$database_hostname:$database_port:$database_default]\n", FILE_APPEND); 512 } 513 } 514 515 // Are the database variables set, and do we have a connection?? 516 // If we don't, we'll only use the default value without storing 517 // so that we can read the database version later. 518 if (isset($database_hostname) && isset($database_port) && isset($database_default) && 519 isset($database_sessions["$database_hostname:$database_port:$database_default"])) { 520 521 // Get the database setting 522 $db_setting = db_fetch_row_prepared('SELECT value FROM settings WHERE name = ?', array($config_name), false); 523 524 // Does the settings exist in the database? 525 if (isset($db_setting['value'])) { 526 527 // It does? lets use it 528 $value = $db_setting['value']; 529 } 530 531 // Store whatever value we have in the array 532 $config_array[$config_name] = $value; 533 534 // Store the array back for later retrieval 535 if (isset($_SESSION)) { 536 $_SESSION['sess_config_array'] = $config_array; 537 } else { 538 $config['config_options_array'] = $config_array; 539 } 540 } 541 } else { 542 // We already have the value stored in the array and 543 // we don't want to force a db read, so use the cached 544 // version 545 $value = $config_array[$config_name]; 546 } 547 548 return $value; 549} 550 551/** 552 * get_selected_theme - checks the user settings and if the user selected 553 * theme is set, returns it otherwise returns the system default. 554 * 555 * @return - the theme name 556 */ 557function get_selected_theme() { 558 global $config, $themes; 559 560 // shortcut if theme is set in session 561 if (isset($_SESSION['selected_theme'])) { 562 if (file_exists($config['base_path'] . '/include/themes/' . $_SESSION['selected_theme'] . '/main.css')) { 563 return $_SESSION['selected_theme']; 564 } 565 } 566 567 // default to system selected theme 568 $theme = read_config_option('selected_theme'); 569 570 // check for a pre-1.x cacti being upgraded 571 if ($theme == '' && !db_table_exists('settings_user')) { 572 return 'modern'; 573 } 574 575 // figure out user defined theme 576 if (isset($_SESSION['sess_user_id'])) { 577 // fetch user defined theme 578 $user_theme = db_fetch_cell_prepared("SELECT value 579 FROM settings_user 580 WHERE name='selected_theme' 581 AND user_id = ?", 582 array($_SESSION['sess_user_id']), '', false); 583 584 // user has a theme 585 if (! empty($user_theme)) { 586 $theme = $user_theme;; 587 } 588 } 589 590 if (!file_exists($config['base_path'] . '/include/themes/' . $theme . '/main.css')) { 591 foreach($themes as $t => $name) { 592 if (file_exists($config['base_path'] . '/include/themes/' . $t . '/main.css')) { 593 $theme = $t; 594 595 db_execute_prepared('UPDATE settings_user 596 SET value = ? 597 WHERE user_id = ? 598 AND name="selected_theme"', 599 array($theme, $_SESSION['sess_user_id'])); 600 601 break; 602 } 603 } 604 } 605 606 // update session 607 $_SESSION['selected_theme'] = $theme; 608 609 return $theme; 610} 611 612/** 613 * form_input_validate - validates the value of a form field and Takes the appropriate action if the input 614 * is not valid 615 * 616 * @param $field_value - the value of the form field 617 * @param $field_name - the name of the $_POST field as specified in the HTML 618 * @param $regexp_match - (optionally) enter a regular expression to match the value against 619 * @param $allow_nulls - (bool) whether to allow an empty string as a value or not 620 * @param $custom_message - (int) the ID of the message to raise upon an error which is defined in the 621 * $messages array in 'include/global_arrays.php' 622 * 623 * @return - the original $field_value 624 */ 625function form_input_validate($field_value, $field_name, $regexp_match, $allow_nulls, $custom_message = 3) { 626 global $messages; 627 628 /* write current values to the "field_values" array so we can retain them */ 629 $_SESSION['sess_field_values'][$field_name] = $field_value; 630 631 if (($allow_nulls == true) && ($field_value == '')) { 632 return $field_value; 633 } 634 635 if ($allow_nulls == false && $field_value == '') { 636 if (read_config_option('log_validation') == 'on') { 637 cacti_log("Form Validation Failed: Variable '$field_name' does not allow nulls and variable is null", false); 638 } 639 640 raise_message($custom_message); 641 642 $_SESSION['sess_error_fields'][$field_name] = $field_name; 643 } elseif ($regexp_match != '' && !preg_match('/' . $regexp_match . '/', $field_value)) { 644 if (read_config_option('log_validation') == 'on') { 645 cacti_log("Form Validation Failed: Variable '$field_name' with Value '$field_value' Failed REGEX '$regexp_match'", false); 646 } 647 648 raise_message($custom_message); 649 650 $_SESSION['sess_error_fields'][$field_name] = $field_name; 651 } 652 653 return $field_value; 654} 655 656/** 657 * check_changed - determines if a request variable has changed between page loads 658 * 659 * @return - true if the value changed between loads 660 */ 661function check_changed($request, $session) { 662 if ((isset_request_var($request)) && (isset($_SESSION[$session]))) { 663 if (get_nfilter_request_var($request) != $_SESSION[$session]) { 664 return 1; 665 } 666 } 667} 668 669/** 670 * is_error_message - finds whether an error message has been raised and has not been outputted to the 671 * user 672 * 673 * @return - whether the messages array contains an error or not 674 */ 675function is_error_message() { 676 global $config, $messages; 677 678 if (isset($_SESSION['sess_error_fields']) && cacti_sizeof($_SESSION['sess_error_fields'])) { 679 return true; 680 } else { 681 return false; 682 } 683} 684 685function get_message_level($current_message) { 686 $current_level = MESSAGE_LEVEL_NONE; 687 688 if (isset($current_message['level'])) { 689 $current_level = $current_message['level']; 690 } elseif (isset($current_message['type'])) { 691 switch ($current_message['type']) { 692 case 'error': 693 $current_level = MESSAGE_LEVEL_ERROR; 694 break; 695 case 'info': 696 $current_level = MESSAGE_LEVEL_INFO; 697 break; 698 } 699 } 700 701 return $current_level; 702} 703 704/** 705 * get_format_message_instance - finds the level of the current message instance 706 * 707 * @param message array the message instance 708 * 709 * @return - a formatted message 710 */ 711function get_format_message_instance($current_message) { 712 if (is_array($current_message)) { 713 $fmessage = isset($current_message['message']) ? $current_message['message'] : __esc('Message Not Found.'); 714 } else { 715 $fmessage = $current_message; 716 } 717 718 $level = get_message_level($current_message); 719 720 switch ($level) { 721 case MESSAGE_LEVEL_NONE: 722 $message = '<span>' . $fmessage . '</span>'; 723 break; 724 case MESSAGE_LEVEL_INFO: 725 $message = '<span class="deviceUp">' . $fmessage . '</span>'; 726 break; 727 case MESSAGE_LEVEL_WARN: 728 $message = '<span class="deviceWarning">' . $fmessage . '</span>'; 729 break; 730 case MESSAGE_LEVEL_ERROR: 731 $message = '<span class="deviceDown">' . $fmessage . '</span>'; 732 break; 733 case MESSAGE_LEVEL_CSRF: 734 $message = '<span class="deviceDown">' . $fmessage . '</span>'; 735 break; 736 default; 737 $message = '<span class="deviceUnknown">' . $fmessage . '</span>'; 738 break; 739 } 740 741 return $message; 742} 743 744/** 745 * get_message_max_type - finds the message and returns its type 746 * 747 * @return - the message type 'info', 'warn', 'error' or 'csrf' 748 */ 749function get_message_max_type() { 750 global $messages; 751 752 $level = MESSAGE_LEVEL_NONE; 753 if (isset($_SESSION['sess_messages'])) { 754 if (is_array($_SESSION['sess_messages'])) { 755 foreach ($_SESSION['sess_messages'] as $current_message_id => $current_message) { 756 $current_level = get_message_level($current_message); 757 if ($current_level == MESSAGE_LEVEL_NONE && isset($messages[$current_message_id])) { 758 $current_level = get_message_level($messages[$current_message_id]); 759 } 760 761 if ($current_level != $level && $level != MESSAGE_LEVEL_NONE) { 762 $level = MESSAGE_LEVEL_MIXED; 763 } else { 764 $level = $current_level; 765 } 766 } 767 } 768 } 769 770 return $level; 771} 772 773/** 774 * raise_message - mark a message to be displayed to the user once display_output_messages() is called 775 * 776 * @param $message_id - the ID of the message to raise as defined in $messages in 'include/global_arrays.php' 777 */ 778function raise_message($message_id, $message = '', $message_level = MESSAGE_LEVEL_NONE) { 779 global $config, $messages, $no_http_headers; 780 781 // This function should always exist, if not its an invalid install 782 if (function_exists('session_status')) { 783 $need_session = (session_status() == PHP_SESSION_NONE) && (!isset($no_http_headers)); 784 } else { 785 return false; 786 } 787 788 if (empty($message)) { 789 if (array_key_exists($message_id, $messages)) { 790 $predefined = $messages[$message_id]; 791 if (isset($predefined['message'])) { 792 $message = $predefined['message']; 793 } else { 794 $message = $predefined; 795 } 796 797 if ($message_level == MESSAGE_LEVEL_NONE) { 798 $message_level = get_message_level($predefined); 799 } 800 } elseif (isset($_SESSION[$message_id])) { 801 $message = $_SESSION[$message_id]; 802 $message_level = MESSAGE_LEVEL_ERROR; 803 } else { 804 $message = __('Message Not Found.'); 805 $message_level = MESSAGE_LEVEL_ERROR; 806 } 807 } 808 809 if ($need_session) { 810 cacti_session_start(); 811 } 812 813 if (!isset($_SESSION['sess_messages'])) { 814 $_SESSION['sess_messages'] = array(); 815 } 816 817 $_SESSION['sess_messages'][$message_id] = array('message' => $message, 'level' => $message_level); 818 819 if ($need_session) { 820 cacti_session_close(); 821 } 822} 823 824/** 825 * display_output_messages - displays all of the cached messages from the raise_message() function and clears 826 * the message cache 827 */ 828function display_output_messages() { 829 global $messages; 830 831 $omessage = array(); 832 $debug_message = debug_log_return('new_graphs'); 833 834 if ($debug_message != '') { 835 $omessage['level'] = MESSAGE_LEVEL_NONE; 836 $omessage['message'] = $debug_message; 837 838 debug_log_clear('new_graphs'); 839 } elseif (isset($_SESSION['sess_messages'])) { 840 if (!is_array($_SESSION['sess_messages'])) { 841 $_SESSION['sess_messages'] = array('custom_error' => array('level' => 3, 'message' => $_SESSION['sess_messages'])); 842 } 843 844 $omessage['level'] = get_message_max_type(); 845 846 foreach ($_SESSION['sess_messages'] as $current_message_id => $current_message) { 847 $message = get_format_message_instance($current_message); 848 849 if (!empty($message)) { 850 $omessage['message'] = (isset($omessage['message']) && $omessage['message'] != '' ? $omessage['message'] . '<br>':'') . $message; 851 } else { 852 cacti_log("ERROR: Cacti Error Message Id '$current_message_id' Not Defined", false, 'WEBUI'); 853 } 854 } 855 } 856 857 clear_messages(); 858 859 return json_encode($omessage); 860} 861 862function display_custom_error_message($message) { 863 raise_message('custom_error', $message); 864} 865 866/** 867 * clear_messages - clears the message cache 868 */ 869function clear_messages() { 870 // This function should always exist, if not its an invalid install 871 if (function_exists('session_status')) { 872 $need_session = (session_status() == PHP_SESSION_NONE) && (!isset($no_http_headers)); 873 } else { 874 return false; 875 } 876 877 if ($need_session) { 878 cacti_session_start(); 879 } 880 881 kill_session_var('sess_messages'); 882 883 if ($need_session) { 884 cacti_session_close(); 885 } 886} 887 888/** 889 * kill_session_var - kills a session variable using unset() 890 */ 891function kill_session_var($var_name) { 892 /* register_global = on: reset local settings cache so the user sees the new settings */ 893 unset($_SESSION[$var_name]); 894 895 /* register_global = off: reset local settings cache so the user sees the new settings */ 896 /* session_unregister is deprecated in PHP 5.3.0, unset is sufficient */ 897 if (version_compare(PHP_VERSION, '5.3.0', '<')) { 898 session_unregister($var_name); 899 } else { 900 unset($var_name); 901 } 902} 903 904/** 905 * force_session_data - forces session data into the session if the session was closed for some reason 906 */ 907function force_session_data() { 908 // This function should always exist, if not its an invalid install 909 if (!function_exists('session_status')) { 910 return false; 911 } elseif (session_status() == PHP_SESSION_NONE) { 912 $data = $_SESSION; 913 914 cacti_session_start(); 915 916 $_SESSION = $data; 917 918 cacti_session_close(); 919 } 920} 921 922/** 923 * array_rekey - changes an array in the form: 924 * 925 * '$arr[0] = array('id' => 23, 'name' => 'blah')' 926 * to the form 927 * '$arr = array(23 => 'blah')' 928 * 929 * @param $array - (array) the original array to manipulate 930 * @param $key - the name of the key 931 * @param $key_value - the name of the key value 932 * 933 * @return - the modified array 934 */ 935function array_rekey($array, $key, $key_value) { 936 $ret_array = array(); 937 938 if (is_array($array)) { 939 foreach ($array as $item) { 940 $item_key = $item[$key]; 941 942 if (is_array($key_value)) { 943 foreach ($key_value as $value) { 944 $ret_array[$item_key][$value] = $item[$value]; 945 } 946 } else { 947 $ret_array[$item_key] = $item[$key_value]; 948 } 949 } 950 } 951 952 return $ret_array; 953} 954 955/** 956 * cacti_log_file - returns the log filename 957 */ 958function cacti_log_file() { 959 global $config; 960 $logfile = read_config_option('path_cactilog'); 961 if ($logfile == '') { 962 $logfile = '/var/log/cacti/log'; 963 } 964 $config['log_path'] = $logfile; 965 return $logfile; 966} 967 968function get_selective_log_level() { 969 static $force_level = null; 970 971 if ($force_level !== null) { 972 return $force_level; 973 } 974 975 if (isset($_SERVER['PHP_SELF'])) { 976 $current_file = basename($_SERVER['PHP_SELF']); 977 $dir_name = dirname($_SERVER['PHP_SELF']); 978 } elseif (isset($_SERVER['SCRIPT_NAME'])) { 979 $current_file = basename($_SERVER['SCRIPT_NAME']); 980 $dir_name = dirname($_SERVER['SCRIPT_NAME']); 981 } elseif (isset($_SERVER['SCRIPT_FILENAME'])) { 982 $current_file = basename($_SERVER['SCRIPT_FILENAME']); 983 $dir_name = dirname($_SERVER['SCRIPT_FILENAME']); 984 } else { 985 $current_file = basename(__FILE__); 986 $dir_name = dirname(__FILE__); 987 } 988 989 $force_level = ''; 990 $debug_files = read_config_option('selective_debug'); 991 if ($debug_files != '') { 992 $files = explode(',', $debug_files); 993 994 if (array_search($current_file, $files) !== false) { 995 $force_level = POLLER_VERBOSITY_DEBUG; 996 } 997 } 998 999 if (strpos($dir_name, 'plugins') !== false) { 1000 $debug_plugins = read_config_option('selective_plugin_debug'); 1001 if ($debug_plugins != '') { 1002 $debug_plugins = explode(',', $debug_plugins); 1003 1004 foreach($debug_plugins as $myplugin) { 1005 if (strpos($dir_name, DIRECTORY_SEPARATOR . $myplugin) !== false) { 1006 $force_level = POLLER_VERBOSITY_DEBUG; 1007 break; 1008 } 1009 } 1010 } 1011 } 1012 1013 return $force_level; 1014} 1015 1016/** 1017 * cacti_log - logs a string to Cacti's log file or optionally to the browser 1018 * 1019 * @param $string - the string to append to the log file 1020 * @param $output - (bool) whether to output the log line to the browser using print() or not 1021 * @param $environ - (string) tells from where the script was called from 1022 * @param $level - (int) only log if above the specified log level 1023 */ 1024function cacti_log($string, $output = false, $environ = 'CMDPHP', $level = '') { 1025 global $config, $database_log; 1026 1027 if (!isset($database_log)) { 1028 $database_log = false; 1029 } 1030 1031 $last_log = $database_log; 1032 $database_log = false; 1033 $force_level = get_selective_log_level(); 1034 1035 /* only log if the specific level is reached, developer debug is special low + specific devdbg calls */ 1036 if ($force_level == '') { 1037 if ($level != '') { 1038 $logVerbosity = read_config_option('log_verbosity'); 1039 if ($logVerbosity == POLLER_VERBOSITY_DEVDBG) { 1040 if ($level != POLLER_VERBOSITY_DEVDBG) { 1041 if ($level > POLLER_VERBOSITY_LOW) { 1042 $database_log = $last_log; 1043 return; 1044 } 1045 } 1046 } elseif ($level > $logVerbosity) { 1047 $database_log = $last_log; 1048 return; 1049 } 1050 } 1051 } 1052 1053 /* fill in the current date for printing in the log */ 1054 if (defined('CACTI_DATE_TIME_FORMAT')) { 1055 $date = date(CACTI_DATE_TIME_FORMAT); 1056 } else { 1057 $date = date('Y-m-d H:i:s'); 1058 } 1059 1060 /* determine how to log data */ 1061 $logdestination = read_config_option('log_destination'); 1062 $logfile = cacti_log_file(); 1063 1064 /* format the message */ 1065 if ($environ == 'POLLER') { 1066 $prefix = "$date - " . $environ . ': Poller[' . $config['poller_id'] . '] PID[' . getmypid() . '] '; 1067 } else { 1068 $prefix = "$date - " . $environ . ' '; 1069 } 1070 1071 /* Log to Logfile */ 1072 $message = clean_up_lines($string) . PHP_EOL; 1073 if (($logdestination == 1 || $logdestination == 2) && read_config_option('log_verbosity') != POLLER_VERBOSITY_NONE) { 1074 /* print the data to the log (append) */ 1075 $fp = @fopen($logfile, 'a'); 1076 1077 if ($fp) { 1078 $message = $prefix . $message; 1079 @fwrite($fp, $message); 1080 fclose($fp); 1081 } 1082 } 1083 1084 /* Log to Syslog/Eventlog */ 1085 /* Syslog is currently Unstable in Win32 */ 1086 if ($logdestination == 2 || $logdestination == 3) { 1087 $log_type = ''; 1088 if (strpos($string, 'ERROR:') !== false) { 1089 $log_type = 'err'; 1090 } elseif (strpos($string, 'WARNING:') !== false) { 1091 $log_type = 'warn'; 1092 } elseif (strpos($string, 'STATS:') !== false) { 1093 $log_type = 'stat'; 1094 } elseif (strpos($string, 'NOTICE:') !== false) { 1095 $log_type = 'note'; 1096 } 1097 1098 if ($log_type != '') { 1099 if ($config['cacti_server_os'] == 'win32') { 1100 openlog('Cacti', LOG_NDELAY | LOG_PID, LOG_USER); 1101 } else { 1102 openlog('Cacti', LOG_NDELAY | LOG_PID, LOG_SYSLOG); 1103 } 1104 1105 if ($log_type == 'err' && read_config_option('log_perror')) { 1106 syslog(LOG_CRIT, $environ . ': ' . $string); 1107 } elseif ($log_type == 'warn' && read_config_option('log_pwarn')) { 1108 syslog(LOG_WARNING, $environ . ': ' . $string); 1109 } elseif (($log_type == 'stat' || $log_type == 'note') && read_config_option('log_pstats')) { 1110 syslog(LOG_INFO, $environ . ': ' . $string); 1111 } 1112 1113 closelog(); 1114 } 1115 } 1116 1117 /* print output to standard out if required */ 1118 if ($output == true && isset($_SERVER['argv'][0])) { 1119 print $message; 1120 } 1121 1122 $database_log = $last_log; 1123} 1124 1125/** 1126 * tail_file - Emulates the tail function with PHP native functions. 1127 * It is used in 0.8.6 to speed the viewing of the Cacti log file, which 1128 * can be problematic in the 0.8.6 branch. 1129 * 1130 * @param $file_name - (char constant) the name of the file to tail 1131 * @param $line_cnt - (int constant) the number of lines to count 1132 * @param $message_type - (int constant) the type of message to return 1133 * @param $filter - (char) the filtering expression to search for 1134 * @param $page_nr - (int) the page we want to show rows for 1135 * @param $total_rows - (int) the total number of rows in the logfile 1136 */ 1137function tail_file($file_name, $number_of_lines, $message_type = -1, $filter = '', &$page_nr = 1, &$total_rows = 0) { 1138 if (!file_exists($file_name)) { 1139 touch($file_name); 1140 return array(); 1141 } 1142 1143 if (!is_readable($file_name)) { 1144 return array(__('Error %s is not readable', $file_name)); 1145 } 1146 1147 $filter = strtolower($filter); 1148 1149 $fp = fopen($file_name, 'r'); 1150 1151 /* Count all lines in the logfile */ 1152 $total_rows = 0; 1153 while (($line = fgets($fp)) !== false) { 1154 if (determine_display_log_entry($message_type, $line, $filter)) { 1155 ++$total_rows; 1156 } 1157 } 1158 1159 // Reset the page count to 1 if the number of lines is exceeded 1160 if (($page_nr - 1) * $number_of_lines > $total_rows) { 1161 set_request_var('page', 1); 1162 $page_nr = 1; 1163 } 1164 1165 /* rewind file pointer, to start all over */ 1166 rewind($fp); 1167 1168 $start = $total_rows - ($page_nr * $number_of_lines); 1169 $end = $start + $number_of_lines; 1170 1171 if ($start < 0) { 1172 $start = 0; 1173 } 1174 1175 force_session_data(); 1176 1177 /* load up the lines into an array */ 1178 $file_array = array(); 1179 $i = 0; 1180 while (($line = fgets($fp)) !== false) { 1181 $display = determine_display_log_entry($message_type, $line, $filter); 1182 1183 if ($display === false) { 1184 continue; 1185 } 1186 if ($i < $start) { 1187 ++$i; 1188 continue; 1189 } 1190 if ($i >= $end) { 1191 break; 1192 } 1193 1194 ++$i; 1195 $file_array[$i] = $line; 1196 } 1197 1198 fclose($fp); 1199 1200 return $file_array; 1201} 1202 1203/** 1204 * determine_display_log_entry - function to determine if we display the line 1205 * 1206 * @param $message_type 1207 * @param $line 1208 * @param $filter 1209 * 1210 * @return - should the entry be displayed 1211 */ 1212function determine_display_log_entry($message_type, $line, $filter) { 1213 /* determine if we are to display the line */ 1214 switch ($message_type) { 1215 case 1: /* stats */ 1216 $display = (strpos($line, 'STATS') !== false); 1217 break; 1218 case 2: /* warnings */ 1219 $display = (strpos($line, 'WARN') !== false); 1220 break; 1221 case 3: /* errors */ 1222 $display = (strpos($line, 'ERROR') !== false); 1223 break; 1224 case 4: /* debug */ 1225 $display = (strpos($line, 'DEBUG') !== false && strpos($line, ' SQL ') === false); 1226 break; 1227 case 5: /* sql calls */ 1228 $display = (strpos($line, ' SQL ') !== false); 1229 break; 1230 default: /* all other lines */ 1231 case -1: /* all */ 1232 $display = true; 1233 break; 1234 } 1235 1236 /* match any lines that match the search string */ 1237 if ($display === true && $filter != '') { 1238 if (stripos($line, $filter) !== false) { 1239 return $line; 1240 } elseif (validate_is_regex($filter) && preg_match('/' . $filter . '/i', $line)) { 1241 return $line; 1242 } 1243 return false; 1244 } 1245 1246 return $display; 1247} 1248 1249/** 1250 * update_host_status - updates the host table with information about its status. 1251 * It will also output to the appropriate log file when an event occurs. 1252 * 1253 * @param $status - (int constant) the status of the host (Up/Down) 1254 * @param $host_id - (int) the host ID for the results 1255 * @param $ping - (class array) results of the ping command. 1256 */ 1257function update_host_status($status, $host_id, &$ping, $ping_availability, $print_data_to_stdout) { 1258 $issue_log_message = false; 1259 $ping_failure_count = read_config_option('ping_failure_count'); 1260 $ping_recovery_count = read_config_option('ping_recovery_count'); 1261 1262 $host = db_fetch_row_prepared('SELECT * FROM host WHERE id = ?', array($host_id)); 1263 1264 /* initialize fail and recovery dates correctly */ 1265 if ($host['status_fail_date'] == '') { 1266 $host['status_fail_date'] = strtotime('0000-00-00 00:00:00'); 1267 } 1268 1269 if ($host['status_rec_date'] == '') { 1270 $host['status_rec_date'] = strtotime('0000-00-00 00:00:00'); 1271 } 1272 1273 if ($status == HOST_DOWN) { 1274 /* Set initial date down. BUGFIX */ 1275 if (empty($host['status_fail_date'])) { 1276 $host['status_fail_date'] = time(); 1277 } 1278 1279 /* update total polls, failed polls and availability */ 1280 $host['failed_polls']++; 1281 $host['total_polls']++; 1282 $host['availability'] = 100 * ($host['total_polls'] - $host['failed_polls']) / $host['total_polls']; 1283 1284 /* determine the error message to display */ 1285 if (($ping_availability == AVAIL_SNMP_AND_PING) || ($ping_availability == AVAIL_SNMP_OR_PING)) { 1286 if (($host['snmp_community'] == '') && ($host['snmp_version'] != 3)) { 1287 /* snmp version 1/2 without community string assume SNMP test to be successful 1288 due to backward compatibility issues */ 1289 $host['status_last_error'] = $ping->ping_response; 1290 } else { 1291 $host['status_last_error'] = $ping->snmp_response . ', ' . $ping->ping_response; 1292 } 1293 } elseif ($ping_availability == AVAIL_SNMP) { 1294 if (($host['snmp_community'] == '') && ($host['snmp_version'] != 3)) { 1295 $host['status_last_error'] = 'Device does not require SNMP'; 1296 } else { 1297 $host['status_last_error'] = $ping->snmp_response; 1298 } 1299 } else { 1300 $host['status_last_error'] = $ping->ping_response; 1301 } 1302 1303 /* determine if to send an alert and update remainder of statistics */ 1304 if ($host['status'] == HOST_UP) { 1305 /* increment the event failure count */ 1306 $host['status_event_count']++; 1307 1308 /* if it's time to issue an error message, indicate so */ 1309 if ($host['status_event_count'] >= $ping_failure_count) { 1310 /* host is now down, flag it that way */ 1311 $host['status'] = HOST_DOWN; 1312 1313 $issue_log_message = true; 1314 1315 /* update the failure date only if the failure count is 1 */ 1316 if ($host['status_event_count'] == $ping_failure_count) { 1317 $host['status_fail_date'] = time(); 1318 } 1319 /* host is down, but not ready to issue log message */ 1320 } else { 1321 /* host down for the first time, set event date */ 1322 if ($host['status_event_count'] == $ping_failure_count) { 1323 $host['status_fail_date'] = time(); 1324 } 1325 } 1326 /* host is recovering, put back in failed state */ 1327 } elseif ($host['status'] == HOST_RECOVERING) { 1328 $host['status_event_count'] = 1; 1329 $host['status'] = HOST_DOWN; 1330 1331 /* host was unknown and now is down */ 1332 } elseif ($host['status'] == HOST_UNKNOWN) { 1333 $host['status'] = HOST_DOWN; 1334 $host['status_event_count'] = 0; 1335 } else { 1336 $host['status_event_count']++; 1337 } 1338 /* host is up!! */ 1339 } else { 1340 /* update total polls and availability */ 1341 $host['total_polls']++; 1342 $host['availability'] = 100 * ($host['total_polls'] - $host['failed_polls']) / $host['total_polls']; 1343 1344 if ((($ping_availability == AVAIL_SNMP_AND_PING) || 1345 ($ping_availability == AVAIL_SNMP_OR_PING) || 1346 ($ping_availability == AVAIL_SNMP)) && 1347 (!is_numeric($ping->snmp_status))) { 1348 $ping->snmp_status = 0.000; 1349 } 1350 1351 if ((($ping_availability == AVAIL_SNMP_AND_PING) || 1352 ($ping_availability == AVAIL_SNMP_OR_PING) || 1353 ($ping_availability == AVAIL_PING)) && 1354 (!is_numeric($ping->ping_status))) { 1355 $ping->ping_status = 0.000; 1356 } 1357 1358 /* determine the ping statistic to set and do so */ 1359 if (($ping_availability == AVAIL_SNMP_AND_PING) || 1360 ($ping_availability == AVAIL_SNMP_OR_PING)) { 1361 if (($host['snmp_community'] == '') && ($host['snmp_version'] != 3)) { 1362 $ping_time = 0.000; 1363 } else { 1364 /* calculate the average of the two times */ 1365 $ping_time = ($ping->snmp_status + $ping->ping_status) / 2; 1366 } 1367 } elseif ($ping_availability == AVAIL_SNMP) { 1368 if (($host['snmp_community'] == '') && ($host['snmp_version'] != 3)) { 1369 $ping_time = 0.000; 1370 } else { 1371 $ping_time = $ping->snmp_status; 1372 } 1373 } elseif ($ping_availability == AVAIL_NONE) { 1374 $ping_time = 0.000; 1375 } else { 1376 $ping_time = $ping->ping_status; 1377 } 1378 1379 /* update times as required */ 1380 if (is_numeric($ping_time)) { 1381 $host['cur_time'] = $ping_time; 1382 1383 /* maximum time */ 1384 if ($ping_time > $host['max_time']) { 1385 $host['max_time'] = $ping_time; 1386 } 1387 1388 /* minimum time */ 1389 if ($ping_time < $host['min_time']) { 1390 $host['min_time'] = $ping_time; 1391 } 1392 1393 /* average time */ 1394 $host['avg_time'] = (($host['total_polls'] - 1 - $host['failed_polls']) 1395 * $host['avg_time'] + $ping_time) / ($host['total_polls'] - $host['failed_polls']); 1396 } 1397 1398 /* the host was down, now it's recovering */ 1399 if (($host['status'] == HOST_DOWN) || ($host['status'] == HOST_RECOVERING )) { 1400 /* just up, change to recovering */ 1401 if ($host['status'] == HOST_DOWN) { 1402 $host['status'] = HOST_RECOVERING; 1403 $host['status_event_count'] = 1; 1404 } else { 1405 $host['status_event_count']++; 1406 } 1407 1408 /* if it's time to issue a recovery message, indicate so */ 1409 if ($host['status_event_count'] >= $ping_recovery_count) { 1410 /* host is up, flag it that way */ 1411 $host['status'] = HOST_UP; 1412 1413 $issue_log_message = true; 1414 1415 /* update the recovery date only if the recovery count is 1 */ 1416 if ($host['status_event_count'] == $ping_recovery_count) { 1417 $host['status_rec_date'] = time(); 1418 } 1419 1420 /* reset the event counter */ 1421 $host['status_event_count'] = 0; 1422 /* host is recovering, but not ready to issue log message */ 1423 } else { 1424 /* host recovering for the first time, set event date */ 1425 if ($host['status_event_count'] == $ping_recovery_count) { 1426 $host['status_rec_date'] = time(); 1427 } 1428 } 1429 } else { 1430 /* host was unknown and now is up */ 1431 $host['status'] = HOST_UP; 1432 $host['status_event_count'] = 0; 1433 } 1434 } 1435 /* if the user wants a flood of information then flood them */ 1436 if (($host['status'] == HOST_UP) || ($host['status'] == HOST_RECOVERING)) { 1437 /* log ping result if we are to use a ping for reachability testing */ 1438 if ($ping_availability == AVAIL_SNMP_AND_PING) { 1439 cacti_log("Device[$host_id] PING: " . $ping->ping_response, $print_data_to_stdout, 'PING', POLLER_VERBOSITY_HIGH); 1440 cacti_log("Device[$host_id] SNMP: " . $ping->snmp_response, $print_data_to_stdout, 'PING', POLLER_VERBOSITY_HIGH); 1441 } elseif ($ping_availability == AVAIL_SNMP) { 1442 if (($host['snmp_community'] == '') && ($host['snmp_version'] != 3)) { 1443 cacti_log("Device[$host_id] SNMP: Device does not require SNMP", $print_data_to_stdout, 'PING', POLLER_VERBOSITY_HIGH); 1444 } else { 1445 cacti_log("Device[$host_id] SNMP: " . $ping->snmp_response, $print_data_to_stdout, 'PING', POLLER_VERBOSITY_HIGH); 1446 } 1447 } else { 1448 cacti_log("Device[$host_id] PING: " . $ping->ping_response, $print_data_to_stdout, 'PING', POLLER_VERBOSITY_HIGH); 1449 } 1450 } else { 1451 if ($ping_availability == AVAIL_SNMP_AND_PING) { 1452 cacti_log("Device[$host_id] PING: " . $ping->ping_response, $print_data_to_stdout, 'PING', POLLER_VERBOSITY_HIGH); 1453 cacti_log("Device[$host_id] SNMP: " . $ping->snmp_response, $print_data_to_stdout, 'PING', POLLER_VERBOSITY_HIGH); 1454 } elseif ($ping_availability == AVAIL_SNMP) { 1455 cacti_log("Device[$host_id] SNMP: " . $ping->snmp_response, $print_data_to_stdout, 'PING', POLLER_VERBOSITY_HIGH); 1456 } else { 1457 cacti_log("Device[$host_id] PING: " . $ping->ping_response, $print_data_to_stdout, 'PING', POLLER_VERBOSITY_HIGH); 1458 } 1459 } 1460 1461 /* if there is supposed to be an event generated, do it */ 1462 if ($issue_log_message) { 1463 if ($host['status'] == HOST_DOWN) { 1464 cacti_log("Device[$host_id] ERROR: HOST EVENT: Device is DOWN Message: " . $host['status_last_error'], $print_data_to_stdout); 1465 } else { 1466 cacti_log("Device[$host_id] NOTICE: HOST EVENT: Device Returned FROM DOWN State: ", $print_data_to_stdout); 1467 } 1468 } 1469 1470 db_execute_prepared('UPDATE host SET 1471 status = ?, 1472 status_event_count = ?, 1473 status_fail_date = FROM_UNIXTIME(?), 1474 status_rec_date = FROM_UNIXTIME(?), 1475 status_last_error = ?, 1476 min_time = ?, 1477 max_time = ?, 1478 cur_time = ?, 1479 avg_time = ?, 1480 total_polls = ?, 1481 failed_polls = ?, 1482 availability = ? 1483 WHERE hostname = ? 1484 AND deleted = ""', 1485 array( 1486 $host['status'], 1487 $host['status_event_count'], 1488 $host['status_fail_date'], 1489 $host['status_rec_date'], 1490 $host['status_last_error'], 1491 $host['min_time'], 1492 $host['max_time'], 1493 $host['cur_time'], 1494 $host['avg_time'], 1495 $host['total_polls'], 1496 $host['failed_polls'], 1497 $host['availability'], 1498 $host['hostname'] 1499 ) 1500 1501 ); 1502} 1503 1504/** 1505 * is_hexadecimal - test whether a string represents a hexadecimal number, 1506 * ignoring space and tab, and case insensitive. 1507 * 1508 * @param $result - the string to test 1509 * @param 1 if the argument is hex, 0 otherwise, and false on error 1510 */ 1511function is_hexadecimal($result) { 1512 $hexstr = str_replace(array(' ', '-'), ':', trim($result)); 1513 1514 $parts = explode(':', $hexstr); 1515 foreach($parts as $part) { 1516 if (strlen($part) != 2) { 1517 return false; 1518 } 1519 if (ctype_xdigit($part) == false) { 1520 return false; 1521 } 1522 } 1523 1524 return true; 1525} 1526 1527/** 1528 * strip_domain - removes the domain from a hostname 1529 * 1530 * @param $hostname - the hostname for a device 1531 * 1532 * @return - the stripped hostname 1533 */ 1534function strip_domain($hostname) { 1535 if (is_ipaddress($hostname)) { 1536 return $hostname; 1537 } elseif (read_config_option('strip_domain') == 'on') { 1538 $parts = explode('.', $hostname); 1539 1540 return $parts[0]; 1541 } else { 1542 return $hostname; 1543 } 1544} 1545 1546 1547/** 1548 * is_mac_address - determines if the result value is a mac address 1549 * 1550 * @param $result - some string to be evaluated 1551 * 1552 * @return - either to result is a mac address of not 1553 */ 1554function is_mac_address($result) { 1555 if (!defined('FILTER_VALIDATE_MAC')) { 1556 if (preg_match('/^([0-9a-f]{1,2}[\.:-]) {5}([0-9a-f]{1,2})$/i', $result)) { 1557 return true; 1558 } else { 1559 return false; 1560 } 1561 } else { 1562 return filter_var($result, FILTER_VALIDATE_MAC); 1563 } 1564} 1565 1566function is_hex_string(&$result) { 1567 if ($result == '') { 1568 return false; 1569 } 1570 1571 $compare = strtolower($result); 1572 1573 /* strip off the 'Hex:, Hex-, and Hex-STRING:' 1574 * Hex- is considered due to the stripping of 'String:' in 1575 * lib/snmp.php 1576 */ 1577 if (substr($compare, 0, 4) == 'hex-') { 1578 $check = trim(str_ireplace('hex-', '', $result)); 1579 } elseif (substr($compare, 0, 11) == 'hex-string:') { 1580 $check = trim(str_ireplace('hex-string:', '', $result)); 1581 } else { 1582 return false; 1583 } 1584 1585 $parts = explode(' ', $check); 1586 1587 /* assume if something is a hex string 1588 it will have a length > 1 */ 1589 if (cacti_sizeof($parts) == 1) { 1590 return false; 1591 } 1592 1593 foreach($parts as $part) { 1594 if (strlen($part) != 2) { 1595 return false; 1596 } 1597 1598 if (ctype_xdigit($part) == false) { 1599 return false; 1600 } 1601 } 1602 1603 $result = $check; 1604 1605 return true; 1606} 1607 1608/** 1609 * prepare_validate_result - determines if the result value is valid or not. If not valid returns a "U" 1610 * 1611 * @param $result - the result from the poll, the result can be modified in the call 1612 * 1613 * @return - either to result is valid or not 1614 */ 1615function prepare_validate_result(&$result) { 1616 /* first trim the string */ 1617 $result = trim($result, "'\"\n\r"); 1618 1619 /* clean off ugly non-numeric data */ 1620 if (is_numeric($result)) { 1621 return true; 1622 } elseif ($result == 'U') { 1623 return true; 1624 } elseif (is_hexadecimal($result)) { 1625 return hexdec($result); 1626 } elseif (substr_count($result, ':') || substr_count($result, '!')) { 1627 /* looking for name value pairs */ 1628 if (substr_count($result, ' ') == 0) { 1629 return true; 1630 } else { 1631 $delim_cnt = 0; 1632 if (substr_count($result, ':')) { 1633 $delim_cnt = substr_count($result, ':'); 1634 } elseif (strstr($result, '!')) { 1635 $delim_cnt = substr_count($result, '!'); 1636 } 1637 1638 $space_cnt = substr_count($result, ' '); 1639 1640 return ($space_cnt+1 == $delim_cnt); 1641 } 1642 } else { 1643 $result = strip_alpha($result); 1644 1645 if ($result === false) { 1646 $result = 'U'; 1647 return false; 1648 } else { 1649 return true; 1650 } 1651 } 1652} 1653 1654/** 1655 * strip_alpha - remove non-numeric data from a string and return the numeric part 1656 * 1657 * @param $string - the string to be evaluated 1658 * 1659 * @return - either the numeric value or false if not numeric 1660 */ 1661function strip_alpha($string) { 1662 /* strip all non numeric data */ 1663 $string = trim(preg_replace('/[^0-9,.+-]/', '', $string)); 1664 1665 /* check the easy cases first */ 1666 /* it has no delimiters, and no space, therefore, must be numeric */ 1667 if (is_numeric($string) || is_float($string)) { 1668 return $string; 1669 } else { 1670 return false; 1671 } 1672} 1673 1674/** 1675 * is_valid_pathname - takes a pathname are verifies it matches file name rules 1676 * 1677 * @param $path - the pathname to be tested 1678 * 1679 * @return - either true or false 1680*/ 1681function is_valid_pathname($path) { 1682 if (preg_match('/^([a-zA-Z0-9\_\.\-\\\:\/]+)$/', trim($path))) { 1683 return true; 1684 } else { 1685 return false; 1686 } 1687} 1688 1689/** 1690 * dsv_log - provides debug logging when tracing Graph/Data Source creation 1691 * 1692 * @param $message - the message to output to the log 1693 * @param $data - the data to be carried with the message 1694*/ 1695function dsv_log($message,$data) { 1696 if (read_config_option('data_source_trace') == 'on') { 1697 cacti_log(($message . ' = ') . (is_array($data) ? json_encode($data) : $data), false, 'DSV'); 1698 } 1699} 1700 1701/** 1702 * test_data_sources 1703 * 1704 * Tests all data sources to confirm that it returns valid data. This 1705 * function is used by automation to prevent the creation of graphs 1706 * that will never generate data. 1707 * 1708 * @param $graph_template_id - The Graph Template to test 1709 * @param $host_id - The Host to test 1710 * 1711 * @return boolean true or false 1712 */ 1713function test_data_sources($graph_template_id, $host_id, $snmp_query_id = 0, $snmp_index = '', $values = array()) { 1714 $data_template_ids = array_rekey( 1715 db_fetch_assoc_prepared('SELECT DISTINCT data_template_id 1716 FROM graph_templates_item AS gti 1717 INNER JOIN data_template_rrd AS dtr 1718 ON gti.task_item_id = dtr.id 1719 WHERE gti.hash != "" 1720 AND gti.local_graph_id = 0 1721 AND dtr.local_data_id = 0 1722 AND gti.graph_template_id = ?', 1723 array($graph_template_id)), 1724 'data_template_id', 'data_template_id' 1725 ); 1726 1727 $test_source = db_fetch_cell_prepared('SELECT test_source 1728 FROM graph_templates 1729 WHERE id = ?', 1730 array($graph_template_id)); 1731 1732 if (cacti_sizeof($data_template_ids) && $test_source == 'on') { 1733 foreach($data_template_ids as $dt) { 1734 if (!test_data_source($dt, $host_id, $snmp_query_id, $snmp_index, $values)) { 1735 return false; 1736 } 1737 } 1738 } 1739 1740 return true; 1741} 1742 1743/** 1744 * test_data_source 1745 * 1746 * Tests a single data source to confirm that it returns valid data. This 1747 * function is used by automation to prevent the creation of graphs 1748 * that will never generate data. 1749 * 1750 * @param $graph_template_id - The Graph Template to test 1751 * @param $host_id - The Host to test 1752 * 1753 * @return boolean true or false 1754 */ 1755function test_data_source($data_template_id, $host_id, $snmp_query_id = 0, $snmp_index = '', $suggested_vals = array()) { 1756 global $called_by_script_server; 1757 1758 $called_by_script_server = true; 1759 1760 dsv_log('test_data_source', [ 'data_template_id' => $data_template_id, 'host_id' => $host_id, 'snmp_query_id' => $snmp_query_id, 'snmp_index' => $snmp_index, 'suggested_vals' => $suggested_vals]); 1761 $data_input = db_fetch_row_prepared('SELECT ' . SQL_NO_CACHE . ' 1762 di.id, di.type_id, dtd.id AS data_template_data_id, 1763 dtd.data_template_id, dtd.active, dtd.rrd_step, di.name 1764 FROM data_template_data AS dtd 1765 INNER JOIN data_input AS di 1766 ON dtd.data_input_id=di.id 1767 WHERE dtd.local_data_id = 0 1768 AND dtd.data_template_id = ?', 1769 array($data_template_id)); 1770 1771 dsv_log('data_input', $data_input); 1772 1773 $host = db_fetch_row_prepared('SELECT ' . SQL_NO_CACHE . ' * 1774 FROM host 1775 WHERE id = ?', 1776 array($host_id)); 1777 1778 dsv_log('host', $host); 1779 1780 $data_template_data_id = 0; 1781 1782 if (cacti_sizeof($data_input) && $data_input['active'] == 'on') { 1783 $data_template_data_id = $data_input['data_template_data_id']; 1784 /* we have to perform some additional sql queries if this is a 'query' */ 1785 if (($data_input['type_id'] == DATA_INPUT_TYPE_SNMP_QUERY) || 1786 ($data_input['type_id'] == DATA_INPUT_TYPE_SCRIPT_QUERY) || 1787 ($data_input['type_id'] == DATA_INPUT_TYPE_QUERY_SCRIPT_SERVER)) { 1788 1789 $field = data_query_field_list($data_template_data_id); 1790 dsv_log('query field', $field); 1791 1792 $params = array(); 1793 $params[] = $data_input['data_template_id']; 1794 1795 if ($field['output_type'] != '') { 1796 $output_type_sql = ' AND sqgr.snmp_query_graph_id = ?'; 1797 $params[] = $field['output_type']; 1798 } else { 1799 $output_type_sql = ''; 1800 } 1801 1802 $outputs_sql = 'SELECT DISTINCT ' . SQL_NO_CACHE . " 1803 sqgr.snmp_field_name, dtr.id as data_template_rrd_id 1804 FROM snmp_query_graph_rrd AS sqgr 1805 INNER JOIN data_template_rrd AS dtr FORCE INDEX (local_data_id) 1806 ON sqgr.data_template_rrd_id = dtr.id 1807 WHERE sqgr.data_template_id = ? 1808 AND dtr.local_data_id = 0 1809 $output_type_sql 1810 ORDER BY dtr.id"; 1811 1812 dsv_log('outputs_sql', $outputs_sql); 1813 dsv_log('outputs_params', $params); 1814 1815 $outputs = db_fetch_assoc_prepared($outputs_sql, $params); 1816 1817 dsv_log('outputs', $outputs); 1818 } 1819 1820 if (($data_input['type_id'] == DATA_INPUT_TYPE_SCRIPT) || 1821 ($data_input['type_id'] == DATA_INPUT_TYPE_PHP_SCRIPT_SERVER)) { 1822 if ($data_input['type_id'] == DATA_INPUT_TYPE_PHP_SCRIPT_SERVER) { 1823 $action = POLLER_ACTION_SCRIPT_PHP; 1824 } else { 1825 $action = POLLER_ACTION_SCRIPT; 1826 } 1827 1828 $script_path = get_full_test_script_path($data_template_id, $host_id); 1829 dsv_log('script_path', $script_path); 1830 1831 $num_output_fields_sql = 'SELECT ' . SQL_NO_CACHE . ' id 1832 FROM data_input_fields 1833 WHERE data_input_id = ? 1834 AND input_output = "out" 1835 AND update_rra="on"'; 1836 dsv_log('num_output_fields_sql',$num_output_fields_sql); 1837 1838 $num_output_fields = cacti_sizeof(db_fetch_assoc_prepared($num_output_fields_sql, array($data_input['id']))); 1839 dsv_log('num_output_fields', $num_output_fields); 1840 1841 if ($num_output_fields == 1) { 1842 $data_template_rrd_id = db_fetch_cell_prepared('SELECT ' . SQL_NO_CACHE . ' id 1843 FROM data_template_rrd 1844 WHERE local_data_id = 0 1845 AND hash != "" 1846 AND data_template_id = ?', 1847 array($data_template_id)); 1848 1849 $data_source_item_name = get_data_source_item_name($data_template_rrd_id); 1850 } else { 1851 $data_source_item_name = ''; 1852 } 1853 1854 dsv_log('data_source_item_name', $data_source_item_name); 1855 if ($action == POLLER_ACTION_SCRIPT) { 1856 dsv_log('script_path', $script_path); 1857 $output = shell_exec($script_path); 1858 } else { 1859 // Script server is a bit more complicated 1860 $php = read_config_option('path_php_binary'); 1861 $parts = explode(' ', $script_path); 1862 1863 dsv_log('parts', $parts); 1864 if (file_exists($parts[0])) { 1865 unset($parts[1]); 1866 1867 $script = implode(' ', $parts); 1868 1869 dsv_log('script', $script); 1870 $output = shell_exec("$php -q $script"); 1871 if ($output == '' || $output == false) { 1872 $output = 'U'; 1873 } 1874 } else { 1875 $output = 'U'; 1876 } 1877 } 1878 1879 dsv_log('output', $output); 1880 if (!is_numeric($output)) { 1881 if ($output == 'U') { 1882 return false; 1883 } elseif (prepare_validate_result($output) === false) { 1884 return false; 1885 } 1886 } 1887 1888 return true; 1889 } elseif ($data_input['type_id'] == DATA_INPUT_TYPE_SNMP) { 1890 /* get host fields first */ 1891 $host_fields_sql = 'SELECT ' . SQL_NO_CACHE . ' dif.id, dif.type_code, did.value 1892 FROM data_input_fields AS dif 1893 LEFT JOIN data_input_data AS did 1894 ON dif.id=did.data_input_field_id 1895 WHERE (type_code LIKE "snmp_%" OR type_code IN("hostname","host_id")) 1896 AND did.data_template_data_id = ? 1897 AND did.value != ""'; 1898 dsv_log('host_fields_sql',$host_fields_sql); 1899 dsv_log('host_fields_sql_params', ['data_template_data_id' => $data_template_data_id]); 1900 1901 $host_fields = array_rekey( 1902 db_fetch_assoc_prepared($host_fields_sql, 1903 array($data_template_data_id)), 1904 'type_code', 'value' 1905 ); 1906 1907 dsv_log('SNMP host_fields', $host_fields); 1908 1909 $data_template_data = db_fetch_assoc_prepared('SELECT ' . SQL_NO_CACHE . ' dif.id, dif.type_code, did.value 1910 FROM data_input_fields AS dif 1911 LEFT JOIN data_input_data AS did 1912 ON dif.id = did.data_input_field_id 1913 WHERE (type_code LIKE "snmp_%" OR type_code="hostname") 1914 AND did.data_template_data_id = ?', 1915 array($data_template_data_id)); 1916 1917 dsv_log('SNMP data_template_data', $data_template_data); 1918 if (cacti_sizeof($data_template_data)) { 1919 foreach ($data_template_data as $field) { 1920 $key = $field['type_code']; 1921 $value = $field['value']; 1922 dsv_log('SNMP field', $field); 1923 //dsv_log('SNMP suggested_val', $suggested_vals['custom_data'][$data_template_id]); 1924 if (!empty($suggested_vals['custom_data'][$data_template_id][$field['id']])) { 1925 $value = $suggested_vals['custom_data'][$data_template_id][$field['id']]; 1926 dsv_log("SNMP value replace suggested $key", $value); 1927 } 1928 1929 if (!empty($value) && !isset($host_fields[$key])) { 1930 $host_fields[$key] = $value; 1931 dsv_log("SNMP value replace template $key", $value); 1932 } 1933 } 1934 } 1935 1936 dsv_log('SNMP [updated] host_fields', $host_fields); 1937 $host = array_merge($host, $host_fields); 1938 1939 dsv_log('SNMP [updated] host', $host); 1940 1941 $session = cacti_snmp_session($host['hostname'], $host['snmp_community'], $host['snmp_version'], 1942 $host['snmp_username'], $host['snmp_password'], $host['snmp_auth_protocol'], $host['snmp_priv_passphrase'], 1943 $host['snmp_priv_protocol'], $host['snmp_context'], $host['snmp_engine_id'], $host['snmp_port'], 1944 $host['snmp_timeout'], $host['ping_retries'], $host['max_oids']); 1945 1946 $output = cacti_snmp_session_get($session, $host['snmp_oid']); 1947 1948 dsv_log('SNMP output', $output); 1949 1950 if (!is_numeric($output)) { 1951 if (prepare_validate_result($output) === false) { 1952 return false; 1953 } 1954 } 1955 1956 return true; 1957 } elseif ($data_input['type_id'] == DATA_INPUT_TYPE_SNMP_QUERY) { 1958 $snmp_queries = get_data_query_array($snmp_query_id); 1959 1960 /* get host fields first */ 1961 $host_fields = array_rekey( 1962 db_fetch_assoc_prepared('SELECT ' . SQL_NO_CACHE . ' dif.id, dif.type_code, did.value 1963 FROM data_input_fields AS dif 1964 LEFT JOIN data_input_data AS did 1965 ON dif.id=did.data_input_field_id 1966 WHERE (type_code LIKE "snmp_%" OR type_code="hostname") 1967 AND did.data_template_data_id = ? 1968 AND did.value != ""', array($data_template_data_id)), 1969 'type_code', 'value' 1970 ); 1971 1972 dsv_log('SNMP_QUERY host_fields', $host_fields); 1973 1974 $data_template_data = db_fetch_assoc_prepared('SELECT ' . SQL_NO_CACHE . ' dif.id, dif.type_code, did.value 1975 FROM data_input_fields AS dif 1976 LEFT JOIN data_input_data AS did 1977 ON dif.id=did.data_input_field_id 1978 WHERE (type_code LIKE "snmp_%" OR type_code="hostname") 1979 AND did.data_template_data_id = ?', 1980 array($data_template_data_id)); 1981 1982 dsv_log('SNMP_QUERY data_template_data', $data_template_data); 1983 1984 if (cacti_sizeof($data_template_data)) { 1985 foreach ($data_template_data as $field) { 1986 $key = $field['type_code']; 1987 $value = $field['value']; 1988 dsv_log('SNMP_QUERY field', $field); 1989 //dsv_log('SNMP_QUERY suggested_val', $suggested_vals['custom_data'][$data_template_id]); 1990 if (!empty($suggested_vals['custom_data'][$data_template_id][$field['id']])) { 1991 $value = $suggested_vals['custom_data'][$data_template_id][$field['id']]; 1992 dsv_log("SNMP_QUERY value replace suggested $key", $value); 1993 } 1994 1995 if (!empty($value) && !isset($host_fields[$key])) { 1996 $host_fields[$key] = $value; 1997 dsv_log("SNMP_QUERY value replace template $key", $value); 1998 } 1999 } 2000 } 2001 2002 dsv_log('SNMP_QUERY [updated] host_fields', $host_fields); 2003 2004 $host = array_merge($host, $host_fields); 2005 2006 dsv_log('SNMP_QUERY [updated] host', $host); 2007 2008 if (cacti_sizeof($outputs) && cacti_sizeof($snmp_queries)) { 2009 foreach ($outputs as $output) { 2010 if (isset($snmp_queries['fields'][$output['snmp_field_name']]['oid'])) { 2011 $oid = $snmp_queries['fields'][$output['snmp_field_name']]['oid'] . '.' . $snmp_index; 2012 2013 if (isset($snmp_queries['fields'][$output['snmp_field_name']]['oid_suffix'])) { 2014 $oid .= '.' . $snmp_queries['fields'][$output['snmp_field_name']]['oid_suffix']; 2015 } 2016 } 2017 2018 if (!empty($oid)) { 2019 $session = cacti_snmp_session($host['hostname'], $host['snmp_community'], $host['snmp_version'], 2020 $host['snmp_username'], $host['snmp_password'], $host['snmp_auth_protocol'], $host['snmp_priv_passphrase'], 2021 $host['snmp_priv_protocol'], $host['snmp_context'], $host['snmp_engine_id'], $host['snmp_port'], 2022 $host['snmp_timeout'], $host['ping_retries'], $host['max_oids']); 2023 2024 $output = cacti_snmp_session_get($session, $oid); 2025 2026 if (!is_numeric($output)) { 2027 if (prepare_validate_result($output) === false) { 2028 return false; 2029 } 2030 } 2031 2032 return true; 2033 } 2034 } 2035 } 2036 } elseif (($data_input['type_id'] == DATA_INPUT_TYPE_SCRIPT_QUERY) || 2037 ($data_input['type_id'] == DATA_INPUT_TYPE_QUERY_SCRIPT_SERVER)) { 2038 $script_queries = get_data_query_array($snmp_query_id); 2039 2040 /* get host fields first */ 2041 $host_fields = array_rekey( 2042 db_fetch_assoc_prepared('SELECT ' . SQL_NO_CACHE . ' dif.id, dif.type_code, did.value 2043 FROM data_input_fields AS dif 2044 LEFT JOIN data_input_data AS did 2045 ON dif.id=did.data_input_field_id 2046 WHERE (type_code LIKE "snmp_%" OR type_code="hostname") 2047 AND did.data_template_data_id = ? 2048 AND did.value != ""', array($data_template_data_id)), 2049 'type_code', 'value' 2050 ); 2051 2052 dsv_log('SCRIPT host_fields', $host_fields); 2053 2054 $data_template_fields = array_rekey( 2055 db_fetch_assoc_prepared('SELECT ' . SQL_NO_CACHE . ' dif.id, dif.type_code, did.value 2056 FROM data_input_fields AS dif 2057 LEFT JOIN data_input_data AS did 2058 ON dif.id=did.data_input_field_id 2059 WHERE (type_code LIKE "snmp_%" OR type_code="hostname") 2060 AND did.data_template_data_id = ? 2061 AND did.value != ""', array($data_template_data_id)), 2062 'type_code', 'value' 2063 ); 2064 2065 $data_template_data = db_fetch_assoc_prepared('SELECT ' . SQL_NO_CACHE . ' dif.id, dif.type_code, did.value 2066 FROM data_input_fields AS dif 2067 LEFT JOIN data_input_data AS did 2068 ON dif.id=did.data_input_field_id 2069 WHERE (type_code LIKE "snmp_%" OR type_code="hostname") 2070 AND did.data_template_data_id = ?', 2071 array($data_template_data_id)); 2072 2073 dsv_log('SCRIPT data_template_data', $data_template_data); 2074 2075 if (cacti_sizeof($data_template_data)) { 2076 foreach ($data_template_data as $field) { 2077 $key = $field['type_code']; 2078 $value = $field['value']; 2079 dsv_log('SCRIPT field', $field); 2080 //dsv_log('SCRIPT suggested_val', $suggested_vals['custom_data'][$data_template_id]); 2081 if (!empty($suggested_vals['custom_data'][$data_template_id][$field['id']])) { 2082 $value = $suggested_vals['custom_data'][$data_template_id][$field['id']]; 2083 dsv_log("SCRIPT value replace suggested $key", $value); 2084 } 2085 2086 if (!empty($value) && !isset($host_fields[$key])) { 2087 $host_fields[$key] = $value; 2088 dsv_log("SCRIPT value replace template $key", $value); 2089 } 2090 } 2091 } 2092 2093 dsv_log('SCRIPT [updated] host_fields', $host_fields); 2094 2095 $host = array_merge($host, $host_fields); 2096 2097 dsv_log('SCRIPT [updated] host', $host); 2098 2099 if (cacti_sizeof($outputs) && cacti_sizeof($script_queries)) { 2100 foreach ($outputs as $output) { 2101 if (isset($script_queries['fields'][$output['snmp_field_name']]['query_name'])) { 2102 $identifier = $script_queries['fields'][$output['snmp_field_name']]['query_name']; 2103 2104 if ($data_input['type_id'] == DATA_INPUT_TYPE_QUERY_SCRIPT_SERVER) { 2105 $action = POLLER_ACTION_SCRIPT; 2106 2107 $prepend = ''; 2108 if (isset($script_queries['arg_prepend']) && $script_queries['arg_prepend'] != '') { 2109 $prepend = $script_queries['arg_prepend']; 2110 } 2111 2112 $script_path = read_config_option('path_php_binary') . ' -q ' . get_script_query_path(trim($prepend . ' ' . $script_queries['arg_get'] . ' ' . $identifier . ' ' . $snmp_index), $script_queries['script_path'], $host_id); 2113 } else { 2114 $action = POLLER_ACTION_SCRIPT; 2115 $script_path = get_script_query_path(trim((isset($script_queries['arg_prepend']) ? $script_queries['arg_prepend'] : '') . ' ' . $script_queries['arg_get'] . ' ' . $identifier . ' ' . $snmp_index), $script_queries['script_path'], $host_id); 2116 } 2117 } 2118 2119 if (isset($script_path)) { 2120 $output = shell_exec($script_path); 2121 2122 if (!is_numeric($output)) { 2123 if (prepare_validate_result($output) === false) { 2124 return false; 2125 } 2126 } 2127 2128 return true; 2129 } 2130 } 2131 } 2132 } 2133 } 2134 2135 return false; 2136} 2137 2138/** 2139 * get_full_test_script_path - gets the full path to the script to execute to obtain data for a 2140 * given data template for testing. this function does not work on SNMP actions, only 2141 * script-based actions 2142 * 2143 * @param $data_template_id - (int) the ID of the data template 2144 * 2145 * @return - the full script path or (bool) false for an error 2146 */ 2147function get_full_test_script_path($data_template_id, $host_id) { 2148 global $config; 2149 2150 $data_source = db_fetch_row_prepared('SELECT ' . SQL_NO_CACHE . ' 2151 dtd.id, 2152 dtd.data_input_id, 2153 di.type_id, 2154 di.input_string 2155 FROM data_template_data AS dtd 2156 INNER JOIN data_input AS di 2157 ON dtd.data_input_id = di.id 2158 WHERE dtd.local_data_id = 0 2159 AND dtd.data_template_id = ?', 2160 array($data_template_id)); 2161 2162 $data = db_fetch_assoc_prepared("SELECT " . SQL_NO_CACHE . " dif.data_name, did.value 2163 FROM data_input_fields AS dif 2164 LEFT JOIN data_input_data AS did 2165 ON dif.id = did.data_input_field_id 2166 WHERE dif.data_input_id = ? 2167 AND did.data_template_data_id = ? 2168 AND dif.input_output = 'in'", 2169 array($data_source['data_input_id'], $data_source['id'])); 2170 2171 $full_path = $data_source['input_string']; 2172 2173 $host = db_fetch_row_prepared('SELECT * FROM host WHERE id = ?', array($host_id)); 2174 2175 if (cacti_sizeof($data)) { 2176 foreach ($data as $item) { 2177 if (isset($host[$item['data_name']])) { 2178 $value = cacti_escapeshellarg($host[$item['data_name']]); 2179 } elseif ($item['data_name'] == 'host_id' || $item['data_name'] == 'hostid') { 2180 $value = cacti_escapeshellarg($host['id']); 2181 } else { 2182 $value = "'" . $item['value'] . "'"; 2183 } 2184 2185 $full_path = str_replace('<' . $item['data_name'] . '>', $value, $full_path); 2186 } 2187 } 2188 2189 $search = array('<path_cacti>', '<path_snmpget>', '<path_php_binary>'); 2190 $replace = array($config['base_path'], read_config_option('path_snmpget'), read_config_option('path_php_binary')); 2191 $full_path = str_replace($search, $replace, $full_path); 2192 2193 /** 2194 * sometimes a certain input value will not have anything entered... null out these fields 2195 * in the input string so we don't mess up the script 2196 */ 2197 return preg_replace('/(<[A-Za-z0-9_]+>)+/', '', $full_path); 2198} 2199 2200/** 2201 * get_full_script_path - gets the full path to the script to execute to obtain data for a 2202 * given data source. this function does not work on SNMP actions, only script-based actions 2203 * 2204 * @param $local_data_id - (int) the ID of the data source 2205 * 2206 * @return - the full script path or (bool) false for an error 2207 */ 2208function get_full_script_path($local_data_id) { 2209 global $config; 2210 2211 $data_source = db_fetch_row_prepared('SELECT ' . SQL_NO_CACHE . ' dtd.id, dtd.data_input_id, 2212 di.type_id, di.input_string 2213 FROM data_template_data AS dtd 2214 INNER JOIN data_input AS di 2215 ON dtd.data_input_id = di.id 2216 WHERE dtd.local_data_id = ?', 2217 array($local_data_id)); 2218 2219 /* snmp-actions don't have paths */ 2220 if (($data_source['type_id'] == DATA_INPUT_TYPE_SNMP) || ($data_source['type_id'] == DATA_INPUT_TYPE_SNMP_QUERY)) { 2221 return false; 2222 } 2223 2224 $data = db_fetch_assoc_prepared("SELECT " . SQL_NO_CACHE . " dif.data_name, did.value 2225 FROM data_input_fields AS dif 2226 LEFT JOIN data_input_data AS did 2227 ON dif.id = did.data_input_field_id 2228 WHERE dif.data_input_id = ? 2229 AND did.data_template_data_id = ? 2230 AND dif.input_output = 'in'", 2231 array($data_source['data_input_id'], $data_source['id'])); 2232 2233 $full_path = $data_source['input_string']; 2234 2235 if (cacti_sizeof($data)) { 2236 foreach ($data as $item) { 2237 $value = cacti_escapeshellarg($item['value']); 2238 2239 if ($value == '') { 2240 $value = "''"; 2241 } 2242 2243 $full_path = str_replace('<' . $item['data_name'] . '>', $value, $full_path); 2244 } 2245 } 2246 2247 $search = array('<path_cacti>', '<path_snmpget>', '<path_php_binary>'); 2248 $replace = array($config['base_path'], read_config_option('path_snmpget'), read_config_option('path_php_binary')); 2249 $full_path = str_replace($search, $replace, $full_path); 2250 2251 /* sometimes a certain input value will not have anything entered... null out these fields 2252 in the input string so we don't mess up the script */ 2253 return preg_replace('/(<[A-Za-z0-9_]+>)+/', '', $full_path); 2254} 2255 2256/** 2257 * get_data_source_item_name - gets the name of a data source item or generates a new one if one does not 2258 * already exist 2259 * 2260 * @param $data_template_rrd_id - (int) the ID of the data source item 2261 * 2262 * @return - the name of the data source item or an empty string for an error 2263 */ 2264function get_data_source_item_name($data_template_rrd_id) { 2265 if (empty($data_template_rrd_id)) { 2266 return ''; 2267 } 2268 2269 $data_source = db_fetch_row_prepared('SELECT ' . SQL_NO_CACHE . ' 2270 dtr.data_source_name, dtd.name 2271 FROM data_template_rrd AS dtr 2272 INNER JOIN data_template_data AS dtd 2273 ON dtr.local_data_id = dtd.local_data_id 2274 WHERE dtr.id = ?', 2275 array($data_template_rrd_id) 2276 ); 2277 2278 /* use the cacti ds name by default or the user defined one, if entered */ 2279 if (empty($data_source['data_source_name'])) { 2280 /* limit input to 19 characters */ 2281 $data_source_name = clean_up_name($data_source['name']); 2282 $data_source_name = substr(strtolower($data_source_name), 0, (19-strlen($data_template_rrd_id))) . $data_template_rrd_id; 2283 2284 return $data_source_name; 2285 } else { 2286 return $data_source['data_source_name']; 2287 } 2288} 2289 2290/** 2291 * get_data_source_path - gets the full path to the .rrd file associated with a given data source 2292 * 2293 * @param $local_data_id - (int) the ID of the data source 2294 * @param $expand_paths - (bool) whether to expand the <path_rra> variable into its full path or not 2295 * 2296 * @return - the full path to the data source or an empty string for an error 2297 */ 2298function get_data_source_path($local_data_id, $expand_paths) { 2299 global $config; 2300 static $data_source_path_cache = array(); 2301 2302 if (empty($local_data_id)) { 2303 return ''; 2304 } 2305 2306 if (isset($data_source_path_cache[$local_data_id])) { 2307 return $data_source_path_cache[$local_data_id]; 2308 } 2309 2310 $data_source = db_fetch_row_prepared('SELECT ' . SQL_NO_CACHE . ' name, data_source_path FROM data_template_data WHERE local_data_id = ?', array($local_data_id)); 2311 2312 if (cacti_sizeof($data_source) > 0) { 2313 if (empty($data_source['data_source_path'])) { 2314 /* no custom path was specified */ 2315 $data_source_path = generate_data_source_path($local_data_id); 2316 } elseif (!strstr($data_source['data_source_path'], '/')) { 2317 $data_source_path = '<path_rra>/' . $data_source['data_source_path']; 2318 } else { 2319 $data_source_path = $data_source['data_source_path']; 2320 } 2321 2322 /* whether to show the "actual" path or the <path_rra> variable name (for edit boxes) */ 2323 if ($expand_paths == true) { 2324 $data_source_path = str_replace('<path_rra>/', $config['rra_path'] . '/', $data_source_path); 2325 } 2326 2327 $data_source_path_cache[$local_data_id] = $data_source_path; 2328 2329 return $data_source_path; 2330 } 2331} 2332 2333/** 2334 * stri_replace - a case insensitive string replace 2335 * 2336 * @param $find - needle 2337 * @param $replace - replace needle with this 2338 * @param $string - haystack 2339 * 2340 * @return - the original string with '$find' replaced by '$replace' 2341 */ 2342function stri_replace($find, $replace, $string) { 2343 $parts = explode(strtolower($find), strtolower($string)); 2344 2345 $pos = 0; 2346 2347 $findLength = strlen($find); 2348 foreach ($parts as $key => $part) { 2349 $partLength = strlen($part); 2350 2351 $parts[$key] = substr($string, $pos, $partLength); 2352 $pos += $partLength + $findLength; 2353 } 2354 2355 return (join($replace, $parts)); 2356} 2357 2358/** 2359 * clean_up_lines - runs a string through a regular expression designed to remove 2360 * new lines and the spaces around them 2361 * 2362 * @param $string - the string to modify/clean 2363 * 2364 * @return - the modified string 2365 */ 2366function clean_up_lines($string) { 2367 return preg_replace('/\s*[\r\n]+\s*/',' ', $string); 2368} 2369 2370/** 2371 * clean_up_name - runs a string through a series of regular expressions designed to 2372 * eliminate "bad" characters 2373 * 2374 * @param $string - the string to modify/clean 2375 * 2376 * @return - the modified string 2377 */ 2378function clean_up_name($string) { 2379 $string = preg_replace('/[\s\.]+/', '_', $string); 2380 $string = preg_replace('/[^a-zA-Z0-9_]+/', '', $string); 2381 $string = preg_replace('/_{2,}/', '_', $string); 2382 2383 return $string; 2384} 2385 2386/** 2387 * clean_up_file name - runs a string through a series of regular expressions designed to 2388 * eliminate "bad" characters 2389 * 2390 * @param $string - the string to modify/clean 2391 * 2392 * @return - the modified string 2393 */ 2394function clean_up_file_name($string) { 2395 $string = preg_replace('/[\s\.]+/', '_', $string); 2396 $string = preg_replace('/[^a-zA-Z0-9_-]+/', '', $string); 2397 $string = preg_replace('/_{2,}/', '_', $string); 2398 2399 return $string; 2400} 2401 2402/** 2403 * clean_up_path - takes any path and makes sure it contains the correct directory 2404 * separators based on the current operating system 2405 * 2406 * @param $path - the path to modify 2407 * 2408 * @return - the modified path 2409 */ 2410function clean_up_path($path) { 2411 global $config; 2412 2413 if ($config['cacti_server_os'] == 'win32') { 2414 return str_replace('/', "\\", $path); 2415 } elseif ($config['cacti_server_os'] == 'unix' || read_config_option('using_cygwin') == 'on' || read_config_option('storage_location')) { 2416 return str_replace("\\", '/', $path); 2417 } else { 2418 return $path; 2419 } 2420} 2421 2422/** 2423 * get_data_source_title - returns the title of a data source without using the title cache 2424 * 2425 * @param $local_data_id - (int) the ID of the data source to get a title for 2426 * 2427 * @return - the data source title 2428 */ 2429function get_data_source_title($local_data_id) { 2430 $data = db_fetch_row_prepared('SELECT dl.host_id, dl.snmp_query_id, dl.snmp_index, dtd.name 2431 FROM data_local AS dl 2432 INNER JOIN data_template_data AS dtd 2433 ON dtd.local_data_id = dl.id 2434 WHERE dl.id = ?', 2435 array($local_data_id)); 2436 2437 if (cacti_sizeof($data)) { 2438 if (strstr($data['name'], '|') !== false && $data['host_id'] > 0) { 2439 $data['name'] = substitute_data_input_data($data['name'], '', $local_data_id); 2440 return expand_title($data['host_id'], $data['snmp_query_id'], $data['snmp_index'], $data['name']); 2441 } else { 2442 return $data['name']; 2443 } 2444 } else { 2445 return 'Missing Datasource ' . $local_data_id; 2446 } 2447} 2448 2449/** 2450 * get_device_name - returns the description of the device in cacti host table 2451 * 2452 * @param $host_id - (int) the ID of the device to get a description for 2453 * 2454 * @return - the device name 2455 */ 2456function get_device_name($host_id) { 2457 return db_fetch_cell_prepared('SELECT description FROM host WHERE id = ?', array($host_id)); 2458} 2459 2460/** 2461 * get_color - returns the hex color value from the cacti colors table 2462 * 2463 * @param $color_id - (int) the ID of the cacti color 2464 * @return - the hex color value 2465 * 2466 */ 2467function get_color($color_id) { 2468 return db_fetch_cell_prepared('SELECT hex FROM colors WHERE id = ?', array($color_id)); 2469} 2470 2471/** 2472 * get_graph_title_cache - returns the title of the graph using the title cache 2473 * 2474 * @param $local_graph_id - (int) the ID of the graph to get the title for 2475 * 2476 * @return - the graph title 2477 */ 2478function get_graph_title_cache($local_graph_id) { 2479 return db_fetch_cell_prepared('SELECT title_cache 2480 FROM graph_templates_graph 2481 WHERE local_graph_id = ?', 2482 array($local_graph_id)); 2483} 2484 2485/** 2486 * get_graph_title - returns the title of a graph without using the title cache 2487 * 2488 * @param $local_graph_id - (int) the ID of the graph to get a title for 2489 * 2490 * @return - the graph title 2491 */ 2492function get_graph_title($local_graph_id) { 2493 $graph = db_fetch_row_prepared('SELECT gl.host_id, gl.snmp_query_id, 2494 gl.snmp_index, gtg.local_graph_id, gtg.t_title, gtg.title 2495 FROM graph_templates_graph AS gtg 2496 INNER JOIN graph_local AS gl 2497 ON gtg.local_graph_id = gl.id 2498 WHERE gl.id = ?', 2499 array($local_graph_id)); 2500 2501 if (cacti_sizeof($graph)) { 2502 if (strstr($graph['title'], '|') !== false && $graph['host_id'] > 0 && empty($graph['t_title'])) { 2503 $graph['title'] = substitute_data_input_data($graph['title'], $graph, 0); 2504 return expand_title($graph['host_id'], $graph['snmp_query_id'], $graph['snmp_index'], $graph['title']); 2505 } else { 2506 return $graph['title']; 2507 } 2508 } else { 2509 return ''; 2510 } 2511} 2512 2513/** 2514 * get_username - returns the username for the selected user 2515 * 2516 * @param $user_id - (int) the ID of the user 2517 * 2518 * @return - the username */ 2519function get_username($user_id) { 2520 return db_fetch_cell_prepared('SELECT username FROM user_auth WHERE id = ?', array($user_id)); 2521} 2522 2523/** 2524 * get_execution_user - returns the username of the running process 2525 * 2526 * @return - the username 2527 */ 2528function get_execution_user() { 2529 if (function_exists('posix_getpwuid')) { 2530 $user_info = posix_getpwuid(posix_geteuid()); 2531 2532 return $user_info['name']; 2533 } else { 2534 return exec('whoami'); 2535 } 2536} 2537 2538/** 2539 * generate_data_source_path - creates a new data source path from scratch using the first data source 2540 * item name and updates the database with the new value 2541 * 2542 * @param $local_data_id - (int) the ID of the data source to generate a new path for 2543 * 2544 * @return - the new generated path 2545 */ 2546function generate_data_source_path($local_data_id) { 2547 global $config; 2548 2549 /* try any prepend the name with the host description */ 2550 $host = db_fetch_row_prepared('SELECT host.id, host.description 2551 FROM (host, data_local) 2552 WHERE data_local.host_id = host.id 2553 AND data_local.id = ? 2554 LIMIT 1', array($local_data_id)); 2555 2556 $host_name = $host['description']; 2557 $host_id = $host['id']; 2558 2559 /* put it all together using the local_data_id at the end */ 2560 if (read_config_option('extended_paths') == 'on') { 2561 $new_path = "<path_rra>/$host_id/$local_data_id.rrd"; 2562 } else { 2563 if (!empty($host_name)) { 2564 $host_part = strtolower(clean_up_file_name($host_name)) . '_'; 2565 } 2566 2567 /* then try and use the internal DS name to identify it */ 2568 $data_source_rrd_name = db_fetch_cell_prepared('SELECT data_source_name 2569 FROM data_template_rrd 2570 WHERE local_data_id = ? 2571 ORDER BY id 2572 LIMIT 1', 2573 array($local_data_id) 2574 ); 2575 2576 if (!empty($data_source_rrd_name)) { 2577 $ds_part = strtolower(clean_up_file_name($data_source_rrd_name)); 2578 } else { 2579 $ds_part = 'ds'; 2580 } 2581 2582 $new_path = "<path_rra>/$host_part$ds_part" . '_' . "$local_data_id.rrd"; 2583 } 2584 2585 /* update our changes to the db */ 2586 db_execute_prepared('UPDATE data_template_data SET data_source_path = ? WHERE local_data_id = ?', array($new_path, $local_data_id)); 2587 2588 return $new_path; 2589} 2590 2591/** 2592 * generate graph_best_cf - takes the requested consolidation function and maps against 2593 * the list of available consolidation functions for the consolidation functions and returns 2594 * the most appropriate. Typically, this will be the requested value 2595 * 2596 * @param $data_template_id 2597 * @param $requested_cf 2598 * @param $ds_step 2599 * 2600 * @return - the best cf to use 2601 */ 2602function generate_graph_best_cf($local_data_id, $requested_cf, $ds_step = 60) { 2603 static $best_cf; 2604 2605 if ($local_data_id > 0) { 2606 $avail_cf_functions = get_rrd_cfs($local_data_id); 2607 2608 if (cacti_sizeof($avail_cf_functions)) { 2609 /* workaround until we have RRA presets in 0.8.8 */ 2610 /* check through the cf's and get the best */ 2611 /* if none was found, take the first */ 2612 $best_cf = $avail_cf_functions[1]; 2613 2614 foreach($avail_cf_functions as $cf) { 2615 if ($cf == $requested_cf) { 2616 $best_cf = $requested_cf; 2617 } 2618 } 2619 } else { 2620 $best_cf = '1'; 2621 } 2622 } 2623 2624 /* if you can not figure it out return average */ 2625 return $best_cf; 2626} 2627 2628/** 2629 * get_rrd_cfs - reads the RRDfile and gets the RRAs stored in it. 2630 * 2631 * @param $local_data_id 2632 * 2633 * @return - array of the CF functions 2634 */ 2635function get_rrd_cfs($local_data_id) { 2636 global $consolidation_functions; 2637 static $rrd_cfs = array(); 2638 2639 if (array_key_exists($local_data_id, $rrd_cfs)) { 2640 return $rrd_cfs[$local_data_id]; 2641 } 2642 2643 $cfs = array(); 2644 2645 $rrdfile = get_data_source_path($local_data_id, true); 2646 2647 $output = @rrdtool_execute("info $rrdfile", false, RRDTOOL_OUTPUT_STDOUT); 2648 2649 /* search for 2650 * rra[0].cf = 'LAST' 2651 * or similar 2652 */ 2653 if ($output != '') { 2654 $output = explode("\n", $output); 2655 2656 if (cacti_sizeof($output)) { 2657 foreach($output as $line) { 2658 if (substr_count($line, '.cf')) { 2659 $values = explode('=',$line); 2660 2661 if (!in_array(trim($values[1], '" '), $cfs)) { 2662 $cfs[] = trim($values[1], '" '); 2663 } 2664 } 2665 } 2666 } 2667 } 2668 2669 $new_cfs = array(); 2670 2671 if (cacti_sizeof($cfs)) { 2672 foreach($cfs as $cf) { 2673 switch($cf) { 2674 case 'AVG': 2675 case 'AVERAGE': 2676 $new_cfs[1] = array_search('AVERAGE', $consolidation_functions); 2677 break; 2678 case 'MIN': 2679 $new_cfs[2] = array_search('MIN', $consolidation_functions); 2680 break; 2681 case 'MAX': 2682 $new_cfs[3] = array_search('MAX', $consolidation_functions); 2683 break; 2684 case 'LAST': 2685 $new_cfs[4] = array_search('LAST', $consolidation_functions); 2686 break; 2687 } 2688 } 2689 } 2690 2691 $rrd_cfs[$local_data_id] = $new_cfs; 2692 2693 return $new_cfs; 2694} 2695 2696/** 2697 * generate_graph_def_name - takes a number and turns each digit into its letter-based 2698 * counterpart for RRDtool DEF names (ex 1 -> a, 2 -> b, etc) 2699 * 2700 * @param $graph_item_id - (int) the ID to generate a letter-based representation of 2701 * 2702 * @return - a letter-based representation of the input argument 2703 */ 2704function generate_graph_def_name($graph_item_id) { 2705 $lookup_table = array('a','b','c','d','e','f','g','h','i','j'); 2706 2707 $result = ''; 2708 $strValGII = strval($graph_item_id); 2709 for ($i=0; $i<strlen($strValGII); $i++) { 2710 $result .= $lookup_table[substr($strValGII, $i, 1)]; 2711 } 2712 2713 if (preg_match('/^(cf|cdef|def)$/', $result)) { 2714 return 'zz' . $result; 2715 } else { 2716 return $result; 2717 } 2718} 2719 2720/** 2721 * generate_data_input_field_sequences - re-numbers the sequences of each field associated 2722 * with a particular data input method based on its position within the input string 2723 * 2724 * @param $string - the input string that contains the field variables in a certain order 2725 * @param $data_input_id - (int) the ID of the data input method 2726 */ 2727function generate_data_input_field_sequences($string, $data_input_id) { 2728 global $config, $registered_cacti_names; 2729 2730 if (preg_match_all('/<([_a-zA-Z0-9]+)>/', $string, $matches)) { 2731 $j = 0; 2732 for ($i=0; ($i < cacti_count($matches[1])); $i++) { 2733 if (in_array($matches[1][$i], $registered_cacti_names) == false) { 2734 $j++; 2735 2736 db_execute_prepared("UPDATE data_input_fields 2737 SET sequence = ? 2738 WHERE data_input_id = ? 2739 AND input_output IN ('in') 2740 AND data_name = ?", 2741 array($j, $data_input_id, $matches[1][$i])); 2742 } 2743 } 2744 2745 update_replication_crc(0, 'poller_replicate_data_input_fields_crc'); 2746 } 2747} 2748 2749/** 2750 * move_graph_group - takes a graph group (parent+children) and swaps it with another graph 2751 * group 2752 * 2753 * @param $graph_template_item_id - (int) the ID of the (parent) graph item that was clicked 2754 * @param $graph_group_array - (array) an array containing the graph group to be moved 2755 * @param $target_id - (int) the ID of the (parent) graph item of the target group 2756 * @param $direction - ('next' or 'previous') whether the graph group is to be swapped with 2757 * group above or below the current group 2758 */ 2759function move_graph_group($graph_template_item_id, $graph_group_array, $target_id, $direction) { 2760 $graph_item = db_fetch_row_prepared('SELECT local_graph_id, graph_template_id 2761 FROM graph_templates_item 2762 WHERE id = ?', 2763 array($graph_template_item_id)); 2764 2765 if (empty($graph_item['local_graph_id'])) { 2766 $sql_where = 'graph_template_id = ' . $graph_item['graph_template_id'] . ' AND local_graph_id = 0'; 2767 } else { 2768 $sql_where = 'local_graph_id = ' . $graph_item['local_graph_id']; 2769 } 2770 2771 /* get a list of parent+children of our target group */ 2772 $target_graph_group_array = get_graph_group($target_id); 2773 2774 /* if this "parent" item has no children, then treat it like a regular gprint */ 2775 if (cacti_sizeof($target_graph_group_array) == 0) { 2776 if ($direction == 'next') { 2777 move_item_down('graph_templates_item', $graph_template_item_id, $sql_where); 2778 } elseif ($direction == 'previous') { 2779 move_item_up('graph_templates_item', $graph_template_item_id, $sql_where); 2780 } 2781 2782 return; 2783 } 2784 2785 /* start the sequence at '1' */ 2786 $sequence_counter = 1; 2787 2788 $graph_items = db_fetch_assoc_prepared("SELECT id, sequence 2789 FROM graph_templates_item 2790 WHERE $sql_where 2791 ORDER BY sequence"); 2792 2793 if (cacti_sizeof($graph_items)) { 2794 foreach ($graph_items as $item) { 2795 /* check to see if we are at the "target" spot in the loop; if we are, update the sequences and move on */ 2796 if ($target_id == $item['id']) { 2797 if ($direction == 'next') { 2798 $group_array1 = $target_graph_group_array; 2799 $group_array2 = $graph_group_array; 2800 } elseif ($direction == 'previous') { 2801 $group_array1 = $graph_group_array; 2802 $group_array2 = $target_graph_group_array; 2803 } 2804 2805 foreach ($group_array1 as $graph_template_item_id) { 2806 db_execute_prepared('UPDATE graph_templates_item 2807 SET sequence = ? 2808 WHERE id = ?', 2809 array($sequence_counter, $graph_template_item_id)); 2810 2811 /* propagate to ALL graphs using this template */ 2812 if (empty($graph_item['local_graph_id'])) { 2813 db_execute_prepared('UPDATE graph_templates_item 2814 SET sequence = ? 2815 WHERE local_graph_template_item_id = ?', 2816 array($sequence_counter, $graph_template_item_id)); 2817 } 2818 2819 $sequence_counter++; 2820 } 2821 2822 foreach ($group_array2 as $graph_template_item_id) { 2823 db_execute_prepared('UPDATE graph_templates_item 2824 SET sequence = ? 2825 WHERE id = ?', 2826 array($sequence_counter, $graph_template_item_id)); 2827 2828 /* propagate to ALL graphs using this template */ 2829 if (empty($graph_item['local_graph_id'])) { 2830 db_execute_prepared('UPDATE graph_templates_item 2831 SET sequence = ? 2832 WHERE local_graph_template_item_id = ?', 2833 array($sequence_counter, $graph_template_item_id)); 2834 } 2835 2836 $sequence_counter++; 2837 } 2838 } 2839 2840 /* make sure to "ignore" the items that we handled above */ 2841 if ((!isset($graph_group_array[$item['id']])) && (!isset($target_graph_group_array[$item['id']]))) { 2842 db_execute_prepared('UPDATE graph_templates_item 2843 SET sequence = ? 2844 WHERE id = ?', 2845 array($sequence_counter, $item['id'])); 2846 2847 $sequence_counter++; 2848 } 2849 } 2850 } 2851} 2852 2853/** 2854 * get_graph_group - returns an array containing each item in the graph group given a single 2855 * graph item in that group 2856 * 2857 * @param $graph_template_item_id - (int) the ID of the graph item to return the group of 2858 * 2859 * @return - (array) an array containing each item in the graph group 2860 */ 2861function get_graph_group($graph_template_item_id) { 2862 global $graph_item_types; 2863 2864 $graph_item = db_fetch_row_prepared('SELECT graph_type_id, sequence, local_graph_id, graph_template_id 2865 FROM graph_templates_item 2866 WHERE id = ?', 2867 array($graph_template_item_id)); 2868 2869 $params[] = $graph_item['sequence']; 2870 2871 if (empty($graph_item['local_graph_id'])) { 2872 $params[] = $graph_item['graph_template_id']; 2873 $sql_where = 'graph_template_id = ? AND local_graph_id = 0'; 2874 } else { 2875 $params[] = $graph_item['sequence']; 2876 $sql_where = 'local_graph_id = ?'; 2877 } 2878 2879 /* parents are LINE%, AREA%, and STACK%. If not return */ 2880 if (!preg_match('/(LINE|AREA|STACK)/', $graph_item_types[$graph_item['graph_type_id']])) { 2881 return array(); 2882 } 2883 2884 $graph_item_children_array = array(); 2885 2886 /* put the parent item in the array as well */ 2887 $graph_item_children_array[$graph_template_item_id] = $graph_template_item_id; 2888 2889 $graph_items = db_fetch_assoc_prepared("SELECT id, graph_type_id, text_format, hard_return 2890 FROM graph_templates_item 2891 WHERE sequence > ? 2892 AND $sql_where 2893 ORDER BY sequence", 2894 $params); 2895 2896 $is_hard = false; 2897 2898 if (cacti_sizeof($graph_items)) { 2899 foreach ($graph_items as $item) { 2900 if ($is_hard) { 2901 return $graph_item_children_array; 2902 } elseif (strstr($graph_item_types[$item['graph_type_id']], 'GPRINT') !== false) { 2903 /* a child must be a GPRINT */ 2904 $graph_item_children_array[$item['id']] = $item['id']; 2905 2906 if ($item['hard_return'] == 'on') { 2907 $is_hard = true; 2908 } 2909 } elseif (strstr($graph_item_types[$item['graph_type_id']], 'COMMENT') !== false) { 2910 if (preg_match_all('/\|([0-9]{1,2}):(bits|bytes):(\d):(current|total|max|total_peak|all_max_current|all_max_peak|aggregate_max|aggregate_sum|aggregate_current|aggregate):(\d)?\|/', $item['text_format'], $matches, PREG_SET_ORDER)) { 2911 $graph_item_children_array[$item['id']] = $item['id']; 2912 } elseif (preg_match_all('/\|sum:(\d|auto):(current|total|atomic):(\d):(\d+|auto)\|/', $item['text_format'], $matches, PREG_SET_ORDER)) { 2913 $graph_item_children_array[$item['id']] = $item['id']; 2914 } else { 2915 /* if not a GPRINT or special COMMENT then get out */ 2916 return $graph_item_children_array; 2917 } 2918 } else { 2919 /* if not a GPRINT or special COMMENT then get out */ 2920 return $graph_item_children_array; 2921 } 2922 } 2923 } 2924 2925 return $graph_item_children_array; 2926} 2927 2928/** 2929 * get_graph_parent - returns the ID of the next or previous parent graph item id 2930 * 2931 * @param $graph_template_item_id - the ID of the current graph item 2932 * @param $direction - ('next' or 'previous') whether to find the next or previous parent 2933 * 2934 * @return - the ID of the next or previous parent graph item id 2935 */ 2936function get_graph_parent($graph_template_item_id, $direction) { 2937 $graph_item = db_fetch_row_prepared('SELECT sequence, local_graph_id, graph_template_id 2938 FROM graph_templates_item 2939 WHERE id = ?', 2940 array($graph_template_item_id)); 2941 2942 if (empty($graph_item['local_graph_id'])) { 2943 $sql_where = 'graph_template_id = ' . $graph_item['graph_template_id'] . ' AND local_graph_id = 0'; 2944 } else { 2945 $sql_where = 'local_graph_id = ' . $graph_item['local_graph_id']; 2946 } 2947 2948 if ($direction == 'next') { 2949 $sql_operator = '>'; 2950 $sql_order = 'ASC'; 2951 } elseif ($direction == 'previous') { 2952 $sql_operator = '<'; 2953 $sql_order = 'DESC'; 2954 } 2955 2956 $next_parent_id = db_fetch_cell("SELECT id 2957 FROM graph_templates_item 2958 WHERE sequence $sql_operator " . $graph_item['sequence'] . " 2959 AND graph_type_id IN (4, 5, 6, 7, 8, 20) 2960 AND $sql_where 2961 ORDER BY sequence $sql_order 2962 LIMIT 1"); 2963 2964 if (empty($next_parent_id)) { 2965 return 0; 2966 } else { 2967 return $next_parent_id; 2968 } 2969} 2970 2971/** 2972 * get_item - returns the ID of the next or previous item id 2973 * 2974 * @param $tblname - the table name that contains the target id 2975 * @param $field - the field name that contains the target id 2976 * @param $startid - (int) the current id 2977 * @param $lmt_query - an SQL "where" clause to limit the query 2978 * @param $direction - ('next' or 'previous') whether to find the next or previous item id 2979 * 2980 * @return - (int) the ID of the next or previous item id 2981 */ 2982function get_item($tblname, $field, $startid, $lmt_query, $direction) { 2983 if ($direction == 'next') { 2984 $sql_operator = '>'; 2985 $sql_order = 'ASC'; 2986 } elseif ($direction == 'previous') { 2987 $sql_operator = '<'; 2988 $sql_order = 'DESC'; 2989 } 2990 2991 $current_sequence = db_fetch_cell_prepared("SELECT $field 2992 FROM $tblname 2993 WHERE id = ?", 2994 array($startid)); 2995 2996 $new_item_id = db_fetch_cell("SELECT id 2997 FROM $tblname 2998 WHERE $field $sql_operator $current_sequence " . ($lmt_query != '' ? " AND $lmt_query":"") . " 2999 ORDER BY $field $sql_order 3000 LIMIT 1"); 3001 3002 if (empty($new_item_id)) { 3003 return $startid; 3004 } else { 3005 return $new_item_id; 3006 } 3007} 3008 3009/** 3010 * get_sequence - returns the next available sequence id 3011 * 3012 * @param $id - (int) the current id 3013 * @param $field - the field name that contains the target id 3014 * @param $table_name - the table name that contains the target id 3015 * @param $group_query - an SQL "where" clause to limit the query 3016 * 3017 * @return - (int) the next available sequence id 3018 */ 3019function get_sequence($id, $field, $table_name, $group_query) { 3020 if (empty($id)) { 3021 $data = db_fetch_row("SELECT max($field)+1 AS seq 3022 FROM $table_name 3023 WHERE $group_query"); 3024 3025 if ($data['seq'] == '') { 3026 return 1; 3027 } else { 3028 return $data['seq']; 3029 } 3030 } else { 3031 $data = db_fetch_row_prepared("SELECT $field 3032 FROM $table_name 3033 WHERE id = ?", 3034 array($id)); 3035 3036 return $data[$field]; 3037 } 3038} 3039 3040/** 3041 * move_item_down - moves an item down by swapping it with the item below it 3042 * 3043 * @param $table_name - the table name that contains the target id 3044 * @param $current_id - (int) the current id 3045 * @param $group_query - an SQL "where" clause to limit the query 3046 */ 3047function move_item_down($table_name, $current_id, $group_query = '') { 3048 $next_item = get_item($table_name, 'sequence', $current_id, $group_query, 'next'); 3049 3050 $sequence = db_fetch_cell_prepared("SELECT sequence 3051 FROM $table_name 3052 WHERE id = ?", 3053 array($current_id)); 3054 3055 $sequence_next = db_fetch_cell_prepared("SELECT sequence 3056 FROM $table_name 3057 WHERE id = ?", 3058 array($next_item)); 3059 3060 db_execute_prepared("UPDATE $table_name 3061 SET sequence = ? 3062 WHERE id = ?", 3063 array($sequence_next, $current_id)); 3064 3065 db_execute_prepared("UPDATE $table_name 3066 SET sequence = ? 3067 WHERE id = ?", 3068 array($sequence, $next_item)); 3069} 3070 3071/** 3072 * move_item_up - moves an item down by swapping it with the item above it 3073 * 3074 * @param $table_name - the table name that contains the target id 3075 * @param $current_id - (int) the current id 3076 * @param $group_query - an SQL "where" clause to limit the query 3077 */ 3078function move_item_up($table_name, $current_id, $group_query = '') { 3079 $last_item = get_item($table_name, 'sequence', $current_id, $group_query, 'previous'); 3080 3081 $sequence = db_fetch_cell_prepared("SELECT sequence 3082 FROM $table_name 3083 WHERE id = ?", 3084 array($current_id)); 3085 3086 $sequence_last = db_fetch_cell_prepared("SELECT sequence 3087 FROM $table_name 3088 WHERE id = ?", 3089 array($last_item)); 3090 3091 db_execute_prepared("UPDATE $table_name 3092 SET sequence = ? 3093 WHERE id = ?", 3094 array($sequence_last, $current_id)); 3095 3096 db_execute_prepared("UPDATE $table_name 3097 SET sequence = ? 3098 WHERE id = ?", 3099 array($sequence, $last_item)); 3100} 3101 3102/** 3103 * exec_into_array - executes a command and puts each line of its output into 3104 * an array 3105 * 3106 * @param $command_line - the command to execute 3107 * 3108 * @return - (array) an array containing the command output 3109 */ 3110function exec_into_array($command_line) { 3111 $out = array(); 3112 $err = 0; 3113 exec($command_line,$out,$err); 3114 3115 return array_values($out); 3116} 3117 3118/** 3119 * get_web_browser - determines the current web browser in use by the client 3120 * 3121 * @return - ('ie' or 'moz' or 'other') 3122 */ 3123function get_web_browser() { 3124 if (!empty($_SERVER['HTTP_USER_AGENT'])) { 3125 if (stristr($_SERVER['HTTP_USER_AGENT'], 'Mozilla') && (!(stristr($_SERVER['HTTP_USER_AGENT'], 'compatible')))) { 3126 return 'moz'; 3127 } elseif (stristr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) { 3128 return 'ie'; 3129 } else { 3130 return 'other'; 3131 } 3132 } else { 3133 return 'other'; 3134 } 3135} 3136 3137function get_guest_account() { 3138 return db_fetch_cell_prepared('SELECT id 3139 FROM user_auth 3140 WHERE username = ? OR id = ?', 3141 array(read_config_option('guest_user'), read_config_option('guest_user'))); 3142} 3143 3144function get_template_account() { 3145 return db_fetch_cell_prepared('SELECT id 3146 FROM user_auth 3147 WHERE username = ? OR id = ?', 3148 array(read_config_option('user_template'), read_config_option('user_template'))); 3149} 3150 3151/** 3152 * draw_login_status - provides a consistent login status page for all pages that use it 3153 */ 3154function draw_login_status($using_guest_account = false) { 3155 global $config; 3156 3157 $guest_account = get_guest_account(); 3158 $auth_method = read_config_option('auth_method'); 3159 3160 if (isset($_SESSION['sess_user_id']) && $_SESSION['sess_user_id'] == $guest_account) { 3161 api_plugin_hook('nav_login_before'); 3162 print __('Logged in as') . " <span id='user' class='user usermenuup'>". __('guest') . "</span></div><div><ul class='menuoptions' style='display:none;'><li><a href='" . $config['url_path'] . "index.php?login=true'>" . __('Login as Regular User') . "</a></li>\n"; 3163 print "<li class='menuHr'><hr class='menu'></li>"; 3164 print "<li id='userCommunity'><a href='https://forums.cacti.net' target='_blank' rel='noopener'>" . __('User Community') . "</a></li>"; 3165 print "<li id='userDocumentation'><a href='https://github.com/Cacti/documentation/blob/develop/README.md' target='_blank' rel='noopener'>" . __('Documentation') . "</a></li>"; 3166 print "</ul>"; 3167 3168 api_plugin_hook('nav_login_after'); 3169 } elseif (isset($_SESSION['sess_user_id']) && $using_guest_account == false) { 3170 $user = db_fetch_row_prepared('SELECT username, password_change, realm FROM user_auth WHERE id = ?', array($_SESSION['sess_user_id'])); 3171 api_plugin_hook('nav_login_before'); 3172 3173 print __('Logged in as') . " <span id='user' class='user usermenuup'>" . html_escape($user['username']) . 3174 "</span></div><div><ul class='menuoptions' style='display:none;'>"; 3175 3176 print "<li><a href='#' class='loggedInAs' style='display:none;'>" . __esc('Logged in as %s', $user['username']) . "</a></li><hr class='menu'>"; 3177 3178 print (is_realm_allowed(20) ? "<li><a href='" . html_escape($config['url_path'] . 'auth_profile.php?action=edit') . "'>" . __('Edit Profile') . '</a></li>':''); 3179 print ($user['password_change'] == 'on' && $user['realm'] == 0 ? "<li><a href='" . html_escape($config['url_path'] . 'auth_changepassword.php') . "'>" . __('Change Password') . '</a></li>':''); 3180 print ((is_realm_allowed(20) || ($user['password_change'] == 'on' && $user['realm'] == 0)) ? "<li class='menuHr'><hr class='menu'></li>":''); 3181 if (is_realm_allowed(28)) { 3182 print "<li id='userCommunity'><a href='https://forums.cacti.net' target='_blank' rel='noopener'>" . __('User Community') . '</a></li>'; 3183 print "<li id='userDocumentation'><a href='https://github.com/Cacti/documentation/blob/develop/README.md' target='_blank' rel='noopener'>" . __('Documentation') . '</a></li>'; 3184 print "<li class='menuHr'><hr class='menu'></li>"; 3185 } 3186 print ($auth_method > 0 && $auth_method != 2 ? "<li><a href='" . html_escape($config['url_path'] . 'logout.php') . "'>" . __('Logout') . '</a></li>':''); 3187 print '</ul>'; 3188 3189 api_plugin_hook('nav_login_after'); 3190 } 3191} 3192 3193/** 3194 * draw_navigation_text - determines the top header navigation text for the current page and displays it to 3195 * 3196 * @param $type - Either 'url' or 'title' 3197 * 3198 * @return - Either the navigation text or title 3199 */ 3200function draw_navigation_text($type = 'url') { 3201 global $config, $navigation; 3202 3203 $nav_level_cache = (isset($_SESSION['sess_nav_level_cache']) ? $_SESSION['sess_nav_level_cache'] : array()); 3204 $navigation = api_plugin_hook_function('draw_navigation_text', $navigation); 3205 $current_page = get_current_page(); 3206 3207 // Do an error check here for bad plugins manipulating the cache 3208 if (!is_array($nav_level_cache)) { 3209 $nav_level_cache = array(); 3210 } 3211 3212 if (!isempty_request_var('action')) { 3213 get_filter_request_var('action', FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => '/^([-a-zA-Z0-9_\s]+)$/'))); 3214 } 3215 3216 $current_action = (isset_request_var('action') ? get_request_var('action') : ''); 3217 3218 // find the current page in the big array 3219 if (isset($navigation[$current_page . ':' . $current_action])) { 3220 $current_array = $navigation[$current_page . ':' . $current_action]; 3221 } else { 3222 $current_array = array( 3223 'mapping' => 'index.php:', 3224 'title' => ucwords(str_replace('_', ' ', basename(get_current_page(), '.php'))), 3225 'level' => 1 3226 ); 3227 } 3228 3229 if (isset($current_array['mapping'])) { 3230 $current_mappings = explode(',', $current_array['mapping']); 3231 } else { 3232 $current_mappings = array(); 3233 } 3234 3235 $current_nav = "<ul id='breadcrumbs'>"; 3236 $title = ''; 3237 $nav_count = 0; 3238 3239 // resolve all mappings to build the navigation string 3240 for ($i=0; ($i < cacti_count($current_mappings)); $i++) { 3241 if (empty($current_mappings[$i])) { 3242 continue; 3243 } 3244 3245 if ($i == 0) { 3246 // always use the default for level == 0 3247 $url = $navigation[basename($current_mappings[$i])]['url']; 3248 3249 if (basename($url) == 'graph_view.php') continue; 3250 } elseif (isset($nav_level_cache[$i]) && !empty($nav_level_cache[$i]['url'])) { 3251 // found a match in the url cache for this level 3252 $url = $nav_level_cache[$i]['url']; 3253 } elseif (isset($current_array['url'])) { 3254 // found a default url in the above array 3255 $url = $current_array['url']; 3256 } else { 3257 // default to no url 3258 $url = ''; 3259 } 3260 3261 if ($current_mappings[$i] == '?') { 3262 // '?' tells us to pull title from the cache at this level 3263 if (isset($nav_level_cache[$i])) { 3264 $current_nav .= (empty($url) ? '' : "<li><a id='nav_$i' href='" . html_escape($url) . "'>"); 3265 $current_nav .= html_escape(resolve_navigation_variables($navigation[$nav_level_cache[$i]['id']]['title'])); 3266 $current_nav .= (empty($url) ? '' : '</a>' . (get_selected_theme() == 'classic' ? ' -> ':'') . '</li>'); 3267 $title .= html_escape(resolve_navigation_variables($navigation[$nav_level_cache[$i]['id']]['title'])) . ' -> '; 3268 } 3269 } else { 3270 // there is no '?' - pull from the above array 3271 $current_nav .= (empty($url) ? '' : "<li><a id='nav_$i' href='" . html_escape($url) . "'>"); 3272 $current_nav .= html_escape(resolve_navigation_variables($navigation[basename($current_mappings[$i])]['title'])); 3273 $current_nav .= (empty($url) ? '' : '</a>' . (get_selected_theme() == 'classic' ? ' -> ':'') . '</li>'); 3274 $title .= html_escape(resolve_navigation_variables($navigation[basename($current_mappings[$i])]['title'])) . ' -> '; 3275 } 3276 3277 $nav_count++; 3278 } 3279 3280 if ($nav_count) { 3281 if (isset($current_array['title'])) { 3282 $current_nav .= "<li><a id='nav_$i' href=#>" . html_escape(resolve_navigation_variables($current_array['title'])) . '</a></li>'; 3283 } 3284 } else { 3285 $current_array = $navigation[$current_page . ':' . $current_action]; 3286 $url = (isset($current_array['url']) ? $current_array['url']:''); 3287 3288 if (isset($current_array['title'])) { 3289 $current_nav .= "<li><a id='nav_$i' href='$url'>" . html_escape(resolve_navigation_variables($current_array['title'])) . '</a></li>'; 3290 } 3291 } 3292 3293 if (isset_request_var('action') || get_nfilter_request_var('action') == 'tree_content') { 3294 $tree_id = 0; 3295 $leaf_id = 0; 3296 3297 if (isset_request_var('node')) { 3298 $parts = explode('-', get_request_var('node')); 3299 3300 // Check for tree anchor 3301 if (strpos(get_request_var('node'), 'tree_anchor') !== false) { 3302 $tree_id = $parts[1]; 3303 $leaf_id = 0; 3304 } elseif (strpos(get_request_var('node'), 'tbranch') !== false) { 3305 // Check for branch 3306 $leaf_id = $parts[1]; 3307 $tree_id = db_fetch_cell_prepared('SELECT graph_tree_id 3308 FROM graph_tree_items 3309 WHERE id = ?', 3310 array($leaf_id)); 3311 } 3312 } 3313 3314 if ($leaf_id > 0) { 3315 $leaf = db_fetch_row_prepared('SELECT host_id, title, graph_tree_id 3316 FROM graph_tree_items 3317 WHERE id = ?', 3318 array($leaf_id)); 3319 3320 if (cacti_sizeof($leaf)) { 3321 if ($leaf['host_id'] > 0) { 3322 $leaf_name = db_fetch_cell_prepared('SELECT description 3323 FROM host 3324 WHERE id = ?', 3325 array($leaf['host_id'])); 3326 } else { 3327 $leaf_name = $leaf['title']; 3328 } 3329 3330 $tree_name = db_fetch_cell_prepared('SELECT name 3331 FROM graph_tree 3332 WHERE id = ?', 3333 array($leaf['graph_tree_id'])); 3334 } else { 3335 $leaf_name = __('Leaf'); 3336 $tree_name = ''; 3337 } 3338 3339 if (isset_request_var('hgd') && get_nfilter_request_var('hgd') != '') { 3340 $parts = explode(':', get_nfilter_request_var('hgd')); 3341 input_validate_input_number($parts[1]); 3342 3343 if ($parts[0] == 'gt') { 3344 $leaf_sub = db_fetch_cell_prepared('SELECT name 3345 FROM graph_templates 3346 WHERE id = ?', 3347 array($parts[1])); 3348 } else { 3349 if ($parts[1] > 0) { 3350 $leaf_sub = db_fetch_cell_prepared('SELECT name 3351 FROM snmp_query 3352 WHERE id = ?', 3353 array($parts[1])); 3354 } else { 3355 $leaf_sub = __('Non Query Based'); 3356 } 3357 } 3358 } else { 3359 $leaf_sub = ''; 3360 } 3361 } else { 3362 $leaf_name = ''; 3363 $leaf_sub = ''; 3364 3365 if ($tree_id > 0) { 3366 $tree_name = db_fetch_cell_prepared('SELECT name 3367 FROM graph_tree 3368 WHERE id = ?', 3369 array($tree_id)); 3370 } else { 3371 $tree_name = ''; 3372 } 3373 } 3374 3375 $tree_title = $tree_name . ($leaf_name != '' ? ' (' . trim($leaf_name):'') . ($leaf_sub != '' ? ':' . trim($leaf_sub) . ')':($leaf_name != '' ? ')':'')); 3376 3377 if ($tree_title != '') { 3378 $current_nav .= "<li><a id='nav_title' href=#>" . html_escape($tree_title) . '</a></li>'; 3379 } 3380 } elseif (preg_match('#link.php\?id=(\d+)#', $_SERVER['REQUEST_URI'], $matches)) { 3381 $externalLinks = db_fetch_row_prepared('SELECT title, style FROM external_links WHERE id = ?', array($matches[1])); 3382 $title = $externalLinks['title']; 3383 $style = $externalLinks['style']; 3384 3385 if ($style == 'CONSOLE') { 3386 $current_nav = "<ul id='breadcrumbs'><li><a id='nav_0' href='" . $config['url_path'] . 3387 "index.php'>" . __('Console') . '</a>' . (get_selected_theme() == 'classic' ? ' -> ':'') . '</li>'; 3388 $current_nav .= "<li><a id='nav_1' href='#'>" . __('Link %s', html_escape($title)) . '</a></li>'; 3389 } else { 3390 $current_nav = "<ul id='breadcrumbs'><li><a id='nav_0'>" . html_escape($title) . '</a></li>'; 3391 } 3392 $tree_title = ''; 3393 } else { 3394 $tree_title = ''; 3395 } 3396 3397 if (isset($current_array['title'])) { 3398 $title .= html_escape(resolve_navigation_variables($current_array['title']) . ' ' . $tree_title); 3399 } 3400 3401 // keep a cache for each level we encounter 3402 $hasNavError = false; 3403 if (is_array($current_page)) { 3404 cacti_log('WARNING: Navigation item suppressed - current page is not a string: ' . var_export($current_page,true)); 3405 $hasNavError = true; 3406 } 3407 3408 if (is_array($current_action)) { 3409 cacti_log('WARNING: Navigation item suppressed - current action is not a string: '. var_export($current_action,true)); 3410 $hasNavError = true; 3411 } 3412 3413 if (is_array($current_array['level'])) { 3414 cacti_log('WARNING: Navigation item suppressed - current level is not a string: ' . var_export($current_array['level'],true)); 3415 $hasNavError = true; 3416 } 3417 3418 if (!$hasNavError) { 3419 $nav_level_cache[$current_array['level']] = array( 3420 'id' => $current_page . ':' . $current_action, 3421 'url' => get_browser_query_string() 3422 ); 3423 } 3424 $current_nav .= '</ul>'; 3425 3426 $_SESSION['sess_nav_level_cache'] = $nav_level_cache; 3427 3428 if ($type == 'url') { 3429 return $current_nav; 3430 } else { 3431 return $title; 3432 } 3433} 3434 3435/** 3436 * resolve_navigation_variables - substitute any variables contained in the navigation text 3437 * 3438 * @param $text - the text to substitute in 3439 * 3440 * @return - the original navigation text with all substitutions made 3441 */ 3442function resolve_navigation_variables($text) { 3443 $graphTitle = get_graph_title(get_filter_request_var('local_graph_id')); 3444 3445 if (preg_match_all("/\|([a-zA-Z0-9_]+)\|/", $text, $matches)) { 3446 for ($i=0; $i<cacti_count($matches[1]); $i++) { 3447 switch ($matches[1][$i]) { 3448 case 'current_graph_title': 3449 $text = str_replace('|' . $matches[1][$i] . '|', $graphTitle, $text); 3450 break; 3451 } 3452 } 3453 } 3454 3455 return $text; 3456} 3457 3458/** 3459 * get_associated_rras - returns a list of all RRAs referenced by a particular graph 3460 * 3461 * @param $local_graph_id - (int) the ID of the graph to retrieve a list of RRAs for 3462 * 3463 * @return - (array) an array containing the name and id of each RRA found 3464 */ 3465function get_associated_rras($local_graph_id, $sql_where = '') { 3466 return db_fetch_assoc_prepared('SELECT DISTINCT ' . SQL_NO_CACHE . " 3467 dspr.id, dsp.step, dspr.steps, dspr.rows, dspr.name, dtd.rrd_step, dspr.timespan 3468 FROM graph_templates_item AS gti 3469 LEFT JOIN data_template_rrd AS dtr 3470 ON gti.task_item_id=dtr.id 3471 LEFT JOIN data_template_data AS dtd 3472 ON dtr.local_data_id = dtd.local_data_id 3473 LEFT JOIN data_source_profiles AS dsp 3474 ON dtd.data_source_profile_id=dsp.id 3475 LEFT JOIN data_source_profiles_rra AS dspr 3476 ON dsp.id=dspr.data_source_profile_id 3477 AND dtd.local_data_id != 0 3478 WHERE gti.local_graph_id = ? 3479 $sql_where 3480 ORDER BY dspr.steps", 3481 array($local_graph_id) 3482 ); 3483} 3484 3485/** 3486 * get_nearest_timespan - returns the nearest defined timespan. Used for adding a default 3487 * graph timespan for data source profile rras. 3488 * 3489 * @param $timespan - (int) the timespan to fine a default for 3490 * 3491 * @return - (int) the timespan to apply for the data source profile rra value 3492 */ 3493function get_nearest_timespan($timespan) { 3494 global $timespans; 3495 3496 $last = end($timespans); 3497 3498 foreach($timespans as $index => $name) { 3499 if ($timespan > $index) { 3500 $last = $index; 3501 continue; 3502 } elseif ($timespan == $index) { 3503 return $index; 3504 } else { 3505 return $last; 3506 } 3507 } 3508 3509 return $last; 3510} 3511 3512/** 3513 * get_browser_query_string - returns the full url, including args requested by the browser 3514 * 3515 * @return - the url requested by the browser 3516 */ 3517function get_browser_query_string() { 3518 if (!empty($_SERVER['REQUEST_URI'])) { 3519 return sanitize_uri($_SERVER['REQUEST_URI']); 3520 } else { 3521 return sanitize_uri(get_current_page() . (empty($_SERVER['QUERY_STRING']) ? '' : '?' . $_SERVER['QUERY_STRING'])); 3522 } 3523} 3524 3525/** 3526 * get_current_page - returns the basename of the current page in a web server friendly way 3527 * 3528 * @return - the basename of the current script file 3529 */ 3530function get_current_page($basename = true) { 3531 if (isset($_SERVER['SCRIPT_NAME']) && $_SERVER['SCRIPT_NAME'] != '') { 3532 if ($basename) { 3533 return basename($_SERVER['SCRIPT_NAME']); 3534 } else { 3535 return $_SERVER['SCRIPT_NAME']; 3536 } 3537 } elseif (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] != '') { 3538 if ($basename) { 3539 return basename($_SERVER['SCRIPT_FILENAME']); 3540 } else { 3541 return $_SERVER['SCRIPT_FILENAME']; 3542 } 3543 } else { 3544 cacti_log('ERROR: unable to determine current_page'); 3545 } 3546 3547 return false; 3548} 3549 3550/** 3551 * get_hash_graph_template - returns the current unique hash for a graph template 3552 * 3553 * @param $graph_template_id - (int) the ID of the graph template to return a hash for 3554 * @param $sub_type (optional) return the hash for a particular subtype of this type 3555 * 3556 * @return - a 128-bit, hexadecimal hash 3557 */ 3558function get_hash_graph_template($graph_template_id, $sub_type = 'graph_template') { 3559 switch ($sub_type) { 3560 case 'graph_template': 3561 $hash = db_fetch_cell_prepared('SELECT hash FROM graph_templates WHERE id = ?', array($graph_template_id)); 3562 break; 3563 case 'graph_template_item': 3564 $hash = db_fetch_cell_prepared('SELECT hash FROM graph_templates_item WHERE id = ?', array($graph_template_id)); 3565 break; 3566 case 'graph_template_input': 3567 $hash = db_fetch_cell_prepared('SELECT hash FROM graph_template_input WHERE id = ?', array($graph_template_id)); 3568 break; 3569 default: 3570 return generate_hash(); 3571 break; 3572 } 3573 3574 if (preg_match('/[a-fA-F0-9]{32}/', $hash)) { 3575 return $hash; 3576 } else { 3577 return generate_hash(); 3578 } 3579} 3580 3581/** 3582 * get_hash_data_template - returns the current unique hash for a data template 3583 * 3584 * @param $graph_template_id - (int) the ID of the data template to return a hash for 3585 * @param $sub_type (optional) return the hash for a particular subtype of this type 3586 * 3587 * @return - a 128-bit, hexadecimal hash 3588 */ 3589function get_hash_data_template($data_template_id, $sub_type = 'data_template') { 3590 switch ($sub_type) { 3591 case 'data_template': 3592 $hash = db_fetch_cell_prepared('SELECT hash FROM data_template WHERE id = ?', array($data_template_id)); 3593 break; 3594 case 'data_template_item': 3595 $hash = db_fetch_cell_prepared('SELECT hash FROM data_template_rrd WHERE id = ?', array($data_template_id)); 3596 break; 3597 default: 3598 return generate_hash(); 3599 break; 3600 } 3601 3602 if (preg_match('/[a-fA-F0-9]{32}/', $hash)) { 3603 return $hash; 3604 } else { 3605 return generate_hash(); 3606 } 3607} 3608 3609/** 3610 * get_hash_data_input - returns the current unique hash for a data input method 3611 * 3612 * @param $graph_template_id - (int) the ID of the data input method to return a hash for 3613 * @param $sub_type (optional) return the hash for a particular subtype of this type 3614 * 3615 * @return - a 128-bit, hexadecimal hash 3616 */ 3617function get_hash_data_input($data_input_id, $sub_type = 'data_input_method') { 3618 switch ($sub_type) { 3619 case 'data_input_method': 3620 $hash = db_fetch_cell_prepared('SELECT hash FROM data_input WHERE id = ?', array($data_input_id)); 3621 break; 3622 case 'data_input_field': 3623 $hash = db_fetch_cell_prepared('SELECT hash FROM data_input_fields WHERE id = ?', array($data_input_id)); 3624 break; 3625 default: 3626 return generate_hash(); 3627 break; 3628 } 3629 3630 if (preg_match('/[a-fA-F0-9]{32}/', $hash)) { 3631 return $hash; 3632 } else { 3633 return generate_hash(); 3634 } 3635} 3636 3637/** 3638 * get_hash_cdef - returns the current unique hash for a cdef 3639 * 3640 * @param $graph_template_id - (int) the ID of the cdef to return a hash for 3641 * @param $sub_type (optional) return the hash for a particular subtype of this type 3642 * 3643 * @return - a 128-bit, hexadecimal hash 3644 */ 3645function get_hash_cdef($cdef_id, $sub_type = 'cdef') { 3646 if (!is_numeric($cdef_id)) { 3647 return generate_hash(); 3648 } 3649 3650 switch ($sub_type) { 3651 case 'cdef': 3652 $hash = db_fetch_cell_prepared('SELECT hash FROM cdef WHERE id = ?', array($cdef_id)); 3653 break; 3654 case 'cdef_item': 3655 $hash = db_fetch_cell_prepared('SELECT hash FROM cdef_items WHERE id = ?', array($cdef_id)); 3656 break; 3657 default: 3658 return generate_hash(); 3659 break; 3660 } 3661 3662 if (strlen($hash) == 32 && ctype_xdigit($hash)) { 3663 return $hash; 3664 } else { 3665 return generate_hash(); 3666 } 3667} 3668 3669/** 3670 * get_hash_gprint - returns the current unique hash for a gprint preset 3671 * 3672 * @param $graph_template_id - (int) the ID of the gprint preset to return a hash for 3673 * 3674 * @return - a 128-bit, hexadecimal hash 3675 */ 3676function get_hash_gprint($gprint_id) { 3677 $hash = db_fetch_cell_prepared('SELECT hash FROM graph_templates_gprint WHERE id = ?', array($gprint_id)); 3678 3679 if (strlen($hash) == 32 && ctype_xdigit($hash)) { 3680 return $hash; 3681 } else { 3682 return generate_hash(); 3683 } 3684} 3685 3686/** 3687 * returns the current unique hash for a vdef 3688 * 3689 * @param $graph_template_id - the ID of the vdef to return a hash for 3690 * @param $sub_type - return the hash for a particular subtype of this type 3691 * 3692 * @return - a 128-bit, hexadecimal hash 3693 */ 3694function get_hash_vdef($vdef_id, $sub_type = "vdef") { 3695 switch ($sub_type) { 3696 case 'vdef': 3697 $hash = db_fetch_cell_prepared('SELECT hash FROM vdef WHERE id = ?', array($vdef_id)); 3698 break; 3699 case 'vdef_item': 3700 $hash = db_fetch_cell_prepared('SELECT hash FROM vdef_items WHERE id = ?', array($vdef_id)); 3701 break; 3702 default: 3703 return generate_hash(); 3704 break; 3705 } 3706 3707 if (strlen($hash) == 32 && ctype_xdigit($hash)) { 3708 return $hash; 3709 } else { 3710 return generate_hash(); 3711 } 3712} 3713 3714/** 3715 * get_hash_data_source_profile - returns the current unique hash for a vdef 3716 * 3717 * @param $data_source_profile_id - the ID of the data_source_profile to return a hash for 3718 * 3719 * @return - a 128-bit, hexadecimal hash 3720 */ 3721function get_hash_data_source_profile($data_source_profile_id) { 3722 $hash = db_fetch_cell_prepared('SELECT hash FROM data_source_profiles WHERE id = ?', array($data_source_profile_id)); 3723 3724 if (strlen($hash) == 32 && ctype_xdigit($hash)) { 3725 return $hash; 3726 } else { 3727 return generate_hash(); 3728 } 3729} 3730 3731/** 3732 * get_hash_host_template - returns the current unique hash for a gprint preset 3733 * 3734 * @param $host_template_id - the ID of the host template to return a hash for 3735 * 3736 * @return - a 128-bit, hexadecimal hash 3737 */ 3738function get_hash_host_template($host_template_id) { 3739 $hash = db_fetch_cell_prepared('SELECT hash FROM host_template WHERE id = ?', array($host_template_id)); 3740 3741 if (strlen($hash) == 32 && ctype_xdigit($hash)) { 3742 return $hash; 3743 } else { 3744 return generate_hash(); 3745 } 3746} 3747 3748/** 3749 * get_hash_data_query - returns the current unique hash for a data query 3750 * 3751 * @param $graph_template_id - the ID of the data query to return a hash for 3752 * @param $sub_type return the hash for a particular subtype of this type 3753 * 3754 * @return - a 128-bit, hexadecimal hash 3755 */ 3756function get_hash_data_query($data_query_id, $sub_type = 'data_query') { 3757 switch ($sub_type) { 3758 case 'data_query': 3759 $hash = db_fetch_cell_prepared('SELECT hash FROM snmp_query WHERE id = ?', array($data_query_id)); 3760 break; 3761 case 'data_query_graph': 3762 $hash = db_fetch_cell_prepared('SELECT hash FROM snmp_query_graph WHERE id = ?', array($data_query_id)); 3763 break; 3764 case 'data_query_sv_data_source': 3765 $hash = db_fetch_cell_prepared('SELECT hash FROM snmp_query_graph_rrd_sv WHERE id = ?', array($data_query_id)); 3766 break; 3767 case 'data_query_sv_graph': 3768 $hash = db_fetch_cell_prepared('SELECT hash FROM snmp_query_graph_sv WHERE id = ?', array($data_query_id)); 3769 break; 3770 default: 3771 return generate_hash(); 3772 break; 3773 } 3774 3775 if (strlen($hash) == 32 && ctype_xdigit($hash)) { 3776 return $hash; 3777 } else { 3778 return generate_hash(); 3779 } 3780} 3781 3782/** 3783 * get_hash_version - returns the item type and cacti version in a hash format 3784 * 3785 * @param $type - the type of item to represent ('graph_template','data_template', 3786 * 'data_input_method','cdef','vdef','gprint_preset','data_query','host_template') 3787 * 3788 * @return - a 24-bit hexadecimal hash (8-bits for type, 16-bits for version) 3789 */ 3790function get_hash_version($type) { 3791 global $hash_type_codes, $cacti_version_codes, $config; 3792 3793 return $hash_type_codes[$type] . $cacti_version_codes[CACTI_VERSION]; 3794} 3795 3796/** 3797 * generate_hash - generates a new unique hash 3798 * 3799 * @return - a 128-bit, hexadecimal hash 3800 */ 3801function generate_hash() { 3802 return md5(session_id() . microtime() . rand(0,1000)); 3803} 3804 3805/** 3806 * debug_log_insert_section_start - creates a header item for breaking down the debug log 3807 * 3808 * @param $type - the 'category' or type of debug message 3809 * @param $text - section header 3810 */ 3811function debug_log_insert_section_start($type, $text, $allowcopy = false) { 3812 $copy_prefix = ''; 3813 $copy_dataid = ''; 3814 if ($allowcopy) { 3815 $uid = generate_hash(); 3816 $copy_prefix = '<div class=\'cactiTableButton debug\'><span><a class=\'linkCopyDark cactiTableCopy\' id=\'copyToClipboard' . $uid . '\'>' . __esc('Copy') . '</a></span></div>'; 3817 $copy_dataid = ' id=\'clipboardData'.$uid.'\''; 3818 $copy_headerid = ' id=\'clipboardHeader'.$uid.'\''; 3819 } 3820 3821 debug_log_insert($type, '<table class=\'cactiTable debug\'' . $copy_headerid . '><tr class=\'tableHeader\'><td>' . html_escape($text) . $copy_prefix . '</td></tr><tr><td style=\'padding:0px;\'><table style=\'display:none;\'' . $copy_dataid . '><tr><td><div style=\'font-family: monospace;\'>'); 3822} 3823 3824/** 3825 * debug_log_insert_section_end - finalizes the header started with the start function 3826 * 3827 * @param $type - the 'category' or type of debug message 3828 */ 3829function debug_log_insert_section_end($type) { 3830 debug_log_insert($type, "</div></td></tr></table></td></tr></td></table>"); 3831} 3832 3833/** 3834 * debug_log_insert - inserts a line of text into the debug log 3835 * 3836 * @param $type - the 'category' or type of debug message 3837 * @param $text - the actual debug message 3838 */ 3839function debug_log_insert($type, $text) { 3840 global $config; 3841 3842 if ($config['poller_id'] == 1 || isset($_SESSION)) { 3843 if (!isset($_SESSION['debug_log'][$type])) { 3844 $_SESSION['debug_log'][$type] = array(); 3845 } 3846 3847 array_push($_SESSION['debug_log'][$type], $text); 3848 } else { 3849 if (!isset($config['debug_log'][$type])) { 3850 $config['debug_log'][$type] = array(); 3851 } 3852 3853 array_push($config['debug_log'][$type], $text); 3854 } 3855} 3856 3857/** 3858 * debug_log_clear - clears the debug log for a particular category 3859 * 3860 * @param $type - the 'category' to clear the debug log for. omitting this argument 3861 * implies all categories 3862 */ 3863function debug_log_clear($type = '') { 3864 if ($type == '') { 3865 kill_session_var('debug_log'); 3866 } else { 3867 if (isset($_SESSION['debug_log'])) { 3868 unset($_SESSION['debug_log'][$type]); 3869 } 3870 } 3871} 3872 3873/** 3874 * debug_log_return - returns the debug log for a particular category 3875 * 3876 * @param $type - the 'category' to return the debug log for. 3877 * 3878 * @return - the full debug log for a particular category 3879 */ 3880function debug_log_return($type) { 3881 $log_text = ''; 3882 3883 if ($type == 'new_graphs') { 3884 if (isset($_SESSION['debug_log'][$type])) { 3885 $log_text .= "<table style='width:100%;'>"; 3886 for ($i=0; $i<cacti_count($_SESSION['debug_log'][$type]); $i++) { 3887 $log_text .= '<tr><td>' . $_SESSION['debug_log'][$type][$i] . '</td></tr>'; 3888 } 3889 $log_text .= '</table>'; 3890 } 3891 } else { 3892 if (isset($_SESSION['debug_log'][$type])) { 3893 $log_text .= "<table style='width:100%;'>"; 3894 foreach($_SESSION['debug_log'][$type] as $key => $val) { 3895 $log_text .= "<tr><td>$val</td></tr>\n"; 3896 unset($_SESSION['debug_log'][$type][$key]); 3897 } 3898 $log_text .= '</table>'; 3899 } 3900 } 3901 3902 return $log_text; 3903} 3904 3905/** 3906 * sanitize_search_string - cleans up a search string submitted by the user to be passed 3907 * to the database. NOTE: some of the code for this function came from the phpBB project. 3908 * 3909 * @param $string - the original raw search string 3910 * 3911 * @return - the sanitized search string 3912 */ 3913function sanitize_search_string($string) { 3914 static $drop_char_match = array('(',')','^', '$', '<', '>', '`', '\'', '"', '|', ',', '?', '+', '[', ']', '{', '}', '#', ';', '!', '=', '*'); 3915 static $drop_char_replace = array('','',' ', ' ', ' ', ' ', '', '', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '); 3916 3917 /* Replace line endings by a space */ 3918 $string = preg_replace('/[\n\r]/is', ' ', $string); 3919 3920 /* HTML entities like */ 3921 $string = preg_replace('/\b&[a-z]+;\b/', ' ', $string); 3922 3923 /* Remove URL's */ 3924 $string = preg_replace('/\b[a-z0-9]+:\/\/[a-z0-9\.\-]+(\/[a-z0-9\?\.%_\-\+=&\/]+)?/', ' ', $string); 3925 3926 /* Filter out strange characters like ^, $, &, change "it's" to "its" */ 3927 for($i = 0; $i < cacti_count($drop_char_match); $i++) { 3928 $string = str_replace($drop_char_match[$i], $drop_char_replace[$i], $string); 3929 } 3930 3931 return $string; 3932} 3933 3934/** 3935 * cleans up a URI, e.g. from REQUEST_URI and/or QUERY_STRING 3936 * in case of XSS attack, expect the result to be broken 3937 * we do NOT sanitize in a way, that attacks are converted to valid HTML 3938 * it is ok, when the result is broken but the application stays alive 3939 * 3940 * @param string $uri - the uri to be sanitized 3941 * 3942 * @return string - the sanitized uri 3943 */ 3944function sanitize_uri($uri) { 3945 static $drop_char_match = array('^', '$', '<', '>', '`', "'", '"', '|', '+', '[', ']', '{', '}', ';', '!', '(', ')'); 3946 static $drop_char_replace = array( '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); 3947 3948 if (strpos($uri, 'graph_view.php')) { 3949 if (!strpos($uri, 'action=')) { 3950 $uri = $uri . (strpos($uri, '?') ? '&':'?') . 'action=' . get_nfilter_request_var('action'); 3951 } 3952 } 3953 3954 return str_replace($drop_char_match, $drop_char_replace, strip_tags(urldecode($uri))); 3955} 3956 3957/** 3958 * Checks to see if a string is base64 encoded 3959 * 3960 * @param string $data - the string to be validated 3961 * 3962 * @return boolean - true is the string is base64 otherwise false 3963 */ 3964function is_base64_encoded($data) { 3965 // Perform a simple check first 3966 if (!preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $data)) { 3967 return false; 3968 } 3969 3970 // Now test with the built-in function 3971 $ndata = base64_decode($data, true); 3972 if ($ndata === false) { 3973 return false; 3974 } 3975 3976 // Do a re-encode test and compare 3977 if (base64_encode($ndata) != $data) { 3978 return false; 3979 } 3980 3981 return true; 3982} 3983 3984/** 3985 * cleans up a CDEF/VDEF string 3986 * the CDEF/VDEF must have passed all magic string replacements beforehand 3987 * 3988 * @param string $cdef - the CDEF/VDEF to be sanitized 3989 * 3990 * @return string - the sanitized CDEF/VDEF 3991 */ 3992function sanitize_cdef($cdef) { 3993 static $drop_char_match = array('^', '$', '<', '>', '`', '\'', '"', '|', '[', ']', '{', '}', ';', '!'); 3994 static $drop_char_replace = array( '', '', '', '', '', '', '', '', '', '', '', '', '', ''); 3995 3996 return str_replace($drop_char_match, $drop_char_replace, $cdef); 3997} 3998 3999/** 4000 * verifies all selected items are numeric to guard against injection 4001 * 4002 * @param array $items - an array of serialized items from a post 4003 * 4004 * @return array - the sanitized selected items array 4005 */ 4006function sanitize_unserialize_selected_items($items) { 4007 if ($items != '') { 4008 $unstripped = stripslashes($items); 4009 4010 // validate that sanitized string is correctly formatted 4011 if (preg_match('/^a:[0-9]+:{/', $unstripped) && !preg_match('/(^|;|{|})O:\+?[0-9]+:"/', $unstripped)) { 4012 $items = unserialize($unstripped); 4013 4014 if (is_array($items)) { 4015 foreach ($items as $item) { 4016 if (is_array($item)) { 4017 return false; 4018 } elseif (!is_numeric($item) && ($item != '')) { 4019 return false; 4020 } 4021 } 4022 } else { 4023 return false; 4024 } 4025 } else { 4026 return false; 4027 } 4028 } else { 4029 return false; 4030 } 4031 4032 return $items; 4033} 4034 4035function cacti_escapeshellcmd($string) { 4036 global $config; 4037 4038 if ($config['cacti_server_os'] == 'unix') { 4039 return escapeshellcmd($string); 4040 } else { 4041 $replacements = "#&;`|*?<>^()[]{}$\\"; 4042 4043 for ($i=0; $i < strlen($replacements); $i++) { 4044 $string = str_replace($replacements[$i], ' ', $string); 4045 } 4046 return $string; 4047 } 4048} 4049 4050 4051/** 4052 * mimics escapeshellarg, even for windows 4053 * 4054 * @param $string - the string to be escaped 4055 * @param $quote - true: do NOT remove quotes from result; false: do remove quotes 4056 * 4057 * @return - the escaped [quoted|unquoted] string 4058 */ 4059function cacti_escapeshellarg($string, $quote = true) { 4060 global $config; 4061 4062 if ($string == '') { 4063 return $string; 4064 } 4065 4066 /* we must use an apostrophe to escape community names under Unix in case the user uses 4067 characters that the shell might interpret. the ucd-snmp binaries on Windows flip out when 4068 you do this, but are perfectly happy with a quotation mark. */ 4069 if ($config['cacti_server_os'] == 'unix') { 4070 $string = escapeshellarg($string); 4071 if ($quote) { 4072 return $string; 4073 } else { 4074 # remove first and last char 4075 return substr($string, 1, (strlen($string)-2)); 4076 } 4077 } else { 4078 /* escapeshellarg takes care of different quotation for both linux and windows, 4079 * but unfortunately, it blanks out percent signs 4080 * we want to keep them, e.g. for GPRINT format strings 4081 * so we need to create our own escapeshellarg 4082 * on windows, command injection requires to close any open quotation first 4083 * so we have to escape any quotation here */ 4084 if (substr_count($string, CACTI_ESCAPE_CHARACTER)) { 4085 $string = str_replace(CACTI_ESCAPE_CHARACTER, "\\" . CACTI_ESCAPE_CHARACTER, $string); 4086 } 4087 4088 /* ... before we add our own quotation */ 4089 if ($quote) { 4090 return CACTI_ESCAPE_CHARACTER . $string . CACTI_ESCAPE_CHARACTER; 4091 } else { 4092 return $string; 4093 } 4094 } 4095} 4096 4097/** 4098 * set a page refresh in Cacti through a callback 4099 * 4100 * @param $refresh - an array containing the page, seconds, and logout 4101 * 4102 * @return - nill 4103 */ 4104function set_page_refresh($refresh) { 4105 if (isset($refresh['seconds'])) { 4106 $_SESSION['refresh']['seconds'] = $refresh['seconds']; 4107 } 4108 4109 if (isset($refresh['logout'])) { 4110 if ($refresh['logout'] == 'true' || $refresh['logout'] === true) { 4111 $_SESSION['refresh']['logout'] = 'true'; 4112 } else { 4113 $_SESSION['refresh']['logout'] = 'false'; 4114 } 4115 } else { 4116 $_SESSION['refresh']['logout'] = 'true'; 4117 } 4118 4119 if (isset($refresh['page'])) { 4120 $_SESSION['refresh']['page'] = $refresh['page']; 4121 } 4122} 4123 4124function bottom_footer() { 4125 global $config, $no_session_write; 4126 4127 include_once($config['base_path'] . '/include/global_session.php'); 4128 4129 if (!isset_request_var('header') || get_nfilter_request_var('header') == 'true') { 4130 include_once($config['base_path'] . '/include/bottom_footer.php'); 4131 } 4132 4133 /* we use this session var to store field values for when a save fails, 4134 this way we can restore the field's previous values. we reset it here, because 4135 they only need to be stored for a single page 4136 */ 4137 kill_session_var('sess_field_values'); 4138 4139 /* make sure the debug log doesn't get too big */ 4140 debug_log_clear(); 4141 4142 /* close the session */ 4143 if (array_search(get_current_page(), $no_session_write) === false) { 4144 cacti_session_close(); 4145 } 4146 4147 /* close the database connection */ 4148 db_close(); 4149} 4150 4151function top_header() { 4152 global $config; 4153 4154 if (!isset_request_var('header') || get_nfilter_request_var('header') == 'true') { 4155 include_once($config['base_path'] . '/include/top_header.php'); 4156 } 4157} 4158 4159function top_graph_header() { 4160 global $config; 4161 if (!isset_request_var('header') || get_nfilter_request_var('header') == 'true') { 4162 include_once($config['base_path'] . '/include/top_graph_header.php'); 4163 } 4164} 4165 4166function general_header() { 4167 global $config; 4168 if (!isset_request_var('header') || get_nfilter_request_var('header') == 'true') { 4169 include_once($config['base_path'] . '/include/top_general_header.php'); 4170 } 4171} 4172 4173function appendHeaderSuppression($url) { 4174 if (strpos($url, 'header=false') < 0) { 4175 return $url . (strpos($url, '?') ? '&':'?') . 'header=false'; 4176 } 4177 4178 return $url; 4179} 4180 4181function admin_email($subject, $message) { 4182 if (read_config_option('admin_user')) { 4183 if (read_config_option('notify_admin')) { 4184 $admin_details = db_fetch_row_prepared('SELECT full_name, email_address 4185 FROM user_auth 4186 WHERE id = ?', 4187 array(read_config_option('admin_user'))); 4188 4189 if (cacti_sizeof($admin_details)) { 4190 $email = read_config_option('settings_from_email'); 4191 $name = read_config_option('settings_from_name'); 4192 4193 if ($name != '') { 4194 $from = '"' . $name . '" <' . $email . '>'; 4195 } else { 4196 $from = $email; 4197 } 4198 4199 if ($admin_details['email_address'] != '') { 4200 if ($admin_details['full_name'] != '') { 4201 $to = '"' . $admin_details['full_name'] . '" <' . $admin_details['email_address'] . '>'; 4202 } else { 4203 $to = $admin_details['email_address']; 4204 } 4205 4206 send_mail($to, $from, $subject, $message, '', '', true); 4207 } else { 4208 cacti_log('WARNING: Primary Admin account does not have an email address! Unable to send administrative Email.', false, 'SYSTEM'); 4209 } 4210 } else { 4211 cacti_log('WARNING: Primary Admin account set to an invalid user! Unable to send administrative Email.', false, 'SYSTEM'); 4212 } 4213 } else { 4214 cacti_log('WARNING: Primary Admin account notifications disabled! Unable to send administrative Email.', false, 'SYSTEM'); 4215 } 4216 } else { 4217 cacti_log('WARNING: Primary Admin account not set! Unable to send administrative Email.', false, 'SYSTEM'); 4218 } 4219} 4220 4221function send_mail($to, $from, $subject, $body, $attachments = '', $headers = '', $html = false) { 4222 $fromname = ''; 4223 if (is_array($from)) { 4224 $fromname = $from[1]; 4225 $from = $from[0]; 4226 } 4227 4228 if ($from == '') { 4229 $from = read_config_option('settings_from_email'); 4230 $fromname = read_config_option('settings_from_name'); 4231 } elseif ($fromname == '') { 4232 $full_name = db_fetch_cell_prepared('SELECT full_name 4233 FROM user_auth 4234 WHERE email_address = ?', 4235 array($from)); 4236 4237 if (empty($full_name)) { 4238 $fromname = $from; 4239 } else { 4240 $fromname = $full_name; 4241 } 4242 } 4243 4244 $from = array(0 => $from, 1 => $fromname); 4245 return mailer($from, $to, '', '', '', $subject, $body, '', $attachments, $headers, $html); 4246} 4247 4248/** 4249 * mailer - function to send mails to users 4250 * 4251 * @param $from - single contact (see below) 4252 * @param $to - single or multiple contacts (see below) 4253 * @param $cc - none, single or multiple contacts (see below) 4254 * @param $bcc - none, single or multiple contacts (see below) 4255 * @param $replyto - none, single or multiple contacts (see below) 4256 * note that this value is used when hitting reply (overriding the default of using from) 4257 * @param $subject - the email subject 4258 * @param $body - the email body, in HTML format. If content_text is not set, the function will attempt to extract 4259 * from the HTML format. 4260 * @param $body_text - the email body in TEXT format. If set, it will override the stripping tags method 4261 * @param $attachments - the emails attachments as an array 4262 * @param $headers - an array of name value pairs representing custom headers. 4263 * @param $html - if set to true, html is the default, otherwise text format will be used 4264 * 4265 * For contact parameters, they can accept arrays containing zero or more values in the forms of: 4266 * 'email@email.com,email2@email.com,email3@email.com' 4267 * array('email1@email.com' => 'My email', 'email2@email.com' => 'Your email', 'email3@email.com' => 'Whose email') 4268 * array(array('email' => 'email1@email.com', 'name' => 'My email'), array('email' => 'email2@email.com', 4269 * 'name' => 'Your email'), array('email' => 'email3@email.com', 'name' => 'Whose email')) 4270 * 4271 * The $from field will only use the first contact specified. If no contact is provided for $replyto 4272 * then $from is used for that too. If $from is empty, it will default to cacti@<server> or if no server name can 4273 * be found, it will use cacti@cacti.net 4274 * 4275 * The $attachments parameter may either be a single string, or a list of attachments 4276 * either as strings or an array. The array can have the following keys: 4277 * 4278 * filename : name of the file to attach (display name for graphs) 4279 * display : displayed name of the attachment 4280 * mime_type : MIME type to be set against the attachment. If blank or missing mailer will attempt to auto detect 4281 * attachment : String containing attachment for image-based attachments (<GRAPH> or <GRAPH:#> activates graph mode 4282 * and requires $body parameter is HTML containing one of those values) 4283 * inline : Whether to attach 'inline' (default for graph mode) or as 'attachment' (default for all others) 4284 * encoding : Encoding type, normally base64 4285 */ 4286function mailer($from, $to, $cc, $bcc, $replyto, $subject, $body, $body_text = '', $attachments = '', $headers = '', $html = true) { 4287 global $config, $cacti_locale, $mail_methods; 4288 4289 require_once($config['include_path'] . '/vendor/phpmailer/src/Exception.php'); 4290 require_once($config['include_path'] . '/vendor/phpmailer/src/PHPMailer.php'); 4291 require_once($config['include_path'] . '/vendor/phpmailer/src/SMTP.php'); 4292 4293 $start_time = microtime(true); 4294 4295 // Create the PHPMailer instance 4296 $mail = new PHPMailer\PHPMailer\PHPMailer; 4297 4298 // Set a reasonable timeout of 5 seconds 4299 $timeout = read_config_option('settings_smtp_timeout'); 4300 if (empty($timeout) || $timeout < 0 || $timeout > 300) { 4301 $mail->Timeout = 5; 4302 } else { 4303 $mail->Timeout = $timeout; 4304 } 4305 4306 $langparts = explode('-', $cacti_locale); 4307 if (file_exists($config['include_path'] . '/vendor/phpmailer/language/phpmailer.lang-' . $langparts[0] . '.php')) { 4308 $mail->setLanguage($langparts[0], $config['include_path'] . '/vendor/phpmailer/language/'); 4309 } 4310 4311 $how = read_config_option('settings_how'); 4312 if ($how < 0 || $how > 2) { 4313 $how = 0; 4314 } 4315 4316 if ($how == 0) { 4317 $mail->isMail(); 4318 } elseif ($how == 1) { 4319 $mail->Sendmail = read_config_option('settings_sendmail_path'); 4320 $mail->isSendmail(); 4321 } elseif ($how == 2) { 4322 $mail->isSMTP(); 4323 $mail->Host = read_config_option('settings_smtp_host'); 4324 $mail->Port = read_config_option('settings_smtp_port'); 4325 4326 if (read_config_option('settings_smtp_username') != '') { 4327 $mail->SMTPAuth = true; 4328 $mail->Username = read_config_option('settings_smtp_username'); 4329 4330 if (read_config_option('settings_smtp_password') != '') { 4331 $mail->Password = read_config_option('settings_smtp_password'); 4332 } 4333 } else { 4334 $mail->SMTPAuth = false; 4335 } 4336 4337 $secure = read_config_option('settings_smtp_secure'); 4338 if (!empty($secure) && $secure != 'none') { 4339 $mail->SMTPSecure = true; 4340 if (substr_count($mail->Host, ':') == 0) { 4341 $mail->Host = $secure . '://' . $mail->Host; 4342 } 4343 } else { 4344 $mail->SMTPAutoTLS = false; 4345 $mail->SMTPSecure = false; 4346 } 4347 } 4348 4349 /* perform data substitution */ 4350 if (strpos($subject, '|date_time|') !== false) { 4351 $date = read_config_option('date'); 4352 if (!empty($date)) { 4353 $time = strtotime($date); 4354 } else { 4355 $time = time(); 4356 } 4357 4358 $subject = str_replace('|date_time|', date(CACTI_DATE_TIME_FORMAT, $time), $subject); 4359 } 4360 4361 /* 4362 * Set the from details using the variable passed in 4363 * - if name is blank, use setting's name 4364 * - if email is blank, use setting's email, otherwise default to 4365 * cacti@<server> or cacti@cacti.net if no known server name 4366 */ 4367 $from = parse_email_details($from, 1); 4368 4369 // from name was empty, use value in settings 4370 if (empty($from['name'])) { 4371 $from['name'] = read_config_option('settings_from_name'); 4372 } 4373 4374 // from email was empty, use email in settings 4375 if (empty($from['email'])) { 4376 $from['email'] = read_config_option('settings_from_email'); 4377 } 4378 4379 if (empty($from['email'])) { 4380 if (isset($_SERVER['HOSTNAME'])) { 4381 $from['email'] = 'Cacti@' . $_SERVER['HOSTNAME']; 4382 } else { 4383 $from['email'] = 'Cacti@cacti.net'; 4384 } 4385 4386 if (empty($from['name'])) { 4387 $from['name'] = 'Cacti'; 4388 } 4389 } 4390 4391 // Sanity test the from email 4392 if (!filter_var($from['email'], FILTER_VALIDATE_EMAIL)) { 4393 return 'Bad email address format. Invalid from email address ' . $from['email']; 4394 } 4395 4396 $fromText = add_email_details(array($from), $result, array($mail, 'setFrom')); 4397 4398 if ($result == false) { 4399 return record_mailer_error($fromText, $mail->ErrorInfo); 4400 } 4401 4402 // Convert $to variable to proper array structure 4403 $to = parse_email_details($to); 4404 $toText = add_email_details($to, $result, array($mail, 'addAddress')); 4405 4406 if ($result == false) { 4407 return record_mailer_error($toText, $mail->ErrorInfo); 4408 } 4409 4410 $cc = parse_email_details($cc); 4411 $ccText = add_email_details($cc, $result, array($mail, 'addCC')); 4412 4413 if ($result == false) { 4414 return record_mailer_error($ccText, $mail->ErrorInfo); 4415 } 4416 4417 $bcc = parse_email_details($bcc); 4418 $bccText = add_email_details($bcc, $result, array($mail, 'addBCC')); 4419 4420 if ($result == false) { 4421 return record_mailer_error($bccText, $mail->ErrorInfo); 4422 } 4423 4424 // This is a failsafe, should never happen now 4425 if (!(cacti_sizeof($to) || cacti_sizeof($cc) || cacti_sizeof($bcc))) { 4426 cacti_log('ERROR: No recipient address set!!', false, 'MAILER'); 4427 cacti_debug_backtrace('MAILER ERROR'); 4428 4429 return __('Mailer Error: No recipient address set!!<br>If using the <i>Test Mail</i> link, please set the <b>Alert e-mail</b> setting.'); 4430 } 4431 4432 $replyto = parse_email_details($replyto); 4433 $replyText = add_email_details($replyto, $result, array($mail, 'addReplyTo')); 4434 4435 if ($result == false) { 4436 return record_mailer_error($replyText, $mail->ErrorInfo); 4437 } 4438 4439 $body = str_replace('<SUBJECT>', $subject, $body); 4440 $body = str_replace('<TO>', $toText, $body); 4441 $body = str_replace('<CC>', $ccText, $body); 4442 $body = str_replace('<FROM>', $fromText, $body); 4443 $body = str_replace('<REPLYTO>', $replyText, $body); 4444 4445 $body_text = str_replace('<SUBJECT>', $subject, $body_text); 4446 $body_text = str_replace('<TO>', $toText, $body_text); 4447 $body_text = str_replace('<CC>', $ccText, $body_text); 4448 $body_text = str_replace('<FROM>', $fromText, $body_text); 4449 $body_text = str_replace('<REPLYTO>', $replyText, $body_text); 4450 4451 // Set the subject 4452 $mail->Subject = $subject; 4453 4454 // Support i18n 4455 $mail->CharSet = 'UTF-8'; 4456 $mail->Encoding = 'base64'; 4457 4458 // Set the wordwrap limits 4459 $wordwrap = read_config_option('settings_wordwrap'); 4460 if ($wordwrap == '') { 4461 $wordwrap = 76; 4462 } elseif ($wordwrap > 9999) { 4463 $wordwrap = 9999; 4464 } elseif ($wordwrap < 0) { 4465 $wordwrap = 76; 4466 } 4467 4468 $mail->WordWrap = $wordwrap; 4469 $mail->setWordWrap(); 4470 4471 if (!$html) { 4472 $mail->ContentType = 'text/plain'; 4473 } else { 4474 $mail->ContentType = 'text/html'; 4475 } 4476 4477 $i = 0; 4478 4479 // Handle Graph Attachments 4480 if (!empty($attachments) && !is_array($attachments)) { 4481 $attachments = array('attachment' => $attachments); 4482 } 4483 4484 if (is_array($attachments) && cacti_sizeof($attachments)) { 4485 $graph_mode = (substr_count($body, '<GRAPH>') > 0); 4486 $graph_ids = (substr_count($body, '<GRAPH:') > 0); 4487 4488 $default_opts = array( 4489 // MIME type to be set against the attachment 4490 'mime_type' => '', 4491 // Display name of the attachment 4492 'filename' => '', 4493 // String containing attachment for image-based attachments 4494 'attachment' => '', 4495 // Whether to attach inline or as attachment 4496 'inline' => ($graph_mode || $graph_ids) ? 'inline' : 'attachment', 4497 // Encoding type, normally base64 4498 'encoding' => 'base64', 4499 ); 4500 4501 foreach($attachments as $attachment) { 4502 if (!is_array($attachment)) { 4503 $attachment = array('attachment' => $attachment); 4504 } 4505 4506 foreach ($default_opts as $opt_name => $opt_default) { 4507 if (!array_key_exists($opt_name, $attachment)) { 4508 $attachment[$opt_name] = $opt_default; 4509 } 4510 } 4511 4512 if (!empty($attachment['attachment'])) { 4513 /* get content id and create attachment */ 4514 $cid = getmypid() . '_' . $i . '@' . 'localhost'; 4515 4516 if (empty($attachment['filename']) && file_exists($attachment['attachment'])) { 4517 $attachment['filename'] = $attachment['attachment']; 4518 } 4519 4520 /* attempt to attach */ 4521 if (!($graph_mode || $graph_ids)) { 4522 if (!empty($attachment['attachment']) && @file_exists($attachment['attachment'])) { 4523 $result = $mail->addAttachment($attachment['attachment'], $attachment['filename'], $attachment['encoding'], $attachment['mime_type'], $attachment['inline']); 4524 } else { 4525 $result = $mail->addStringAttachment($attachment['attachment'], $attachment['filename'], 'base64', $attachment['mime_type'], $attachment['inline']); 4526 } 4527 } else { 4528 if (!empty($attachment['attachment']) && @file_exists($attachment['attachment'])) { 4529 $result = $mail->addEmbeddedImage($attachment['attachment'], $cid, $attachment['filename'], $attachment['encoding'], $attachment['mime_type'], $attachment['inline']); 4530 } else { 4531 $result = $mail->addStringEmbeddedImage($attachment['attachment'], $cid, $attachment['filename'], 'base64', $attachment['mime_type'], $attachment['inline']); 4532 } 4533 } 4534 4535 if ($result == false) { 4536 cacti_log('ERROR: ' . $mail->ErrorInfo, false, 'MAILER'); 4537 return $mail->ErrorInfo; 4538 } 4539 4540 $i++; 4541 if ($graph_mode) { 4542 $body = str_replace('<GRAPH>', "<br><br><img src='cid:$cid'>", $body); 4543 } elseif ($graph_ids) { 4544 /* handle the body text */ 4545 switch ($attachment['inline']) { 4546 case 'inline': 4547 $body = str_replace('<GRAPH:' . $attachment['local_graph_id'] . ':' . $attachment['timespan'] . '>', "<img src='cid:$cid' >", $body); 4548 break; 4549 case 'attachment': 4550 $body = str_replace('<GRAPH:' . $attachment['local_graph_id'] . ':' . $attachment['timespan'] . '>', '', $body); 4551 break; 4552 } 4553 } 4554 } 4555 } 4556 } 4557 4558 /* process custom headers */ 4559 if (is_array($headers) && cacti_sizeof($headers)) { 4560 foreach($headers as $name => $value) { 4561 $mail->addCustomHeader($name, $value); 4562 } 4563 } 4564 4565 // Set both html and non-html bodies 4566 $brs = array('<br>', '<br />', '</br>'); 4567 if ($html) { 4568 $body = $body . '<br>'; 4569 } 4570 4571 if ($body_text == '') { 4572 $body_text = strip_tags(str_ireplace($brs, "\n", $body)); 4573 } 4574 4575 $mail->isHTML($html); 4576 $mail->Body = ($html ? $body : $body_text); 4577 if ($html && $body_text != '') { 4578 $mail->AltBody = $body_text; 4579 } 4580 4581 $result = $mail->send(); 4582 $error = $mail->ErrorInfo; //$result ? '' : $mail->ErrorInfo; 4583 $method = $mail_methods[intval(read_config_option('settings_how'))]; 4584 $rtype = $result ? 'INFO' : 'WARNING'; 4585 $rmsg = $result ? 'successfully sent' : 'failed'; 4586 $end_time = microtime(true); 4587 4588 if ($error != '') { 4589 $message = sprintf("%s: Mail %s via %s from '%s', to '%s', cc '%s', and took %2.2f seconds, Subject '%s'%s", 4590 $rtype, $rmsg, $method, $fromText, $toText, $ccText, ($end_time - $start_time), $subject, 4591 ", Error: $error"); 4592 } else { 4593 $message = sprintf("%s: Mail %s via %s from '%s', to '%s', cc '%s', and took %2.2f seconds, Subject '%s'", 4594 $rtype, $rmsg, $method, $fromText, $toText, $ccText, ($end_time - $start_time), $subject); 4595 } 4596 4597 cacti_log($message, false, 'MAILER'); 4598 if ($result == false) { 4599 cacti_log(cacti_debug_backtrace($rtype), false, 'MAILER'); 4600 } 4601 4602 return $error; 4603} 4604 4605function record_mailer_error($retError, $mailError) { 4606 $errorInfo = empty($retError) ? $mailError : $retError; 4607 cacti_log('ERROR: ' . $errorInfo, false, 'CMDPHP MAILER'); 4608 cacti_debug_backtrace('MAILER ERROR'); 4609 return $errorInfo; 4610} 4611 4612function add_email_details($emails, &$result, callable $addFunc) { 4613 $arrText = array(); 4614 4615 foreach ($emails as $e) { 4616 if (!empty($e['email'])) { 4617 //if (is_callable($addFunc)) { 4618 if (!empty($addFunc)) { 4619 $result = $addFunc($e['email'], $e['name']); 4620 if (!$result) { 4621 return ''; 4622 } 4623 } 4624 4625 $arrText[] = create_emailtext($e); 4626 } elseif (!empty($e['name'])) { 4627 $result = false; 4628 return 'Bad email format, name but no address: ' . $e['name']; 4629 } 4630 } 4631 4632 $text = implode(',', $arrText); 4633 //print "add_email_sw_details(): $text\n"; 4634 return $text; 4635} 4636 4637function parse_email_details($emails, $max_records = 0, $details = array()) { 4638 if (!is_array($emails)) { 4639 $emails = array($emails); 4640 } 4641 4642 $update = array(); 4643 foreach ($emails as $check_email) { 4644 if (!empty($check_email)) { 4645 if (!is_array($check_email)) { 4646 $emails = explode(',', $check_email); 4647 4648 foreach($emails as $email) { 4649 $email_array = split_emaildetail($email); 4650 $details[] = $email_array; 4651 } 4652 } else { 4653 $has_name = array_key_exists('name', $check_email); 4654 $has_email = array_key_exists('email', $check_email); 4655 4656 if ($has_name || $has_email) { 4657 $name = $has_name ? $check_email['name'] : ''; 4658 $email = $has_email ? $check_email['email'] : ''; 4659 } else { 4660 $name = array_key_exists(1, $check_email) ? $check_email[1] : ''; 4661 $email = array_key_exists(0, $check_email) ? $check_email[0] : ''; 4662 } 4663 4664 $details[] = array('name' => trim($name), 'email' => trim($email)); 4665 } 4666 } 4667 } 4668 4669 if ($max_records == 1) { 4670 $results = count($details) ? $details[0] : array(); 4671 } elseif ($max_records != 0 && $max_records < count($details)) { 4672 $results = array(); 4673 foreach ($details as $d) { 4674 $results[] = $d; 4675 $max_records--; 4676 if ($max_records == 0) { 4677 break; 4678 } 4679 } 4680 } else { 4681 $results = $details; 4682 } 4683 4684 return $results; 4685} 4686 4687function split_emaildetail($email) { 4688 $rname = ''; 4689 $rmail = ''; 4690 4691 if (!is_array($email)) { 4692 $email = trim($email); 4693 4694 $sPattern = '/(?:"?([^"]*)"?\s)?(?:<?(.+@[^>]+)>?)/i'; 4695 preg_match($sPattern, $email, $aMatch); 4696 4697 if (isset($aMatch[1])) { 4698 $rname = trim($aMatch[1]); 4699 } 4700 4701 if (isset($aMatch[2])) { 4702 $rmail = trim($aMatch[2]); 4703 } 4704 } else { 4705 $rmail = $email[0]; 4706 $rname = $email[1]; 4707 } 4708 4709 return array('name' => $rname, 'email' => $rmail); 4710} 4711 4712function create_emailtext($e) { 4713 if (empty($e['email'])) { 4714 $text = ''; 4715 } else { 4716 if (empty($e['name'])) { 4717 $text = $e['email']; 4718 } else { 4719 $text = $e['name'] . ' <' . $e['email'] . '>'; 4720 } 4721 } 4722 4723 return $text; 4724} 4725 4726function ping_mail_server($host, $port, $user, $password, $timeout = 10, $secure = 'none') { 4727 global $config; 4728 4729 require_once($config['include_path'] . '/vendor/phpmailer/src/Exception.php'); 4730 require_once($config['include_path'] . '/vendor/phpmailer/src/PHPMailer.php'); 4731 require_once($config['include_path'] . '/vendor/phpmailer/src/SMTP.php'); 4732 4733 //Create a new SMTP instance 4734 $smtp = new PHPMailer\PHPMailer\SMTP; 4735 4736 if (!empty($secure) && $secure != 'none') { 4737 $smtp->SMTPSecure = $secure; 4738 if (substr_count($host, ':') == 0) { 4739 $host = $secure . '://' . $host; 4740 } 4741 } else { 4742 $smtp->SMTPAutoTLS = false; 4743 $smtp->SMTPSecure = false; 4744 } 4745 4746 //Enable connection-level debug output 4747 $smtp->do_debug = 0; 4748 //$smtp->do_debug = SMTP::DEBUG_LOWLEVEL; 4749 4750 $results = true; 4751 try { 4752 //Connect to an SMTP server 4753 if ($smtp->connect($host, $port, $timeout)) { 4754 //Say hello 4755 if ($smtp->hello(gethostbyname(gethostname()))) { //Put your host name in here 4756 //Authenticate 4757 if ($user != '') { 4758 if ($smtp->authenticate($user, $password)) { 4759 $results = true; 4760 } else { 4761 throw new Exception(__('Authentication failed: %s', $smtp->getLastReply())); 4762 } 4763 } 4764 } else { 4765 throw new Exception(__('HELO failed: %s', $smtp->getLastReply())); 4766 } 4767 } else { 4768 throw new Exception(__('Connect failed: %s', $smtp->getLastReply())); 4769 } 4770 } catch (Exception $e) { 4771 $results = __('SMTP error: ') . $e->getMessage(); 4772 cacti_log($results); 4773 } 4774 4775 //Whatever happened, close the connection. 4776 $smtp->quit(true); 4777 4778 return $results; 4779} 4780 4781function email_test() { 4782 global $config; 4783 4784 $message = __('This is a test message generated from Cacti. This message was sent to test the configuration of your Mail Settings.') . '<br><br>'; 4785 $message .= __('Your email settings are currently set as follows') . '<br><br>'; 4786 $message .= '<b>' . __('Method') . '</b>: '; 4787 4788 print __('Checking Configuration...<br>'); 4789 4790 $ping_results = true; 4791 $how = read_config_option('settings_how'); 4792 if ($how < 0 || $how > 2) 4793 $how = 0; 4794 if ($how == 0) { 4795 $mail = __('PHP\'s Mailer Class'); 4796 } elseif ($how == 1) { 4797 $mail = __('Sendmail') . '<br><b>' . __('Sendmail Path'). '</b>: '; 4798 $sendmail = read_config_option('settings_sendmail_path'); 4799 $mail .= $sendmail; 4800 } elseif ($how == 2) { 4801 print __('Method: SMTP') . '<br>'; 4802 $mail = __('SMTP') . '<br>'; 4803 $smtp_host = read_config_option('settings_smtp_host'); 4804 $smtp_port = read_config_option('settings_smtp_port'); 4805 $smtp_username = read_config_option('settings_smtp_username'); 4806 $smtp_password = read_config_option('settings_smtp_password'); 4807 $smtp_secure = read_config_option('settings_smtp_secure'); 4808 $smtp_timeout = read_config_option('settings_smtp_timeout'); 4809 4810 $mail .= "<b>" . __('Device') . "</b>: $smtp_host<br>"; 4811 $mail .= "<b>" . __('Port') . "</b>: $smtp_port<br>"; 4812 4813 if ($smtp_username != '' && $smtp_password != '') { 4814 $mail .= '<b>' . __('Authentication') . '</b>: true<br>'; 4815 $mail .= '<b>' . __('Username') . "</b>: $smtp_username<br>"; 4816 $mail .= '<b>' . __('Password') . '</b>: (' . __('Not Shown for Security Reasons') . ')<br>'; 4817 $mail .= '<b>' . __('Security') . "</b>: $smtp_secure<br>"; 4818 } else { 4819 $mail .= '<b>' . __('Authentication') . '</b>: false<br>'; 4820 } 4821 4822 if (read_config_option('settings_ping_mail') == 0) { 4823 $ping_results = ping_mail_server($smtp_host, $smtp_port, $smtp_username, $smtp_password, $smtp_timeout, $smtp_secure); 4824 4825 print __('Ping Results:') . ' ' . ($ping_results == 1 ? __('Success'):$ping_results) . '<br>'; 4826 4827 if ($ping_results != 1) { 4828 $mail .= '<b>' . __('Ping Results') . '</b>: ' . $ping_results . '<br>'; 4829 } else { 4830 $mail .= '<b>' . __('Ping Results') . '</b>: ' . __('Success') . '<br>'; 4831 } 4832 } else { 4833 $ping_results = 1; 4834 $mail .= '<b>' . __('Ping Results') . '</b>: ' . __('Bypassed') . '<br>'; 4835 } 4836 } 4837 $message .= $mail; 4838 $message .= '<br>'; 4839 4840 $errors = ''; 4841 if ($ping_results == 1) { 4842 print __('Creating Message Text...') . '<br><br>'; 4843 print "<center><table><tr><td>"; 4844 print "<table style='width:100%;'><tr><td>$message</td><tr></table></table></center><br>"; 4845 print __('Sending Message...') . '<br><br>'; 4846 4847 $global_alert_address = read_config_option('settings_test_email'); 4848 4849 $errors = send_mail($global_alert_address, '', __('Cacti Test Message'), $message, '', '', true); 4850 if ($errors == '') { 4851 $errors = __('Success!'); 4852 } 4853 } else { 4854 print __('Message Not Sent due to ping failure.'). '<br><br>'; 4855 } 4856 4857 print "<center><table><tr><td>"; 4858 print "<table><tr><td>$errors</td><tr></table></table></center>"; 4859} 4860 4861/** 4862 * gethostbyaddr_wtimeout - This function provides a good method of performing 4863 * a rapid lookup of a DNS entry for a host so long as you don't have to look far. 4864 */ 4865function get_dns_from_ip ($ip, $dns, $timeout = 1000) { 4866 /* random transaction number (for routers etc to get the reply back) */ 4867 $data = rand(10, 99); 4868 4869 /* trim it to 2 bytes */ 4870 $data = substr($data, 0, 2); 4871 4872 /* create request header */ 4873 $data .= "\1\0\0\1\0\0\0\0\0\0"; 4874 4875 /* split IP into octets */ 4876 $octets = explode('.', $ip); 4877 4878 /* perform a quick error check */ 4879 if (cacti_count($octets) != 4) return 'ERROR'; 4880 4881 /* needs a byte to indicate the length of each segment of the request */ 4882 for ($x=3; $x>=0; $x--) { 4883 switch (strlen($octets[$x])) { 4884 case 1: // 1 byte long segment 4885 $data .= "\1"; break; 4886 case 2: // 2 byte long segment 4887 $data .= "\2"; break; 4888 case 3: // 3 byte long segment 4889 $data .= "\3"; break; 4890 default: // segment is too big, invalid IP 4891 return 'ERROR'; 4892 } 4893 4894 /* and the segment itself */ 4895 $data .= $octets[$x]; 4896 } 4897 4898 /* and the final bit of the request */ 4899 $data .= "\7in-addr\4arpa\0\0\x0C\0\1"; 4900 4901 /* create UDP socket */ 4902 $handle = @fsockopen("udp://$dns", 53); 4903 4904 @stream_set_timeout($handle, floor($timeout/1000), ($timeout*1000)%1000000); 4905 @stream_set_blocking($handle, 1); 4906 4907 /* send our request (and store request size so we can cheat later) */ 4908 $requestsize = @fwrite($handle, $data); 4909 4910 /* get the response */ 4911 $response = @fread($handle, 1000); 4912 4913 /* check to see if it timed out */ 4914 $info = @stream_get_meta_data($handle); 4915 4916 /* close the socket */ 4917 @fclose($handle); 4918 4919 if ($info['timed_out']) { 4920 return 'timed_out'; 4921 } 4922 4923 /* more error handling */ 4924 if ($response == '') { return $ip; } 4925 4926 /* parse the response and find the response type */ 4927 $type = @unpack('s', substr($response, $requestsize+2)); 4928 4929 if (isset($type[1]) && $type[1] == 0x0C00) { 4930 /* set up our variables */ 4931 $host = ''; 4932 $len = 0; 4933 4934 /* set our pointer at the beginning of the hostname uses the request 4935 size from earlier rather than work it out. 4936 */ 4937 $position = $requestsize + 12; 4938 4939 /* reconstruct the hostname */ 4940 do { 4941 /* get segment size */ 4942 $len = unpack('c', substr($response, $position)); 4943 4944 /* null terminated string, so length 0 = finished */ 4945 if ($len[1] == 0) { 4946 /* return the hostname, without the trailing '.' */ 4947 return strtoupper(substr($host, 0, strlen($host) -1)); 4948 } 4949 4950 /* add the next segment to our host */ 4951 $host .= substr($response, $position+1, $len[1]) . '.'; 4952 4953 /* move pointer on to the next segment */ 4954 $position += $len[1] + 1; 4955 } while ($len != 0); 4956 4957 /* error - return the hostname we constructed (without the . on the end) */ 4958 return strtoupper($ip); 4959 } 4960 4961 /* error - return the hostname */ 4962 return strtoupper($ip); 4963} 4964 4965function poller_maintenance () { 4966 global $config; 4967 4968 $command_string = cacti_escapeshellcmd(read_config_option('path_php_binary')); 4969 4970 // If its not set, just assume its in the path 4971 if (trim($command_string) == '') { 4972 $command_string = 'php'; 4973 } 4974 4975 $extra_args = ' -q ' . cacti_escapeshellarg($config['base_path'] . '/poller_maintenance.php'); 4976 4977 exec_background($command_string, $extra_args); 4978} 4979 4980function clog_admin() { 4981 if (!isset($_SESSION['sess_clog_level'])) { 4982 clog_authorized(); 4983 } 4984 4985 if ($_SESSION["sess_clog_level"] == CLOG_PERM_ADMIN) { 4986 return true; 4987 } else { 4988 return false; 4989 } 4990} 4991 4992function clog_authorized() { 4993 if (!isset($_SESSION['sess_clog_level'])) { 4994 if (isset($_SESSION['sess_user_id'])) { 4995 if (is_realm_allowed(18)) { 4996 $_SESSION['sess_clog_level'] = CLOG_PERM_ADMIN; 4997 } else { 4998 if (is_realm_allowed(19)) { 4999 $_SESSION['sess_clog_level'] = CLOG_PERM_USER; 5000 } else { 5001 $_SESSION['sess_clog_level'] = CLOG_PERM_NONE; 5002 } 5003 } 5004 } else { 5005 $_SESSION['sess_clog_level'] = CLOG_PERM_NONE; 5006 } 5007 } 5008 5009 if ($_SESSION['sess_clog_level'] == CLOG_PERM_USER) { 5010 return true; 5011 } elseif ($_SESSION['sess_clog_level'] == CLOG_PERM_ADMIN) { 5012 return true; 5013 } else { 5014 return false; 5015 } 5016} 5017 5018function cacti_debug_backtrace($entry = '', $html = false, $record = true, $limit = 0, $skip = 0) { 5019 global $config; 5020 5021 $skip = $skip >= 0 ? $skip : 1; 5022 $limit = $limit > 0 ? ($limit + $skip) : 0; 5023 5024 $callers = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $limit); 5025 while ($skip > 0) { 5026 array_shift($callers); 5027 $skip--; 5028 } 5029 5030 $s=''; 5031 foreach ($callers as $c) { 5032 if (isset($c['line'])) { 5033 $line = '[' . $c['line'] . ']'; 5034 } else { 5035 $line = ''; 5036 } 5037 5038 if (isset($c['file'])) { 5039 $file = str_replace($config['base_path'], '', $c['file']) . $line; 5040 } else { 5041 $file = $line; 5042 } 5043 5044 $func = $c['function'].'()'; 5045 if (isset($c['class'])) { 5046 $func = $c['class'] . $c['type'] . $func; 5047 } 5048 5049 $s = ($file != '' ? $file . ':':'') . "$func" . (empty($s) ? '' : ', ') . $s; 5050 } 5051 5052 if (!empty($s)) { 5053 $s = ' (' . $s . ')'; 5054 } 5055 5056 if ($record) { 5057 if ($html) { 5058 print "<table style='width:100%;text-align:center;'><tr><td>$s</td></tr></table>\n"; 5059 } 5060 5061 cacti_log(trim("$entry Backtrace: " . clean_up_lines($s)), false); 5062 } else { 5063 if (!empty($entry)) { 5064 return trim("$entry Backtrace: " . clean_up_lines($s)); 5065 } else { 5066 return trim(clean_up_lines($s)); 5067 } 5068 } 5069} 5070 5071/** 5072 * calculate_percentiles - Given and array of numbers, calculate the Nth percentile, 5073 * optionally, return an array of numbers containing elements required for 5074 * a whisker chart. 5075 * 5076 * @param $data - an array of data 5077 * @param $percentile - the Nth percentile to calculate. By default 95th. 5078 * @param $whisker - if whisker is true, an array of values will be returned 5079 * including 25th, median, 75th, and 90th percentiles. 5080 * 5081 * @return - either the Nth percentile, the elements for a whisker chart, 5082 * or false if there is insufficient data to determine. 5083 */ 5084function calculate_percentiles($data, $percentile = 95, $whisker = false) { 5085 if ($percentile > 0 && $percentile < 1) { 5086 $p = $percentile; 5087 } elseif ($percentile > 1 && $percentile <= 100) { 5088 $p = $percentile * .01; 5089 } else { 5090 return false; 5091 } 5092 5093 if ($whisker) { 5094 $tiles = array( 5095 '25th' => 0.25, 5096 '50th' => 0.50, 5097 '75th' => 0.75, 5098 '90th' => 0.90, 5099 '95th' => 0.95, 5100 ); 5101 } else { 5102 $tiles = array( 5103 'custom' => $p 5104 ); 5105 } 5106 5107 $results = array(); 5108 $elements = cacti_sizeof($data); 5109 5110 /* sort the array to return */ 5111 sort($data); 5112 5113 foreach($tiles as $index => $p) { 5114 /* calculate offsets into the array */ 5115 $allindex = ($elements - 1) * $p; 5116 $intvalindex = intval($allindex); 5117 $floatval = $allindex - $intvalindex; 5118 5119 if (!is_float($floatval)) { 5120 $ptile = $data[$intvalindex]; 5121 } else { 5122 if ($elements > $intvalindex + 1) { 5123 $ptile = $floatval * ($data[$intvalindex + 1] - $data[$intvalindex]) + $data[$intvalindex]; 5124 } else { 5125 $ptile = $data[$intvalindex]; 5126 } 5127 } 5128 5129 if ($index == 'custom') { 5130 return $ptile; 5131 } else { 5132 $results[$index] = $ptile; 5133 } 5134 } 5135 5136 return $results; 5137} 5138 5139function get_timeinstate($host) { 5140 $interval = read_config_option('poller_interval'); 5141 if ($host['availability_method'] == 0) { 5142 $time = 0; 5143 } elseif (isset($host['instate'])) { 5144 $time = $host['instate']; 5145 } elseif ($host['status_event_count'] > 0 && ($host['status'] == 1 || $host['status'] == 2 || $host['status'] == 5)) { 5146 $time = $host['status_event_count'] * $interval; 5147 } elseif (strtotime($host['status_rec_date']) < 943916400 && ($host['status'] == 0 || $host['status'] == 3)) { 5148 $time = $host['total_polls'] * $interval; 5149 } elseif (strtotime($host['status_rec_date']) > 943916400) { 5150 $time = time() - strtotime($host['status_rec_date']); 5151 } elseif ($host['snmp_sysUpTimeInstance'] > 0) { 5152 $time = $host['snmp_sysUpTimeInstance']/100; 5153 } else { 5154 $time = 0; 5155 } 5156 5157 if ($time > 2E13) { 5158 $time = 0; 5159 } 5160 5161 return ($time > 0) ? get_daysfromtime($time) : __('N/A'); 5162} 5163 5164function get_uptime($host) { 5165 return ($host['snmp_sysUpTimeInstance'] > 0) ? get_daysfromtime($host['snmp_sysUpTimeInstance']/100) : __('N/A'); 5166} 5167 5168function get_daysfromtime($time, $secs = false, $pad = '', $format = DAYS_FORMAT_SHORT, $all = false) { 5169 global $days_from_time_settings; 5170 5171 // Ensure we use an existing format or we'll end up with no text at all 5172 if (!isset($days_from_time_settings['text'][$format])) { 5173 $format = DAYS_FORMAT_SHORT; 5174 } 5175 5176 $mods = $days_from_time_settings['mods']; 5177 $text = $days_from_time_settings['text'][$format]; 5178 5179 $result = ''; 5180 foreach ($mods as $index => $mod) { 5181 if ($mod > 0 || $secs) { 5182 if ($time >= $mod) { 5183 if ($mod < 1) { 5184 $mod = 1; 5185 } 5186 $val = floor($time/$mod); 5187 $time %= $mod; 5188 } else { 5189 $val = 0; 5190 } 5191 5192 if ($all || $val > 0) { 5193 $result .= padleft($pad, $val, 2) . $text['prefix'] . $text[$index] . $text['suffix']; 5194 $all = true; 5195 } 5196 } 5197 } 5198 5199 return trim($result,$text['suffix']); 5200} 5201 5202function padleft($pad = '', $value = '', $min = 2) { 5203 $result = "$value"; 5204 if (strlen($result) < $min && $pad != '') { 5205 $padded = $pad . $result; 5206 while ($padded != $result && strlen($result) < $min) { 5207 $padded = $pad . $result; 5208 } 5209 $result = $padded; 5210 } 5211 return $result; 5212} 5213 5214function get_classic_tabimage($text, $down = false) { 5215 global $config; 5216 5217 $images = array( 5218 false => 'tab_template_blue.gif', 5219 true => 'tab_template_red.gif' 5220 ); 5221 5222 if ($text == '') return false; 5223 5224 $text = strtolower($text); 5225 5226 $possibles = array( 5227 array('DejaVuSans-Bold.ttf', 9, true), 5228 array('DejaVuSansCondensed-Bold.ttf', 9, false), 5229 array('DejaVuSans-Bold.ttf', 9, false), 5230 array('DejaVuSansCondensed-Bold.ttf', 9, false), 5231 array('DejaVuSans-Bold.ttf', 8, false), 5232 array('DejaVuSansCondensed-Bold.ttf', 8, false), 5233 array('DejaVuSans-Bold.ttf', 7, false), 5234 array('DejaVuSansCondensed-Bold.ttf', 7, true), 5235 ); 5236 5237 $y = 30; 5238 $x = 44; 5239 $wlimit = 72; 5240 $wrapsize = 12; 5241 5242 if (file_exists($config['base_path'] . '/images/' . $images[$down])) { 5243 $originalpath = getenv('GDFONTPATH'); 5244 putenv('GDFONTPATH=' . $config['base_path'] . '/include/fonts/'); 5245 5246 $template = imagecreatefromgif ($config['base_path'] . '/images/' . $images[$down]); 5247 5248 $w = imagesx($template); 5249 $h = imagesy($template); 5250 5251 $tab = imagecreatetruecolor($w, $h); 5252 imagecopy($tab, $template, 0, 0, 0, 0, $w, $h); 5253 5254 $txcol = imagecolorat($tab, 0, 0); 5255 imagecolortransparent($tab,$txcol); 5256 5257 $white = imagecolorallocate($tab, 255, 255, 255); 5258 $ttf_functions = function_exists('imagettftext') && function_exists('imagettfbbox'); 5259 5260 if ($ttf_functions) { 5261 foreach ($possibles as $variation) { 5262 $font = $variation[0]; 5263 $fontsize = $variation[1]; 5264 5265 $lines = array(); 5266 5267 // if no wrapping is requested, or no wrapping is possible... 5268 if ((!$variation[2]) || ($variation[2] && strpos($text,' ') === false)) { 5269 $bounds = imagettfbbox($fontsize, 0, $font, $text); 5270 $w = $bounds[4] - $bounds[0]; 5271 $h = $bounds[1] - $bounds[5]; 5272 $realx = $x - $w/2 -1; 5273 $lines[] = array($text, $font, $fontsize, $realx, $y); 5274 $maxw = $w; 5275 } else { 5276 $texts = explode("\n", wordwrap($text, $wrapsize), 2); 5277 $line = 1; 5278 $maxw = 0; 5279 foreach ($texts as $txt) { 5280 $bounds = imagettfbbox($fontsize, 0, $font, $txt); 5281 $w = $bounds[4] - $bounds[0]; 5282 $h = $bounds[1] - $bounds[5]; 5283 $realx = $x - $w/2 -1; 5284 $realy = $y - $h * $line + 3; 5285 $lines[] = array($txt, $font, $fontsize, $realx, $realy); 5286 if ($maxw < $w) { 5287 $maxw = $w; 5288 } 5289 5290 $line--; 5291 } 5292 } 5293 5294 if ($maxw<$wlimit) break; 5295 } 5296 } else { 5297 while ($text > '') { 5298 for ($fontid = 5; $fontid>0; $fontid--) { 5299 $fontw = imagefontwidth($fontid); 5300 $fonth = imagefontheight($fontid); 5301 $realx = ($w - ($fontw * strlen($text)))/2; 5302 $realy = ($h - $fonth - 5); 5303 5304 // Since we can't use FreeType, lets use a fixed location 5305 $lines = array(); 5306 $lines[] = array($text, $fontid, 0, $realx, $realy); 5307 5308 if ($realx > 10 && $realy > 0) break; 5309 } 5310 5311 if ($fontid == 0) { 5312 $spacer = strrpos($text,' '); 5313 if ($spacer === false) { 5314 $spacer = strlen($text) - 1; 5315 } 5316 $text = substr($text,0,$spacer); 5317 } else { 5318 break; 5319 } 5320 } 5321 } 5322 5323 5324 foreach ($lines as $line) { 5325 if ($ttf_functions) { 5326 imagettftext($tab, $line[2], 0, $line[3], $line[4], $white, $line[1], $line[0]); 5327 } else { 5328 imagestring($tab, $line[1], $line[3], $line[4], $line[0], $white); 5329 } 5330 } 5331 5332 putenv('GDFONTPATH=' . $originalpath); 5333 5334 imagetruecolortopalette($tab, true, 256); 5335 5336 // generate the image an return the data directly 5337 ob_start(); 5338 imagegif ($tab); 5339 $image = ob_get_contents(); 5340 ob_end_clean(); 5341 5342 return("data:image/gif;base64," . base64_encode($image)); 5343 } else { 5344 return false; 5345 } 5346} 5347 5348function cacti_oid_numeric_format() { 5349 if (function_exists('snmp_set_oid_output_format')) { 5350 snmp_set_oid_output_format(SNMP_OID_OUTPUT_NUMERIC); 5351 } elseif (function_exists("snmp_set_oid_numeric_print")) { 5352 snmp_set_oid_numeric_print(true); 5353 } 5354} 5355 5356function IgnoreErrorHandler($message) { 5357 global $snmp_error; 5358 5359 $snmp_ignore = array( 5360 'No response from', 5361 'noSuchName', 5362 'No Such Object', 5363 'Error in packet', 5364 'This name does not exist', 5365 'End of MIB', 5366 'Timeout', 5367 'Unknown host', 5368 'Invalid object identifier', 5369 'Name or service not known' 5370 ); 5371 5372 foreach ($snmp_ignore as $i) { 5373 if (strpos($message, $i)) { 5374 $snmp_error = trim($message, "\\\n\t "); 5375 return true; 5376 } 5377 } 5378 5379 $ignore = array( 5380 'unable to read from socket' # ping.php line 387 socket refusal 5381 ); 5382 5383 foreach ($ignore as $i) { 5384 if (strpos($message, $i)) { 5385 return true; 5386 } 5387 } 5388 5389 return false; 5390} 5391 5392function CactiErrorHandler($level, $message, $file, $line, $context = array()) { 5393 global $phperrors; 5394 5395 if (defined('IN_CACTI_INSTALL')) { 5396 return true; 5397 } 5398 5399 if (IgnoreErrorHandler($message)) { 5400 return true; 5401 } 5402 5403 if (error_reporting() == 0) { 5404 return true; 5405 } 5406 5407 preg_match("/.*\/plugins\/([\w-]*)\/.*/", $file, $output_array); 5408 5409 $plugin = (isset($output_array[1]) ? $output_array[1] : ''); 5410 $error = 'PHP ' . $phperrors[$level] . ($plugin != '' ? " in Plugin '$plugin'" : '') . ": $message in file: $file on line: $line"; 5411 5412 switch ($level) { 5413 case E_COMPILE_ERROR: 5414 case E_CORE_ERROR: 5415 case E_ERROR: 5416 case E_PARSE: 5417 cacti_log($error, false, 'ERROR'); 5418 cacti_debug_backtrace('PHP ERROR PARSE', false, true, 0, 1); 5419 if ($plugin != '') { 5420 api_plugin_disable_all($plugin); 5421 cacti_log("ERRORS DETECTED - DISABLING PLUGIN '$plugin'"); 5422 admin_email(__('Cacti System Warning'), __('Cacti disabled plugin %s due to the following error: %s! See the Cacti logfile for more details.', $plugin, $error)); 5423 } 5424 break; 5425 case E_RECOVERABLE_ERROR: 5426 case E_USER_ERROR: 5427 cacti_log($error, false, 'ERROR'); 5428 cacti_debug_backtrace('PHP ERROR', false, true, 0, 1); 5429 break; 5430 case E_COMPILE_WARNING: 5431 case E_CORE_WARNING: 5432 case E_USER_WARNING: 5433 case E_WARNING: 5434 cacti_log($error, false, 'ERROR'); 5435 cacti_debug_backtrace('PHP ERROR WARNING', false, true, 0, 1); 5436 break; 5437 case E_NOTICE: 5438 case E_USER_NOTICE: 5439 cacti_log($error, false, 'ERROR'); 5440 cacti_debug_backtrace('PHP ERROR NOTICE', false, true, 0, 1); 5441 break; 5442 case E_STRICT: 5443 cacti_log($error, false, 'ERROR'); 5444 cacti_debug_backtrace('PHP ERROR STRICT', false, true, 0, 1); 5445 break; 5446 default: 5447 cacti_log($error, false, 'ERROR'); 5448 cacti_debug_backtrace('PHP ERROR', false, true, 0, 1); 5449 } 5450 5451 return false; 5452} 5453 5454function CactiShutdownHandler() { 5455 global $phperrors; 5456 $error = error_get_last(); 5457 5458 if (is_array($error)) { 5459 if (isset($error['message']) && IgnoreErrorHandler($error['message'])) { 5460 return true; 5461 } 5462 5463 if (isset($error['type'])) { 5464 switch ($error['type']) { 5465 case E_ERROR: 5466 case E_CORE_ERROR: 5467 case E_COMPILE_ERROR: 5468 case E_CORE_WARNING: 5469 case E_COMPILE_WARNING: 5470 case E_PARSE: 5471 preg_match('/.*\/plugins\/([\w-]*)\/.*/', $error['file'], $output_array); 5472 5473 $plugin = (isset($output_array[1]) ? $output_array[1] : '' ); 5474 5475 $message = 'PHP ' . $phperrors[$error['type']] . 5476 ($plugin != '' ? " in Plugin '$plugin'" : '') . ': ' . $error['message'] . 5477 ' in file: ' . $error['file'] . ' on line: ' . $error['line']; 5478 5479 cacti_log($message, false, 'ERROR'); 5480 cacti_debug_backtrace('PHP ERROR', false, true, 0, 1); 5481 5482 if ($plugin != '') { 5483 api_plugin_disable_all($plugin); 5484 cacti_log("ERRORS DETECTED - DISABLING PLUGIN '$plugin'"); 5485 admin_email(__('Cacti System Warning'), __('Cacti disabled plugin %s due to the following error: %s! See the Cacti logfile for more details.', $plugin, $message)); 5486 } 5487 } 5488 } 5489 } 5490} 5491 5492/** 5493 * enable_device_debug - Enables device debug for a device 5494 * if it is disabled. 5495 * 5496 * @param $host_id - the device id to search for 5497 * 5498 * @return - void 5499 */ 5500function enable_device_debug($host_id) { 5501 $device_debug = read_config_option('selective_device_debug', true); 5502 if ($device_debug != '') { 5503 $devices = explode(',', $device_debug); 5504 if (array_search($host_id, $devices) === false) { 5505 set_config_option('selective_device_debug', $device_debug . ',' . $host_id); 5506 } 5507 } else { 5508 set_config_option('selective_device_debug', $host_id); 5509 } 5510} 5511 5512/** 5513 * disable_device_debug - Disables device debug for a device 5514 * if it is enabled. 5515 * 5516 * @param $host_id - the device id to search for 5517 * 5518 * @return - void 5519 */ 5520function disable_device_debug($host_id) { 5521 $device_debug = read_config_option('selective_device_debug', true); 5522 if ($device_debug != '') { 5523 $devices = explode(',', $device_debug); 5524 foreach($devices as $key => $device) { 5525 if ($device == $host_id) { 5526 unset($devices[$key]); 5527 break; 5528 } 5529 } 5530 set_config_option('selective_device_debug', implode(',', $devices)); 5531 } 5532} 5533 5534/** 5535 * is_device_debug_enabled - Determines if device debug is enabled 5536 * for a device. 5537 * 5538 * @param $host_id - the device id to search for 5539 * 5540 * @return - boolean true or false 5541 */ 5542function is_device_debug_enabled($host_id) { 5543 $device_debug = read_config_option('selective_device_debug', true); 5544 if ($device_debug != '') { 5545 $devices = explode(',', $device_debug); 5546 if (array_search($host_id, $devices) !== false) { 5547 return true; 5548 } 5549 } 5550 5551 return false; 5552} 5553 5554/** 5555 * get_url_type - Determines if remote communications are over 5556 * http or https for remote services. 5557 * 5558 * @return - http or https 5559 */ 5560function get_url_type() { 5561 if (read_config_option('force_https') == 'on') { 5562 return 'https'; 5563 } else { 5564 return 'http'; 5565 } 5566} 5567 5568/** 5569 * get_default_contextoption - Sets default context options for self-signed SSL 5570 * related protocols if necessary. Allows plugins to add additional header information 5571 * to fulfill system setup related requirements like the usage of Web Single Login 5572 * cookies for example. 5573 * 5574 * @return - an array of stream context options or false 5575 */ 5576function get_default_contextoption($timeout = '') { 5577 $fgc_contextoption = false; 5578 5579 if ($timeout == '') { 5580 $timeout = read_config_option('remote_agent_timeout'); 5581 } 5582 5583 if (!is_numeric($timeout)) { 5584 $timeout = 5; 5585 } 5586 5587 $protocol = get_url_type(); 5588 5589 if (in_array($protocol, array('ssl', 'https', 'ftps'))) { 5590 $fgc_contextoption = array( 5591 'ssl' => array( 5592 'verify_peer' => false, 5593 'verify_peer_name' => false, 5594 'allow_self_signed' => true, 5595 ) 5596 ); 5597 } 5598 5599 if ($protocol == 'https') { 5600 $fgc_contextoption['https'] = array( 5601 'timeout' => $timeout, 5602 'ignore_errors' => true 5603 ); 5604 } elseif ($protocol == 'http') { 5605 $fgc_contextoption['http'] = array( 5606 'timeout' => $timeout, 5607 'ignore_errors' => true 5608 ); 5609 } 5610 5611 $fgc_contextoption = api_plugin_hook_function('fgc_contextoption', $fgc_contextoption); 5612 5613 return $fgc_contextoption; 5614} 5615 5616/** 5617 * repair_system_data_input_methods - This utility will repair 5618 * system data input methods when they are detected on the system 5619 * 5620 * @return - null 5621 */ 5622function repair_system_data_input_methods($step = 'import') { 5623 $system_hashes = array( 5624 '3eb92bb845b9660a7445cf9740726522', // Get SNMP Data 5625 'bf566c869ac6443b0c75d1c32b5a350e', // Get SNMP Data (Indexed) 5626 '80e9e4c4191a5da189ae26d0e237f015', // Get Script Data (Indexed) 5627 '332111d8b54ac8ce939af87a7eac0c06', // Get Script Server Data (Indexed) 5628 ); 5629 5630 $good_field_hashes = array( 5631 '3eb92bb845b9660a7445cf9740726522' => array( // Get SNMP Data (1) 5632 '92f5906c8dc0f964b41f4253df582c38', // IP Address 5633 '012ccb1d3687d3edb29c002ea66e72da', // SNMP Version 5634 '32285d5bf16e56c478f5e83f32cda9ef', // SNMP Community 5635 'fc64b99742ec417cc424dbf8c7692d36', // SNMP Port 5636 'ad14ac90641aed388139f6ba86a2e48b', // SNMP Username 5637 '9c55a74bd571b4f00a96fd4b793278c6', // SNMP Password 5638 '20832ce12f099c8e54140793a091af90', // SNMP Authentication Protocol 5639 'c60c9aac1e1b3555ea0620b8bbfd82cb', // SNMP Privacy Passphrase 5640 'feda162701240101bc74148415ef415a', // SNMP Privacy Protocol 5641 '4276a5ec6e3fe33995129041b1909762' // SNMP OID 5642 ), 5643 'bf566c869ac6443b0c75d1c32b5a350e' => array( // Get SNMP Data (Indexed) (2) 5644 '617cdc8a230615e59f06f361ef6e7728', // IP Address 5645 'b5c23f246559df38662c255f4aa21d6b', // SNMP Version 5646 'acb449d1451e8a2a655c2c99d31142c7', // SNMP Community 5647 'c1f36ee60c3dc98945556d57f26e475b', // SNMP Port 5648 'f4facc5e2ca7ebee621f09bc6d9fc792', // SNMP Username 5649 '1cc1493a6781af2c478fa4de971531cf', // SNMP Password 5650 '2cf7129ad3ff819a7a7ac189bee48ce8', // SNMP Authentication Protocol 5651 '6b13ac0a0194e171d241d4b06f913158', // SNMP Privacy Passphrase 5652 '3a33d4fc65b8329ab2ac46a36da26b72', // SNMP Privacy Protocol 5653 '6027a919c7c7731fbe095b6f53ab127b', // Index Type 5654 'cbbe5c1ddfb264a6e5d509ce1c78c95f', // Index Value 5655 'e6deda7be0f391399c5130e7c4a48b28' // Output Type ID 5656 ), 5657 '80e9e4c4191a5da189ae26d0e237f015' => array( // Get Script Data (Indexed) 11 5658 'd39556ecad6166701bfb0e28c5a11108', // Index Type 5659 '3b7caa46eb809fc238de6ef18b6e10d5', // Index Value 5660 '74af2e42dc12956c4817c2ef5d9983f9', // Output Type ID 5661 '8ae57f09f787656bf4ac541e8bd12537' // Output Value 5662 ), 5663 '332111d8b54ac8ce939af87a7eac0c06' => array( // Get Script Server Data (Indexed) 12 5664 '172b4b0eacee4948c6479f587b62e512', // Index Type 5665 '30fb5d5bcf3d66bb5abe88596f357c26', // Index Value 5666 '31112c85ae4ff821d3b288336288818c', // Output Type ID 5667 '5be8fa85472d89c621790b43510b5043' // Output Value 5668 ) 5669 ); 5670 5671 foreach($good_field_hashes as $hash => $field_hashes) { 5672 $data_input_id = db_fetch_cell_prepared('SELECT id FROM data_input WHERE hash = ?', array($hash)); 5673 5674 if (!empty($data_input_id)) { 5675 $bad_hashes = db_fetch_assoc_prepared('SELECT * 5676 FROM data_input_fields 5677 WHERE hash NOT IN ("' . implode('","', $field_hashes) . '") 5678 AND hash != "" 5679 AND data_input_id = ?', 5680 array($data_input_id)); 5681 5682 if (cacti_sizeof($bad_hashes)) { 5683 cacti_log(strtoupper($step) . ' NOTE: Repairing ' . cacti_sizeof($bad_hashes) . ' Damaged data_input_fields', false); 5684 5685 foreach($bad_hashes as $bhash) { 5686 $good_field_id = db_fetch_cell_prepared('SELECT id 5687 FROM data_input_fields 5688 WHERE hash != ? 5689 AND data_input_id = ? 5690 AND data_name = ?', 5691 array($bhash['hash'], $data_input_id, $bhash['data_name'])); 5692 5693 if (!empty($good_field_id)) { 5694 cacti_log("Data Input ID $data_input_id Bad Field ID is " . $bhash['id'] . ", Good Field ID: " . $good_field_id, false, 'WEBUI', POLLER_VERBOSITY_DEVDBG); 5695 5696 cacti_log('Executing Data Input Data Check', false, 'WEBUI', POLLER_VERBOSITY_DEVDBG); 5697 5698 // Data Input Data 5699 $bad_mappings = db_fetch_assoc_prepared('SELECT * 5700 FROM data_input_data 5701 WHERE data_input_field_id = ?', 5702 array($bhash['id'])); 5703 5704 if (cacti_sizeof($bad_mappings)) { 5705 cacti_log(strtoupper($step) . ' NOTE: Found ' . cacti_sizeof($bad_mappings) . ' Damaged data_input_fields', false); 5706 foreach($bad_mappings as $mfid) { 5707 $good_found = db_fetch_cell_prepared('SELECT COUNT(*) 5708 FROM data_input_data 5709 WHERE data_input_field_id = ? 5710 AND data_template_data_id = ?', 5711 array($good_field_id, $mfid['data_template_data_id'])); 5712 5713 if ($good_found > 0) { 5714 cacti_log('Good Found for ' . $mfid['data_input_field_id'] . ', Fixing', false, 'WEBUI', POLLER_VERBOSITY_DEVDBG); 5715 5716 db_execute_prepared('DELETE FROM data_input_data 5717 WHERE data_input_field_id = ? 5718 AND data_template_data_id = ?', 5719 array($mfid['data_input_field_id'], $mfid['data_template_data_id'])); 5720 } else { 5721 cacti_log('Good NOT Found for ' . $mfid['data_input_field_id'] . ', Fixing', false, 'WEBUI', POLLER_VERBOSITY_DEVDBG); 5722 5723 db_execute_prepared('UPDATE data_input_data 5724 SET data_input_field_id = ? 5725 WHERE data_input_field_id = ? 5726 AND data_template_data_id = ?', 5727 array($good_field_id, $mfid['data_input_field_id'], $mfid['data_template_data_id'])); 5728 } 5729 } 5730 } else { 5731 cacti_log('No Bad Data Input Data Records', false, 'WEBUI', POLLER_VERBOSITY_DEVDBG); 5732 } 5733 5734 // Data Template RRD 5735 cacti_log('Executing Data Template RRD Check', false, 'WEBUI', POLLER_VERBOSITY_DEVDBG);; 5736 5737 $bad_mappings = db_fetch_assoc_prepared('SELECT * 5738 FROM data_template_rrd 5739 WHERE data_input_field_id = ?', 5740 array($bhash['id'])); 5741 5742 if (cacti_sizeof($bad_mappings)) { 5743 cacti_log(strtoupper($step) . ' NOTE: Found ' . cacti_sizeof($bad_mappings) . ' Damaged data_template_rrd', false); 5744 5745 foreach($bad_mappings as $mfid) { 5746 $good_found = db_fetch_cell_prepared('SELECT COUNT(*) 5747 FROM data_template_rrd 5748 WHERE data_input_field_id = ? 5749 AND id = ?', 5750 array($good_field_id, $mfid['id'])); 5751 5752 if ($good_found > 0) { 5753 cacti_log('Good Found for ' . $mfid['data_input_field_id'] . ', Fixing', false, 'WEBUI', POLLER_VERBOSITY_DEVDBG); 5754 5755 db_execute_prepared('DELETE FROM data_template_rrd 5756 WHERE data_input_field_id = ? 5757 AND id = ?', 5758 array($mfid['data_input_field_id'], $mfid['id'])); 5759 } else { 5760 cacti_log('Good NOT Found for ' . $mfid['data_input_field_id'] . ', Fixing', false, 'WEBUI', POLLER_VERBOSITY_DEVDBG); 5761 5762 db_execute_prepared('UPDATE data_template_rrd 5763 SET data_input_field_id = ? 5764 WHERE data_input_field_id = ? 5765 AND id = ?', 5766 array($good_field_id, $mfid['data_input_field_id'], $mfid['id'])); 5767 } 5768 } 5769 } else { 5770 cacti_log('No Bad Data Template RRD Records', false, 'WEBUI', POLLER_VERBOSITY_DEVDBG); 5771 } 5772 5773 db_execute_prepared('DELETE FROM data_input_fields WHERE hash = ?', array($bhash['hash'])); 5774 } else { 5775 cacti_log('WARNING: Could not find Cacti default matching hash for unknown system hash "' . $bhash['hash'] . '" for ' . $data_input_id . '. No repair performed.'); 5776 } 5777 } 5778 } 5779 } else { 5780 cacti_log("Could not find hash '" . $hash . "' for Data Input", false, 'WEBUI', POLLER_VERBOSITY_DEVDBG); 5781 } 5782 } 5783} 5784 5785if (isset($config['cacti_server_os']) && $config['cacti_server_os'] == 'win32' && !function_exists('posix_kill')) { 5786 function posix_kill($pid, $signal = SIGTERM) { 5787 $wmi = new COM("winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\cimv2"); 5788 $procs = $wmi->ExecQuery("SELECT ProcessId FROM Win32_Process WHERE ProcessId='" . $pid . "'"); 5789 5790 if (cacti_sizeof($procs)) { 5791 if ($signal == SIGTERM) { 5792 foreach($procs as $proc) { 5793 $proc->Terminate(); 5794 } 5795 } else { 5796 return true; 5797 } 5798 } else { 5799 return false; 5800 } 5801 } 5802} 5803 5804function is_ipaddress($ip_address = '') { 5805 /* check for ipv4/v6 */ 5806 if (function_exists('filter_var')) { 5807 if (filter_var($ip_address, FILTER_VALIDATE_IP) !== false) { 5808 return true; 5809 } else { 5810 return false; 5811 } 5812 } elseif (inet_pton($ip_address) !== false) { 5813 return true; 5814 } else { 5815 return false; 5816 } 5817} 5818 5819/** 5820 * date_time_format create a format string for date/time 5821 * 5822 * @return string returns date time format 5823 */ 5824function date_time_format() { 5825 $datechar = array( 5826 GDC_HYPHEN => '-', 5827 GDC_SLASH => '/', 5828 GDC_DOT => '.' 5829 ); 5830 5831 /* setup date format */ 5832 $date_fmt = read_config_option('default_date_format'); 5833 $dateCharSetting = read_config_option('default_datechar'); 5834 5835 if (!isset($datechar[$dateCharSetting])) { 5836 $dateCharSetting = GDC_SLASH; 5837 } 5838 5839 $datecharacter = $datechar[$dateCharSetting]; 5840 5841 switch ($date_fmt) { 5842 case GD_MO_D_Y: 5843 return 'm' . $datecharacter . 'd' . $datecharacter . 'Y H:i:s'; 5844 case GD_MN_D_Y: 5845 return 'M' . $datecharacter . 'd' . $datecharacter . 'Y H:i:s'; 5846 case GD_D_MO_Y: 5847 return 'd' . $datecharacter . 'm' . $datecharacter . 'Y H:i:s'; 5848 case GD_D_MN_Y: 5849 return 'd' . $datecharacter . 'M' . $datecharacter . 'Y H:i:s'; 5850 case GD_Y_MO_D: 5851 return 'Y' . $datecharacter . 'm' . $datecharacter . 'd H:i:s'; 5852 case GD_Y_MN_D: 5853 return 'Y' . $datecharacter . 'M' . $datecharacter . 'd H:i:s'; 5854 default: 5855 return 'Y' . $datecharacter . 'm' . $datecharacter . 'd H:i:s'; 5856 } 5857} 5858 5859/** 5860 * get_cacti_version Generic function to get the cacti version 5861 */ 5862function get_cacti_version() { 5863 static $version = ''; 5864 5865 if ($version == '') { 5866 $version = trim(db_fetch_cell('SELECT cacti FROM version LIMIT 1')); 5867 } 5868 5869 return $version; 5870} 5871 5872/** 5873 * get_cacti_version_text Return the cacti version text including beta moniker 5874 */ 5875function get_cacti_version_text($include_version = true) { 5876 if ($include_version) { 5877 return trim(__('Version %s %s', CACTI_VERSION, (defined('CACTI_VERSION_BETA') ? __('- Beta %s', CACTI_VERSION_BETA):''))); 5878 } else { 5879 return trim(__('%s %s', CACTI_VERSION, (defined('CACTI_VERSION_BETA') ? __('- Beta %s', CACTI_VERSION_BETA):''))); 5880 } 5881} 5882 5883/** 5884 * get_cacti_cli_version() { 5885 */ 5886function get_cacti_cli_version() { 5887 $dbversion = get_cacti_version(); 5888 $version = get_cacti_version_text(false); 5889 return $version . ' (DB: ' . $dbversion . ')'; 5890} 5891 5892/** 5893 * cacti_version_compare - Compare Cacti version numbers 5894 */ 5895function cacti_version_compare($version1, $version2, $operator = '>') { 5896 if ($version1 == 'new_install') { 5897 $version1 = CACTI_VERSION; 5898 } 5899 5900 $length = max(cacti_sizeof(explode('.', $version1)), cacti_sizeof(explode('.', $version2))); 5901 $version1 = version_to_decimal($version1, $length); 5902 $version2 = version_to_decimal($version2, $length); 5903 5904 switch ($operator) { 5905 case '<': 5906 if ($version1 < $version2) { 5907 return true; 5908 } 5909 break; 5910 case '<=': 5911 if ($version1 <= $version2) { 5912 return true; 5913 } 5914 break; 5915 case '>=': 5916 if ($version1 >= $version2) { 5917 return true; 5918 } 5919 break; 5920 case '>': 5921 if ($version1 > $version2) { 5922 return true; 5923 } 5924 break; 5925 case '==': 5926 if ($version1 == $version2) { 5927 return true; 5928 } 5929 break; 5930 default: 5931 return version_compare($version1, $version2, $operator); 5932 } 5933 return false; 5934} 5935 5936/** 5937 * version_to_decimal - convert version string to decimal 5938 */ 5939function version_to_decimal($version, $length = 1) { 5940 $newver = ''; 5941 $minor = ''; 5942 5943 $parts = explode('.', $version); 5944 foreach($parts as $part) { 5945 if (is_numeric($part)) { 5946 $part = substr('00' . $part, -2); 5947 $newver .= $part; 5948 } else { 5949 $minor = substr($part, -1); 5950 $major = substr($part, 0, strlen($part)-1); 5951 $major = substr('00' . $major, -2); 5952 $newver .= $major; 5953 } 5954 } 5955 5956 if (cacti_sizeof($parts) < $length) { 5957 $i = cacti_sizeof($parts); 5958 while($i < $length) { 5959 $newver .= '00'; 5960 $i++; 5961 } 5962 } 5963 5964 if ($minor != '') { 5965 $int = ord($minor); 5966 } else { 5967 $int = 0; 5968 } 5969 5970 return @hexdec($newver) * 1000 + $int; 5971} 5972 5973/** 5974 * cacti_gethostinfo - obtains the dns information for a host 5975 */ 5976function cacti_gethostinfo($hostname, $type = DNS_ALL) { 5977 return dns_get_record($hostname, $type); 5978} 5979 5980/** 5981 * cacti_gethostbyname - a ip/ipv6 replacement for php's gethostbyname function 5982 */ 5983function cacti_gethostbyname($hostname, $type = '') { 5984 if ($type == '') { 5985 $type = DNS_A + DNS_AAAA; 5986 } 5987 5988 if ($type != DNS_AAAA) { 5989 $host = gethostbyname($hostname); 5990 if ($host !== $hostname) { 5991 return $host; 5992 } 5993 } 5994 5995 $return = cacti_gethostinfo($hostname, $type); 5996 5997 if (cacti_sizeof($return)) { 5998 foreach($return as $record) { 5999 switch($record['type']) { 6000 case 'A': 6001 return $record['ip']; 6002 break; 6003 case 'AAAA': 6004 return $record['ipv6']; 6005 break; 6006 } 6007 } 6008 } 6009 6010 return $hostname; 6011} 6012 6013function get_nonsystem_data_input($data_input_id) { 6014 global $hash_system_data_inputs; 6015 6016 $diid = db_fetch_cell_prepared('SELECT id FROM data_input 6017 WHERE hash NOT IN ("' . implode('","', $hash_system_data_inputs) . '") 6018 AND id = ?', 6019 array($data_input_id)); 6020 6021 return $diid; 6022} 6023 6024function get_rrdtool_version() { 6025 static $version = ''; 6026 6027 if ($version == '') { 6028 $version = str_replace('rrd-', '', str_replace('.x', '.0', read_config_option('rrdtool_version', true))); 6029 } 6030 6031 return $version; 6032} 6033 6034function get_installed_rrdtool_version() { 6035 global $config, $rrdtool_versions; 6036 static $version = ''; 6037 6038 if ($version == '') { 6039 if ($config['cacti_server_os'] == 'win32') { 6040 $shell = shell_exec(cacti_escapeshellcmd(read_config_option('path_rrdtool')) . ' -v'); 6041 } else { 6042 $shell = shell_exec(cacti_escapeshellcmd(read_config_option('path_rrdtool')) . ' -v 2>&1'); 6043 } 6044 6045 $version = false; 6046 if (preg_match('/^RRDtool ([0-9.]+) /', $shell, $matches)) { 6047 foreach ($rrdtool_versions as $rrdtool_version => $rrdtool_version_text) { 6048 if (cacti_version_compare($rrdtool_version, $matches[1], '<=')) { 6049 $version = $rrdtool_version; 6050 } 6051 } 6052 } 6053 } 6054 6055 return $version; 6056} 6057 6058function get_md5_hash($path) { 6059 $md5 = 0; 6060 6061 if (db_table_exists('poller_resource_cache')) { 6062 $md5 = db_fetch_cell_prepared('SELECT md5sum 6063 FROM poller_resource_cache 6064 WHERE `path` = ?', 6065 array($path)); 6066 } 6067 6068 if (empty($md5)) { 6069 if (file_exists($path)) { 6070 $md5 = md5_file($path); 6071 } else { 6072 $md5 = md5_file(dirname(__FILE__) . '/../' . $path); 6073 } 6074 } 6075 6076 return $md5; 6077} 6078 6079function get_md5_include_js($path, $async = false) { 6080 global $config; 6081 6082 if (file_exists($path)) { 6083 $npath = str_replace($config['base_path'] . '/', '', $path); 6084 } else { 6085 $npath = $path; 6086 } 6087 6088 if ($async) { 6089 return '<script type=\'text/javascript\' src=\'' . $config['url_path'] . $npath . '?' . get_md5_hash($path) . '\' async></script>' . PHP_EOL; 6090 } else { 6091 return '<script type=\'text/javascript\' src=\'' . $config['url_path'] . $npath . '?' . get_md5_hash($path) . '\'></script>' . PHP_EOL; 6092 } 6093} 6094 6095function get_md5_include_css($path) { 6096 global $config; 6097 6098 return '<link href=\''. $config['url_path'] . $path . '?' . get_md5_hash($path) . '\' type=\'text/css\' rel=\'stylesheet\'>' . PHP_EOL; 6099} 6100 6101function is_resource_writable($path) { 6102 if (empty($path)) { 6103 return false; 6104 } 6105 6106 if ($path[strlen($path)-1] == '/') { 6107 return is_resource_writable($path . uniqid(mt_rand()) . '.tmp'); 6108 } 6109 6110 if (file_exists($path)) { 6111 if (($f = @fopen($path, 'a'))) { 6112 fclose($f); 6113 6114 return true; 6115 } 6116 6117 return false; 6118 } 6119 6120 if (($f = @fopen($path, 'w'))) { 6121 fclose($f); 6122 unlink($path); 6123 6124 return true; 6125 } 6126 6127 return false; 6128} 6129 6130function get_validated_theme($theme, $defaultTheme) { 6131 global $config; 6132 if (isset($theme) && strlen($theme)) { 6133 $themePath = $config['base_path'] . '/include/themes/' . $theme . '/main.css'; 6134 if (file_exists($themePath)) { 6135 return $theme; 6136 } 6137 } 6138 6139 return $defaultTheme; 6140} 6141 6142function get_validated_language($language, $defaultLanguage) { 6143 if (isset($language) && strlen($language)) { 6144 return $language; 6145 } 6146 6147 return $defaultLanguage; 6148} 6149 6150function get_running_user() { 6151 global $config; 6152 6153 static $tmp_user = ''; 6154 6155 if (empty($tmp_user)) { 6156 if (function_exists('posix_geteuid')) { 6157 $tmp_user = posix_getpwuid(posix_geteuid())['name']; 6158 } 6159 } 6160 6161 if (empty($tmp_user)) { 6162 $tmp_file = tempnam(sys_get_temp_dir(), 'uid'); $f_owner = ''; 6163 6164 if (is_resource_writable($tmp_file)) { 6165 if (file_exists($tmp_file)) { 6166 unlink($tmp_file); 6167 } 6168 6169 file_put_contents($tmp_file, 'cacti'); 6170 6171 $f_owner = fileowner($tmp_file); 6172 $f_source = 'file'; 6173 6174 if (file_exists($tmp_file)) { 6175 unlink($tmp_file); 6176 } 6177 } 6178 6179 if (empty($f_owner) && function_exists('posix_getuid')) { 6180 $f_owner = posix_getuid(); 6181 $f_source = 'posix'; 6182 } 6183 6184 if (!empty($f_owner) && function_exists('posix_getpwuid1')) { 6185 $f_array = posix_getpwuid($f_owner); 6186 if (isset($f_array['name'])) { 6187 $tmp_user = $f_array['name']; 6188 } 6189 } 6190 6191 if (empty($tmp_user)) { 6192 exec('id -nu', $o, $r); 6193 if ($r == 0) { 6194 $tmp_user = trim($o['0']); 6195 } 6196 } 6197 6198 /*** Code left here for future development, don't think it is right *** 6199 * 6200 if (empty($tmp_user) && !empty($f_owner) && is_readable('/etc/passwd')) 6201 { 6202 exec(sprintf('grep :%s: /etc/passwd | cut -d: -f1', (int) $uid), $o, $r); 6203 if ($r == 0) { 6204 $tmp_user = 'passwd-' . trim($o['0']); 6205 } 6206 } 6207 */ 6208 6209 // Easy way first 6210 if (empty($tmp_user)) { 6211 $user = get_current_user(); 6212 if ($user != '') { 6213 $tmp_user = $user; 6214 } 6215 } 6216 6217 // Falback method 6218 if (empty($tmp_user)) { 6219 $user = getenv('USERNAME'); 6220 if ($user != '') { 6221 $tmp_user = $user; 6222 } 6223 6224 if (empty($tmp_user)) { 6225 $user = getenv('USER'); 6226 if ($user != '') { 6227 $tmp_user = $user; 6228 } 6229 } 6230 } 6231 } 6232 6233 return (empty($tmp_user) ? 'apache' : $tmp_user); 6234} 6235 6236function get_debug_prefix() { 6237 $dateTime = new DateTime('NOW'); 6238 $dateTime = $dateTime->format('Y-m-d H:i:s.u'); 6239 6240 return sprintf('<[ %s | %7d ]> -- ', $dateTime, getmypid()); 6241} 6242 6243function get_client_addr($client_addr = false) { 6244 6245 $http_addr_headers = array( 6246 'X-Forwarded-For', 6247 'X-Client-IP', 6248 'X-Real-IP', 6249 'X-ProxyUser-Ip', 6250 'CF-Connecting-IP', 6251 'True-Client-IP', 6252 'HTTP_X_FORWARDED', 6253 'HTTP_X_FORWARDED_FOR', 6254 'HTTP_X_CLUSTER_CLIENT_IP', 6255 'HTTP_FORWARDED_FOR', 6256 'HTTP_FORWARDED', 6257 'HTTP_CLIENT_IP', 6258 'REMOTE_ADDR', 6259 ); 6260 6261 $client_addr = false; 6262 foreach ($http_addr_headers as $header) { 6263 if (!empty($_SERVER[$header])) { 6264 $header_ips = explode(',', $_SERVER[$header]); 6265 foreach ($header_ips as $header_ip) { 6266 if (!empty($header_ip)) { 6267 if (!filter_var($header_ip, FILTER_VALIDATE_IP)) { 6268 cacti_log('ERROR: Invalid remote client IP Address found in header (' . $header . ').', false, 'AUTH', POLLER_VERBOSITY_DEBUG); 6269 } else { 6270 $client_addr = $header_ip; 6271 cacti_log('DEBUG: Using remote client IP Address found in header (' . $header . '): ' . $client_addr . ' (' . $_SERVER[$header] . ')', false, 'AUTH', POLLER_VERBOSITY_DEBUG); 6272 break 2; 6273 } 6274 } 6275 } 6276 } 6277 } 6278 6279 return $client_addr; 6280} 6281 6282function cacti_pton($ipaddr) { 6283 // Strip out the netmask, if there is one. 6284 $subnet_pos = strpos($ipaddr, '/'); 6285 if ($subnet_pos) { 6286 $subnet = substr($ipaddr, $subnet_pos+1); 6287 $ipaddr = substr($ipaddr, 0, $subnet_pos); 6288 } else { 6289 $subnet = null; // No netmask present 6290 } 6291 6292 // Convert address to packed format 6293 $addr = @inet_pton($ipaddr); 6294 if ($addr === false) { 6295 return false; 6296 } 6297 6298 // Maximum netmask length = same as packed address 6299 $len = 8*strlen($addr); 6300 6301 if (!empty($subnet)) { 6302 if (!is_numeric($subnet)) { 6303 return false; 6304 } elseif ($subnet > $len) { 6305 return false; 6306 } 6307 } 6308 6309 if (!is_numeric($subnet)) { 6310 $subnet=$len; 6311 } else { 6312 $subnet=(int)$subnet; 6313 } 6314 6315 // Create a hex expression of the subnet mask 6316 $mask=str_repeat('f',$subnet>>2); 6317 switch($subnet&3) { 6318 case 3: 6319 $mask.='e'; 6320 break; 6321 case 2: 6322 $mask.='c'; break; 6323 case 1: 6324 $mask.='8'; break; 6325 } 6326 $mask=str_pad($mask,$len>>2,'0'); 6327 6328 // Packed representation of netmask 6329 $mask=pack('H*',$mask); 6330 6331 $result = array('ip' => $addr, 'subnet' => $mask); 6332 return $result; 6333} 6334 6335function cacti_ntop($addr) { 6336 if (empty($addr)) { 6337 return false; 6338 } 6339 6340 if (is_array($addr)) { 6341 foreach ($addr as $ip) { 6342 $addr = $ip; 6343 break; 6344 } 6345 } 6346 return @inet_ntop($addr); 6347} 6348 6349function cacti_ntoc($subnet, $ipv6 = false) { 6350 $result = false; 6351 $count = 0; 6352 foreach(str_split($subnet) as $char) { 6353 $i = ord($char); 6354 while (($i & 128) == 128) { 6355 $count++; 6356 $i = ($i << 1) % 256; 6357 } 6358 } 6359 6360 return $count; 6361} 6362 6363function cacti_ptoa($title, $addr) { 6364 // Let's display it as hexadecimal format 6365 foreach(str_split($addr) as $char) { 6366 print str_pad(dechex(ord($char)),2,'0',STR_PAD_LEFT); 6367 } 6368} 6369 6370function cacti_sizeof($array) { 6371 return ($array === false || !is_array($array)) ? 0 : sizeof($array); 6372} 6373 6374function cacti_count($array) { 6375 return ($array === false || !is_array($array)) ? 0 : count($array); 6376} 6377 6378function is_function_enabled($name) { 6379 return function_exists($name) && 6380 !in_array($name, array_map('trim', explode(', ', ini_get('disable_functions')))) && 6381 strtolower(ini_get('safe_mode')) != 1; 6382} 6383 6384function is_page_ajax() { 6385 if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' ) { 6386 return true; 6387 } 6388 6389 return false; 6390} 6391 6392function raise_ajax_permission_denied() { 6393 if (is_page_ajax()) { 6394 header('HTTP/1.1 401 ' . __('Permission Denied')); 6395 print __('You are not permitted to access this section of Cacti.') . ' ' . __('If you feel that this is an error. Please contact your Cacti Administrator.'); 6396 exit; 6397 } 6398} 6399 6400/** 6401 * cacti_session_start - Create a Cacti session from the settings set by the administrator 6402 * 6403 * @return - null 6404 */ 6405function cacti_session_start() { 6406 global $config; 6407 6408 /* initialize php session */ 6409 if (!function_exists('session_name')) { 6410 die('PHP Session Management is missing, please install PHP Session module'); 6411 } 6412 6413 session_name($config['cacti_session_name']); 6414 6415 $session_restart = ''; 6416 if (session_status() === PHP_SESSION_NONE) { 6417 $session_result = session_start($config['cookie_options']); 6418 } else { 6419 $session_restart = 're'; 6420 $session_result = session_start(); 6421 } 6422 6423 if (!$session_result) { 6424 cacti_log('Session "' . session_id() . '" ' . $session_restart . 'start failed! ' . cacti_debug_backtrace('', false, false, 0, 1), false, 'WARNING:'); 6425 } 6426} 6427 6428/** 6429 * cacti_session_close - Closes the open Cacti session if it is open 6430 * it can be re-opened afterwards in the case after a long running query 6431 * 6432 * @return - null 6433 */ 6434function cacti_session_close() { 6435 session_write_close(); 6436} 6437 6438/** 6439 * cacti_session_destroy - Destroys the login current session 6440 * 6441 * @return - null 6442 */ 6443function cacti_session_destroy() { 6444 session_unset(); 6445 session_destroy(); 6446} 6447 6448/** 6449 * cacti_cookie_set - Allows for settings an arbitry cookie name and value 6450 * used for CSRF protection. 6451 * 6452 * @return - null 6453 */ 6454function cacti_cookie_set($session, $val) { 6455 global $config; 6456 6457 if (isset($config['cookie_options']['cookie_domain'])) { 6458 $domain = $config['cookie_options']['cookie_domain']; 6459 } else { 6460 $domain = ''; 6461 } 6462 6463 if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') { 6464 setcookie($session, $val, time() + 3600, $config['url_path'], $domain, true, true); 6465 } else { 6466 setcookie($session, $val, time() + 3600, $config['url_path'], $domain, false, true); 6467 } 6468} 6469 6470/** 6471 * cacti_cookie_logout - Clears the Cacti and the 'keep me logged in' cookies 6472 * 6473 * @return - null 6474 */ 6475function cacti_cookie_logout() { 6476 global $config; 6477 6478 if (isset($config['cookie_options']['cookie_domain'])) { 6479 $domain = $config['cookie_options']['cookie_domain']; 6480 } else { 6481 $domain = ''; 6482 } 6483 6484 setcookie(session_name(), '', time() - 3600, $config['url_path'], $domain); 6485 setcookie('cacti_remembers', '', time() - 3600, $config['url_path'], $domain); 6486 6487 unset($_COOKIE[$config['cacti_session_name']]); 6488} 6489 6490/** 6491 * cacti_cookie_session_set - Sets the cacti 'keep me logged in' cookie 6492 * 6493 * @return - null 6494 */ 6495function cacti_cookie_session_set($user, $nssecret) { 6496 global $config; 6497 6498 if (isset($config['cookie_options']['cookie_domain'])) { 6499 $domain = $config['cookie_options']['cookie_domain']; 6500 } else { 6501 $domain = ''; 6502 } 6503 6504 $_SESSION['cacti_remembers'] = true; 6505 6506 if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') { 6507 setcookie('cacti_remembers', $user . ',' . $nssecret, time()+(86400*30), $config['url_path'], $domain, true, true); 6508 } else { 6509 setcookie('cacti_remembers', $user . ',' . $nssecret, time()+(86400*30), $config['url_path'], $domain, false, true); 6510 } 6511} 6512 6513/** 6514 * cacti_cookie_session_logout - Logs out of Cacti and the remember me session 6515 * 6516 * @return - null 6517 */ 6518function cacti_cookie_session_logout() { 6519 global $config; 6520 6521 if (isset($config['cookie_options']['cookie_domain'])) { 6522 $domain = $config['cookie_options']['cookie_domain']; 6523 } else { 6524 $domain = ''; 6525 } 6526 6527 setcookie('cacti_remembers', '', time() - 3600, $config['url_path'], $domain); 6528} 6529 6530/** 6531 * cacti_browser_zone_set - Set the PHP timezone to the 6532 * browsers timezone. 6533 * 6534 * @return - null 6535 */ 6536function cacti_browser_zone_set() { 6537 if (isset($_SESSION['sess_browser_php_tz'])) { 6538cacti_log('going there'); 6539 ini_set('date.timezone', $_SESSION['sess_browser_php_tz']); 6540 putenv('TZ=' . $_SESSION['sess_browser_system_tz']); 6541 } 6542} 6543 6544/** 6545 * cacti_system_zone_set - Set the PHP timezone to the 6546 * systems timezone. 6547 * 6548 * @return - null 6549 */ 6550function cacti_system_zone_set() { 6551 if (isset($_SESSION['sess_php_tz'])) { 6552cacti_log('and back again there'); 6553 ini_set('date.timezone', $_SESSION['sess_php_tz']); 6554 putenv('TZ=' . $_SESSION['sess_system_tz']); 6555 } 6556} 6557 6558/** 6559 * cacti_time_zone_set - Givin an offset in minutes, attempt 6560 * to set a PHP date.timezone. There are some oddballs that 6561 * we have to accomodate. 6562 * 6563 * @return - null 6564 */ 6565function cacti_time_zone_set($gmt_offset) { 6566 $hours = floor($gmt_offset / 60); 6567 $remaining = $gmt_offset % 60; 6568 6569 if (!isset($_SESSION['sess_php_tz'])) { 6570 $_SESSION['sess_php_tz'] = ini_get('date.timezone'); 6571 $_SESSION['sess_system_tz'] = getenv('TZ'); 6572 } 6573 6574 if ($remaining == 0) { 6575 putenv('TZ=GMT' . ($hours > 0 ? '-':'+') . abs($hours)); 6576 6577 $php_offset = 'Etc/GMT' . ($hours > 0 ? '-':'+') . abs($hours); 6578 $sys_offset = 'GMT' . ($hours > 0 ? '-':'+') . abs($hours); 6579 6580 ini_set('date.timezone', 'Etc/GMT' . ($hours > 0 ? '-':'+') . abs($hours)); 6581 6582 $_SESSION['sess_browser_system_tz'] = $sys_offset; 6583 $_SESSION['sess_browser_php_tz'] = $php_offset; 6584 } else { 6585 $time = ($hours > 0 ? '-':'+') . abs($hours) . ':' . substr('00' . $remaining, -2); 6586 6587 // Attempt to get the zone via the php function 6588 $zone = timezone_name_from_abbr('', $time); 6589 6590 if ($zone === false) { 6591 switch($time) { 6592 case '+3:30': 6593 $zone = 'IRST'; 6594 break; 6595 case '+4:30': 6596 $zone = 'IRDT'; 6597 break; 6598 case '+5:30': 6599 $zone = 'IST'; 6600 break; 6601 case '+5:45': 6602 $zone = 'NPT'; 6603 break; 6604 case '+6:30': 6605 $zone = 'CCT'; 6606 break; 6607 case '+9:30': 6608 $zone = 'ACST'; 6609 break; 6610 case '+10:30': 6611 $zone = 'ACDT'; 6612 break; 6613 case '+8:45': 6614 $zone = 'ACWST'; 6615 break; 6616 case '+12:45': 6617 $zone = 'CHAST'; 6618 break; 6619 case '+13:45': 6620 $zone = 'CHADT'; 6621 break; 6622 case '-3:30': 6623 $zone = 'NST'; 6624 break; 6625 case '-2:30': 6626 $zone = 'NDT'; 6627 break; 6628 case '-9:30': 6629 $zone = 'MART'; 6630 break; 6631 } 6632 6633 if ($zone !== false) { 6634 $zone = timezone_name_from_abbr($zone); 6635 } 6636 } 6637 6638 $php_offset = $zone; 6639 $sys_offset = 'GMT' . $time; 6640 6641 putenv('TZ=GMT' . $time); 6642 6643 if ($zone != '') { 6644 ini_set('date.timezone', $zone); 6645 } 6646 6647 $_SESSION['sess_browser_system_tz'] = $sys_offset; 6648 $_SESSION['sess_browser_php_tz'] = $php_offset; 6649 } 6650} 6651 6652