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 * Convert windows events type constant in to the string representation
24 *
25 * @param int $logtype
26 * @return string
27 */
28function get_item_logtype_description($logtype) {
29	switch ($logtype) {
30		case ITEM_LOGTYPE_INFORMATION:
31			return _('Information');
32		case ITEM_LOGTYPE_WARNING:
33			return _('Warning');
34		case ITEM_LOGTYPE_ERROR:
35			return _('Error');
36		case ITEM_LOGTYPE_FAILURE_AUDIT:
37			return _('Failure Audit');
38		case ITEM_LOGTYPE_SUCCESS_AUDIT:
39			return _('Success Audit');
40		case ITEM_LOGTYPE_CRITICAL:
41			return _('Critical');
42		case ITEM_LOGTYPE_VERBOSE:
43			return _('Verbose');
44		default:
45			return _('Unknown');
46	}
47}
48
49/**
50 * Convert windows events type constant in to the CSS style name
51 *
52 * @param int $logtype
53 * @return string
54 */
55function get_item_logtype_style($logtype) {
56	switch ($logtype) {
57		case ITEM_LOGTYPE_INFORMATION:
58		case ITEM_LOGTYPE_SUCCESS_AUDIT:
59		case ITEM_LOGTYPE_VERBOSE:
60			return ZBX_STYLE_LOG_INFO_BG;
61
62		case ITEM_LOGTYPE_WARNING:
63			return ZBX_STYLE_LOG_WARNING_BG;
64
65		case ITEM_LOGTYPE_ERROR:
66		case ITEM_LOGTYPE_FAILURE_AUDIT:
67			return ZBX_STYLE_LOG_HIGH_BG;
68
69		case ITEM_LOGTYPE_CRITICAL:
70			return ZBX_STYLE_LOG_DISASTER_BG;
71
72		default:
73			return ZBX_STYLE_LOG_NA_BG;
74	}
75}
76
77/**
78 * Get item type string name by item type number, or array of all item types if null passed
79 *
80 * @param int|null $type
81 * @return array|string
82 */
83function item_type2str($type = null) {
84	$types = [
85		ITEM_TYPE_ZABBIX => _('Zabbix agent'),
86		ITEM_TYPE_ZABBIX_ACTIVE => _('Zabbix agent (active)'),
87		ITEM_TYPE_SIMPLE => _('Simple check'),
88		ITEM_TYPE_SNMPV1 => _('SNMPv1 agent'),
89		ITEM_TYPE_SNMPV2C => _('SNMPv2 agent'),
90		ITEM_TYPE_SNMPV3 => _('SNMPv3 agent'),
91		ITEM_TYPE_SNMPTRAP => _('SNMP trap'),
92		ITEM_TYPE_INTERNAL => _('Zabbix internal'),
93		ITEM_TYPE_TRAPPER => _('Zabbix trapper'),
94		ITEM_TYPE_AGGREGATE => _('Zabbix aggregate'),
95		ITEM_TYPE_EXTERNAL => _('External check'),
96		ITEM_TYPE_DB_MONITOR => _('Database monitor'),
97		ITEM_TYPE_IPMI => _('IPMI agent'),
98		ITEM_TYPE_SSH => _('SSH agent'),
99		ITEM_TYPE_TELNET => _('TELNET agent'),
100		ITEM_TYPE_JMX => _('JMX agent'),
101		ITEM_TYPE_CALCULATED => _('Calculated'),
102		ITEM_TYPE_HTTPTEST => _('Web monitoring')
103	];
104	if (is_null($type)) {
105		return $types;
106	}
107	elseif (isset($types[$type])) {
108		return $types[$type];
109	}
110	else {
111		return _('Unknown');
112	}
113}
114
115/**
116 * Returns human readable an item value type
117 *
118 * @param int $valueType
119 *
120 * @return string
121 */
122function itemValueTypeString($valueType) {
123	switch ($valueType) {
124		case ITEM_VALUE_TYPE_UINT64:
125			return _('Numeric (unsigned)');
126		case ITEM_VALUE_TYPE_FLOAT:
127			return _('Numeric (float)');
128		case ITEM_VALUE_TYPE_STR:
129			return _('Character');
130		case ITEM_VALUE_TYPE_LOG:
131			return _('Log');
132		case ITEM_VALUE_TYPE_TEXT:
133			return _('Text');
134	}
135	return _('Unknown');
136}
137
138function item_data_type2str($type = null) {
139	$types = [
140		ITEM_DATA_TYPE_BOOLEAN => _('Boolean'),
141		ITEM_DATA_TYPE_OCTAL => _('Octal'),
142		ITEM_DATA_TYPE_DECIMAL => _('Decimal'),
143		ITEM_DATA_TYPE_HEXADECIMAL => _('Hexadecimal')
144	];
145	if (is_null($type)) {
146		return $types;
147	}
148	elseif (isset($types[$type])) {
149		return $types[$type];
150	}
151	else {
152		return _('Unknown');
153	}
154}
155
156function item_status2str($type = null) {
157	$types = [
158		ITEM_STATUS_ACTIVE => _('Enabled'),
159		ITEM_STATUS_DISABLED => _('Disabled')
160	];
161	if (is_null($type)) {
162		return $types;
163	}
164	elseif (isset($types[$type])) {
165		return $types[$type];
166	}
167	else {
168		return _('Unknown');
169	}
170}
171
172/**
173 * Returns the names of supported item states.
174 *
175 * If the $state parameter is passed, returns the name of the specific state, otherwise - returns an array of all
176 * supported states.
177 *
178 * @param string $state
179 *
180 * @return array|string
181 */
182function itemState($state = null) {
183	$states = [
184		ITEM_STATE_NORMAL => _('Normal'),
185		ITEM_STATE_NOTSUPPORTED => _('Not supported')
186	];
187
188	if ($state === null) {
189		return $states;
190	}
191	elseif (isset($states[$state])) {
192		return $states[$state];
193	}
194	else {
195		return _('Unknown');
196	}
197}
198
199/**
200 * Returns the text indicating the items status and state. If the $state parameter is not given, only the status of
201 * the item will be taken into account.
202 *
203 * @param int $status
204 * @param int $state
205 *
206 * @return string
207 */
208function itemIndicator($status, $state = null) {
209	if ($status == ITEM_STATUS_ACTIVE) {
210		return ($state == ITEM_STATE_NOTSUPPORTED) ? _('Not supported') : _('Enabled');
211	}
212	elseif ($status == ITEM_STATUS_DISABLED) {
213		return _('Disabled');
214	}
215
216	return _('Unknown');
217}
218
219/**
220 * Returns the CSS class for the items status and state indicator. If the $state parameter is not given, only the status of
221 * the item will be taken into account.
222 *
223 * @param int $status
224 * @param int $state
225 *
226 * @return string
227 */
228function itemIndicatorStyle($status, $state = null) {
229	if ($status == ITEM_STATUS_ACTIVE) {
230		return ($state == ITEM_STATE_NOTSUPPORTED) ?
231			ZBX_STYLE_GREY :
232			ZBX_STYLE_GREEN;
233	}
234	elseif ($status == ITEM_STATUS_DISABLED) {
235		return ZBX_STYLE_RED;
236	}
237
238	return ZBX_STYLE_GREY;
239}
240
241/**
242 * Orders items by both status and state. Items are sorted in the following order: enabled, disabled, not supported.
243 *
244 * Keep in sync with orderTriggersByStatus().
245 *
246 * @param array  $items
247 * @param string $sortorder
248 */
249function orderItemsByStatus(array &$items, $sortorder = ZBX_SORT_UP) {
250	$sort = [];
251
252	foreach ($items as $key => $item) {
253		if ($item['status'] == ITEM_STATUS_ACTIVE) {
254			$statusOrder = ($item['state'] == ITEM_STATE_NOTSUPPORTED) ? 2 : 0;
255		}
256		elseif ($item['status'] == ITEM_STATUS_DISABLED) {
257			$statusOrder = 1;
258		}
259
260		$sort[$key] = $statusOrder;
261	}
262
263	if ($sortorder == ZBX_SORT_UP) {
264		asort($sort);
265	}
266	else {
267		arsort($sort);
268	}
269
270	$sortedItems = [];
271	foreach ($sort as $key => $val) {
272		$sortedItems[$key] = $items[$key];
273	}
274	$items = $sortedItems;
275}
276
277/**
278 * Returns the name of the given interface type. Items "status" and "state" properties must be defined.
279 *
280 * @param int $type
281 *
282 * @return null
283 */
284function interfaceType2str($type) {
285	$interfaceGroupLabels = [
286		INTERFACE_TYPE_AGENT => _('Agent'),
287		INTERFACE_TYPE_SNMP => _('SNMP'),
288		INTERFACE_TYPE_JMX => _('JMX'),
289		INTERFACE_TYPE_IPMI => _('IPMI'),
290	];
291
292	return isset($interfaceGroupLabels[$type]) ? $interfaceGroupLabels[$type] : null;
293}
294
295function itemTypeInterface($type = null) {
296	$types = [
297		ITEM_TYPE_SNMPV1 => INTERFACE_TYPE_SNMP,
298		ITEM_TYPE_SNMPV2C => INTERFACE_TYPE_SNMP,
299		ITEM_TYPE_SNMPV3 => INTERFACE_TYPE_SNMP,
300		ITEM_TYPE_SNMPTRAP => INTERFACE_TYPE_SNMP,
301		ITEM_TYPE_IPMI => INTERFACE_TYPE_IPMI,
302		ITEM_TYPE_ZABBIX => INTERFACE_TYPE_AGENT,
303		ITEM_TYPE_SIMPLE => INTERFACE_TYPE_ANY,
304		ITEM_TYPE_EXTERNAL => INTERFACE_TYPE_ANY,
305		ITEM_TYPE_SSH => INTERFACE_TYPE_ANY,
306		ITEM_TYPE_TELNET => INTERFACE_TYPE_ANY,
307		ITEM_TYPE_JMX => INTERFACE_TYPE_JMX
308	];
309	if (is_null($type)) {
310		return $types;
311	}
312	elseif (isset($types[$type])) {
313		return $types[$type];
314	}
315	else {
316		return false;
317	}
318}
319
320function update_item_status($itemids, $status) {
321	zbx_value2array($itemids);
322	$result = true;
323
324	$db_items = DBselect('SELECT i.* FROM items i WHERE '.dbConditionInt('i.itemid', $itemids));
325	while ($item = DBfetch($db_items)) {
326		$old_status = $item['status'];
327		if ($status != $old_status) {
328			$result &= DBexecute(
329				'UPDATE items SET status='.zbx_dbstr($status).' WHERE itemid='.zbx_dbstr($item['itemid'])
330			);
331			if ($result) {
332				$host = get_host_by_hostid($item['hostid']);
333				$item_new = get_item_by_itemid($item['itemid']);
334				add_audit_ext(AUDIT_ACTION_UPDATE, AUDIT_RESOURCE_ITEM, $item['itemid'], $host['host'].NAME_DELIMITER.$item['name'], 'items', $item, $item_new);
335			}
336		}
337	}
338	return $result;
339}
340
341function copyItemsToHosts($srcItemIds, $dstHostIds) {
342	$srcItems = API::Item()->get([
343		'itemids' => $srcItemIds,
344		'output' => [
345			'type', 'snmp_community', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status', 'value_type',
346			'trapper_hosts', 'units', 'multiplier', 'delta', 'snmpv3_contextname', 'snmpv3_securityname',
347			'snmpv3_securitylevel', 'snmpv3_authprotocol', 'snmpv3_authpassphrase', 'snmpv3_privprotocol',
348			'snmpv3_privpassphrase', 'formula', 'logtimefmt', 'valuemapid', 'delay_flex', 'params', 'ipmi_sensor',
349			'data_type', 'authtype', 'username', 'password', 'publickey', 'privatekey', 'flags', 'port',
350			'description', 'inventory_link'
351		],
352		'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL],
353		'selectApplications' => ['applicationid']
354	]);
355
356	$dstHosts = API::Host()->get([
357		'output' => ['hostid', 'host', 'status'],
358		'selectInterfaces' => ['interfaceid', 'type', 'main'],
359		'hostids' => $dstHostIds,
360		'preservekeys' => true,
361		'nopermissions' => true,
362		'templated_hosts' => true
363	]);
364
365	foreach ($dstHosts as $dstHost) {
366		$interfaceids = [];
367		foreach ($dstHost['interfaces'] as $interface) {
368			if ($interface['main'] == 1) {
369				$interfaceids[$interface['type']] = $interface['interfaceid'];
370			}
371		}
372		foreach ($srcItems as &$srcItem) {
373			if ($dstHost['status'] != HOST_STATUS_TEMPLATE) {
374				$type = itemTypeInterface($srcItem['type']);
375
376				if ($type == INTERFACE_TYPE_ANY) {
377					foreach ([INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_JMX, INTERFACE_TYPE_IPMI] as $itype) {
378						if (isset($interfaceids[$itype])) {
379							$srcItem['interfaceid'] = $interfaceids[$itype];
380							break;
381						}
382					}
383				}
384				elseif ($type !== false) {
385					if (!isset($interfaceids[$type])) {
386						error(_s('Cannot find host interface on "%1$s" for item key "%2$s".', $dstHost['host'], $srcItem['key_']));
387						return false;
388					}
389					$srcItem['interfaceid'] = $interfaceids[$type];
390				}
391			}
392			unset($srcItem['itemid']);
393			$srcItem['hostid'] = $dstHost['hostid'];
394			$srcItem['applications'] = get_same_applications_for_host(zbx_objectValues($srcItem['applications'], 'applicationid'), $dstHost['hostid']);
395		}
396		unset($srcItem);
397
398		if (!API::Item()->create($srcItems)) {
399			return false;
400		}
401	}
402	return true;
403}
404
405function copyItems($srcHostId, $dstHostId) {
406	$srcItems = API::Item()->get([
407		'hostids' => $srcHostId,
408		'output' => [
409			'type', 'snmp_community', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status', 'value_type',
410			'trapper_hosts', 'units', 'multiplier', 'delta', 'snmpv3_contextname', 'snmpv3_securityname',
411			'snmpv3_securitylevel', 'snmpv3_authprotocol', 'snmpv3_authpassphrase', 'snmpv3_privprotocol',
412			'snmpv3_privpassphrase', 'formula', 'logtimefmt', 'valuemapid', 'delay_flex', 'params', 'ipmi_sensor',
413			'data_type', 'authtype', 'username', 'password', 'publickey', 'privatekey', 'flags', 'port',
414			'description', 'inventory_link'
415		],
416		'inherited' => false,
417		'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL],
418		'selectApplications' => ['applicationid']
419	]);
420	$dstHosts = API::Host()->get([
421		'output' => ['hostid', 'host', 'status'],
422		'selectInterfaces' => ['interfaceid', 'type', 'main'],
423		'hostids' => $dstHostId,
424		'preservekeys' => true,
425		'nopermissions' => true,
426		'templated_hosts' => true
427	]);
428	$dstHost = reset($dstHosts);
429
430	foreach ($srcItems as &$srcItem) {
431		if ($dstHost['status'] != HOST_STATUS_TEMPLATE) {
432			// find a matching interface
433			$interface = CItem::findInterfaceForItem($srcItem, $dstHost['interfaces']);
434			if ($interface) {
435				$srcItem['interfaceid'] = $interface['interfaceid'];
436			}
437			// no matching interface found, throw an error
438			elseif ($interface !== false) {
439				error(_s('Cannot find host interface on "%1$s" for item key "%2$s".', $dstHost['host'], $srcItem['key_']));
440			}
441		}
442		unset($srcItem['itemid']);
443		unset($srcItem['templateid']);
444		$srcItem['hostid'] = $dstHostId;
445		$srcItem['applications'] = get_same_applications_for_host(zbx_objectValues($srcItem['applications'], 'applicationid'), $dstHostId);
446	}
447	unset($srcItem);
448
449	return API::Item()->create($srcItems);
450}
451
452/**
453 * Copy applications to a different host.
454 *
455 * @param string $source_hostid
456 * @param string $destination_hostid
457 *
458 * @return bool
459 */
460function copyApplications($source_hostid, $destination_hostid) {
461	$applications_to_create = API::Application()->get([
462		'output' => ['name'],
463		'hostids' => [$source_hostid],
464		'inherited' => false,
465		'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL]
466	]);
467
468	if (!$applications_to_create) {
469		return true;
470	}
471
472	foreach ($applications_to_create as &$application) {
473		$application['hostid'] = $destination_hostid;
474		unset($application['applicationid'], $application['templateid']);
475	}
476	unset($application);
477
478	return (bool) API::Application()->create($applications_to_create);
479}
480
481function activate_item($itemids) {
482	zbx_value2array($itemids);
483
484	// first update status for child items
485	$child_items = [];
486	$db_items = DBselect('SELECT i.itemid,i.hostid FROM items i WHERE '.dbConditionInt('i.templateid', $itemids));
487	while ($item = DBfetch($db_items)) {
488		$child_items[$item['itemid']] = $item['itemid'];
489	}
490	if (!empty($child_items)) {
491		activate_item($child_items); // Recursion !!!
492	}
493	return update_item_status($itemids, ITEM_STATUS_ACTIVE);
494}
495
496function disable_item($itemids) {
497	zbx_value2array($itemids);
498
499	// first update status for child items
500	$chd_items = [];
501	$db_tmp_items = DBselect('SELECT i.itemid,i.hostid FROM items i WHERE '.dbConditionInt('i.templateid', $itemids));
502	while ($db_tmp_item = DBfetch($db_tmp_items)) {
503		$chd_items[$db_tmp_item['itemid']] = $db_tmp_item['itemid'];
504	}
505	if (!empty($chd_items)) {
506		disable_item($chd_items); // Recursion !!!
507	}
508	return update_item_status($itemids, ITEM_STATUS_DISABLED);
509}
510
511function get_item_by_itemid($itemid) {
512	$db_items = DBfetch(DBselect('SELECT i.* FROM items i WHERE i.itemid='.zbx_dbstr($itemid)));
513	if ($db_items) {
514		return $db_items;
515	}
516	error(_s('No item with itemid="%1$s".', $itemid));
517	return false;
518}
519
520function get_item_by_itemid_limited($itemid) {
521	$row = DBfetch(DBselect(
522		'SELECT i.itemid,i.interfaceid,i.name,i.key_,i.hostid,i.delay,i.history,i.status,i.type,i.lifetime,'.
523			'i.snmp_community,i.snmp_oid,i.value_type,i.data_type,i.trapper_hosts,i.port,i.units,i.multiplier,'.
524			'i.delta,i.snmpv3_contextname,i.snmpv3_securityname,i.snmpv3_securitylevel,i.snmpv3_authprotocol,'.
525			'i.snmpv3_authpassphrase,i.snmpv3_privprotocol,i.snmpv3_privpassphrase,i.formula,i.trends,i.logtimefmt,'.
526			'i.valuemapid,i.delay_flex,i.params,i.ipmi_sensor,i.templateid,i.authtype,i.username,i.password,'.
527			'i.publickey,i.privatekey,i.flags,i.description,i.inventory_link'.
528		' FROM items i'.
529		' WHERE i.itemid='.zbx_dbstr($itemid)));
530	if ($row) {
531		return $row;
532	}
533	error(_s('No item with itemid "%1$s".', $itemid));
534	return false;
535}
536
537/**
538 * Description:
539 * Replace items for specified host
540 *
541 * Comments:
542 * $error= true : rise Error if item doesn't exist (error generated), false: special processing (NO error generated)
543 */
544function get_same_item_for_host($item, $dest_hostids) {
545	$return_array = is_array($dest_hostids);
546	zbx_value2array($dest_hostids);
547
548	if (!is_array($item)) {
549		$itemid = $item;
550	}
551	elseif (isset($item['itemid'])) {
552		$itemid = $item['itemid'];
553	}
554
555	$same_item = null;
556	$same_items = [];
557
558	if (isset($itemid)) {
559		$db_items = DBselect(
560			'SELECT src.*'.
561			' FROM items src,items dest'.
562			' WHERE dest.itemid='.zbx_dbstr($itemid).
563				' AND src.key_=dest.key_'.
564				' AND '.dbConditionInt('src.hostid', $dest_hostids)
565		);
566		while ($db_item = DBfetch($db_items)) {
567			if (is_array($item)) {
568				$same_item = $db_item;
569				$same_items[$db_item['itemid']] = $db_item;
570			}
571			else {
572				$same_item = $db_item['itemid'];
573				$same_items[$db_item['itemid']] = $db_item['itemid'];
574			}
575		}
576		if ($return_array) {
577			return $same_items;
578		}
579		else {
580			return $same_item;
581		}
582	}
583	return false;
584}
585
586function get_realhost_by_itemid($itemid) {
587	$item = get_item_by_itemid($itemid);
588	if ($item['templateid'] <> 0) {
589		return get_realhost_by_itemid($item['templateid']); // attention recursion!
590	}
591	return get_host_by_itemid($itemid);
592}
593
594function fillItemsWithChildTemplates(&$items) {
595	$processSecondLevel = false;
596	$dbItems = DBselect('SELECT i.itemid,i.templateid FROM items i WHERE '.dbConditionInt('i.itemid', zbx_objectValues($items, 'templateid')));
597	while ($dbItem = DBfetch($dbItems)) {
598		foreach ($items as $itemid => $item) {
599			if ($item['templateid'] == $dbItem['itemid'] && !empty($dbItem['templateid'])) {
600				$items[$itemid]['templateid'] = $dbItem['templateid'];
601				$processSecondLevel = true;
602			}
603		}
604	}
605	if ($processSecondLevel) {
606		fillItemsWithChildTemplates($items); // attention recursion!
607	}
608}
609
610function get_realrule_by_itemid_and_hostid($itemid, $hostid) {
611	$item = get_item_by_itemid($itemid);
612	if (bccomp($hostid,$item['hostid']) == 0) {
613		return $item['itemid'];
614	}
615	if ($item['templateid'] <> 0) {
616		return get_realrule_by_itemid_and_hostid($item['templateid'], $hostid);
617	}
618	return $item['itemid'];
619}
620
621/**
622 * Retrieve overview table object for items.
623 *
624 * @param array  		$hostIds
625 * @param array|null	$applicationIds		IDs of applications to filter items by
626 * @param int    		$viewMode
627 *
628 * @return CTableInfo
629 */
630function getItemsDataOverview($hostIds, array $applicationIds = null, $viewMode) {
631	$sqlFrom = '';
632	$sqlWhere = '';
633
634	if ($applicationIds !== null) {
635		$sqlFrom = 'items_applications ia,';
636		$sqlWhere = ' AND i.itemid=ia.itemid AND '.dbConditionInt('ia.applicationid', $applicationIds);
637	}
638
639	$dbItems = DBfetchArray(DBselect(
640		'SELECT DISTINCT h.hostid,h.name AS hostname,i.itemid,i.key_,i.value_type,i.units,'.
641			'i.name,t.priority,i.valuemapid,t.value AS tr_value,t.triggerid'.
642		' FROM hosts h,'.$sqlFrom.'items i'.
643			' LEFT JOIN functions f ON f.itemid=i.itemid'.
644			' LEFT JOIN triggers t ON t.triggerid=f.triggerid AND t.status='.TRIGGER_STATUS_ENABLED.
645		' WHERE '.dbConditionInt('h.hostid', $hostIds).
646			' AND h.status='.HOST_STATUS_MONITORED.
647			' AND h.hostid=i.hostid'.
648			' AND i.status='.ITEM_STATUS_ACTIVE.
649			' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_CREATED]).
650				$sqlWhere
651	));
652
653	$dbItems = CMacrosResolverHelper::resolveItemNames($dbItems);
654
655	CArrayHelper::sort($dbItems, [
656		['field' => 'name_expanded', 'order' => ZBX_SORT_UP],
657		['field' => 'itemid', 'order' => ZBX_SORT_UP]
658	]);
659
660	// fetch latest values
661	$history = Manager::History()->getLast(zbx_toHash($dbItems, 'itemid'), 1, ZBX_HISTORY_PERIOD);
662
663	// fetch data for the host JS menu
664	$hosts = API::Host()->get([
665		'output' => ['name', 'hostid', 'status'],
666		'monitored_hosts' => true,
667		'hostids' => $hostIds,
668		'with_monitored_items' => true,
669		'preservekeys' => true,
670		'selectGraphs' => API_OUTPUT_COUNT,
671		'selectScreens' => ($viewMode == STYLE_LEFT) ? API_OUTPUT_COUNT : null
672	]);
673
674	$items = [];
675	$item_counter = [];
676	$host_items = [];
677	foreach ($dbItems as $dbItem) {
678		$item_name = $dbItem['name_expanded'];
679		$host_name = $dbItem['hostname'];
680		$hostNames[$dbItem['hostid']] = $host_name;
681
682		if (!array_key_exists($host_name, $item_counter)) {
683			$item_counter[$host_name] = [];
684		}
685
686		if (!array_key_exists($item_name, $item_counter[$host_name])) {
687			$item_counter[$host_name][$item_name] = 0;
688		}
689
690		if (!array_key_exists($item_name, $host_items) || !array_key_exists($host_name, $host_items[$item_name])) {
691			$host_items[$item_name][$host_name] = [];
692		}
693
694		// a little tricky check for attempt to overwrite active trigger (value=1) with
695		// inactive or active trigger with lower priority.
696		if (!array_key_exists($dbItem['itemid'], $host_items[$item_name][$host_name])
697			|| (($host_items[$item_name][$host_name][$dbItem['itemid']]['tr_value'] == TRIGGER_VALUE_FALSE && $dbItem['tr_value'] == TRIGGER_VALUE_TRUE)
698				|| (($host_items[$item_name][$host_name][$dbItem['itemid']]['tr_value'] == TRIGGER_VALUE_FALSE || $dbItem['tr_value'] == TRIGGER_VALUE_TRUE)
699					&& $dbItem['priority'] > $host_items[$item_name][$host_name][$dbItem['itemid']]['severity']))) {
700
701			if (array_key_exists($dbItem['itemid'], $host_items[$item_name][$host_name])) {
702				$item_place = $host_items[$item_name][$host_name][$dbItem['itemid']]['item_place'];
703			}
704			else {
705				$item_place = $item_counter[$host_name][$item_name];
706				$item_counter[$host_name][$item_name]++;
707			}
708
709			$items[$item_name][$item_place][$host_name] = [
710				'itemid' => $dbItem['itemid'],
711				'value_type' => $dbItem['value_type'],
712				'value' => isset($history[$dbItem['itemid']]) ? $history[$dbItem['itemid']][0]['value'] : null,
713				'units' => $dbItem['units'],
714				'valuemapid' => $dbItem['valuemapid'],
715				'severity' => $dbItem['priority'],
716				'tr_value' => $dbItem['tr_value'],
717				'triggerid' => $dbItem['triggerid'],
718				'item_place' => $item_place
719			];
720
721			$host_items[$item_name][$host_name][$dbItem['itemid']] = $items[$item_name][$item_place][$host_name];
722		}
723	}
724
725	$table = new CTableInfo();
726	if (empty($hostNames)) {
727		return $table;
728	}
729	$table->makeVerticalRotation();
730
731	order_result($hostNames);
732
733	if ($viewMode == STYLE_TOP) {
734		$header = [_('Items')];
735		foreach ($hostNames as $hostName) {
736			$header[] = (new CColHeader($hostName))->addClass('vertical_rotation');
737		}
738		$table->setHeader($header);
739
740		foreach ($items as $item_name => $item_data) {
741			foreach ($item_data as $ithosts) {
742				$tableRow = [nbsp($item_name)];
743				foreach ($hostNames as $hostName) {
744					$tableRow = getItemDataOverviewCells($tableRow, $ithosts, $hostName);
745				}
746				$table->addRow($tableRow);
747			}
748		}
749	}
750	else {
751		$scripts = API::Script()->getScriptsByHosts(zbx_objectValues($hosts, 'hostid'));
752
753		$header = [_('Hosts')];
754		foreach ($items as $item_name => $item_data) {
755			foreach ($item_data as $ithosts) {
756				$header[] = (new CColHeader($item_name))->addClass('vertical_rotation');
757			}
758		}
759		$table->setHeader($header);
760
761		foreach ($hostNames as $hostId => $hostName) {
762			$host = $hosts[$hostId];
763
764			$name = (new CSpan($host['name']))
765				->addClass(ZBX_STYLE_LINK_ACTION)
766				->setMenuPopup(CMenuPopupHelper::getHost($host, $scripts[$hostId]));
767
768			$tableRow = [(new CCol($name))->addClass(ZBX_STYLE_NOWRAP)];
769			foreach ($items as $item_data) {
770				foreach ($item_data as $ithosts) {
771					$tableRow = getItemDataOverviewCells($tableRow, $ithosts, $hostName);
772				}
773			}
774			$table->addRow($tableRow);
775		}
776	}
777
778	return $table;
779}
780
781function getItemDataOverviewCells($tableRow, $ithosts, $hostName) {
782	$ack = null;
783	$css = '';
784	$value = UNKNOWN_VALUE;
785
786	if (isset($ithosts[$hostName])) {
787		$item = $ithosts[$hostName];
788
789		if ($item['tr_value'] == TRIGGER_VALUE_TRUE) {
790			$css = getSeverityStyle($item['severity']);
791
792			// Display event acknowledgement.
793			$config = select_config();
794			if ($config['event_ack_enable']) {
795				$ack = get_last_event_by_triggerid($item['triggerid']);
796				$ack = ($ack['acknowledged'] == 1)
797					? [SPACE, (new CSpan())->addClass(ZBX_STYLE_ICON_ACKN)]
798					: null;
799			}
800		}
801
802		if ($item['value'] !== null) {
803			$value = formatHistoryValue($item['value'], $item);
804		}
805	}
806
807	if ($value != UNKNOWN_VALUE) {
808		$value = $value;
809	}
810
811	$column = (new CCol([$value, $ack]))->addClass($css);
812
813	if (isset($ithosts[$hostName])) {
814		$column
815			->setMenuPopup(CMenuPopupHelper::getHistory($item))
816			->addClass(ZBX_STYLE_CURSOR_POINTER)
817			->addClass(ZBX_STYLE_NOWRAP);
818	}
819
820	$tableRow[] = $column;
821
822	return $tableRow;
823}
824
825/**
826 * Get same application IDs on destination host and return array with keys as source application IDs
827 * and values as destination application IDs.
828 *
829 * Comments: !!! Don't forget sync code with C !!!
830 *
831 * @param array  $applicationIds
832 * @param string $hostId
833 *
834 * @return array
835 */
836function get_same_applications_for_host(array $applicationIds, $hostId) {
837	$applications = [];
838
839	$dbApplications = DBselect(
840		'SELECT a1.applicationid AS dstappid,a2.applicationid AS srcappid'.
841		' FROM applications a1,applications a2'.
842		' WHERE a1.name=a2.name'.
843			' AND a1.hostid='.zbx_dbstr($hostId).
844			' AND '.dbConditionInt('a2.applicationid', $applicationIds)
845	);
846
847	while ($dbApplication = DBfetch($dbApplications)) {
848		$applications[$dbApplication['srcappid']] = $dbApplication['dstappid'];
849	}
850
851	return $applications;
852}
853
854/******************************************************************************
855 *                                                                            *
856 * Comments: !!! Don't forget sync code with C !!!                            *
857 *                                                                            *
858 ******************************************************************************/
859function get_applications_by_itemid($itemids, $field = 'applicationid') {
860	zbx_value2array($itemids);
861	$result = [];
862	$db_applications = DBselect(
863		'SELECT DISTINCT app.'.$field.' AS result'.
864		' FROM applications app,items_applications ia'.
865		' WHERE app.applicationid=ia.applicationid'.
866			' AND '.dbConditionInt('ia.itemid', $itemids)
867	);
868	while ($db_application = DBfetch($db_applications)) {
869		array_push($result, $db_application['result']);
870	}
871
872	return $result;
873}
874
875/**
876 * Clear item history and trends by provided item IDs.
877 *
878 * @param array $itemIds
879 *
880 * @return bool
881 */
882function deleteHistoryByItemIds(array $itemIds) {
883	$result = DBexecute('DELETE FROM trends WHERE '.dbConditionInt('itemid', $itemIds));
884	$result = ($result && DBexecute('DELETE FROM trends_uint WHERE '.dbConditionInt('itemid', $itemIds)));
885	$result = ($result && DBexecute('DELETE FROM history_text WHERE '.dbConditionInt('itemid', $itemIds)));
886	$result = ($result && DBexecute('DELETE FROM history_log WHERE '.dbConditionInt('itemid', $itemIds)));
887	$result = ($result && DBexecute('DELETE FROM history_uint WHERE '.dbConditionInt('itemid', $itemIds)));
888	$result = ($result && DBexecute('DELETE FROM history_str WHERE '.dbConditionInt('itemid', $itemIds)));
889	$result = ($result && DBexecute('DELETE FROM history WHERE '.dbConditionInt('itemid', $itemIds)));
890
891	return $result;
892}
893
894/**
895 * Format history value.
896 * First format the value according to the configuration of the item. Then apply the value mapping to the formatted (!)
897 * value.
898 *
899 * @param mixed     $value
900 * @param array     $item
901 * @param int       $item['value_type']     type of the value: ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64, ...
902 * @param string    $item['units']          units of item
903 * @param int       $item['valuemapid']     id of mapping set of values
904 * @param bool      $trim
905 *
906 * @return string
907 */
908function formatHistoryValue($value, array $item, $trim = true) {
909	$mapping = false;
910
911	// format value
912	if ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) {
913		$value = convert_units([
914				'value' => $value,
915				'units' => $item['units']
916		]);
917	}
918	elseif ($item['value_type'] != ITEM_VALUE_TYPE_STR
919		&& $item['value_type'] != ITEM_VALUE_TYPE_TEXT
920		&& $item['value_type'] != ITEM_VALUE_TYPE_LOG) {
921
922		$value = _('Unknown value type');
923	}
924
925	// apply value mapping
926	switch ($item['value_type']) {
927		case ITEM_VALUE_TYPE_STR:
928			$mapping = getMappedValue($value, $item['valuemapid']);
929		// break; is not missing here
930		case ITEM_VALUE_TYPE_TEXT:
931		case ITEM_VALUE_TYPE_LOG:
932			if ($trim && mb_strlen($value) > 20) {
933				$value = mb_substr($value, 0, 20).'...';
934			}
935
936			if ($mapping !== false) {
937				$value = $mapping.' ('.$value.')';
938			}
939			break;
940		default:
941			$value = applyValueMap($value, $item['valuemapid']);
942	}
943
944	return $value;
945}
946
947/**
948 * Retrieves from DB historical data for items and applies functional calculations.
949 * If fails for some reason, returns UNRESOLVED_MACRO_STRING.
950 *
951 * @param array		$item
952 * @param string	$item['value_type']	type of item, allowed: ITEM_VALUE_TYPE_FLOAT and ITEM_VALUE_TYPE_UINT64
953 * @param string	$item['itemid']		ID of item
954 * @param string	$item['units']		units of item
955 * @param string	$function			function to apply to time period from param, allowed: min, max and avg
956 * @param string	$parameter			formatted parameter for function, example: "2w" meaning 2 weeks
957 *
958 * @return string item functional value from history
959 */
960function getItemFunctionalValue($item, $function, $parameter) {
961	// check whether function is allowed
962	if (!in_array($function, ['min', 'max', 'avg']) || $parameter === '') {
963		return UNRESOLVED_MACRO_STRING;
964	}
965
966	$parameter = convertFunctionValue($parameter);
967
968	if (bccomp($parameter, 0) == 0) {
969		return UNRESOLVED_MACRO_STRING;
970	}
971
972	// allowed item types for min, max and avg function
973	$historyTables = [ITEM_VALUE_TYPE_FLOAT => 'history', ITEM_VALUE_TYPE_UINT64 => 'history_uint'];
974
975	if (!isset($historyTables[$item['value_type']])) {
976		return UNRESOLVED_MACRO_STRING;
977	}
978	else {
979		// search for item function data in DB corresponding history table
980		$result = DBselect(
981			'SELECT '.$function.'(value) AS value'.
982			' FROM '.$historyTables[$item['value_type']].
983			' WHERE clock>'.(time() - $parameter).
984			' AND itemid='.zbx_dbstr($item['itemid']).
985			' HAVING COUNT(*)>0' // necessary because DBselect() return 0 if empty data set, for graph templates
986		);
987		if ($row = DBfetch($result)) {
988			return convert_units(['value' => $row['value'], 'units' => $item['units']]);
989		}
990		// no data in history
991		else {
992			return UNRESOLVED_MACRO_STRING;
993		}
994	}
995}
996
997/**
998 * Returns the history value of the item at the given time. If no value exists at the given time, the function
999 * will return the previous value.
1000 *
1001 * The $db_item parameter must have the value_type and itemid properties set.
1002 *
1003 * @param array $db_item
1004 * @param int $clock
1005 * @param int $ns
1006 *
1007 * @return string
1008 */
1009function item_get_history($db_item, $clock, $ns) {
1010	$value = null;
1011
1012	$table = CHistoryManager::getTableName($db_item['value_type']);
1013
1014	$sql = 'SELECT value'.
1015			' FROM '.$table.
1016			' WHERE itemid='.zbx_dbstr($db_item['itemid']).
1017				' AND clock='.zbx_dbstr($clock).
1018				' AND ns='.zbx_dbstr($ns);
1019	if (null != ($row = DBfetch(DBselect($sql, 1)))) {
1020		$value = $row['value'];
1021	}
1022	if ($value != null) {
1023		return $value;
1024	}
1025
1026	$max_clock = 0;
1027
1028	$sql = 'SELECT DISTINCT clock'.
1029			' FROM '.$table.
1030			' WHERE itemid='.zbx_dbstr($db_item['itemid']).
1031				' AND clock='.zbx_dbstr($clock).
1032				' AND ns<'.zbx_dbstr($ns);
1033	if (null != ($row = DBfetch(DBselect($sql)))) {
1034		$max_clock = $row['clock'];
1035	}
1036	if ($max_clock == 0) {
1037		$sql = 'SELECT MAX(clock) AS clock'.
1038				' FROM '.$table.
1039				' WHERE itemid='.zbx_dbstr($db_item['itemid']).
1040					' AND clock<'.zbx_dbstr($clock).
1041					(ZBX_HISTORY_PERIOD ? ' AND clock>='.zbx_dbstr($clock - ZBX_HISTORY_PERIOD) : '');
1042		if (null != ($row = DBfetch(DBselect($sql)))) {
1043			$max_clock = $row['clock'];
1044		}
1045	}
1046	if ($max_clock == 0) {
1047		return $value;
1048	}
1049
1050	if ($clock == $max_clock) {
1051		$sql = 'SELECT value'.
1052				' FROM '.$table.
1053				' WHERE itemid='.zbx_dbstr($db_item['itemid']).
1054					' AND clock='.zbx_dbstr($clock).
1055					' AND ns<'.zbx_dbstr($ns);
1056	}
1057	else {
1058		$sql = 'SELECT value'.
1059				' FROM '.$table.
1060				' WHERE itemid='.zbx_dbstr($db_item['itemid']).
1061					' AND clock='.zbx_dbstr($max_clock).
1062				' ORDER BY itemid,clock desc,ns desc';
1063	}
1064
1065	if (null != ($row = DBfetch(DBselect($sql, 1)))) {
1066		$value = $row['value'];
1067	}
1068
1069	return $value;
1070}
1071
1072/**
1073 * Check if current time is within the given period.
1074 *
1075 * @param string $period	time period format: "wd[-wd2],hh:mm-hh:mm"
1076 * @param int $now			current timestamp
1077 *
1078 * @return bool		true - within period, false - out of period
1079 */
1080function checkTimePeriod($period, $now) {
1081	if (sscanf($period, '%d-%d,%d:%d-%d:%d', $d1, $d2, $h1, $m1, $h2, $m2) != 6) {
1082		if (sscanf($period, '%d,%d:%d-%d:%d', $d1, $h1, $m1, $h2, $m2) != 5) {
1083			// delay period format is wrong - skip
1084			return false;
1085		}
1086		$d2 = $d1;
1087	}
1088
1089	$tm = localtime($now, true);
1090	$day = ($tm['tm_wday'] == 0) ? 7 : $tm['tm_wday'];
1091	$sec = SEC_PER_HOUR * $tm['tm_hour'] + SEC_PER_MIN * $tm['tm_min'] + $tm['tm_sec'];
1092
1093	$sec1 = SEC_PER_HOUR * $h1 + SEC_PER_MIN * $m1;
1094	$sec2 = SEC_PER_HOUR * $h2 + SEC_PER_MIN * $m2;
1095
1096	return $d1 <= $day && $day <= $d2 && $sec1 <= $sec && $sec < $sec2;
1097}
1098
1099/**
1100 * Get item minimum delay.
1101 *
1102 * @param string $delay
1103 * @param array $flexible_intervals
1104 *
1105 * @return string
1106 */
1107function getItemDelay($delay, array $flexible_intervals) {
1108	if ($delay != 0 || !$flexible_intervals) {
1109		return $delay;
1110	}
1111
1112	$min_delay = SEC_PER_YEAR;
1113
1114	foreach ($flexible_intervals as $flexible_interval) {
1115		$flexible_interval_parts = explode('/', $flexible_interval);
1116		$flexible_delay = (int) $flexible_interval_parts[0];
1117
1118		$min_delay = min($min_delay, $flexible_delay);
1119	}
1120
1121	return $min_delay;
1122}
1123
1124/**
1125 * Return delay value that is currently applicable
1126 *
1127 * @param int $delay					default delay
1128 * @param array $flexible_intervals		array of intervals in format: "d/wd[-wd2],hh:mm-hh:mm"
1129 * @param int $now						current timestamp
1130 *
1131 * @return int							delay for a current timestamp
1132 */
1133function getCurrentDelay($delay, array $flexible_intervals, $now) {
1134	if (!$flexible_intervals) {
1135		return $delay;
1136	}
1137
1138	$current_delay = -1;
1139
1140	foreach ($flexible_intervals as $flexible_interval) {
1141		list($flexible_delay, $flexible_period) = explode('/', $flexible_interval);
1142		$flexible_delay = (int) $flexible_delay;
1143
1144		if (($current_delay == -1 || $flexible_delay < $current_delay) && checkTimePeriod($flexible_period, $now)) {
1145			$current_delay = $flexible_delay;
1146		}
1147	}
1148
1149	if ($current_delay == -1) {
1150		return $delay;
1151	}
1152
1153	return $current_delay;
1154}
1155
1156/**
1157 * Return time of next flexible interval
1158 *
1159 * @param array $flexible_intervals  array of intervals in format: "d/wd[-wd2],hh:mm-hh:mm"
1160 * @param int $now                   current timestamp
1161 * @param int $next_interval          timestamp of a next interval
1162 *
1163 * @return bool                      false if no flexible intervals defined
1164 */
1165function getNextDelayInterval(array $flexible_intervals, $now, &$next_interval) {
1166	if (!$flexible_intervals) {
1167		return false;
1168	}
1169
1170	$next = 0;
1171	$tm = localtime($now, true);
1172	$day = ($tm['tm_wday'] == 0) ? 7 : $tm['tm_wday'];
1173	$sec = SEC_PER_HOUR * $tm['tm_hour'] + SEC_PER_MIN * $tm['tm_min'] + $tm['tm_sec'];
1174
1175	foreach ($flexible_intervals as $flexible_interval) {
1176		$flexible_interval_parts = explode('/', $flexible_interval);
1177
1178		if (sscanf($flexible_interval_parts[1], '%d-%d,%d:%d-%d:%d', $d1, $d2, $h1, $m1, $h2, $m2) != 6) {
1179			if (sscanf($flexible_interval_parts[1], '%d,%d:%d-%d:%d', $d1, $h1, $m1, $h2, $m2) != 5) {
1180				continue;
1181			}
1182			$d2 = $d1;
1183		}
1184
1185		$sec1 = SEC_PER_HOUR * $h1 + SEC_PER_MIN * $m1;
1186		$sec2 = SEC_PER_HOUR * $h2 + SEC_PER_MIN * $m2;
1187
1188		// current period
1189		if ($d1 <= $day && $day <= $d2 && $sec1 <= $sec && $sec < $sec2) {
1190			if ($next == 0 || $next > $now - $sec + $sec2) {
1191				// the next second after the current interval's upper bound
1192				$next = $now - $sec + $sec2;
1193			}
1194		}
1195		// will be active today
1196		elseif ($d1 <= $day && $d2 >= $day && $sec < $sec1) {
1197			if ($next == 0 || $next > $now - $sec + $sec1) {
1198				$next = $now - $sec + $sec1;
1199			}
1200		}
1201		else {
1202			$nextDay = ($day + 1 <= 7) ? $day + 1 : 1;
1203
1204			// will be active tomorrow
1205			if ($d1 <= $nextDay && $nextDay <= $d2) {
1206				if ($next == 0 || $next > $now - $sec + SEC_PER_DAY + $sec1) {
1207					$next = $now - $sec + SEC_PER_DAY + $sec1;
1208				}
1209			}
1210			// later in the future
1211			else {
1212				$dayDiff = -1;
1213
1214				if ($day < $d1) {
1215					$dayDiff = $d1 - $day;
1216				}
1217				if ($day >= $d2) {
1218					$dayDiff = ($d1 + 7) - $day;
1219				}
1220				if ($d1 <= $day && $day < $d2) {
1221					// should never happen, could not deduce day difference
1222					$dayDiff = -1;
1223				}
1224				if ($dayDiff != -1 && ($next == 0 || $next > $now - $sec + SEC_PER_DAY * $dayDiff + $sec1)) {
1225					$next = $now - $sec + SEC_PER_DAY * $dayDiff + $sec1;
1226				}
1227			}
1228		}
1229	}
1230
1231	if ($next != 0) {
1232		$next_interval = $next;
1233	}
1234
1235	return ($next != 0);
1236}
1237
1238/**
1239 * Calculate nextcheck timestamp for an item using flexible intervals.
1240 *
1241 * the parameter $flexible_intervals is an array if strings that are in the following format:
1242 *
1243 *           +------------[;]<----------+
1244 *           |                          |
1245 *         ->+-[d/wd[-wd2],hh:mm-hh:mm]-+
1246 *
1247 *         d       - delay (0-n)
1248 *         wd, wd2 - day of week (1-7)
1249 *         hh      - hours (0-24)
1250 *         mm      - minutes (0-59)
1251 *
1252 * @param int $seed						seed value applied to delay to spread item checks over the delay period
1253 * @param string $delay					default delay, can be overridden
1254 * @param array $flexible_intervals		array of flexible intervals
1255 * @param int $now						current timestamp
1256 *
1257 * @return int
1258 */
1259function calculateItemNextCheck($seed, $delay, $flexible_intervals, $now) {
1260	/*
1261	 * Try to find the nearest 'nextcheck' value with condition 'now' < 'nextcheck' < 'now' + SEC_PER_YEAR
1262	 * If it is not possible to check the item within a year, fail.
1263	 */
1264
1265	$t = $now;
1266	$tMax = $now + SEC_PER_YEAR;
1267	$try = 0;
1268
1269	while ($t < $tMax) {
1270		// Calculate 'nextcheck' value for the current interval.
1271		$currentDelay = getCurrentDelay($delay, $flexible_intervals, $t);
1272
1273		if ($currentDelay != 0) {
1274			$nextCheck = $currentDelay * floor($t / $currentDelay) + ($seed % $currentDelay);
1275
1276			if ($try == 0) {
1277				while ($nextCheck <= $t) {
1278					$nextCheck += $currentDelay;
1279				}
1280			}
1281			else {
1282				while ($nextCheck < $t) {
1283					$nextCheck += $currentDelay;
1284				}
1285			}
1286		}
1287		else {
1288			$nextCheck = ZBX_JAN_2038;
1289		}
1290
1291		/*
1292		 * Is 'nextcheck' < end of the current interval and the end of the current interval
1293		 * is the beginning of the next interval - 1.
1294		 */
1295		if (getNextDelayInterval($flexible_intervals, $t, $nextInterval) && $nextCheck >= $nextInterval) {
1296			// 'nextcheck' is beyond the current interval.
1297			$t = $nextInterval;
1298			$try++;
1299		}
1300		else {
1301			break;
1302		}
1303	}
1304
1305	return $nextCheck;
1306}
1307
1308/*
1309 * Description:
1310 *	Function returns true if http items exists in the $items array.
1311 *	The array should contain a field 'type'
1312 */
1313function httpItemExists($items) {
1314	foreach ($items as $item) {
1315		if ($item['type'] == ITEM_TYPE_HTTPTEST) {
1316			return true;
1317		}
1318	}
1319	return false;
1320}
1321
1322function getParamFieldNameByType($itemType) {
1323	switch ($itemType) {
1324		case ITEM_TYPE_SSH:
1325		case ITEM_TYPE_TELNET:
1326		case ITEM_TYPE_JMX:
1327			return 'params_es';
1328		case ITEM_TYPE_DB_MONITOR:
1329			return 'params_ap';
1330		case ITEM_TYPE_CALCULATED:
1331			return 'params_f';
1332		default:
1333			return 'params';
1334	}
1335}
1336
1337function getParamFieldLabelByType($itemType) {
1338	switch ($itemType) {
1339		case ITEM_TYPE_SSH:
1340		case ITEM_TYPE_TELNET:
1341		case ITEM_TYPE_JMX:
1342			return _('Executed script');
1343		case ITEM_TYPE_DB_MONITOR:
1344			return _('SQL query');
1345		case ITEM_TYPE_CALCULATED:
1346			return _('Formula');
1347		default:
1348			return 'params';
1349	}
1350}
1351
1352/*
1353 * Quoting $param if it contain special characters.
1354 *
1355 * @param string $param
1356 * @param bool   $forced
1357 *
1358 * @return string
1359 */
1360function quoteItemKeyParam($param, $forced = false) {
1361	if (!$forced) {
1362		if (!isset($param[0]) || ($param[0] != '"' && false === strpbrk($param, ',]'))) {
1363			return $param;
1364		}
1365	}
1366
1367	return '"'.str_replace('"', '\\"', $param).'"';
1368}
1369