1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (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** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22/** 23 * Verify that function exists and can be called as a function. 24 * 25 * @param array $names 26 * 27 * @return bool 28 */ 29function zbx_is_callable(array $names) { 30 foreach ($names as $name) { 31 if (!is_callable($name)) { 32 return false; 33 } 34 } 35 36 return true; 37} 38 39/************ REQUEST ************/ 40function redirect($url) { 41 $curl = (new CUrl($url))->removeArgument('sid'); 42 header('Location: '.$curl->getUrl()); 43 exit; 44} 45 46function jsRedirect($url, $timeout = null) { 47 $script = is_numeric($timeout) 48 ? 'setTimeout(\'window.location="'.$url.'"\', '.($timeout * 1000).')' 49 : 'window.location.replace("'.$url.'");'; 50 51 insert_js($script); 52} 53 54/** 55 * Check if request exist. 56 * 57 * @param string $name 58 * 59 * @return bool 60 */ 61function hasRequest($name) { 62 return isset($_REQUEST[$name]); 63} 64 65/** 66 * Check request, if exist request - return request value, else return default value. 67 * 68 * @param string $name 69 * @param mixed $def 70 * 71 * @return mixed 72 */ 73function getRequest($name, $def = null) { 74 return isset($_REQUEST[$name]) ? $_REQUEST[$name] : $def; 75} 76 77function countRequest($str = null) { 78 if (!empty($str)) { 79 $count = 0; 80 81 foreach ($_REQUEST as $name => $value) { 82 if (strpos($name, $str) !== false) { 83 $count++; 84 } 85 } 86 87 return $count; 88 } 89 else { 90 return count($_REQUEST); 91 } 92} 93 94/************ COOKIES ************/ 95function get_cookie($name, $default_value = null) { 96 if (isset($_COOKIE[$name])) { 97 return $_COOKIE[$name]; 98 } 99 100 return $default_value; 101} 102 103function zbx_setcookie($name, $value, $time = null) { 104 setcookie($name, $value, isset($time) ? $time : 0, CSession::getDefaultCookiePath(), null, HTTPS, true); 105 $_COOKIE[$name] = $value; 106} 107 108function zbx_unsetcookie($name) { 109 zbx_setcookie($name, null, -99999); 110 unset($_COOKIE[$name]); 111} 112 113/************* DATE *************/ 114function getMonthCaption($num) { 115 switch ($num) { 116 case 1: return _('January'); 117 case 2: return _('February'); 118 case 3: return _('March'); 119 case 4: return _('April'); 120 case 5: return _('May'); 121 case 6: return _('June'); 122 case 7: return _('July'); 123 case 8: return _('August'); 124 case 9: return _('September'); 125 case 10: return _('October'); 126 case 11: return _('November'); 127 case 12: return _('December'); 128 } 129 130 return _s('[Wrong value for month: "%s" ]', $num); 131} 132 133function getDayOfWeekCaption($num) { 134 switch ($num) { 135 case 1: return _('Monday'); 136 case 2: return _('Tuesday'); 137 case 3: return _('Wednesday'); 138 case 4: return _('Thursday'); 139 case 5: return _('Friday'); 140 case 6: return _('Saturday'); 141 case 0: 142 case 7: return _('Sunday'); 143 } 144 145 return _s('[Wrong value for day: "%s" ]', $num); 146} 147 148// Convert seconds (0..SEC_PER_WEEK) to string representation. For example, 212400 -> 'Tuesday 11:00' 149function dowHrMinToStr($value, $display24Hours = false) { 150 $dow = $value - $value % SEC_PER_DAY; 151 $hr = $value - $dow; 152 $hr -= $hr % SEC_PER_HOUR; 153 $min = $value - $dow - $hr; 154 $min -= $min % SEC_PER_MIN; 155 156 $dow /= SEC_PER_DAY; 157 $hr /= SEC_PER_HOUR; 158 $min /= SEC_PER_MIN; 159 160 if ($display24Hours && $hr == 0 && $min == 0) { 161 $dow--; 162 $hr = 24; 163 } 164 165 return sprintf('%s %02d:%02d', getDayOfWeekCaption($dow), $hr, $min); 166} 167 168// Convert Day Of Week, Hours and Minutes to seconds representation. For example, 2 11:00 -> 212400. false if error occurred 169function dowHrMinToSec($dow, $hr, $min) { 170 if (zbx_empty($dow) || zbx_empty($hr) || zbx_empty($min) || !zbx_ctype_digit($dow) || !zbx_ctype_digit($hr) || !zbx_ctype_digit($min)) { 171 return false; 172 } 173 174 if ($dow == 7) { 175 $dow = 0; 176 } 177 178 if ($dow < 0 || $dow > 6) { 179 return false; 180 } 181 182 if ($hr < 0 || $hr > 24) { 183 return false; 184 } 185 186 if ($min < 0 || $min > 59) { 187 return false; 188 } 189 190 return $dow * SEC_PER_DAY + $hr * SEC_PER_HOUR + $min * SEC_PER_MIN; 191} 192 193// Convert timestamp to string representation. Return 'Never' if 0. 194function zbx_date2str($format, $value = null) { 195 static $weekdaynames, $weekdaynameslong, $months, $monthslong; 196 197 $prefix = ''; 198 199 if ($value === null) { 200 $value = time(); 201 } 202 elseif ($value > ZBX_MAX_DATE) { 203 $prefix = '> '; 204 $value = ZBX_MAX_DATE; 205 } 206 elseif (!$value) { 207 return _('Never'); 208 } 209 210 if (!is_array($weekdaynames)) { 211 $weekdaynames = [ 212 0 => _('Sun'), 213 1 => _('Mon'), 214 2 => _('Tue'), 215 3 => _('Wed'), 216 4 => _('Thu'), 217 5 => _('Fri'), 218 6 => _('Sat') 219 ]; 220 } 221 222 if (!is_array($weekdaynameslong)) { 223 $weekdaynameslong = [ 224 0 => _('Sunday'), 225 1 => _('Monday'), 226 2 => _('Tuesday'), 227 3 => _('Wednesday'), 228 4 => _('Thursday'), 229 5 => _('Friday'), 230 6 => _('Saturday') 231 ]; 232 } 233 234 if (!is_array($months)) { 235 $months = [ 236 1 => _('Jan'), 237 2 => _('Feb'), 238 3 => _('Mar'), 239 4 => _('Apr'), 240 5 => _x('May', 'May short'), 241 6 => _('Jun'), 242 7 => _('Jul'), 243 8 => _('Aug'), 244 9 => _('Sep'), 245 10 => _('Oct'), 246 11 => _('Nov'), 247 12 => _('Dec') 248 ]; 249 } 250 251 if (!is_array($monthslong)) { 252 $monthslong = [ 253 1 => _('January'), 254 2 => _('February'), 255 3 => _('March'), 256 4 => _('April'), 257 5 => _('May'), 258 6 => _('June'), 259 7 => _('July'), 260 8 => _('August'), 261 9 => _('September'), 262 10 => _('October'), 263 11 => _('November'), 264 12 => _('December') 265 ]; 266 } 267 268 $rplcs = [ 269 'l' => $weekdaynameslong[date('w', $value)], 270 'F' => $monthslong[date('n', $value)], 271 'D' => $weekdaynames[date('w', $value)], 272 'M' => $months[date('n', $value)] 273 ]; 274 275 $output = $part = ''; 276 $length = strlen($format); 277 278 for ($i = 0; $i < $length; $i++) { 279 $pchar = ($i > 0) ? substr($format, $i - 1, 1) : ''; 280 $char = substr($format, $i, 1); 281 282 if ($pchar != '\\' && isset($rplcs[$char])) { 283 $output .= (strlen($part) ? date($part, $value) : '').$rplcs[$char]; 284 $part = ''; 285 } 286 else { 287 $part .= $char; 288 } 289 } 290 291 $output .= (strlen($part) > 0) ? date($part, $value) : ''; 292 293 return $prefix.$output; 294} 295 296/** 297 * Calculates and converts timestamp to string representation. 298 * 299 * @param int|string $start_date Start date timestamp. 300 * @param int|string $end_date End date timestamp. 301 * 302 * @return string 303 */ 304function zbx_date2age($start_date, $end_date = 0) { 305 $end_date = ($end_date != 0) ? $end_date : time(); 306 307 return convertUnitsS($end_date - $start_date); 308} 309 310function zbxDateToTime($strdate) { 311 if (6 == sscanf($strdate, '%04d%02d%02d%02d%02d%02d', $year, $month, $date, $hours, $minutes, $seconds)) { 312 return mktime($hours, $minutes, $seconds, $month, $date, $year); 313 } 314 elseif (5 == sscanf($strdate, '%04d%02d%02d%02d%02d', $year, $month, $date, $hours, $minutes)) { 315 return mktime($hours, $minutes, 0, $month, $date, $year); 316 } 317 else { 318 return ($strdate && is_numeric($strdate)) ? $strdate : time(); 319 } 320} 321 322/** 323 * Correcting adding one unix timestamp to another. 324 * 325 * @param int $sec 326 * @param mixed $unixtime Can accept values: 327 * 1) int - unix timestamp, 328 * 2) string - date in YmdHis or YmdHi formats, 329 * 3) null - current unixtime stamp will be used 330 * 331 * @return int 332 */ 333function zbxAddSecondsToUnixtime($sec, $unixtime) { 334 return strtotime('+'.$sec.' seconds', zbxDateToTime($unixtime)); 335} 336 337/*************** CONVERTING ******************/ 338/** 339 * Convert the Windows new line (CR+LF) to Linux style line feed (LF). 340 * 341 * @param string $string Input string that will be converted. 342 * 343 * @return string 344 */ 345function CRLFtoLF($string) { 346 return str_replace("\r\n", "\n", $string); 347} 348 349function rgb2hex($color) { 350 $HEX = [ 351 dechex($color[0]), 352 dechex($color[1]), 353 dechex($color[2]) 354 ]; 355 foreach ($HEX as $id => $value) { 356 if (strlen($value) != 2) { 357 $HEX[$id] = '0'.$value; 358 } 359 } 360 361 return $HEX[0].$HEX[1].$HEX[2]; 362} 363 364function hex2rgb($color) { 365 if ($color[0] == '#') { 366 $color = substr($color, 1); 367 } 368 369 if (strlen($color) == 6) { 370 list($r, $g, $b) = [$color[0].$color[1], $color[2].$color[3], $color[4].$color[5]]; 371 } 372 elseif (strlen($color) == 3) { 373 list($r, $g, $b) = [$color[0].$color[0], $color[1].$color[1], $color[2].$color[2]]; 374 } 375 else { 376 return false; 377 } 378 379 return [hexdec($r), hexdec($g), hexdec($b)]; 380} 381 382function getColorVariations($color, $variations_requested = 1) { 383 if ($variations_requested <= 1) { 384 return [$color]; 385 } 386 387 $change = hex2rgb('#ffffff'); // Color which is increased/decreased in variations. 388 $max = 50; 389 390 $color = hex2rgb($color); 391 $variations = []; 392 393 $range = range(-1 * $max, $max, $max * 2 / $variations_requested); 394 395 // Remove redundant values. 396 while (count($range) > $variations_requested) { 397 (count($range) % 2) ? array_shift($range) : array_pop($range); 398 } 399 400 // Calculate colors. 401 foreach ($range as $var) { 402 $r = $color[0] + ($change[0] / 100 * $var); 403 $g = $color[1] + ($change[1] / 100 * $var); 404 $b = $color[2] + ($change[2] / 100 * $var); 405 406 $variations[] = '#' . rgb2hex([ 407 $r < 0 ? 0 : ($r > 255 ? 255 : (int) $r), 408 $g < 0 ? 0 : ($g > 255 ? 255 : (int) $g), 409 $b < 0 ? 0 : ($b > 255 ? 255 : (int) $b) 410 ]); 411 } 412 413 return $variations; 414} 415 416function zbx_num2bitstr($num, $rev = false) { 417 if (!is_numeric($num)) { 418 return 0; 419 } 420 421 $sbin = 0; 422 $strbin = ''; 423 424 $len = 32; 425 if (bccomp($num, ZBX_MAX_INT32) > 0) { 426 $len = 64; 427 } 428 429 for ($i = 0; $i < $len; $i++) { 430 $sbin = 1 << $i; 431 $bit = ($sbin & $num) ? '1' : '0'; 432 if ($rev) { 433 $strbin .= $bit; 434 } 435 else { 436 $strbin = $bit.$strbin; 437 } 438 } 439 440 return $strbin; 441} 442 443/** 444 * Converts strings like 2M or 5k to bytes. 445 * 446 * @param string $val 447 * 448 * @return int 449 */ 450function str2mem($val) { 451 $val = trim($val); 452 $last = strtolower(substr($val, -1)); 453 $val = (int) $val; 454 455 switch ($last) { 456 case 'g': 457 $val *= ZBX_GIBIBYTE; 458 break; 459 case 'm': 460 $val *= ZBX_MEBIBYTE; 461 break; 462 case 'k': 463 $val *= ZBX_KIBIBYTE; 464 break; 465 } 466 467 return $val; 468} 469 470/** 471 * Converts bytes into human-readable form. 472 * 473 * @param string|int $size 474 * 475 * @return string 476 */ 477function mem2str($size) { 478 $prefix = 'B'; 479 if ($size > ZBX_MEBIBYTE) { 480 $size = $size / ZBX_MEBIBYTE; 481 $prefix = 'M'; 482 } 483 elseif ($size > ZBX_KIBIBYTE) { 484 $size = $size / ZBX_KIBIBYTE; 485 $prefix = 'K'; 486 } 487 488 return round($size, ZBX_UNITS_ROUNDOFF_LOWER_LIMIT).$prefix; 489} 490 491function convertUnitsUptime($value) { 492 if (($secs = round($value)) < 0) { 493 $value = '-'; 494 $secs = -$secs; 495 } 496 else { 497 $value = ''; 498 } 499 500 $days = floor($secs / SEC_PER_DAY); 501 $secs -= $days * SEC_PER_DAY; 502 503 $hours = floor($secs / SEC_PER_HOUR); 504 $secs -= $hours * SEC_PER_HOUR; 505 506 $mins = floor($secs / SEC_PER_MIN); 507 $secs -= $mins * SEC_PER_MIN; 508 509 if ($days != 0) { 510 $value .= _n('%1$d day', '%1$d days', $days).', '; 511 } 512 $value .= sprintf('%02d:%02d:%02d', $hours, $mins, $secs); 513 514 return $value; 515} 516 517/** 518 * Converts a time period to a human-readable format. 519 * 520 * The following units are used: years, months, days, hours, minutes, seconds and milliseconds. 521 * 522 * Only the three highest units are displayed: #y #m #d, #m #d #h, #d #h #mm and so on. 523 * 524 * If some value is equal to zero, it is omitted. For example, if the period is 1y 0m 4d, it will be displayed as 525 * 1y 4d, not 1y 0m 4d or 1y 4d #h. 526 * 527 * @param int $value Time period in seconds. 528 * @param bool $ignore_millisec Without ms (1s 200 ms = 1.2s). 529 * 530 * @return string 531 */ 532function convertUnitsS($value, $ignore_millisec = false) { 533 $secs = round($value * 1000, ZBX_UNITS_ROUNDOFF_UPPER_LIMIT) / 1000; 534 if ($secs < 0) { 535 $secs = -$secs; 536 $str = '-'; 537 } 538 else { 539 $str = ''; 540 } 541 542 $values = ['y' => null, 'm' => null, 'd' => null, 'h' => null, 'mm' => null, 's' => null, 'ms' => null]; 543 544 /* 545 * $n_unit == 4, (#y #m #d) 546 * $n_unit == 3, (#m #d #h) 547 * $n_unit == 2, (#d #h #mm) 548 * $n_unit == 1, (#h #mm #s) 549 * $n_unit == 0, (#mm #s) or (#mm #s #ms) 550 */ 551 $n_unit = 0; 552 553 $n = floor($secs / SEC_PER_YEAR); 554 if ($n != 0) { 555 $secs -= $n * SEC_PER_YEAR; 556 $n_unit = 4; 557 558 $values['y'] = $n; 559 } 560 561 $n = floor($secs / SEC_PER_MONTH); 562 $secs -= $n * SEC_PER_MONTH; 563 564 if ($n == 12) { 565 $values['y']++; 566 } 567 else { 568 if ($n != 0) { 569 $values['m'] = $n; 570 if ($n_unit == 0) { 571 $n_unit = 3; 572 } 573 } 574 575 $n = floor($secs / SEC_PER_DAY); 576 if ($n != 0) { 577 $secs -= $n * SEC_PER_DAY; 578 $values['d'] = $n; 579 if ($n_unit == 0) { 580 $n_unit = 2; 581 } 582 } 583 584 $n = floor($secs / SEC_PER_HOUR); 585 if ($n_unit < 4 && $n != 0) { 586 $secs -= $n * SEC_PER_HOUR; 587 $values['h'] = $n; 588 if ($n_unit == 0) { 589 $n_unit = 1; 590 } 591 } 592 593 $n = floor($secs / SEC_PER_MIN); 594 if ($n_unit < 3 && $n != 0) { 595 $secs -= $n * SEC_PER_MIN; 596 $values['mm'] = $n; 597 } 598 599 $n = floor($secs); 600 if ($n_unit < 2 && $n != 0) { 601 $secs -= $n; 602 $values['s'] = $n; 603 } 604 605 if ($ignore_millisec) { 606 $n = round($secs, ZBX_UNITS_ROUNDOFF_UPPER_LIMIT); 607 if ($n_unit < 1 && $n != 0) { 608 $values['s'] += $n; 609 } 610 } 611 else { 612 $n = round($secs * 1000, ZBX_UNITS_ROUNDOFF_UPPER_LIMIT); 613 if ($n_unit < 1 && $n != 0) { 614 $values['ms'] = $n; 615 } 616 } 617 } 618 619 $units = [ 620 'y' => _x('y', 'year short'), 621 'm' => _x('m', 'month short'), 622 'd' => _x('d', 'day short'), 623 'h' => _x('h', 'hour short'), 624 'mm' => _x('m', 'minute short'), 625 's' => _x('s', 'second short'), 626 'ms' => _x('ms', 'millisecond short') 627 ]; 628 629 foreach (array_filter($values) as $unit => $value) { 630 $str .= ' '.$value.$units[$unit]; 631 } 632 633 return $str ? trim($str) : '0'; 634} 635 636/** 637 * Converts value to actual value. 638 * Example: 639 * 6442450944 B convert to 6 GB 640 * 641 * @param array $options 642 * @param string $options['value'] 643 * @param string $options['units'] 644 * @param string $options['convert'] 645 * @param string $options['byteStep'] 646 * @param string $options['pow'] 647 * @param bool $options['ignoreMillisec'] 648 * @param string $options['length'] 649 * 650 * @return string 651 */ 652function convert_units($options = []) { 653 $defOptions = [ 654 'value' => null, 655 'units' => null, 656 'convert' => ITEM_CONVERT_WITH_UNITS, 657 'byteStep' => false, 658 'pow' => false, 659 'ignoreMillisec' => false, 660 'length' => false 661 ]; 662 663 $options = zbx_array_merge($defOptions, $options); 664 665 // special processing for unix timestamps 666 if ($options['units'] == 'unixtime') { 667 return zbx_date2str(DATE_TIME_FORMAT_SECONDS, $options['value']); 668 } 669 670 // special processing of uptime 671 if ($options['units'] == 'uptime') { 672 return convertUnitsUptime($options['value']); 673 } 674 675 // special processing for seconds 676 if ($options['units'] == 's') { 677 return convertUnitsS($options['value'], $options['ignoreMillisec']); 678 } 679 680 // black list of units that should have no multiplier prefix (K, M, G etc) applied 681 $blackList = ['%', 'ms', 'rpm', 'RPM']; 682 683 // add to the blacklist if unit is prefixed with '!' 684 if ($options['units'] !== null && $options['units'] !== '' && $options['units'][0] === '!') { 685 $options['units'] = substr($options['units'], 1); 686 $blackList[] = $options['units']; 687 } 688 689 // any other unit 690 if (in_array($options['units'], $blackList) || (zbx_empty($options['units']) 691 && ($options['convert'] == ITEM_CONVERT_WITH_UNITS))) { 692 if (preg_match('/\.\d+$/', $options['value'])) { 693 $format = (abs($options['value']) >= ZBX_UNITS_ROUNDOFF_THRESHOLD) 694 ? '%.'.ZBX_UNITS_ROUNDOFF_MIDDLE_LIMIT.'f' 695 : '%.'.ZBX_UNITS_ROUNDOFF_LOWER_LIMIT.'f'; 696 $options['value'] = sprintf($format, $options['value']); 697 } 698 $options['value'] = preg_replace('/^([\-0-9]+)(\.)([0-9]*)[0]+$/U', '$1$2$3', $options['value']); 699 $options['value'] = rtrim($options['value'], '.'); 700 701 return trim($options['value'].' '.$options['units']); 702 } 703 704 // if one or more items is B or Bps, then Y-scale use base 8 and calculated in bytes 705 if ($options['byteStep']) { 706 $step = ZBX_KIBIBYTE; 707 } 708 else { 709 switch ($options['units']) { 710 case 'Bps': 711 case 'B': 712 $step = ZBX_KIBIBYTE; 713 $options['convert'] = $options['convert'] ? $options['convert'] : ITEM_CONVERT_NO_UNITS; 714 break; 715 case 'b': 716 case 'bps': 717 $options['convert'] = $options['convert'] ? $options['convert'] : ITEM_CONVERT_NO_UNITS; 718 default: 719 $step = 1000; 720 } 721 } 722 723 if ($options['value'] < 0) { 724 $abs = bcmul($options['value'], '-1'); 725 } 726 else { 727 $abs = $options['value']; 728 } 729 730 if (bccomp($abs, 1) == -1) { 731 $options['value'] = round($options['value'], ZBX_UNITS_ROUNDOFF_MIDDLE_LIMIT); 732 $options['value'] = ($options['length'] && $options['value'] != 0) 733 ? sprintf('%.'.$options['length'].'f',$options['value']) : $options['value']; 734 735 return trim($options['value'].' '.$options['units']); 736 } 737 738 // init intervals 739 static $digitUnits; 740 if (is_null($digitUnits)) { 741 $digitUnits = []; 742 } 743 744 if (!isset($digitUnits[$step])) { 745 $digitUnits[$step] = [ 746 ['pow' => 0, 'short' => ''], 747 ['pow' => 1, 'short' => 'K'], 748 ['pow' => 2, 'short' => 'M'], 749 ['pow' => 3, 'short' => 'G'], 750 ['pow' => 4, 'short' => 'T'], 751 ['pow' => 5, 'short' => 'P'], 752 ['pow' => 6, 'short' => 'E'], 753 ['pow' => 7, 'short' => 'Z'], 754 ['pow' => 8, 'short' => 'Y'] 755 ]; 756 757 foreach ($digitUnits[$step] as $dunit => $data) { 758 // skip milli & micro for values without units 759 $digitUnits[$step][$dunit]['value'] = bcpow($step, $data['pow'], 9); 760 } 761 } 762 763 764 $valUnit = ['pow' => 0, 'short' => '', 'value' => $options['value']]; 765 766 if ($options['pow'] === false || $options['value'] == 0) { 767 foreach ($digitUnits[$step] as $dnum => $data) { 768 if (bccomp($abs, $data['value']) > -1) { 769 $valUnit = $data; 770 } 771 else { 772 break; 773 } 774 } 775 } 776 else { 777 foreach ($digitUnits[$step] as $data) { 778 if ($options['pow'] == $data['pow']) { 779 $valUnit = $data; 780 break; 781 } 782 } 783 } 784 785 if (round($valUnit['value'], ZBX_UNITS_ROUNDOFF_MIDDLE_LIMIT) > 0) { 786 $valUnit['value'] = bcdiv(sprintf('%.10f',$options['value']), sprintf('%.10f', $valUnit['value']) 787 , ZBX_PRECISION_10); 788 } 789 else { 790 $valUnit['value'] = 0; 791 } 792 793 switch ($options['convert']) { 794 case 0: $options['units'] = trim($options['units']); 795 case 1: $desc = $valUnit['short']; break; 796 } 797 798 $options['value'] = preg_replace('/^([\-0-9]+)(\.)([0-9]*)[0]+$/U','$1$2$3', round($valUnit['value'], 799 ZBX_UNITS_ROUNDOFF_UPPER_LIMIT)); 800 801 $options['value'] = rtrim($options['value'], '.'); 802 803 // fix negative zero 804 if (bccomp($options['value'], 0) == 0) { 805 $options['value'] = 0; 806 } 807 808 return trim(sprintf('%s %s%s', $options['length'] 809 ? sprintf('%.'.$options['length'].'f',$options['value']) 810 : $options['value'], $desc, $options['units'])); 811} 812 813/** 814 * Convert time format with suffixes to seconds. 815 * Examples: 816 * 10m = 600 817 * 3d = 10800 818 * -10m = -600 819 * 820 * @param string $time 821 * 822 * @return null|string 823 */ 824function timeUnitToSeconds($time) { 825 preg_match('/^(?<sign>[\-+])?(?<number>(\d)+)(?<suffix>['.ZBX_TIME_SUFFIXES.'])?$/', $time, $matches); 826 827 $is_negative = (array_key_exists('sign', $matches) && $matches['sign'] === '-'); 828 829 if (!array_key_exists('number', $matches)) { 830 return null; 831 } 832 833 if (array_key_exists('suffix', $matches)) { 834 $time = $matches['number']; 835 836 switch ($matches['suffix']) { 837 case 's': 838 $sec = $time; 839 break; 840 case 'm': 841 $sec = bcmul($time, SEC_PER_MIN); 842 break; 843 case 'h': 844 $sec = bcmul($time, SEC_PER_HOUR); 845 break; 846 case 'd': 847 $sec = bcmul($time, SEC_PER_DAY); 848 break; 849 case 'w': 850 $sec = bcmul($time, SEC_PER_WEEK); 851 break; 852 } 853 } 854 else { 855 $sec = $matches['number']; 856 } 857 858 return $is_negative ? bcmul($sec, -1) : $sec; 859} 860 861/** 862 * Converts value with suffix to actual value. 863 * Supported time suffixes: s, m, h, d, w 864 * Supported metric suffixes: K, M, G, T 865 * 866 * @param string $value 867 * @param int $scale The number of digits after the decimal place in the result. 868 * 869 * @return string 870 */ 871function convertFunctionValue($value, $scale = 0) { 872 $suffix = substr($value, -1); 873 874 if (ctype_digit($suffix)) { 875 return $value; 876 } 877 878 $value = substr($value, 0, -1); 879 880 switch ($suffix) { 881 case 'm': 882 return bcmul($value, '60', $scale); 883 884 case 'h': 885 return bcmul($value, '3600', $scale); 886 887 case 'd': 888 return bcmul($value, '86400', $scale); 889 890 case 'w': 891 return bcmul($value, '604800', $scale); 892 893 case 'K': 894 return bcmul($value, ZBX_KIBIBYTE, $scale); 895 896 case 'M': 897 return bcmul($value, ZBX_MEBIBYTE, $scale); 898 899 case 'G': 900 return bcmul($value, ZBX_GIBIBYTE, $scale); 901 902 case 'T': 903 return bcmul($value, '1099511627776', $scale); 904 905 case 's': 906 default: 907 return $value; 908 } 909} 910 911/************* ZBX MISC *************/ 912 913/** 914 * Swap two values. 915 * 916 * @param mixed $a first value 917 * @param mixed $b second value 918 */ 919function zbx_swap(&$a, &$b) { 920 $tmp = $a; 921 $a = $b; 922 $b = $tmp; 923} 924 925function zbx_avg($values) { 926 zbx_value2array($values); 927 $sum = 0; 928 foreach ($values as $value) { 929 $sum = bcadd($sum, $value); 930 } 931 932 return bcdiv($sum, count($values)); 933} 934 935/** 936 * Check if every character in given string value is a decimal digit. 937 * 938 * @param string | int $x Value to check. 939 * 940 * @return boolean 941 */ 942function zbx_ctype_digit($x) { 943 return ctype_digit(strval($x)); 944} 945 946/** 947 * Returns true if the value is an empty string, empty array or null. 948 * 949 * @deprecated use strict comparison instead 950 * 951 * @param $value 952 * 953 * @return bool 954 */ 955function zbx_empty($value) { 956 if ($value === null) { 957 return true; 958 } 959 if (is_array($value) && empty($value)) { 960 return true; 961 } 962 if (is_string($value) && $value === '') { 963 return true; 964 } 965 966 return false; 967} 968 969function zbx_is_int($var) { 970 if (is_int($var)) { 971 return true; 972 } 973 974 if (is_string($var)) { 975 if (function_exists('ctype_digit') && ctype_digit($var) || strcmp(intval($var), $var) == 0) { 976 return true; 977 } 978 } 979 else { 980 if ($var > 0 && zbx_ctype_digit($var)) { 981 return true; 982 } 983 } 984 985 return preg_match("/^\-?\d{1,20}+$/", $var); 986} 987 988/** 989 * Look for two arrays field value and create 3 array lists, one with arrays where field value exists only in first array 990 * second with arrays where field values are only in second array and both where field values are in both arrays. 991 * 992 * @param array $primary 993 * @param array $secondary 994 * @param string $field field that is searched in arrays 995 * 996 * @return array 997 */ 998function zbx_array_diff(array $primary, array $secondary, $field) { 999 $fields1 = zbx_objectValues($primary, $field); 1000 $fields2 = zbx_objectValues($secondary, $field); 1001 1002 $first = array_diff($fields1, $fields2); 1003 $first = zbx_toHash($first); 1004 1005 $second = array_diff($fields2, $fields1); 1006 $second = zbx_toHash($second); 1007 1008 $result = [ 1009 'first' => [], 1010 'second' => [], 1011 'both' => [] 1012 ]; 1013 1014 foreach ($primary as $array) { 1015 if (!isset($array[$field])) { 1016 $result['first'][] = $array; 1017 } 1018 elseif (isset($first[$array[$field]])) { 1019 $result['first'][] = $array; 1020 } 1021 else { 1022 $result['both'][$array[$field]] = $array; 1023 } 1024 } 1025 1026 foreach ($secondary as $array) { 1027 if (!isset($array[$field])) { 1028 $result['second'][] = $array; 1029 } 1030 elseif (isset($second[$array[$field]])) { 1031 $result['second'][] = $array; 1032 } 1033 } 1034 1035 return $result; 1036} 1037 1038function zbx_array_push(&$array, $add) { 1039 foreach ($array as $key => $value) { 1040 foreach ($add as $newKey => $newValue) { 1041 $array[$key][$newKey] = $newValue; 1042 } 1043 } 1044} 1045 1046/** 1047 * Find if array has any duplicate values and return an array with info about them. 1048 * In case of no duplicates, empty array is returned. 1049 * Example of usage: 1050 * $result = zbx_arrayFindDuplicates( 1051 * array('a', 'b', 'c', 'c', 'd', 'd', 'd', 'e') 1052 * ); 1053 * array( 1054 * 'd' => 3, 1055 * 'c' => 2, 1056 * ) 1057 * 1058 * @param array $array 1059 * 1060 * @return array 1061 */ 1062function zbx_arrayFindDuplicates(array $array) { 1063 $countValues = array_count_values($array); // counting occurrences of every value in array 1064 foreach ($countValues as $value => $count) { 1065 if ($count <= 1) { 1066 unset($countValues[$value]); 1067 } 1068 } 1069 arsort($countValues); // sorting, so that the most duplicates would be at the top 1070 1071 return $countValues; 1072} 1073 1074/************* STRING *************/ 1075function zbx_nl2br($str) { 1076 $str_res = []; 1077 foreach (explode("\n", $str) as $str_line) { 1078 array_push($str_res, $str_line, BR()); 1079 } 1080 array_pop($str_res); 1081 1082 return $str_res; 1083} 1084 1085function zbx_formatDomId($value) { 1086 return str_replace(['[', ']'], ['_', ''], $value); 1087} 1088 1089/** 1090 * Sort an array of objects so that the objects whose $column value matches $pattern are at the top. 1091 * Return the first $limit objects. 1092 * 1093 * @param array $table array of objects to sort 1094 * @param string $column name of the $column to search 1095 * @param string $pattern string to match the value of $column against 1096 * @param int $limit number of objects to return 1097 * 1098 * @return array 1099 */ 1100function selectByPattern(array $table, $column, $pattern, $limit) { 1101 $chunk_size = $limit; 1102 1103 $rsTable = []; 1104 foreach ($table as $num => $row) { 1105 if (mb_strtolower($row[$column]) === mb_strtolower($pattern)) { 1106 $rsTable = [$num => $row] + $rsTable; 1107 } 1108 elseif ($limit > 0) { 1109 $rsTable[$num] = $row; 1110 } 1111 else { 1112 continue; 1113 } 1114 $limit--; 1115 } 1116 1117 if (!empty($rsTable)) { 1118 $rsTable = array_chunk($rsTable, $chunk_size, true); 1119 $rsTable = $rsTable[0]; 1120 } 1121 1122 return $rsTable; 1123} 1124 1125/************* SORT *************/ 1126function natksort(&$array) { 1127 $keys = array_keys($array); 1128 natcasesort($keys); 1129 1130 $new_array = []; 1131 1132 foreach ($keys as $k) { 1133 $new_array[$k] = $array[$k]; 1134 } 1135 1136 $array = $new_array; 1137 1138 return true; 1139} 1140 1141// recursively sort an array by key 1142function zbx_rksort(&$array, $flags = null) { 1143 if (is_array($array)) { 1144 foreach ($array as $id => $data) { 1145 zbx_rksort($array[$id]); 1146 } 1147 ksort($array, $flags); 1148 } 1149 1150 return $array; 1151} 1152 1153/** 1154 * Sorts the data using a natural sort algorithm. 1155 * 1156 * Not suitable for sorting macros, use order_macros() instead. 1157 * 1158 * @param $data 1159 * @param null $sortfield 1160 * @param string $sortorder 1161 * 1162 * @return bool 1163 * 1164 * @see order_macros() 1165 */ 1166function order_result(&$data, $sortfield = null, $sortorder = ZBX_SORT_UP) { 1167 if (empty($data)) { 1168 return false; 1169 } 1170 1171 if (is_null($sortfield)) { 1172 natcasesort($data); 1173 if ($sortorder != ZBX_SORT_UP) { 1174 $data = array_reverse($data, true); 1175 } 1176 return true; 1177 } 1178 1179 $sort = []; 1180 foreach ($data as $key => $arr) { 1181 if (!isset($arr[$sortfield])) { 1182 return false; 1183 } 1184 $sort[$key] = $arr[$sortfield]; 1185 } 1186 natcasesort($sort); 1187 1188 if ($sortorder != ZBX_SORT_UP) { 1189 $sort = array_reverse($sort, true); 1190 } 1191 1192 $tmp = $data; 1193 $data = []; 1194 foreach ($sort as $key => $val) { 1195 $data[$key] = $tmp[$key]; 1196 } 1197 1198 return true; 1199} 1200 1201/** 1202 * Sorts the macros in the given order. Supports user and LLD macros. 1203 * 1204 * order_result() is not suitable for sorting macros, because it treats the "}" as a symbol with a lower priority 1205 * then any alphanumeric character, and the result will be invalid. 1206 * 1207 * E.g: order_result() will sort array('{$DD}', '{$D}', '{$D1}') as 1208 * array('{$D1}', '{$DD}', '{$D}') while the correct result is array('{$D}', '{$D1}', '{$DD}'). 1209 * 1210 * @param array $macros 1211 * @param string $sortfield 1212 * @param string $order 1213 * 1214 * @return array 1215 */ 1216function order_macros(array $macros, $sortfield, $order = ZBX_SORT_UP) { 1217 $temp = []; 1218 foreach ($macros as $key => $macro) { 1219 $temp[$key] = substr($macro[$sortfield], 2, strlen($macro[$sortfield]) - 3); 1220 } 1221 order_result($temp, null, $order); 1222 1223 $rs = []; 1224 foreach ($temp as $key => $macroLabel) { 1225 $rs[$key] = $macros[$key]; 1226 } 1227 1228 return $rs; 1229} 1230 1231// preserve keys 1232function zbx_array_merge() { 1233 $args = func_get_args(); 1234 $result = []; 1235 foreach ($args as &$array) { 1236 if (!is_array($array)) { 1237 return false; 1238 } 1239 foreach ($array as $key => $value) { 1240 $result[$key] = $value; 1241 } 1242 } 1243 unset($array); 1244 1245 return $result; 1246} 1247 1248function uint_in_array($needle, $haystack) { 1249 foreach ($haystack as $value) { 1250 if (bccomp($needle, $value) == 0) { 1251 return true; 1252 } 1253 } 1254 1255 return false; 1256} 1257 1258function str_in_array($needle, $haystack, $strict = false) { 1259 if (is_array($needle)) { 1260 return in_array($needle, $haystack, $strict); 1261 } 1262 elseif ($strict) { 1263 foreach ($haystack as $value) { 1264 if ($needle === $value) { 1265 return true; 1266 } 1267 } 1268 } 1269 else { 1270 foreach ($haystack as $value) { 1271 if (strcmp($needle, $value) == 0) { 1272 return true; 1273 } 1274 } 1275 } 1276 1277 return false; 1278} 1279 1280function zbx_value2array(&$values) { 1281 if (!is_array($values) && !is_null($values)) { 1282 $tmp = []; 1283 if (is_object($values)) { 1284 $tmp[] = $values; 1285 } 1286 else { 1287 $tmp[$values] = $values; 1288 } 1289 $values = $tmp; 1290 } 1291} 1292 1293// creates chain of relation parent -> child, for all chain levels 1294function createParentToChildRelation(&$chain, $link, $parentField, $childField) { 1295 if (!isset($chain[$link[$parentField]])) { 1296 $chain[$link[$parentField]] = []; 1297 } 1298 1299 $chain[$link[$parentField]][$link[$childField]] = $link[$childField]; 1300 if (isset($chain[$link[$childField]])) { 1301 $chain[$link[$parentField]] = zbx_array_merge($chain[$link[$parentField]], $chain[$link[$childField]]); 1302 } 1303} 1304 1305// object or array of objects to hash 1306function zbx_toHash($value, $field = null) { 1307 if (is_null($value)) { 1308 return $value; 1309 } 1310 $result = []; 1311 1312 if (!is_array($value)) { 1313 $result = [$value => $value]; 1314 } 1315 elseif (isset($value[$field])) { 1316 $result[$value[$field]] = $value; 1317 } 1318 else { 1319 foreach ($value as $val) { 1320 if (!is_array($val)) { 1321 $result[$val] = $val; 1322 } 1323 elseif (isset($val[$field])) { 1324 $result[$val[$field]] = $val; 1325 } 1326 } 1327 } 1328 1329 return $result; 1330} 1331 1332/** 1333 * Transforms a single or an array of values to an array of objects, where the values are stored under the $field 1334 * key. 1335 * 1336 * E.g: 1337 * zbx_toObject(array(1, 2), 'hostid') // returns array(array('hostid' => 1), array('hostid' => 2)) 1338 * zbx_toObject(3, 'hostid') // returns array(array('hostid' => 3)) 1339 * zbx_toObject(array('a' => 1), 'hostid', true) // returns array('a' => array('hostid' => 1)) 1340 * 1341 * @param $value 1342 * @param $field 1343 * @param $preserve_keys 1344 * 1345 * @return array 1346 */ 1347function zbx_toObject($value, $field, $preserve_keys = false) { 1348 if (is_null($value)) { 1349 return $value; 1350 } 1351 $result = []; 1352 1353 // Value or Array to Object or Array of objects 1354 if (!is_array($value)) { 1355 $result = [[$field => $value]]; 1356 } 1357 elseif (!isset($value[$field])) { 1358 foreach ($value as $key => $val) { 1359 if (!is_array($val)) { 1360 $result[$key] = [$field => $val]; 1361 } 1362 } 1363 1364 if (!$preserve_keys) { 1365 $result = array_values($result); 1366 } 1367 } 1368 1369 return $result; 1370} 1371 1372/** 1373 * Converts the given value to a numeric array: 1374 * - a scalar value will be converted to an array and added as the only element; 1375 * - an array with first element key containing only numeric characters will be converted to plain zero-based numeric array. 1376 * This is used for resetting nonsequential numeric arrays; 1377 * - an associative array will be returned in an array as the only element, except if first element key contains only numeric characters. 1378 * 1379 * @param mixed $value 1380 * 1381 * @return array 1382 */ 1383function zbx_toArray($value) { 1384 if ($value === null) { 1385 return $value; 1386 } 1387 1388 if (is_array($value)) { 1389 // reset() is needed to move internal array pointer to the beginning of the array 1390 reset($value); 1391 1392 if (zbx_ctype_digit(key($value))) { 1393 $result = array_values($value); 1394 } 1395 elseif (!empty($value)) { 1396 $result = [$value]; 1397 } 1398 else { 1399 $result = []; 1400 } 1401 } 1402 else { 1403 $result = [$value]; 1404 } 1405 1406 return $result; 1407} 1408 1409// value OR object OR array of objects TO an array 1410function zbx_objectValues($value, $field) { 1411 if (is_null($value)) { 1412 return $value; 1413 } 1414 1415 if (!is_array($value)) { 1416 $result = [$value]; 1417 } 1418 elseif (isset($value[$field])) { 1419 $result = [$value[$field]]; 1420 } 1421 else { 1422 $result = []; 1423 1424 foreach ($value as $val) { 1425 if (!is_array($val)) { 1426 $result[] = $val; 1427 } 1428 elseif (isset($val[$field])) { 1429 $result[] = $val[$field]; 1430 } 1431 } 1432 } 1433 1434 return $result; 1435} 1436 1437function zbx_cleanHashes(&$value) { 1438 if (is_array($value)) { 1439 // reset() is needed to move internal array pointer to the beginning of the array 1440 reset($value); 1441 if (zbx_ctype_digit(key($value))) { 1442 $value = array_values($value); 1443 } 1444 } 1445 1446 return $value; 1447} 1448 1449function zbx_toCSV($values) { 1450 $csv = ''; 1451 $glue = '","'; 1452 foreach ($values as $row) { 1453 if (!is_array($row)) { 1454 $row = [$row]; 1455 } 1456 foreach ($row as $num => $value) { 1457 if (is_null($value)) { 1458 unset($row[$num]); 1459 } 1460 else { 1461 $row[$num] = str_replace('"', '""', $value); 1462 } 1463 } 1464 $csv .= '"'.implode($glue, $row).'"'."\n"; 1465 } 1466 1467 return $csv; 1468} 1469 1470function zbx_array_mintersect($keys, $array) { 1471 $result = []; 1472 1473 foreach ($keys as $field) { 1474 if (is_array($field)) { 1475 foreach ($field as $sub_field) { 1476 if (isset($array[$sub_field])) { 1477 $result[$sub_field] = $array[$sub_field]; 1478 break; 1479 } 1480 } 1481 } 1482 elseif (isset($array[$field])) { 1483 $result[$field] = $array[$field]; 1484 } 1485 } 1486 1487 return $result; 1488} 1489 1490function zbx_str2links($text) { 1491 $result = []; 1492 1493 foreach (explode("\n", $text) as $line) { 1494 $line = rtrim($line, "\r "); 1495 1496 preg_match_all('#https?://[^\n\t\r ]+#u', $line, $matches); 1497 1498 $start = 0; 1499 1500 foreach ($matches[0] as $match) { 1501 if (($pos = mb_strpos($line, $match, $start)) !== false) { 1502 if ($pos != $start) { 1503 $result[] = mb_substr($line, $start, $pos - $start); 1504 } 1505 $result[] = new CLink(CHtml::encode($match), $match); 1506 $start = $pos + mb_strlen($match); 1507 } 1508 } 1509 1510 if (mb_strlen($line) != $start) { 1511 $result[] = mb_substr($line, $start); 1512 } 1513 1514 $result[] = BR(); 1515 } 1516 1517 array_pop($result); 1518 1519 return $result; 1520} 1521 1522function zbx_subarray_push(&$mainArray, $sIndex, $element = null, $key = null) { 1523 if (!isset($mainArray[$sIndex])) { 1524 $mainArray[$sIndex] = []; 1525 } 1526 if ($key) { 1527 $mainArray[$sIndex][$key] = is_null($element) ? $sIndex : $element; 1528 } 1529 else { 1530 $mainArray[$sIndex][] = is_null($element) ? $sIndex : $element; 1531 } 1532} 1533 1534/*************** PAGE SORTING ******************/ 1535 1536/** 1537 * Returns header with sorting options. 1538 * 1539 * @param string obj Header item. 1540 * @param string $tabfield Table field. 1541 * @param string $sortField Sorting field. 1542 * @param string $sortOrder Sorting order. 1543 * @param string $link Sorting link. 1544 * 1545 * @return CColHeader 1546 */ 1547function make_sorting_header($obj, $tabfield, $sortField, $sortOrder, $link = null) { 1548 $sortorder = ($sortField == $tabfield && $sortOrder == ZBX_SORT_UP) ? ZBX_SORT_DOWN : ZBX_SORT_UP; 1549 1550 $link = CUrlFactory::getContextUrl($link); 1551 1552 $link->setArgument('sort', $tabfield); 1553 $link->setArgument('sortorder', $sortorder); 1554 1555 zbx_value2array($obj); 1556 1557 $arrow = null; 1558 if ($tabfield == $sortField) { 1559 if ($sortorder == ZBX_SORT_UP) { 1560 $arrow = (new CSpan())->addClass(ZBX_STYLE_ARROW_DOWN); 1561 } 1562 else { 1563 $arrow = (new CSpan())->addClass(ZBX_STYLE_ARROW_UP); 1564 } 1565 } 1566 1567 return new CColHeader(new CLink([$obj, $arrow], $link->getUrl())); 1568} 1569 1570/** 1571 * Returns the list page number for the current page. 1572 * 1573 * The functions first looks for a page number in the HTTP request. If no number is given, falls back to the profile. 1574 * Defaults to 1. 1575 * 1576 * @return int 1577 */ 1578function getPageNumber() { 1579 global $page; 1580 1581 $pageNumber = getRequest('page'); 1582 if (!$pageNumber) { 1583 $lastPage = CProfile::get('web.paging.lastpage'); 1584 // For MVC pages $page is not set so we use action instead 1585 if (isset($page['file']) && $lastPage == $page['file']) { 1586 $pageNumber = CProfile::get('web.paging.page', 1); 1587 } 1588 elseif (isset($_REQUEST['action']) && $lastPage == $_REQUEST['action']) { 1589 $pageNumber = CProfile::get('web.paging.page', 1); 1590 } 1591 else { 1592 $pageNumber = 1; 1593 } 1594 } 1595 1596 return $pageNumber; 1597} 1598 1599/** 1600 * Returns paging line and recursively slice $items of current page. 1601 * 1602 * @param array $items list of elements 1603 * @param string $sortorder the order in which items are sorted ASC or DESC 1604 * @param CUrl $url URL object containing arguments and query 1605 * 1606 * @return CDiv 1607 */ 1608function getPagingLine(&$items, $sortorder, CUrl $url) { 1609 global $page; 1610 1611 $rowsPerPage = (int) CWebUser::$data['rows_per_page']; 1612 $config = select_config(); 1613 1614 $itemsCount = count($items); 1615 $limit_exceeded = ($config['search_limit'] < $itemsCount); 1616 $offset = 0; 1617 1618 if ($limit_exceeded) { 1619 if ($sortorder == ZBX_SORT_DOWN) { 1620 $offset = $itemsCount - $config['search_limit']; 1621 } 1622 $itemsCount = $config['search_limit']; 1623 } 1624 1625 $pagesCount = ($itemsCount > 0) ? ceil($itemsCount / $rowsPerPage) : 1; 1626 $currentPage = getPageNumber(); 1627 1628 if ($currentPage < 1) { 1629 $currentPage = 1; 1630 } 1631 elseif ($currentPage > $pagesCount) { 1632 $currentPage = $pagesCount; 1633 } 1634 1635 $tags = []; 1636 1637 if ($pagesCount > 1) { 1638 // For MVC pages $page is not set 1639 if (isset($page['file'])) { 1640 CProfile::update('web.paging.lastpage', $page['file'], PROFILE_TYPE_STR); 1641 CProfile::update('web.paging.page', $currentPage, PROFILE_TYPE_INT); 1642 } 1643 elseif (isset($_REQUEST['action'])) { 1644 CProfile::update('web.paging.lastpage', $_REQUEST['action'], PROFILE_TYPE_STR); 1645 CProfile::update('web.paging.page', $currentPage, PROFILE_TYPE_INT); 1646 } 1647 1648 // viewed pages (better to use odd) 1649 $pagingNavRange = 11; 1650 1651 $endPage = $currentPage + floor($pagingNavRange / 2); 1652 if ($endPage < $pagingNavRange) { 1653 $endPage = $pagingNavRange; 1654 } 1655 if ($endPage > $pagesCount) { 1656 $endPage = $pagesCount; 1657 } 1658 1659 $startPage = ($endPage > $pagingNavRange) ? $endPage - $pagingNavRange + 1 : 1; 1660 1661 if ($startPage > 1) { 1662 $url->setArgument('page', 1); 1663 $tags[] = new CLink(_x('First', 'page navigation'), $url->getUrl()); 1664 } 1665 1666 if ($currentPage > 1) { 1667 $url->setArgument('page', $currentPage - 1); 1668 $tags[] = new CLink( 1669 (new CSpan())->addClass(ZBX_STYLE_ARROW_LEFT), $url->getUrl() 1670 ); 1671 } 1672 1673 for ($p = $startPage; $p <= $endPage; $p++) { 1674 $url->setArgument('page', $p); 1675 $link = new CLink($p, $url->getUrl()); 1676 if ($p == $currentPage) { 1677 $link->addClass(ZBX_STYLE_PAGING_SELECTED); 1678 } 1679 1680 $tags[] = $link; 1681 } 1682 1683 if ($currentPage < $pagesCount) { 1684 $url->setArgument('page', $currentPage + 1); 1685 $tags[] = new CLink((new CSpan())->addClass(ZBX_STYLE_ARROW_RIGHT), $url->getUrl()); 1686 } 1687 1688 if ($p < $pagesCount) { 1689 $url->setArgument('page', $pagesCount); 1690 $tags[] = new CLink(_x('Last', 'page navigation'), $url->getUrl()); 1691 } 1692 } 1693 1694 $total = $limit_exceeded ? $itemsCount.'+' : $itemsCount; 1695 $start = ($currentPage - 1) * $rowsPerPage; 1696 $end = $start + $rowsPerPage; 1697 1698 if ($end > $itemsCount) { 1699 $end = $itemsCount; 1700 } 1701 1702 if ($pagesCount == 1) { 1703 $table_stats = _s('Displaying %1$s of %2$s found', $itemsCount, $total); 1704 } 1705 else { 1706 $table_stats = _s('Displaying %1$s to %2$s of %3$s found', $start + 1, $end, $total); 1707 } 1708 1709 // Trim array with elements to contain elements for current page. 1710 $items = array_slice($items, $start + $offset, $end - $start, true); 1711 1712 return (new CDiv()) 1713 ->addClass(ZBX_STYLE_TABLE_PAGING) 1714 ->addItem( 1715 (new CDiv()) 1716 ->addClass(ZBX_STYLE_PAGING_BTN_CONTAINER) 1717 ->addItem($tags) 1718 ->addItem( 1719 (new CDiv()) 1720 ->addClass(ZBX_STYLE_TABLE_STATS) 1721 ->addItem($table_stats) 1722 ) 1723 ); 1724} 1725 1726/************* MATH *************/ 1727function bcfloor($number) { 1728 if (strpos($number, '.') !== false) { 1729 if (($tmp = preg_replace('/\.0+$/', '', $number)) !== $number) { 1730 $number = $tmp; 1731 } 1732 elseif ($number[0] != '-') { 1733 $number = bcadd($number, 0, 0); 1734 } 1735 else { 1736 $number = bcsub($number, 1, 0); 1737 } 1738 } 1739 1740 return $number == '-0' ? '0' : $number; 1741} 1742 1743function bcceil($number) { 1744 if (strpos($number, '.') !== false) { 1745 if (($tmp = preg_replace('/\.0+$/', '', $number)) !== $number) { 1746 $number = $tmp; 1747 } 1748 elseif ($number[0] != '-') { 1749 $number = bcadd($number, 1, 0); 1750 } 1751 else { 1752 $number = bcsub($number, 0, 0); 1753 } 1754 } 1755 1756 return $number == '-0' ? '0' : $number; 1757} 1758 1759/** 1760 * Converts number to letter representation. 1761 * From A to Z, then from AA to ZZ etc. 1762 * Example: 0 => A, 25 => Z, 26 => AA, 27 => AB, 52 => BA, ... 1763 * 1764 * Keep in sync with JS num2letter(). 1765 * 1766 * @param int $number 1767 * 1768 * @return string 1769 */ 1770function num2letter($number) { 1771 $start = ord('A'); 1772 $base = 26; 1773 $str = ''; 1774 $level = 0; 1775 1776 do { 1777 if ($level++ > 0) { 1778 $number--; 1779 } 1780 $remainder = $number % $base; 1781 $number = ($number - $remainder) / $base; 1782 $str = chr($start + $remainder).$str; 1783 } while (0 != $number); 1784 1785 return $str; 1786} 1787 1788/** 1789 * Renders an "access denied" message and stops the execution of the script. 1790 * 1791 * The $mode parameters controls the layout of the message for logged in users: 1792 * - ACCESS_DENY_OBJECT - render the message when denying access to a specific object 1793 * - ACCESS_DENY_PAGE - render a complete access denied page 1794 * 1795 * If visitor is without any access permission then layout of the message is same as in ACCESS_DENY_PAGE mode. 1796 * 1797 * @param int $mode 1798 */ 1799function access_deny($mode = ACCESS_DENY_OBJECT) { 1800 // deny access to an object 1801 if ($mode == ACCESS_DENY_OBJECT && CWebUser::isLoggedIn()) { 1802 require_once dirname(__FILE__).'/page_header.php'; 1803 show_error_message(_('No permissions to referred object or it does not exist!')); 1804 require_once dirname(__FILE__).'/page_footer.php'; 1805 } 1806 // deny access to a page 1807 else { 1808 // url to redirect the user to after he logs in 1809 $url = (new CUrl(!empty($_REQUEST['request']) ? $_REQUEST['request'] : ''))->removeArgument('sid'); 1810 $config = select_config(); 1811 1812 if ($config['http_login_form'] == ZBX_AUTH_FORM_HTTP && $config['http_auth_enabled'] == ZBX_AUTH_HTTP_ENABLED 1813 && (!CWebUser::isLoggedIn() || CWebUser::isGuest())) { 1814 $redirect_to = (new CUrl('index_http.php'))->setArgument('request', $url->toString()); 1815 redirect($redirect_to->toString()); 1816 1817 exit; 1818 } 1819 1820 $url = urlencode($url->toString()); 1821 1822 // if the user is logged in - render the access denied message 1823 if (CWebUser::isLoggedIn()) { 1824 $data = [ 1825 'header' => _('Access denied'), 1826 'messages' => [ 1827 _s('You are logged in as "%1$s".', CWebUser::$data['alias']).' '._('You have no permissions to access this page.'), 1828 _('If you think this message is wrong, please consult your administrators about getting the necessary permissions.') 1829 ], 1830 'buttons' => [] 1831 ]; 1832 1833 // display the login button only for guest users 1834 if (CWebUser::isGuest()) { 1835 $data['buttons'][] = (new CButton('login', _('Login'))) 1836 ->onClick('javascript: document.location = "index.php?request='.$url.'";'); 1837 } 1838 $data['buttons'][] = (new CButton('back', _('Go to dashboard'))) 1839 ->onClick('javascript: document.location = "zabbix.php?action=dashboard.view"'); 1840 } 1841 // if the user is not logged in - offer to login 1842 else { 1843 $data = [ 1844 'header' => _('You are not logged in'), 1845 'messages' => [ 1846 _('You must login to view this page.'), 1847 _('If you think this message is wrong, please consult your administrators about getting the necessary permissions.') 1848 ], 1849 'buttons' => [ 1850 (new CButton('login', _('Login')))->onClick('javascript: document.location = "index.php?request='.$url.'";') 1851 ] 1852 ]; 1853 } 1854 1855 $data['theme'] = getUserTheme(CWebUser::$data); 1856 1857 if (detect_page_type() == PAGE_TYPE_JS) { 1858 (new CView('layout.json', ['main_block' => json_encode(['error' => $data['header']])]))->render(); 1859 } 1860 else { 1861 (new CView('general.warning', $data))->render(); 1862 } 1863 exit; 1864 } 1865} 1866 1867function detect_page_type($default = PAGE_TYPE_HTML) { 1868 if (isset($_REQUEST['output'])) { 1869 switch (strtolower($_REQUEST['output'])) { 1870 case 'text': 1871 return PAGE_TYPE_TEXT; 1872 case 'ajax': 1873 return PAGE_TYPE_JS; 1874 case 'json': 1875 return PAGE_TYPE_JSON; 1876 case 'json-rpc': 1877 return PAGE_TYPE_JSON_RPC; 1878 case 'html': 1879 return PAGE_TYPE_HTML_BLOCK; 1880 case 'img': 1881 return PAGE_TYPE_IMAGE; 1882 case 'css': 1883 return PAGE_TYPE_CSS; 1884 } 1885 } 1886 1887 return $default; 1888} 1889 1890function makeMessageBox($good, array $messages, $title = null, $show_close_box = true, $show_details = false) 1891{ 1892 $class = $good ? ZBX_STYLE_MSG_GOOD : ZBX_STYLE_MSG_BAD; 1893 $msg_details = null; 1894 $link_details = null; 1895 1896 if ($messages) { 1897 if ($title !== null) { 1898 $link_details = (new CLinkAction()) 1899 ->addItem(_('Details')) 1900 ->addItem(' ') // space 1901 ->addItem((new CSpan()) 1902 ->setId('details-arrow') 1903 ->addClass($show_details ? ZBX_STYLE_ARROW_UP : ZBX_STYLE_ARROW_DOWN) 1904 ) 1905 ->setAttribute('aria-expanded', $show_details ? 'true' : 'false') 1906 ->onClick('javascript: '. 1907 'showHide(jQuery(this).siblings(\'.'.ZBX_STYLE_MSG_DETAILS.'\')'. 1908 '.find(\'.'.ZBX_STYLE_MSG_DETAILS_BORDER.'\'));'. 1909 'jQuery("#details-arrow", $(this)).toggleClass("'.ZBX_STYLE_ARROW_UP.' '.ZBX_STYLE_ARROW_DOWN.'");'. 1910 'jQuery(this).attr(\'aria-expanded\', jQuery(this).find(\'.'.ZBX_STYLE_ARROW_DOWN.'\').length == 0)' 1911 ); 1912 } 1913 1914 $list = new CList(); 1915 if ($title !== null) { 1916 $list->addClass(ZBX_STYLE_MSG_DETAILS_BORDER); 1917 1918 if (!$show_details) { 1919 $list->setAttribute('style', 'display: none;'); 1920 } 1921 } 1922 foreach ($messages as $message) { 1923 foreach (explode("\n", $message['message']) as $message_part) { 1924 $list->addItem($message_part); 1925 } 1926 } 1927 $msg_details = (new CDiv())->addClass(ZBX_STYLE_MSG_DETAILS)->addItem($list); 1928 } 1929 1930 if ($title !== null) { 1931 $title = new CSpan($title); 1932 } 1933 1934 // Details link should be in front of title. 1935 $msg_box = (new CTag('output', true, [$link_details, $title, $msg_details])) 1936 ->addClass($class) 1937 ->setAttribute('role', 'contentinfo') 1938 ->setAttribute('aria-label', $good ? _('Success message') : _('Error message')); 1939 1940 if ($show_close_box) { 1941 $msg_box->addItem((new CSimpleButton()) 1942 ->addClass(ZBX_STYLE_OVERLAY_CLOSE_BTN) 1943 ->onClick('jQuery(this).closest(\'.'.$class.'\').remove();') 1944 ->setTitle(_('Close'))); 1945 } 1946 1947 return $msg_box; 1948} 1949 1950/** 1951 * Filters messages that can be displayed to user based on defines (see ZBX_SHOW_TECHNICAL_ERRORS) and user settings. 1952 * 1953 * @param array $messages List of messages to filter. 1954 * 1955 * @return array 1956 */ 1957function filter_messages(array $messages = []) { 1958 if (!ZBX_SHOW_TECHNICAL_ERRORS && CWebUser::getType() != USER_TYPE_SUPER_ADMIN && !CWebUser::getDebugMode()) { 1959 $filtered_messages = []; 1960 $generic_exists = false; 1961 1962 foreach ($messages as $message) { 1963 if (array_key_exists('src', $message) && ($message['src'] === 'sql' || $message['src'] === 'php')) { 1964 if (!$generic_exists) { 1965 $message['message'] = _('System error occurred. Please contact Zabbix administrator.'); 1966 $filtered_messages[] = $message; 1967 $generic_exists = true; 1968 } 1969 } 1970 else { 1971 $filtered_messages[] = $message; 1972 } 1973 } 1974 $messages = $filtered_messages; 1975 } 1976 1977 return $messages; 1978} 1979 1980/** 1981 * Returns the message box when messages are present; null otherwise 1982 * 1983 * @param boolean $good Parameter passed to makeMessageBox to specify message box style. 1984 * @param string $title Message box title. 1985 * @global array $ZBX_MESSAGES 1986 * 1987 * @return CDiv|null 1988 */ 1989function getMessages($good = false, $title = null) { 1990 global $ZBX_MESSAGES; 1991 1992 $messages = (isset($ZBX_MESSAGES) && $ZBX_MESSAGES) ? filter_messages($ZBX_MESSAGES) : []; 1993 1994 $message_box = ($title || $messages) 1995 ? makeMessageBox($good, $messages, $title) 1996 : null; 1997 1998 $ZBX_MESSAGES = []; 1999 2000 return $message_box; 2001} 2002 2003function show_messages($good = false, $okmsg = null, $errmsg = null) { 2004 global $page, $ZBX_MESSAGES; 2005 2006 if (!defined('PAGE_HEADER_LOADED')) { 2007// return null; 2008 } 2009 if (defined('ZBX_API_REQUEST')) { 2010 return null; 2011 } 2012 if (!isset($page['type'])) { 2013 $page['type'] = PAGE_TYPE_HTML; 2014 } 2015 2016 $imageMessages = []; 2017 2018 $title = $good ? $okmsg : $errmsg; 2019 $messages = isset($ZBX_MESSAGES) ? filter_messages($ZBX_MESSAGES) : []; 2020 $ZBX_MESSAGES = []; 2021 2022 switch ($page['type']) { 2023 case PAGE_TYPE_IMAGE: 2024 if ($title !== null) { 2025 $imageMessages[] = [ 2026 'text' => $title, 2027 'color' => (!$good) ? ['R' => 255, 'G' => 0, 'B' => 0] : ['R' => 34, 'G' => 51, 'B' => 68] 2028 ]; 2029 } 2030 2031 foreach ($messages as $message) { 2032 $imageMessages[] = [ 2033 'text' => $message['message'], 2034 'color' => $message['type'] == 'error' 2035 ? ['R' => 255, 'G' => 55, 'B' => 55] 2036 : ['R' => 155, 'G' => 155, 'B' => 55] 2037 ]; 2038 } 2039 break; 2040 case PAGE_TYPE_HTML: 2041 default: 2042 if ($title || $messages) { 2043 makeMessageBox($good, $messages, $title, true, !$good)->show(); 2044 } 2045 break; 2046 } 2047 2048 // draw an image with the messages 2049 if ($imageMessages) { 2050 $imageFontSize = 8; 2051 2052 // calculate the size of the text 2053 $imageWidth = 0; 2054 $imageHeight = 0; 2055 foreach ($imageMessages as &$msg) { 2056 $size = imageTextSize($imageFontSize, 0, $msg['text']); 2057 $msg['height'] = $size['height'] - $size['baseline']; 2058 2059 // calculate the total size of the image 2060 $imageWidth = max($imageWidth, $size['width']); 2061 $imageHeight += $size['height'] + 1; 2062 } 2063 unset($msg); 2064 2065 // additional padding 2066 $imageWidth += 2; 2067 $imageHeight += 2; 2068 2069 // create the image 2070 $canvas = imagecreate($imageWidth, $imageHeight); 2071 imagefilledrectangle($canvas, 0, 0, $imageWidth, $imageHeight, imagecolorallocate($canvas, 255, 255, 255)); 2072 2073 // draw each message 2074 $y = 1; 2075 foreach ($imageMessages as $msg) { 2076 $y += $msg['height']; 2077 imageText($canvas, $imageFontSize, 0, 1, $y, 2078 imagecolorallocate($canvas, $msg['color']['R'], $msg['color']['G'], $msg['color']['B']), 2079 $msg['text'] 2080 ); 2081 } 2082 imageOut($canvas); 2083 imagedestroy($canvas); 2084 } 2085} 2086 2087function show_message($msg) { 2088 show_messages(true, $msg, ''); 2089} 2090 2091function show_error_message($msg) { 2092 show_messages(false, '', $msg); 2093} 2094 2095function info($msgs) { 2096 global $ZBX_MESSAGES; 2097 2098 if (!isset($ZBX_MESSAGES)) { 2099 $ZBX_MESSAGES = []; 2100 } 2101 2102 zbx_value2array($msgs); 2103 2104 foreach ($msgs as $msg) { 2105 $ZBX_MESSAGES[] = ['type' => 'info', 'message' => $msg]; 2106 } 2107} 2108 2109/* 2110 * Add an error to global message array. 2111 * 2112 * @param string | array $msg Error message text. 2113 * @param string $src The source of error message. 2114 */ 2115function error($msgs, $src = '') { 2116 global $ZBX_MESSAGES; 2117 2118 if (!isset($ZBX_MESSAGES)) { 2119 $ZBX_MESSAGES = []; 2120 } 2121 2122 $msgs = zbx_toArray($msgs); 2123 2124 foreach ($msgs as $msg) { 2125 $ZBX_MESSAGES[] = [ 2126 'type' => 'error', 2127 'message' => $msg, 2128 'src' => $src 2129 ]; 2130 } 2131} 2132 2133/** 2134 * Add multiple errors under single header. 2135 * 2136 * @param array $data 2137 * @param string $data['header'] common header for all error messages 2138 * @param array $data['msgs'] array of error messages 2139 */ 2140function error_group($data) { 2141 foreach (zbx_toArray($data['msgs']) as $msg) { 2142 error($data['header'] . ' ' . $msg); 2143 } 2144} 2145 2146function clear_messages($count = null) { 2147 global $ZBX_MESSAGES; 2148 2149 if ($count != null) { 2150 $result = []; 2151 2152 while ($count-- > 0) { 2153 array_unshift($result, array_pop($ZBX_MESSAGES)); 2154 } 2155 } 2156 else { 2157 $result = $ZBX_MESSAGES; 2158 $ZBX_MESSAGES = []; 2159 } 2160 2161 return $result ? filter_messages($result) : $result; 2162} 2163 2164function fatal_error($msg) { 2165 require_once dirname(__FILE__).'/page_header.php'; 2166 show_error_message($msg); 2167 require_once dirname(__FILE__).'/page_footer.php'; 2168} 2169 2170function parse_period($str) { 2171 $out = null; 2172 $time_periods_parser = new CTimePeriodsParser(); 2173 2174 if ($time_periods_parser->parse($str) != CParser::PARSE_SUCCESS) { 2175 return null; 2176 } 2177 2178 foreach ($time_periods_parser->getPeriods() as $period) { 2179 if (!preg_match('/^([1-7])-([1-7]),([0-9]{1,2}):([0-9]{1,2})-([0-9]{1,2}):([0-9]{1,2})$/', $period, $matches)) { 2180 return null; 2181 } 2182 2183 for ($i = $matches[1]; $i <= $matches[2]; $i++) { 2184 if (!isset($out[$i])) { 2185 $out[$i] = []; 2186 } 2187 array_push($out[$i], [ 2188 'start_h' => $matches[3], 2189 'start_m' => $matches[4], 2190 'end_h' => $matches[5], 2191 'end_m' => $matches[6] 2192 ]); 2193 } 2194 } 2195 2196 return $out; 2197} 2198 2199function get_status() { 2200 global $ZBX_SERVER, $ZBX_SERVER_PORT; 2201 2202 $status = [ 2203 'is_running' => false, 2204 'has_status' => false 2205 ]; 2206 2207 $server = new CZabbixServer($ZBX_SERVER, $ZBX_SERVER_PORT, ZBX_SOCKET_TIMEOUT, ZBX_SOCKET_BYTES_LIMIT); 2208 $status['is_running'] = $server->isRunning(get_cookie(ZBX_SESSION_NAME)); 2209 2210 if ($status['is_running'] === false) { 2211 return $status; 2212 } 2213 2214 $server = new CZabbixServer($ZBX_SERVER, $ZBX_SERVER_PORT, 15, ZBX_SOCKET_BYTES_LIMIT); 2215 $server_status = $server->getStatus(get_cookie(ZBX_SESSION_NAME)); 2216 $status['has_status'] = (bool) $server_status; 2217 2218 if ($server_status === false) { 2219 error($server->getError()); 2220 return $status; 2221 } 2222 2223 $status += [ 2224 'triggers_count_disabled' => 0, 2225 'triggers_count_off' => 0, 2226 'triggers_count_on' => 0, 2227 'items_count_monitored' => 0, 2228 'items_count_disabled' => 0, 2229 'items_count_not_supported' => 0, 2230 'hosts_count_monitored' => 0, 2231 'hosts_count_not_monitored' => 0, 2232 'hosts_count_template' => 0, 2233 'users_count' => 0, 2234 'users_online' => 0 2235 ]; 2236 2237 // hosts 2238 foreach ($server_status['template stats'] as $stats) { 2239 $status['hosts_count_template'] += $stats['count']; 2240 } 2241 2242 foreach ($server_status['host stats'] as $stats) { 2243 if ($stats['attributes']['proxyid'] == 0) { 2244 switch ($stats['attributes']['status']) { 2245 case HOST_STATUS_MONITORED: 2246 $status['hosts_count_monitored'] += $stats['count']; 2247 break; 2248 2249 case HOST_STATUS_NOT_MONITORED: 2250 $status['hosts_count_not_monitored'] += $stats['count']; 2251 break; 2252 } 2253 } 2254 } 2255 $status['hosts_count'] = $status['hosts_count_monitored'] + $status['hosts_count_not_monitored'] 2256 + $status['hosts_count_template']; 2257 2258 // items 2259 foreach ($server_status['item stats'] as $stats) { 2260 if ($stats['attributes']['proxyid'] == 0) { 2261 switch ($stats['attributes']['status']) { 2262 case ITEM_STATUS_ACTIVE: 2263 if (array_key_exists('state', $stats['attributes'])) { 2264 switch ($stats['attributes']['state']) { 2265 case ITEM_STATE_NORMAL: 2266 $status['items_count_monitored'] += $stats['count']; 2267 break; 2268 2269 case ITEM_STATE_NOTSUPPORTED: 2270 $status['items_count_not_supported'] += $stats['count']; 2271 break; 2272 } 2273 } 2274 break; 2275 2276 case ITEM_STATUS_DISABLED: 2277 $status['items_count_disabled'] += $stats['count']; 2278 break; 2279 } 2280 } 2281 } 2282 $status['items_count'] = $status['items_count_monitored'] + $status['items_count_disabled'] 2283 + $status['items_count_not_supported']; 2284 2285 // triggers 2286 foreach ($server_status['trigger stats'] as $stats) { 2287 switch ($stats['attributes']['status']) { 2288 case TRIGGER_STATUS_ENABLED: 2289 if (array_key_exists('value', $stats['attributes'])) { 2290 switch ($stats['attributes']['value']) { 2291 case TRIGGER_VALUE_FALSE: 2292 $status['triggers_count_off'] += $stats['count']; 2293 break; 2294 2295 case TRIGGER_VALUE_TRUE: 2296 $status['triggers_count_on'] += $stats['count']; 2297 break; 2298 } 2299 } 2300 break; 2301 2302 case TRIGGER_STATUS_DISABLED: 2303 $status['triggers_count_disabled'] += $stats['count']; 2304 break; 2305 } 2306 } 2307 $status['triggers_count_enabled'] = $status['triggers_count_off'] + $status['triggers_count_on']; 2308 $status['triggers_count'] = $status['triggers_count_enabled'] + $status['triggers_count_disabled']; 2309 2310 // users 2311 foreach ($server_status['user stats'] as $stats) { 2312 switch ($stats['attributes']['status']) { 2313 case ZBX_SESSION_ACTIVE: 2314 $status['users_online'] += $stats['count']; 2315 break; 2316 2317 case ZBX_SESSION_PASSIVE: 2318 $status['users_count'] += $stats['count']; 2319 break; 2320 } 2321 } 2322 $status['users_count'] += $status['users_online']; 2323 2324 // performance 2325 if (array_key_exists('required performance', $server_status)) { 2326 $status['vps_total'] = 0; 2327 2328 foreach ($server_status['required performance'] as $stats) { 2329 if ($stats['attributes']['proxyid'] == 0) { 2330 $status['vps_total'] += $stats['count']; 2331 } 2332 } 2333 } 2334 2335 return $status; 2336} 2337 2338function set_image_header() { 2339 global $IMAGE_FORMAT_DEFAULT; 2340 2341 switch ($IMAGE_FORMAT_DEFAULT) { 2342 case IMAGE_FORMAT_JPEG: 2343 header('Content-type: image/jpeg'); 2344 break; 2345 2346 case IMAGE_FORMAT_TEXT: 2347 header('Content-type: text/html'); 2348 break; 2349 2350 default: 2351 header('Content-type: image/png'); 2352 } 2353 2354 header('Expires: Mon, 17 Aug 1998 12:51:50 GMT'); 2355} 2356 2357function imageOut(&$image, $format = null) { 2358 global $page, $IMAGE_FORMAT_DEFAULT; 2359 2360 if (is_null($format)) { 2361 $format = $IMAGE_FORMAT_DEFAULT; 2362 } 2363 2364 ob_start(); 2365 2366 if (IMAGE_FORMAT_JPEG == $format) { 2367 imagejpeg($image); 2368 } 2369 else { 2370 imagepng($image); 2371 } 2372 2373 $imageSource = ob_get_contents(); 2374 ob_end_clean(); 2375 2376 if ($page['type'] != PAGE_TYPE_IMAGE) { 2377 $imageId = md5(strlen($imageSource)); 2378 CSession::setValue('image_id', [$imageId => $imageSource]); 2379 } 2380 2381 switch ($page['type']) { 2382 case PAGE_TYPE_IMAGE: 2383 echo $imageSource; 2384 break; 2385 case PAGE_TYPE_JSON: 2386 $json = new CJson(); 2387 echo $json->encode(['result' => $imageId]); 2388 break; 2389 case PAGE_TYPE_TEXT: 2390 default: 2391 echo $imageId; 2392 } 2393} 2394 2395/** 2396 * Check if we have error messages to display. 2397 * 2398 * @global array $ZBX_MESSAGES 2399 * 2400 * @return bool 2401 */ 2402function hasErrorMesssages() { 2403 global $ZBX_MESSAGES; 2404 2405 if (isset($ZBX_MESSAGES)) { 2406 foreach ($ZBX_MESSAGES as $message) { 2407 if ($message['type'] === 'error') { 2408 return true; 2409 } 2410 } 2411 } 2412 2413 return false; 2414} 2415 2416/** 2417 * Clears table rows selection's cookies. 2418 * 2419 * @param string $parentid parent ID, is used as sessionStorage suffix 2420 * @param array $keepids checked rows ids 2421 */ 2422function uncheckTableRows($parentid = null, $keepids = []) { 2423 $key = implode('_', array_filter(['cb', basename($_SERVER['SCRIPT_NAME'], '.php'), $parentid])); 2424 2425 if ($keepids) { 2426 // If $keepids will not have same key as value, it will create mess, when new checkbox will be checked. 2427 $keepids = array_combine($keepids, $keepids); 2428 2429 insert_js('sessionStorage.setItem("'.$key.'", JSON.stringify('.CJs::encodeJson($keepids).'))'); 2430 } 2431 else { 2432 insert_js('sessionStorage.removeItem("'.$key.'")'); 2433 } 2434} 2435 2436/** 2437 * Trim each element of the script path. For example, " a / b / c d " => "a/b/c d" 2438 * 2439 * @param string $name 2440 * 2441 * @return string 2442 */ 2443function trimPath($name) { 2444 $path = splitPath($name); 2445 $path = array_map('trim', $path); 2446 $path = str_replace(['\\', '/'], ['\\\\', '\\/'], $path); 2447 return implode('/', $path); 2448} 2449 2450/** 2451 * Splitting string using slashes with escape backslash support and non-pair backslash cleanup. 2452 * 2453 * @param string $path 2454 * 2455 * @return array 2456 */ 2457function splitPath($path) { 2458 $path_items = []; 2459 $path_item = ''; 2460 2461 for ($i = 0; isset($path[$i]); $i++) { 2462 switch ($path[$i]) { 2463 case '/': 2464 $path_items[] = $path_item; 2465 $path_item = ''; 2466 break; 2467 2468 case '\\': 2469 if (isset($path[++$i])) { 2470 $path_item .= $path[$i]; 2471 } 2472 break; 2473 2474 default: 2475 $path_item .= $path[$i]; 2476 } 2477 } 2478 2479 $path_items[] = $path_item; 2480 2481 return $path_items; 2482} 2483 2484/** 2485 * Allocate color for an image. 2486 * 2487 * @param resource $image 2488 * @param string $color a hexadecimal color identifier like "1F2C33" 2489 * @param int $alpha 2490 */ 2491function get_color($image, $color, $alpha = 0) { 2492 $red = hexdec('0x'.substr($color, 0, 2)); 2493 $green = hexdec('0x'.substr($color, 2, 2)); 2494 $blue = hexdec('0x'.substr($color, 4, 2)); 2495 2496 if (function_exists('imagecolorexactalpha') && function_exists('imagecreatetruecolor') 2497 && @imagecreatetruecolor(1, 1)) { 2498 return imagecolorexactalpha($image, $red, $green, $blue, $alpha); 2499 } 2500 2501 return imagecolorallocate($image, $red, $green, $blue); 2502} 2503 2504/** 2505 * Get graphic theme based on user configuration. 2506 * 2507 * @return array 2508 */ 2509function getUserGraphTheme() { 2510 $themes = DB::find('graph_theme', [ 2511 'theme' => getUserTheme(CWebUser::$data) 2512 ]); 2513 2514 if ($themes) { 2515 return $themes[0]; 2516 } 2517 2518 return [ 2519 'theme' => 'blue-theme', 2520 'textcolor' => '1F2C33', 2521 'highlightcolor' => 'E33734', 2522 'backgroundcolor' => 'FFFFFF', 2523 'graphcolor' => 'FFFFFF', 2524 'gridcolor' => 'CCD5D9', 2525 'maingridcolor' => 'ACBBC2', 2526 'gridbordercolor' => 'ACBBC2', 2527 'nonworktimecolor' => 'EBEBEB', 2528 'leftpercentilecolor' => '429E47', 2529 'righttpercentilecolor' => 'E33734', 2530 'colorpalette' => '1A7C11,F63100,2774A4,A54F10,FC6EA3,6C59DC,AC8C14,611F27,F230E0,5CCD18,BB2A02,5A2B57,'. 2531 '89ABF8,7EC25C,274482,2B5429,8048B4,FD5434,790E1F,87AC4D,E89DF4' 2532 ]; 2533} 2534 2535/** 2536 * Custom error handler for PHP errors. 2537 * 2538 * @param int $errno Level of the error raised. 2539 * @param string $errstr Error message. 2540 * @param string $errfile Filename that the error was raised in. 2541 * @param int $errline Line number the error was raised in. 2542 */ 2543function zbx_err_handler($errno, $errstr, $errfile, $errline) { 2544 // Necessary to suppress errors when calling with error control operator like @function_name(). 2545 if (error_reporting() === 0) { 2546 return true; 2547 } 2548 2549 // Don't show the call to this handler function. 2550 error($errstr.' ['.CProfiler::getInstance()->formatCallStack().']', 'php'); 2551} 2552 2553/** 2554 * Creates an array with all possible variations of time units. 2555 * For example: '14d' => ['1209600', '1209600s', '20160m', '336h', '14d', '2w'] 2556 * 2557 * @param string|array $values 2558 * 2559 * @return array 2560 */ 2561function getTimeUnitFilters($values) { 2562 if (is_array($values)) { 2563 $res = []; 2564 2565 foreach ($values as $value) { 2566 $res = array_merge($res, getTimeUnitFilters($value)); 2567 } 2568 2569 return array_unique($res, SORT_STRING); 2570 } 2571 2572 $simple_interval_parser = new CSimpleIntervalParser(); 2573 2574 if ($simple_interval_parser->parse($values) != CParser::PARSE_SUCCESS) { 2575 return [$values]; 2576 } 2577 2578 $sec = timeUnitToSeconds($values); 2579 2580 $res = [$sec, $sec.'s']; 2581 2582 if (bcmod($sec, SEC_PER_MIN) == 0) { 2583 $res[] = bcdiv($sec, SEC_PER_MIN, 0).'m'; 2584 } 2585 2586 if (bcmod($sec, SEC_PER_HOUR) == 0) { 2587 $res[] = bcdiv($sec, SEC_PER_HOUR, 0).'h'; 2588 } 2589 2590 if (bcmod($sec, SEC_PER_DAY) == 0) { 2591 $res[] = bcdiv($sec, SEC_PER_DAY, 0).'d'; 2592 } 2593 2594 if (bcmod($sec, SEC_PER_WEEK) == 0) { 2595 $res[] = bcdiv($sec, SEC_PER_WEEK, 0).'w'; 2596 } 2597 2598 return $res; 2599} 2600 2601/** 2602 * Creates SQL filter to search all possible variations of time units. 2603 * 2604 * @param string $field_name 2605 * @param string|array $values 2606 * 2607 * @return string 2608 */ 2609function makeUpdateIntervalFilter($field_name, $values) { 2610 $filters = []; 2611 2612 foreach (getTimeUnitFilters($values) as $filter) { 2613 $filter = str_replace("!", "!!", $filter); 2614 $filter = str_replace("%", "!%", $filter); 2615 $filter = str_replace("_", "!_", $filter); 2616 2617 $filters[] = $field_name.' LIKE '.zbx_dbstr($filter).' ESCAPE '.zbx_dbstr('!'); 2618 $filters[] = $field_name.' LIKE '.zbx_dbstr($filter.';%').' ESCAPE '.zbx_dbstr('!'); 2619 } 2620 2621 $res = $filters ? implode(' OR ', $filters) : ''; 2622 2623 if (count($filters) > 1) { 2624 $res = '('.$res.')'; 2625 } 2626 2627 return $res; 2628} 2629 2630/** 2631 * Update profile with new time selector range. 2632 * 2633 * @param array $options 2634 * @param string $options['profileIdx'] 2635 * @param int $options['profileIdx2'] 2636 * @param string|null $options['from'] 2637 * @param string|null $options['to'] 2638 */ 2639function updateTimeSelectorPeriod(array $options) { 2640 if ($options['from'] !== null && $options['to'] !== null) { 2641 CProfile::update($options['profileIdx'].'.from', $options['from'], PROFILE_TYPE_STR, $options['profileIdx2']); 2642 CProfile::update($options['profileIdx'].'.to', $options['to'], PROFILE_TYPE_STR, $options['profileIdx2']); 2643 } 2644} 2645 2646/** 2647 * Get profile stored 'from' and 'to'. If profileIdx is null then default values will be returned. If one of fields 2648 * not exist in $options array 'from' and 'to' value will be read from user profile. Calculates from_ts, to_ts. 2649 * 2650 * @param array $options Array with period fields data: profileIdx, profileIdx2, from, to. 2651 * 2652 * @return array 2653 */ 2654function getTimeSelectorPeriod(array $options) { 2655 $profileIdx = array_key_exists('profileIdx', $options) ? $options['profileIdx'] : null; 2656 $profileIdx2 = array_key_exists('profileIdx2', $options) ? $options['profileIdx2'] : null; 2657 2658 if ($profileIdx === null) { 2659 $options['from'] = ZBX_PERIOD_DEFAULT_FROM; 2660 $options['to'] = ZBX_PERIOD_DEFAULT_TO; 2661 } 2662 elseif (!array_key_exists('from', $options) || !array_key_exists('to', $options) 2663 || $options['from'] === null || $options['to'] === null) { 2664 $options['from'] = CProfile::get($profileIdx.'.from', ZBX_PERIOD_DEFAULT_FROM, $profileIdx2); 2665 $options['to'] = CProfile::get($profileIdx.'.to', ZBX_PERIOD_DEFAULT_TO, $profileIdx2); 2666 } 2667 2668 $range_time_parser = new CRangeTimeParser(); 2669 2670 $range_time_parser->parse($options['from']); 2671 $options['from_ts'] = $range_time_parser->getDateTime(true)->getTimestamp(); 2672 $range_time_parser->parse($options['to']); 2673 $options['to_ts'] = $range_time_parser->getDateTime(false)->getTimestamp(); 2674 2675 return $options; 2676} 2677 2678/** 2679 * Convert relative date range string to translated string. Function does not check is passed date range correct. 2680 * 2681 * @param string $from Start date of date range. 2682 * @param string $to End date of date range. 2683 * 2684 * @return string 2685 */ 2686function relativeDateToText($from, $to) { 2687 $key = $from.':'.$to; 2688 $ranges = [ 2689 'now-1d/d:now-1d/d' => _('Yesterday'), 2690 'now-2d/d:now-2d/d' => _('Day before yesterday'), 2691 'now-1w/d:now-1w/d' => _('This day last week'), 2692 'now-1w/w:now-1w/w' => _('Previous week'), 2693 'now-1M/M:now-1M/M' => _('Previous month'), 2694 'now-1y/y:now-1y/y' => _('Previous year'), 2695 'now/d:now/d' => _('Today'), 2696 'now/d:now' => _('Today so far'), 2697 'now/w:now/w' => _('This week'), 2698 'now/w:now' => _('This week so far'), 2699 'now/M:now/M' => _('This month'), 2700 'now/M:now' => _('This month so far'), 2701 'now/y:now/y' => _('This year'), 2702 'now/y:now' => _('This year so far') 2703 ]; 2704 2705 if (array_key_exists($key, $ranges)) { 2706 return $ranges[$key]; 2707 } 2708 2709 if ($to === 'now') { 2710 $relative_time_parser = new CRelativeTimeParser(); 2711 2712 if ($relative_time_parser->parse($from) == CParser::PARSE_SUCCESS) { 2713 $tokens = $relative_time_parser->getTokens(); 2714 2715 if (count($tokens) == 1 && $tokens[0]['type'] == CRelativeTimeParser::ZBX_TOKEN_OFFSET 2716 && $tokens[0]['sign'] === '-') { 2717 $suffix = $tokens[0]['suffix']; 2718 $value = (int) $tokens[0]['value']; 2719 2720 switch ($suffix) { 2721 case 's': 2722 if ($value < 60 || $value % 60 != 0) { 2723 return _n('Last %1$d second', 'Last %1$d seconds', $value); 2724 } 2725 $value /= 60; 2726 // break; is not missing here. 2727 2728 case 'm': 2729 if ($value < 60 || $value % 60 != 0) { 2730 return _n('Last %1$d minute', 'Last %1$d minutes', $value); 2731 } 2732 $value /= 60; 2733 // break; is not missing here. 2734 2735 case 'h': 2736 if ($value < 24 || $value % 24 != 0) { 2737 return _n('Last %1$d hour', 'Last %1$d hours', $value); 2738 } 2739 $value /= 24; 2740 // break; is not missing here. 2741 2742 case 'd': 2743 return _n('Last %1$d day', 'Last %1$d days', $value); 2744 2745 case 'M': 2746 return _n('Last %1$d month', 'Last %1$d months', $value); 2747 2748 case 'y': 2749 return _n('Last %1$d year', 'Last %1$d years', $value); 2750 } 2751 } 2752 } 2753 } 2754 2755 return $from.' – '.$to; 2756} 2757