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