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
22require_once dirname(__FILE__).'/include/config.inc.php';
23require_once dirname(__FILE__).'/include/hosts.inc.php';
24require_once dirname(__FILE__).'/include/items.inc.php';
25require_once dirname(__FILE__).'/include/forms.inc.php';
26
27$page['title'] = _('Configuration of item prototypes');
28$page['file'] = 'disc_prototypes.php';
29$page['scripts'] = ['effects.js', 'class.cviewswitcher.js', 'multilineinput.js', 'multiselect.js', 'items.js',
30	'textareaflexible.js'
31];
32
33require_once dirname(__FILE__).'/include/page_header.php';
34
35$paramsFieldName = getParamFieldNameByType(getRequest('type', 0));
36
37// VAR	TYPE	OPTIONAL	FLAGS	VALIDATION	EXCEPTION
38$fields = [
39	'parent_discoveryid' =>			[T_ZBX_INT, O_MAND, P_SYS,	DB_ID,		null],
40	'hostid' =>						[T_ZBX_INT, O_OPT, P_SYS,	DB_ID,		null],
41	'itemid' =>						[T_ZBX_INT, O_OPT, P_SYS,	DB_ID,		'isset({form}) && {form} == "update"'],
42	'interfaceid' =>				[T_ZBX_INT, O_OPT, P_SYS,	DB_ID,		null, _('Interface')],
43	'name' =>						[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,	'isset({add}) || isset({update})',
44										_('Name')
45									],
46	'description' =>				[T_ZBX_STR, O_OPT, null,	null,		'isset({add}) || isset({update})'],
47	'key' =>						[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,	'isset({add}) || isset({update})',
48										_('Key')
49									],
50	'master_itemid' =>				[T_ZBX_STR, O_OPT, null,	null,
51										'(isset({add}) || isset({update})) && isset({type})'.
52											' && {type} == '.ITEM_TYPE_DEPENDENT,
53										_('Master item')
54									],
55	'delay' =>						[T_ZBX_TU, O_OPT, P_ALLOW_USER_MACRO | P_ALLOW_LLD_MACRO, null,
56										'(isset({add}) || isset({update}))'.
57											' && isset({type}) && {type} != '.ITEM_TYPE_TRAPPER.
58												' && {type} != '.ITEM_TYPE_SNMPTRAP.
59												' && {type} != '.ITEM_TYPE_DEPENDENT,
60										_('Update interval')
61									],
62	'delay_flex' =>					[T_ZBX_STR, O_OPT, null,	null,			null],
63	'status' =>						[T_ZBX_INT, O_OPT, null,	IN([ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED]), null],
64	'discover' =>					[T_ZBX_INT, O_OPT, null,	IN([ZBX_PROTOTYPE_DISCOVER, ZBX_PROTOTYPE_NO_DISCOVER]), null],
65	'type' =>						[T_ZBX_INT, O_OPT, null,
66										IN([-1, ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE,
67											ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_AGGREGATE,
68											ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH,
69											ITEM_TYPE_TELNET, ITEM_TYPE_JMX, ITEM_TYPE_CALCULATED, ITEM_TYPE_SNMPTRAP,
70											ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP
71										]),
72										'isset({add}) || isset({update})'
73									],
74	'value_type' =>					[T_ZBX_INT, O_OPT, null,	IN('0,1,2,3,4'), 'isset({add}) || isset({update})'],
75	'valuemapid' =>					[T_ZBX_INT, O_OPT, null,	DB_ID,
76										'(isset({add}) || isset({update})) && isset({value_type})'.
77											' && '.IN(ITEM_VALUE_TYPE_FLOAT.','.ITEM_VALUE_TYPE_UINT64, 'value_type')
78									],
79	'authtype' =>					[T_ZBX_INT, O_OPT, null,	IN(ITEM_AUTHTYPE_PASSWORD.','.ITEM_AUTHTYPE_PUBLICKEY),
80										'(isset({add}) || isset({update})) && isset({type}) && {type} == '.ITEM_TYPE_SSH
81									],
82	'username' =>					[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,
83										'(isset({add}) || isset({update})) && isset({type})'.
84											' && '.IN(ITEM_TYPE_SSH.','.ITEM_TYPE_TELNET, 'type'),
85										_('User name')
86									],
87	'password' =>					[T_ZBX_STR, O_OPT, null,	null,
88										'(isset({add}) || isset({update})) && isset({type})'.
89											' && '.IN(ITEM_TYPE_SSH.','.ITEM_TYPE_TELNET, 'type')
90									],
91	'publickey' =>					[T_ZBX_STR, O_OPT, null,	null,
92										'(isset({add}) || isset({update})) && isset({type})'.
93											' && {type} == '.ITEM_TYPE_SSH.' && {authtype} == '.ITEM_AUTHTYPE_PUBLICKEY
94									],
95	'privatekey' =>					[T_ZBX_STR, O_OPT, null,	null,
96										'(isset({add}) || isset({update})) && isset({type})'.
97											' && {type} == '.ITEM_TYPE_SSH.' && {authtype} == '.ITEM_AUTHTYPE_PUBLICKEY
98									],
99	$paramsFieldName =>				[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,
100										'(isset({add}) || isset({update})) && isset({type})'.
101											' && '.IN(ITEM_TYPE_SSH.','.ITEM_TYPE_DB_MONITOR.','.ITEM_TYPE_TELNET.','.
102												ITEM_TYPE_CALCULATED, 'type'
103											),
104										getParamFieldLabelByType(getRequest('type', 0))
105									],
106	'snmp_oid' =>					[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,
107										'(isset({add}) || isset({update})) && isset({type})'.
108											' && {type} == '.ITEM_TYPE_SNMP,
109										_('SNMP OID')
110									],
111	'ipmi_sensor' =>				[T_ZBX_STR, O_OPT, P_NO_TRIM, null,
112										'(isset({add}) || isset({update})) && isset({type})'.
113											' && {type} == '.ITEM_TYPE_IPMI,
114										_('IPMI sensor')
115									],
116	'trapper_hosts' =>				[T_ZBX_STR, O_OPT, null,	null,
117										'(isset({add}) || isset({update})) && isset({type})'.
118											' && {type} == '.ITEM_TYPE_TRAPPER
119									],
120	'units' =>						[T_ZBX_STR, O_OPT, null,	null,
121										'(isset({add}) || isset({update})) && isset({value_type})'.
122											' && '.IN(ITEM_VALUE_TYPE_FLOAT.','.ITEM_VALUE_TYPE_UINT64, 'value_type')
123									],
124	'logtimefmt' =>					[T_ZBX_STR, O_OPT, null,	null,
125										'(isset({add}) || isset({update})) && isset({value_type})'.
126											' && {value_type} == '.ITEM_VALUE_TYPE_LOG
127									],
128	'preprocessing' =>				[T_ZBX_STR, O_OPT, P_NO_TRIM,	null,	null],
129	'group_itemid' =>				[T_ZBX_INT, O_OPT, null,	DB_ID,		null],
130	'new_application' =>			[T_ZBX_STR, O_OPT, null,	null,		'isset({add}) || isset({update})'],
131	'new_application_prototype' =>	[T_ZBX_STR, O_OPT, null,	null,
132										'(isset({add}) || isset({update})) && isset({parent_discoveryid})'
133									],
134	'applications' =>				[T_ZBX_STR, O_OPT, null,	null,		null],
135	'application_prototypes' =>		[T_ZBX_STR, O_OPT, null,	null,		null],
136	'massupdate_app_action' =>		[T_ZBX_INT, O_OPT, null,
137										IN([ZBX_ACTION_ADD, ZBX_ACTION_REPLACE, ZBX_ACTION_REMOVE]),
138										null
139									],
140	'massupdate_app_prot_action' =>	[T_ZBX_INT, O_OPT, null,
141										IN([ZBX_ACTION_ADD, ZBX_ACTION_REPLACE, ZBX_ACTION_REMOVE]),
142										null
143									],
144	'history_mode' =>				[T_ZBX_INT, O_OPT, null,	IN([ITEM_STORAGE_OFF, ITEM_STORAGE_CUSTOM]), null],
145	'history' =>					[T_ZBX_STR, O_OPT, null,	null,
146										'(isset({add}) || isset({update}))'.
147											' && isset({history_mode}) && {history_mode}=='.ITEM_STORAGE_CUSTOM,
148										_('History storage period')
149									],
150	'trends_mode' =>				[T_ZBX_INT, O_OPT, null,	IN([ITEM_STORAGE_OFF, ITEM_STORAGE_CUSTOM]), null],
151	'trends' =>						[T_ZBX_STR, O_OPT, null,	null,
152										'(isset({add}) || isset({update}))'.
153											' && isset({trends_mode}) && {trends_mode}=='.ITEM_STORAGE_CUSTOM.
154											' && isset({value_type})'.
155											' && '.IN(ITEM_VALUE_TYPE_FLOAT.','.ITEM_VALUE_TYPE_UINT64, 'value_type'),
156										_('Trend storage period')
157									],
158	'jmx_endpoint' =>				[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,
159										'(isset({add}) || isset({update})) && isset({type}) && {type} == '.ITEM_TYPE_JMX
160									],
161	'timeout' =>	 				[T_ZBX_TU, O_OPT, P_ALLOW_USER_MACRO|P_ALLOW_LLD_MACRO,	null,
162										'(isset({add}) || isset({update})) && isset({type})'.
163											' && {type} == '.ITEM_TYPE_HTTPAGENT,
164										_('Timeout')
165									],
166	'url' =>						[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,
167										'(isset({add}) || isset({update})) && isset({type})'.
168											' && {type} == '.ITEM_TYPE_HTTPAGENT,
169										_('URL')
170									],
171	'query_fields' =>				[T_ZBX_STR, O_OPT, null,	null,		null],
172	'posts' =>						[T_ZBX_STR, O_OPT, null,	null,		null],
173	'status_codes' =>				[T_ZBX_STR, O_OPT, null,	null,		null],
174	'follow_redirects' =>			[T_ZBX_INT, O_OPT, null,
175										IN([HTTPTEST_STEP_FOLLOW_REDIRECTS_OFF, HTTPTEST_STEP_FOLLOW_REDIRECTS_ON]),
176										null
177									],
178	'post_type' =>					[T_ZBX_INT, O_OPT, null,
179										IN([ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML]),
180										null
181									],
182	'http_proxy' =>					[T_ZBX_STR, O_OPT, null,	null,		null],
183	'headers' =>					[T_ZBX_STR, O_OPT, null,	null,		null],
184	'retrieve_mode' =>				[T_ZBX_INT, O_OPT, null,
185										IN([HTTPTEST_STEP_RETRIEVE_MODE_CONTENT, HTTPTEST_STEP_RETRIEVE_MODE_HEADERS,
186											HTTPTEST_STEP_RETRIEVE_MODE_BOTH
187										]),
188										null
189									],
190	'request_method' =>				[T_ZBX_INT, O_OPT, null,
191										IN([HTTPCHECK_REQUEST_GET, HTTPCHECK_REQUEST_POST, HTTPCHECK_REQUEST_PUT,
192											HTTPCHECK_REQUEST_HEAD
193										]),
194										null
195									],
196	'output_format' =>				[T_ZBX_INT, O_OPT, null,	IN([HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON]), null],
197	'allow_traps' =>				[T_ZBX_INT, O_OPT, null,
198										IN([HTTPCHECK_ALLOW_TRAPS_OFF, HTTPCHECK_ALLOW_TRAPS_ON]),
199										null
200									],
201	'ssl_cert_file' =>				[T_ZBX_STR, O_OPT, null,	null,		null],
202	'ssl_key_file' =>				[T_ZBX_STR, O_OPT, null,	null,		null],
203	'ssl_key_password' =>			[T_ZBX_STR, O_OPT, null,	null,		null],
204	'verify_peer' =>				[T_ZBX_INT, O_OPT, null,
205										IN([HTTPTEST_VERIFY_PEER_OFF, HTTPTEST_VERIFY_PEER_ON]),
206										null
207									],
208	'verify_host' =>				[T_ZBX_INT, O_OPT, null,
209										IN([HTTPTEST_VERIFY_HOST_OFF, HTTPTEST_VERIFY_HOST_ON]),
210										null
211									],
212	'http_authtype' =>				[T_ZBX_INT, O_OPT, null,
213										IN([HTTPTEST_AUTH_NONE, HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM,
214											HTTPTEST_AUTH_KERBEROS
215										]),
216										null
217									],
218	'http_username' =>				[T_ZBX_STR, O_OPT, null,	null,
219										'(isset({add}) || isset({update})) && isset({http_authtype})'.
220											' && ({http_authtype} == '.HTTPTEST_AUTH_BASIC.
221												' || {http_authtype} == '.HTTPTEST_AUTH_NTLM.
222												' || {http_authtype} == '.HTTPTEST_AUTH_KERBEROS.
223											')',
224										_('Username')
225									],
226	'http_password' =>				[T_ZBX_STR, O_OPT, null,	null,
227										'(isset({add}) || isset({update})) && isset({http_authtype})'.
228											' && ({http_authtype} == '.HTTPTEST_AUTH_BASIC.
229												' || {http_authtype} == '.HTTPTEST_AUTH_NTLM.
230												' || {http_authtype} == '.HTTPTEST_AUTH_KERBEROS.
231											')',
232										_('Password')
233									],
234	'visible' =>					[T_ZBX_STR, O_OPT, null,	null,		null],
235	// actions
236	'action' =>						[T_ZBX_STR, O_OPT, P_SYS|P_ACT,
237										IN('"itemprototype.massdelete","itemprototype.massdisable",'.
238											'"itemprototype.massenable","itemprototype.massupdateform"'
239										),
240										null
241									],
242	'add' =>						[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
243	'update' =>						[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
244	'clone' =>						[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
245	'delete' =>						[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
246	'massupdate' =>					[T_ZBX_STR, O_OPT, P_SYS,		null,	null],
247	'cancel' =>						[T_ZBX_STR, O_OPT, P_SYS,	null,		null],
248	'form' =>						[T_ZBX_STR, O_OPT, P_SYS,	null,		null],
249	'form_refresh' =>				[T_ZBX_INT, O_OPT, null,	null,		null],
250	// filter
251	'filter_set' =>					[T_ZBX_STR, O_OPT, P_SYS,	null,		null],
252	// sort and sortorder
253	'sort' =>						[T_ZBX_STR, O_OPT, P_SYS,
254										IN('"delay","history","key_","name","status","trends","type","discover"'), null
255									],
256	'sortorder' =>					[T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'),	null]
257];
258$valid_input = check_fields($fields);
259
260$_REQUEST['params'] = getRequest($paramsFieldName, '');
261unset($_REQUEST[$paramsFieldName]);
262
263// permissions
264$discoveryRule = API::DiscoveryRule()->get([
265	'output' => ['hostid'],
266	'itemids' => getRequest('parent_discoveryid'),
267	'editable' => true
268]);
269$discoveryRule = reset($discoveryRule);
270if (!$discoveryRule) {
271	access_deny();
272}
273
274$itemPrototypeId = getRequest('itemid');
275if ($itemPrototypeId) {
276	$item_prorotypes = API::ItemPrototype()->get([
277		'output' => [],
278		'itemids' => $itemPrototypeId,
279		'editable' => true
280	]);
281
282	if (!$item_prorotypes) {
283		access_deny();
284	}
285}
286
287// Convert CR+LF to LF in preprocessing script.
288if (hasRequest('preprocessing')) {
289	foreach ($_REQUEST['preprocessing'] as &$step) {
290		if ($step['type'] == ZBX_PREPROC_SCRIPT) {
291			$step['params'][0] = CRLFtoLF($step['params'][0]);
292		}
293	}
294	unset($step);
295}
296
297/*
298 * Actions
299 */
300if (hasRequest('delete') && hasRequest('itemid')) {
301	DBstart();
302	$result = API::ItemPrototype()->delete([getRequest('itemid')]);
303	$result = DBend($result);
304
305	if ($result) {
306		uncheckTableRows(getRequest('parent_discoveryid'));
307	}
308	show_messages($result, _('Item prototype deleted'), _('Cannot delete item prototype'));
309
310	unset($_REQUEST['itemid'], $_REQUEST['form']);
311}
312elseif (isset($_REQUEST['clone']) && isset($_REQUEST['itemid'])) {
313	unset($_REQUEST['itemid']);
314	$_REQUEST['form'] = 'clone';
315}
316elseif (hasRequest('add') || hasRequest('update')) {
317	$applications = getRequest('applications', []);
318	$application = reset($applications);
319	if ($application == 0) {
320		array_shift($applications);
321	}
322
323	$result = true;
324	DBstart();
325
326	if (!zbx_empty($_REQUEST['new_application'])) {
327		$new_appid = API::Application()->create([
328			'name' => $_REQUEST['new_application'],
329			'hostid' => $discoveryRule['hostid']
330		]);
331		if ($new_appid) {
332			$new_appid = reset($new_appid['applicationids']);
333			$applications[$new_appid] = $new_appid;
334		}
335		else {
336			$result = false;
337		}
338	}
339
340	$delay = getRequest('delay', DB::getDefault('items', 'delay'));
341	$type = getRequest('type', ITEM_TYPE_ZABBIX);
342
343	/*
344	 * "delay_flex" is a temporary field that collects flexible and scheduling intervals separated by a semicolon.
345	 * In the end, custom intervals together with "delay" are stored in the "delay" variable.
346	 */
347	if ($type != ITEM_TYPE_TRAPPER && $type != ITEM_TYPE_SNMPTRAP && hasRequest('delay_flex')) {
348		$intervals = [];
349		$simple_interval_parser = new CSimpleIntervalParser([
350			'usermacros' => true,
351			'lldmacros' => true
352		]);
353		$time_period_parser = new CTimePeriodParser([
354			'usermacros' => true,
355			'lldmacros' => true
356		]);
357		$scheduling_interval_parser = new CSchedulingIntervalParser([
358			'usermacros' => true,
359			'lldmacros' => true
360		]);
361
362		foreach (getRequest('delay_flex') as $interval) {
363			if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
364				if ($interval['delay'] === '' && $interval['period'] === '') {
365					continue;
366				}
367
368				if ($simple_interval_parser->parse($interval['delay']) != CParser::PARSE_SUCCESS) {
369					$result = false;
370					info(_s('Invalid interval "%1$s".', $interval['delay']));
371					break;
372				}
373				elseif ($time_period_parser->parse($interval['period']) != CParser::PARSE_SUCCESS) {
374					$result = false;
375					info(_s('Invalid interval "%1$s".', $interval['period']));
376					break;
377				}
378
379				$intervals[] = $interval['delay'].'/'.$interval['period'];
380			}
381			else {
382				if ($interval['schedule'] === '') {
383					continue;
384				}
385
386				if ($scheduling_interval_parser->parse($interval['schedule']) != CParser::PARSE_SUCCESS) {
387					$result = false;
388					info(_s('Invalid interval "%1$s".', $interval['schedule']));
389					break;
390				}
391
392				$intervals[] = $interval['schedule'];
393			}
394		}
395
396		if ($intervals) {
397			$delay .= ';'.implode(';', $intervals);
398		}
399	}
400
401	if ($result) {
402		$application_prototypes = getRequest('application_prototypes', []);
403		$application_prototype = reset($application_prototypes);
404
405		if ($application_prototype === '0') {
406			array_shift($application_prototypes);
407		}
408
409		if ($application_prototypes) {
410			foreach ($application_prototypes as &$application_prototype) {
411				$application_prototype = ['name' => $application_prototype];
412			}
413			unset($application_prototype);
414		}
415
416		$new_application_prototype = getRequest('new_application_prototype', '');
417		if ($new_application_prototype !== '') {
418			$application_prototypes[] = ['name' => $new_application_prototype];
419		}
420
421		$preprocessing = getRequest('preprocessing', []);
422
423		foreach ($preprocessing as &$step) {
424			switch ($step['type']) {
425				case ZBX_PREPROC_MULTIPLIER:
426				case ZBX_PREPROC_PROMETHEUS_TO_JSON:
427					$step['params'] = trim($step['params'][0]);
428					break;
429
430				case ZBX_PREPROC_RTRIM:
431				case ZBX_PREPROC_LTRIM:
432				case ZBX_PREPROC_TRIM:
433				case ZBX_PREPROC_XPATH:
434				case ZBX_PREPROC_JSONPATH:
435				case ZBX_PREPROC_VALIDATE_REGEX:
436				case ZBX_PREPROC_VALIDATE_NOT_REGEX:
437				case ZBX_PREPROC_ERROR_FIELD_JSON:
438				case ZBX_PREPROC_ERROR_FIELD_XML:
439				case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
440				case ZBX_PREPROC_SCRIPT:
441					$step['params'] = $step['params'][0];
442					break;
443
444				case ZBX_PREPROC_VALIDATE_RANGE:
445				case ZBX_PREPROC_PROMETHEUS_PATTERN:
446					foreach ($step['params'] as &$param) {
447						$param = trim($param);
448					}
449					unset($param);
450
451					$step['params'] = implode("\n", $step['params']);
452					break;
453
454				case ZBX_PREPROC_REGSUB:
455				case ZBX_PREPROC_ERROR_FIELD_REGEX:
456				case ZBX_PREPROC_STR_REPLACE:
457					$step['params'] = implode("\n", $step['params']);
458					break;
459
460				// ZBX-16642
461				case ZBX_PREPROC_CSV_TO_JSON:
462					if (!array_key_exists(2, $step['params'])) {
463						$step['params'][2] = ZBX_PREPROC_CSV_NO_HEADER;
464					}
465					$step['params'] = implode("\n", $step['params']);
466					break;
467
468				default:
469					$step['params'] = '';
470			}
471
472			$step += [
473				'error_handler' => ZBX_PREPROC_FAIL_DEFAULT,
474				'error_handler_params' => ''
475			];
476		}
477		unset($step);
478
479		$item = [
480			'name'			=> getRequest('name'),
481			'description'	=> getRequest('description'),
482			'key_'			=> getRequest('key'),
483			'hostid'		=> $discoveryRule['hostid'],
484			'interfaceid'	=> getRequest('interfaceid'),
485			'delay'			=> $delay,
486			'status'		=> getRequest('status', ITEM_STATUS_DISABLED),
487			'discover'		=> getRequest('discover', ZBX_PROTOTYPE_DISCOVER),
488			'type'			=> getRequest('type'),
489			'snmp_oid'		=> getRequest('snmp_oid'),
490			'value_type'	=> getRequest('value_type'),
491			'trapper_hosts'	=> getRequest('trapper_hosts'),
492			'history'		=> (getRequest('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
493				? ITEM_NO_STORAGE_VALUE
494				: getRequest('history'),
495			'units'			=> getRequest('units'),
496			'logtimefmt'	=> getRequest('logtimefmt'),
497			'valuemapid'	=> getRequest('valuemapid'),
498			'authtype'		=> getRequest('authtype'),
499			'username'		=> getRequest('username'),
500			'password'		=> getRequest('password'),
501			'publickey'		=> getRequest('publickey'),
502			'privatekey'	=> getRequest('privatekey'),
503			'params'		=> getRequest('params'),
504			'ipmi_sensor'	=> getRequest('ipmi_sensor'),
505			'ruleid'		=> getRequest('parent_discoveryid')
506		];
507
508		if ($item['type'] == ITEM_TYPE_JMX) {
509			$item['jmx_endpoint'] = getRequest('jmx_endpoint', '');
510		}
511
512		if ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) {
513			$item['trends'] = (getRequest('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
514				? ITEM_NO_STORAGE_VALUE
515				: getRequest('trends');
516		}
517
518		if ($item['type'] == ITEM_TYPE_DEPENDENT) {
519			$item['master_itemid'] = getRequest('master_itemid');
520		}
521
522		if (hasRequest('update')) {
523			$itemId = getRequest('itemid');
524
525			$db_item = API::ItemPrototype()->get([
526				'output' => ['type', 'snmp_oid', 'hostid', 'name', 'key_', 'delay', 'history',
527					'trends', 'status', 'value_type', 'trapper_hosts', 'units',
528					'logtimefmt', 'templateid', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username',
529					'password', 'publickey', 'privatekey', 'interfaceid', 'description', 'jmx_endpoint',
530					'master_itemid', 'timeout', 'url', 'query_fields', 'posts', 'status_codes', 'follow_redirects',
531					'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method', 'output_format',
532					'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host', 'allow_traps',
533					'discover'
534				],
535				'selectApplications' => ['applicationid'],
536				'selectApplicationPrototypes' => ['name'],
537				'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
538				'itemids' => [$itemId]
539			]);
540
541			$db_item = $db_item[0];
542
543			if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
544				$http_item = [
545					'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout')),
546					'url' => getRequest('url'),
547					'query_fields' => getRequest('query_fields', []),
548					'posts' => getRequest('posts'),
549					'status_codes' => getRequest('status_codes', DB::getDefault('items', 'status_codes')),
550					'follow_redirects' => (int) getRequest('follow_redirects'),
551					'post_type' => (int) getRequest('post_type'),
552					'http_proxy' => getRequest('http_proxy'),
553					'headers' => getRequest('headers', []),
554					'retrieve_mode' => (int) getRequest('retrieve_mode'),
555					'request_method' => (int) getRequest('request_method'),
556					'output_format' => (int) getRequest('output_format'),
557					'allow_traps' => (int) getRequest('allow_traps', HTTPCHECK_ALLOW_TRAPS_OFF),
558					'ssl_cert_file' => getRequest('ssl_cert_file'),
559					'ssl_key_file' => getRequest('ssl_key_file'),
560					'ssl_key_password' => getRequest('ssl_key_password'),
561					'verify_peer' => (int) getRequest('verify_peer'),
562					'verify_host' => (int) getRequest('verify_host'),
563					'authtype' => getRequest('http_authtype', HTTPTEST_AUTH_NONE),
564					'username' => getRequest('http_username', ''),
565					'password' => getRequest('http_password', '')
566				];
567				$item = prepareItemHttpAgentFormData($http_item) + $item;
568			}
569
570			$item = CArrayHelper::unsetEqualValues($item, $db_item);
571			$item['itemid'] = $itemId;
572
573			$db_item['applications'] = zbx_objectValues($db_item['applications'], 'applicationid');
574
575			// compare applications
576			natsort($db_item['applications']);
577			natsort($applications);
578
579			if (array_values($db_item['applications']) !== array_values($applications)) {
580				$item['applications'] = $applications;
581			}
582
583			// compare application prototypes
584			$db_application_prototype_names = zbx_objectValues($db_item['applicationPrototypes'], 'name');
585			natsort($db_application_prototype_names);
586
587			$application_prototype_names = zbx_objectValues($application_prototypes, 'name');
588			natsort($application_prototype_names);
589
590			if (array_values($db_application_prototype_names) !== array_values($application_prototype_names)) {
591				$item['applicationPrototypes'] = $application_prototypes;
592			}
593
594			if ($db_item['preprocessing'] !== $preprocessing) {
595				$item['preprocessing'] = $preprocessing;
596			}
597
598			$result = API::ItemPrototype()->update($item);
599		}
600		else {
601			$item['applications'] = $applications;
602			$item['applicationPrototypes'] = $application_prototypes;
603
604			if (getRequest('type') == ITEM_TYPE_HTTPAGENT) {
605				$http_item = [
606					'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout')),
607					'url' => getRequest('url'),
608					'query_fields' => getRequest('query_fields', []),
609					'posts' => getRequest('posts'),
610					'status_codes' => getRequest('status_codes', DB::getDefault('items', 'status_codes')),
611					'follow_redirects' => (int) getRequest('follow_redirects'),
612					'post_type' => (int) getRequest('post_type'),
613					'http_proxy' => getRequest('http_proxy'),
614					'headers' => getRequest('headers', []),
615					'retrieve_mode' => (int) getRequest('retrieve_mode'),
616					'request_method' => (int) getRequest('request_method'),
617					'output_format' => (int) getRequest('output_format'),
618					'allow_traps' => (int) getRequest('allow_traps', HTTPCHECK_ALLOW_TRAPS_OFF),
619					'ssl_cert_file' => getRequest('ssl_cert_file'),
620					'ssl_key_file' => getRequest('ssl_key_file'),
621					'ssl_key_password' => getRequest('ssl_key_password'),
622					'verify_peer' => (int) getRequest('verify_peer'),
623					'verify_host' => (int) getRequest('verify_host'),
624					'authtype' => getRequest('http_authtype', HTTPTEST_AUTH_NONE),
625					'username' => getRequest('http_username', ''),
626					'password' => getRequest('http_password', '')
627				];
628				$item = prepareItemHttpAgentFormData($http_item) + $item;
629			}
630
631			if ($preprocessing) {
632				$item['preprocessing'] = $preprocessing;
633			}
634
635			$result = API::ItemPrototype()->create($item);
636		}
637	}
638
639	$result = DBend($result);
640
641	if (hasRequest('add')) {
642		show_messages($result, _('Item prototype added'), _('Cannot add item prototype'));
643	}
644	else {
645		show_messages($result, _('Item prototype updated'), _('Cannot update item prototype'));
646	}
647
648	if ($result) {
649		unset($_REQUEST['itemid'], $_REQUEST['form']);
650		uncheckTableRows(getRequest('parent_discoveryid'));
651	}
652}
653elseif (hasRequest('action') && hasRequest('group_itemid')
654		&& str_in_array(getRequest('action'), ['itemprototype.massenable', 'itemprototype.massdisable'])) {
655	$itemids = getRequest('group_itemid');
656	$status = (getRequest('action') == 'itemprototype.massenable') ? ITEM_STATUS_ACTIVE : ITEM_STATUS_DISABLED;
657
658	$item_prototypes = [];
659	foreach ($itemids as $itemid) {
660		$item_prototypes[] = ['itemid' => $itemid, 'status' => $status];
661	}
662
663	$result = (bool) API::ItemPrototype()->update($item_prototypes);
664
665	if ($result) {
666		uncheckTableRows(getRequest('parent_discoveryid'));
667	}
668
669	$updated = count($itemids);
670
671	$messageSuccess = _n('Item prototype updated', 'Item prototypes updated', $updated);
672	$messageFailed = _n('Cannot update item prototype', 'Cannot update item prototypes', $updated);
673
674	show_messages($result, $messageSuccess, $messageFailed);
675}
676elseif (hasRequest('action') && getRequest('action') === 'itemprototype.massdelete' && hasRequest('group_itemid')) {
677	DBstart();
678
679	$result = API::ItemPrototype()->delete(getRequest('group_itemid'));
680	$result = DBend($result);
681
682	if ($result) {
683		uncheckTableRows(getRequest('parent_discoveryid'));
684	}
685	show_messages($result, _('Item prototypes deleted'), _('Cannot delete item prototypes'));
686}
687elseif ($valid_input && hasRequest('massupdate') && hasRequest('group_itemid')) {
688	$visible = getRequest('visible', []);
689	$item_prototypeids = getRequest('group_itemid');
690	$result = true;
691
692	$applications = getRequest('applications', []);
693	$applicationids = [];
694
695	$application_prototypes = getRequest('application_prototypes', []);
696	$application_prototypeids = [];
697
698	if (isset($visible['delay'])) {
699		$delay = getRequest('delay', DB::getDefault('items', 'delay'));
700
701		if (hasRequest('delay_flex')) {
702			$intervals = [];
703			$simple_interval_parser = new CSimpleIntervalParser(['usermacros' => true]);
704			$time_period_parser = new CTimePeriodParser(['usermacros' => true]);
705			$scheduling_interval_parser = new CSchedulingIntervalParser(['usermacros' => true]);
706
707			foreach (getRequest('delay_flex') as $interval) {
708				if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
709					if ($interval['delay'] === '' && $interval['period'] === '') {
710						continue;
711					}
712
713					if ($simple_interval_parser->parse($interval['delay']) != CParser::PARSE_SUCCESS) {
714						$result = false;
715						info(_s('Invalid interval "%1$s".', $interval['delay']));
716						break;
717					}
718					elseif ($time_period_parser->parse($interval['period']) != CParser::PARSE_SUCCESS) {
719						$result = false;
720						info(_s('Invalid interval "%1$s".', $interval['period']));
721						break;
722					}
723
724					$intervals[] = $interval['delay'].'/'.$interval['period'];
725				}
726				else {
727					if ($interval['schedule'] === '') {
728						continue;
729					}
730
731					if ($scheduling_interval_parser->parse($interval['schedule']) != CParser::PARSE_SUCCESS) {
732						$result = false;
733						info(_s('Invalid interval "%1$s".', $interval['schedule']));
734						break;
735					}
736
737					$intervals[] = $interval['schedule'];
738				}
739			}
740
741			if ($intervals) {
742				$delay .= ';'.implode(';', $intervals);
743			}
744		}
745	}
746	else {
747		$delay = null;
748	}
749
750	if ($result) {
751		try {
752			DBstart();
753
754			// Collect submitted applications and create new applications if necessary.
755			if (array_key_exists('applications', $visible)) {
756				$massupdate_app_action = getRequest('massupdate_app_action');
757
758				if ($massupdate_app_action == ZBX_ACTION_ADD || $massupdate_app_action == ZBX_ACTION_REPLACE) {
759					$new_applications = [];
760
761					foreach ($applications as $application) {
762						if (is_array($application) && array_key_exists('new', $application)) {
763							$new_applications[] = [
764								'name' => $application['new'],
765								'hostid' => getRequest('hostid')
766							];
767						}
768						else {
769							$applicationids[] = $application;
770						}
771					}
772
773					if ($new_applications) {
774						if ($new_application = API::Application()->create($new_applications)) {
775							$applicationids = array_merge($applicationids, $new_application['applicationids']);
776						}
777						else {
778							throw new Exception();
779						}
780					}
781				}
782				else {
783					foreach ($applications as $application) {
784						$applicationids[] = $application;
785					}
786				}
787			}
788
789			// Collect submitted application prototypes.
790			if (array_key_exists('applicationPrototypes', $visible)) {
791				$massupdate_app_prot_action = getRequest('massupdate_app_prot_action');
792
793				if ($massupdate_app_prot_action == ZBX_ACTION_ADD
794						|| $massupdate_app_prot_action == ZBX_ACTION_REPLACE) {
795					$new_application_prototypes = [];
796
797					foreach ($application_prototypes as $application_prototype) {
798						if (is_array($application_prototype) && array_key_exists('new', $application_prototype)) {
799							$new_application_prototypes[] = [
800								'name' => $application_prototype['new']
801							];
802						}
803						else {
804							$application_prototypeids[] = $application_prototype;
805						}
806					}
807				}
808				else {
809					foreach ($application_prototypes as $application_prototype) {
810						$application_prototypeids[] = $application_prototype;
811					}
812				}
813			}
814
815			$item_prototypes = API::ItemPrototype()->get([
816				'output' => ['itemid', 'type'],
817				'selectApplications' => ['applicationid'],
818				'selectApplicationPrototypes' => ['application_prototypeid', 'name'],
819				'itemids' => $item_prototypeids,
820				'preservekeys' => true
821			]);
822
823			$item_prototypes_to_update = [];
824
825			if ($item_prototypes) {
826				$item_prototype = [
827					'interfaceid' => getRequest('interfaceid'),
828					'description' => getRequest('description'),
829					'delay' => $delay,
830					'history' => (getRequest('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
831						? ITEM_NO_STORAGE_VALUE
832						: getRequest('history'),
833					'type' => getRequest('type'),
834					'snmp_oid' => getRequest('snmp_oid'),
835					'value_type' => getRequest('value_type'),
836					'trapper_hosts' => getRequest('trapper_hosts'),
837					'units' => getRequest('units'),
838					'trends' => (getRequest('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
839						? ITEM_NO_STORAGE_VALUE
840						: getRequest('trends'),
841					'logtimefmt' => getRequest('logtimefmt'),
842					'valuemapid' => getRequest('valuemapid'),
843					'authtype' => getRequest('authtype'),
844					'jmx_endpoint' => getRequest('jmx_endpoint'),
845					'username' => getRequest('username'),
846					'password' => getRequest('password'),
847					'publickey' => getRequest('publickey'),
848					'privatekey' => getRequest('privatekey'),
849					'applications' => [],
850					'applicationPrototypes' => [],
851					'status' => getRequest('status'),
852					'discover' => getRequest('discover'),
853					'master_itemid' => getRequest('master_itemid'),
854					'url' =>  getRequest('url'),
855					'post_type' => getRequest('post_type'),
856					'posts' => getRequest('posts'),
857					'headers' => getRequest('headers', []),
858					'allow_traps' => getRequest('allow_traps', HTTPCHECK_ALLOW_TRAPS_OFF),
859					'preprocessing' => []
860				];
861
862				if ($item_prototype['headers']) {
863					$headers = [];
864
865					foreach ($item_prototype['headers']['name'] as $index => $key) {
866						if (array_key_exists($index, $item_prototype['headers']['value'])) {
867							$headers[$key] = $item_prototype['headers']['value'][$index];
868						}
869					}
870
871					// Ignore single row if it is empty.
872					if (count($headers) == 1 && $key === '' && $item_prototype['headers']['value'][$index] === '') {
873						$headers = [];
874					}
875
876					$item_prototype['headers'] = $headers;
877				}
878
879				if (hasRequest('preprocessing')) {
880					$preprocessing = getRequest('preprocessing');
881
882					foreach ($preprocessing as &$step) {
883						switch ($step['type']) {
884							case ZBX_PREPROC_MULTIPLIER:
885							case ZBX_PREPROC_PROMETHEUS_TO_JSON:
886								$step['params'] = trim($step['params'][0]);
887								break;
888
889							case ZBX_PREPROC_RTRIM:
890							case ZBX_PREPROC_LTRIM:
891							case ZBX_PREPROC_TRIM:
892							case ZBX_PREPROC_XPATH:
893							case ZBX_PREPROC_JSONPATH:
894							case ZBX_PREPROC_VALIDATE_REGEX:
895							case ZBX_PREPROC_VALIDATE_NOT_REGEX:
896							case ZBX_PREPROC_ERROR_FIELD_JSON:
897							case ZBX_PREPROC_ERROR_FIELD_XML:
898							case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
899							case ZBX_PREPROC_SCRIPT:
900								$step['params'] = $step['params'][0];
901								break;
902
903							case ZBX_PREPROC_VALIDATE_RANGE:
904							case ZBX_PREPROC_PROMETHEUS_PATTERN:
905								foreach ($step['params'] as &$param) {
906									$param = trim($param);
907								}
908								unset($param);
909
910								$step['params'] = implode("\n", $step['params']);
911								break;
912
913							case ZBX_PREPROC_REGSUB:
914							case ZBX_PREPROC_ERROR_FIELD_REGEX:
915							case ZBX_PREPROC_STR_REPLACE:
916								$step['params'] = implode("\n", $step['params']);
917								break;
918
919							// ZBX-16642
920							case ZBX_PREPROC_CSV_TO_JSON:
921								if (!array_key_exists(2, $step['params'])) {
922									$step['params'][2] = ZBX_PREPROC_CSV_NO_HEADER;
923								}
924								$step['params'] = implode("\n", $step['params']);
925								break;
926
927							default:
928								$step['params'] = '';
929						}
930
931						$step += [
932							'error_handler' => ZBX_PREPROC_FAIL_DEFAULT,
933							'error_handler_params' => ''
934						];
935					}
936					unset($step);
937
938					$item_prototype['preprocessing'] = $preprocessing;
939				}
940
941				// Check "visible" for differences and update only necessary fields.
942				$item_prototype = array_intersect_key($item_prototype, $visible);
943
944				foreach ($item_prototypeids as $item_prototypeid) {
945					if (array_key_exists($item_prototypeid, $item_prototypes)) {
946						if ($item_prototype) {
947							// Process applications.
948							if (array_key_exists('applications', $visible)) {
949								if ($applicationids) {
950									// If there are existing applications submitted.
951									$db_applicationids = zbx_objectValues(
952										$item_prototypes[$item_prototypeid]['applications'],
953										'applicationid'
954									);
955
956									switch ($massupdate_app_action) {
957										case ZBX_ACTION_ADD:
958											$upd_applicationids = array_merge($applicationids, $db_applicationids);
959											break;
960
961										case ZBX_ACTION_REPLACE:
962											$upd_applicationids = $applicationids;
963											break;
964
965										case ZBX_ACTION_REMOVE:
966											$upd_applicationids = array_diff($db_applicationids, $applicationids);
967											break;
968									}
969
970									/*
971									 * $upd_applicationids now contains new and existing application IDs depending on
972									 * operation we want to perform.
973									 */
974									$item_prototype['applications'] = array_keys(array_flip($upd_applicationids));
975								}
976								else {
977									/*
978									 * No applications were submitted in form. In case we want to replace applications,
979									 * leave $item['applications'] empty, remove it otherwise.
980									 */
981									if ($massupdate_app_action == ZBX_ACTION_ADD
982											|| $massupdate_app_action == ZBX_ACTION_REMOVE) {
983										unset($item_prototype['applications']);
984									}
985								}
986							}
987
988							// Process application prototypes.
989							if (array_key_exists('applicationPrototypes', $visible)) {
990								$ex_application_prototypes
991									= $item_prototypes[$item_prototypeid]['applicationPrototypes'];
992								$ex_application_prototypeids = zbx_objectValues($ex_application_prototypes,
993									'application_prototypeid'
994								);
995								$upd_application_prototypeids = [];
996								$application_prototypes = [];
997
998								switch ($massupdate_app_prot_action) {
999									case ZBX_ACTION_ADD:
1000										// Append submitted existing application prototypes.
1001										if ($application_prototypeids) {
1002											$upd_application_prototypeids = array_unique(
1003												array_merge($application_prototypeids, $ex_application_prototypeids)
1004											);
1005										}
1006
1007										// Append new application prototypes.
1008										if ($new_application_prototypes) {
1009											foreach ($new_application_prototypes as $new_application_prototype) {
1010												if (!in_array($new_application_prototype['name'],
1011														zbx_objectValues($application_prototypes, 'name'))) {
1012													$application_prototypes[] = $new_application_prototype;
1013												}
1014											}
1015										}
1016
1017										// Append already existing application prototypes so that they are not deleted.
1018										if (($upd_application_prototypeids || $new_application_prototypes)
1019												&& $ex_application_prototypes) {
1020											foreach ($ex_application_prototypes as $db_application_prototype) {
1021												$application_prototypes[] = $db_application_prototype;
1022											}
1023										}
1024										break;
1025
1026									case ZBX_ACTION_REPLACE:
1027										if ($application_prototypeids) {
1028											$upd_application_prototypeids = $application_prototypeids;
1029										}
1030
1031										if ($new_application_prototypes) {
1032											foreach ($new_application_prototypes as $new_application_prototype) {
1033												if (!in_array($new_application_prototype['name'],
1034														zbx_objectValues($application_prototypes, 'name'))) {
1035													$application_prototypes[] = $new_application_prototype;
1036												}
1037											}
1038										}
1039										break;
1040
1041									case ZBX_ACTION_REMOVE:
1042										if ($application_prototypeids) {
1043											$upd_application_prototypeids = array_diff($ex_application_prototypeids,
1044												$application_prototypeids
1045											);
1046										}
1047										break;
1048								}
1049
1050								/*
1051								 * There might be added an existing application prototype that belongs to the discovery
1052								 * rule, not just chosen application prototypes ($ex_application_prototypes).
1053								 */
1054								if ($upd_application_prototypeids) {
1055									// Collect existing application prototype names. Those are required by API.
1056									$db_application_prototypes = DBfetchArray(DBselect(
1057										'SELECT ap.application_prototypeid,ap.name'.
1058										' FROM application_prototype ap'.
1059										' WHERE '.dbConditionId('ap.application_prototypeid',
1060											$upd_application_prototypeids
1061										)
1062									));
1063
1064									// Append those application prototypes to update list.
1065									foreach ($db_application_prototypes as $db_application_prototype) {
1066										if (!in_array($db_application_prototype['application_prototypeid'],
1067												zbx_objectValues($application_prototypes,
1068													'application_prototypeid'))) {
1069											$application_prototypes[] = $db_application_prototype;
1070										}
1071									}
1072								}
1073
1074								if ($application_prototypes) {
1075									$item_prototype['applicationPrototypes'] = $application_prototypes;
1076								}
1077								else {
1078									if ($massupdate_app_prot_action == ZBX_ACTION_REPLACE) {
1079										$item_prototype['applicationPrototypes'] = [];
1080									}
1081									else {
1082										unset($item_prototype['applicationPrototypes']);
1083									}
1084								}
1085							}
1086
1087							$item_prototypes_to_update[] = ['itemid' => $item_prototypeid] + $item_prototype;
1088						}
1089					}
1090				}
1091			}
1092
1093			if ($item_prototypes_to_update) {
1094				foreach ($item_prototypes_to_update as &$update_item_prototype) {
1095					$type = array_key_exists('type', $update_item_prototype)
1096						? $update_item_prototype['type']
1097						: $item_prototypes[$update_item_prototype['itemid']]['type'];
1098
1099					if ($type != ITEM_TYPE_JMX) {
1100						unset($update_item_prototype['jmx_endpoint']);
1101					}
1102				}
1103				unset($update_item_prototype);
1104
1105				$result = API::ItemPrototype()->update($item_prototypes_to_update);
1106			}
1107		}
1108		catch (Exception $e) {
1109			$result = false;
1110		}
1111
1112		$result = DBend($result);
1113	}
1114
1115	if ($result) {
1116		unset($_REQUEST['group_itemid'], $_REQUEST['massupdate'], $_REQUEST['form']);
1117		uncheckTableRows(getRequest('parent_discoveryid'));
1118	}
1119	show_messages($result, _('Item prototypes updated'), _('Cannot update item prototypes'));
1120}
1121
1122if (hasRequest('action') && getRequest('action') !== 'itemprototype.massupdateform' && hasRequest('group_itemid')
1123		&& !$result) {
1124	$item_prototypes = API::ItemPrototype()->get([
1125		'itemids' => getRequest('group_itemid'),
1126		'output' => [],
1127		'editable' => true
1128	]);
1129	uncheckTableRows(getRequest('parent_discoveryid'), zbx_objectValues($item_prototypes, 'itemid'));
1130}
1131
1132/*
1133 * Display
1134 */
1135if (isset($_REQUEST['form'])) {
1136	$itemPrototype = [];
1137	$has_errors = false;
1138
1139	if (hasRequest('itemid')) {
1140		$itemPrototype = API::ItemPrototype()->get([
1141			'itemids' => getRequest('itemid'),
1142			'output' => [
1143				'itemid', 'type', 'snmp_oid', 'hostid', 'name', 'key_', 'delay', 'history',
1144				'trends', 'status', 'value_type', 'trapper_hosts', 'units', 'logtimefmt', 'templateid',
1145				'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username', 'password', 'publickey', 'privatekey',
1146				'interfaceid', 'description', 'jmx_endpoint', 'master_itemid', 'timeout', 'url', 'query_fields',
1147				'posts', 'status_codes', 'follow_redirects', 'post_type', 'http_proxy', 'headers', 'retrieve_mode',
1148				'request_method', 'output_format', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password',
1149				'verify_peer', 'verify_host', 'allow_traps', 'discover'
1150			],
1151			'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params']
1152		]);
1153		$itemPrototype = reset($itemPrototype);
1154
1155		foreach ($itemPrototype['preprocessing'] as &$step) {
1156			if ($step['type'] == ZBX_PREPROC_SCRIPT) {
1157				$step['params'] = [$step['params'], ''];
1158			}
1159			else {
1160				$step['params'] = explode("\n", $step['params']);
1161			}
1162		}
1163		unset($step);
1164
1165		if ($itemPrototype['type'] != ITEM_TYPE_JMX) {
1166			$itemPrototype['jmx_endpoint'] = ZBX_DEFAULT_JMX_ENDPOINT;
1167		}
1168
1169		if (getRequest('type', $itemPrototype['type']) == ITEM_TYPE_DEPENDENT) {
1170			$master_prototypes = API::Item()->get([
1171				'output' => ['itemid', 'hostid', 'name', 'key_'],
1172				'itemids' => [getRequest('master_itemid', $itemPrototype['master_itemid'])],
1173				'hostids' => [$itemPrototype['hostid']],
1174				'webitems' => true
1175			])
1176			+ API::ItemPrototype()->get([
1177				'output' => ['itemid', 'hostid', 'name', 'key_'],
1178				'itemids' => getRequest('master_itemid', $itemPrototype['master_itemid'])
1179			]);
1180
1181			if ($master_prototypes) {
1182				$itemPrototype['master_item'] = reset($master_prototypes);
1183			}
1184		}
1185	}
1186	elseif (getRequest('master_itemid')) {
1187		$master_prototypes = API::Item()->get([
1188			'output' => ['itemid', 'hostid', 'name', 'key_'],
1189			'itemids' => getRequest('master_itemid'),
1190			'webitems' => true
1191		])
1192		+ API::ItemPrototype()->get([
1193			'output' => ['itemid', 'hostid', 'name', 'key_'],
1194			'itemids' => getRequest('master_itemid')
1195		]);
1196
1197		if ($master_prototypes) {
1198			$itemPrototype['master_item'] = reset($master_prototypes);
1199		}
1200		else {
1201			show_messages(false, '', _('No permissions to referred object or it does not exist!'));
1202			$has_errors = true;
1203		}
1204	}
1205
1206	$data = getItemFormData($itemPrototype);
1207	$data['config'] = select_config();
1208	$data['preprocessing_test_type'] = CControllerPopupItemTestEdit::ZBX_TEST_TYPE_ITEM_PROTOTYPE;
1209	$data['preprocessing_types'] = CItemPrototype::$supported_preprocessing_types;
1210	$data['trends_default'] = DB::getDefault('items', 'trends');
1211
1212	$history_in_seconds = timeUnitToSeconds($data['history']);
1213	if (!getRequest('form_refresh') && $history_in_seconds !== null && $history_in_seconds == ITEM_NO_STORAGE_VALUE) {
1214		$data['history_mode'] = getRequest('history_mode', ITEM_STORAGE_OFF);
1215		$data['history'] = DB::getDefault('items', 'history');
1216	}
1217	else {
1218		$data['history_mode'] = getRequest('history_mode', ITEM_STORAGE_CUSTOM);
1219	}
1220
1221	$trends_in_seconds = timeUnitToSeconds($data['trends']);
1222	if (!getRequest('form_refresh') && $trends_in_seconds !== null && $trends_in_seconds == ITEM_NO_STORAGE_VALUE) {
1223		$data['trends_mode'] = getRequest('trends_mode', ITEM_STORAGE_OFF);
1224		$data['trends'] = $data['trends_default'];
1225	}
1226	else {
1227		$data['trends_mode'] = getRequest('trends_mode', ITEM_STORAGE_CUSTOM);
1228	}
1229
1230	// render view
1231	if (!$has_errors) {
1232		echo (new CView('configuration.item.prototype.edit', $data))->getOutput();
1233	}
1234}
1235elseif (((hasRequest('action') && getRequest('action') === 'itemprototype.massupdateform') || hasRequest('massupdate'))
1236		&& hasRequest('group_itemid')) {
1237	$data = [
1238		'form' => getRequest('form'),
1239		'action' => 'itemprototype.massupdateform',
1240		'hostid' => getRequest('hostid', 0),
1241		'parent_discoveryid' => getRequest('parent_discoveryid'),
1242		'item_prototypeids' => getRequest('group_itemid', []),
1243		'description' => getRequest('description', ''),
1244		'delay' => getRequest('delay', ZBX_ITEM_DELAY_DEFAULT),
1245		'delay_flex' => getRequest('delay_flex', []),
1246		'history' => getRequest('history', DB::getDefault('items', 'history')),
1247		'status' => getRequest('status', 0),
1248		'discover' => getRequest('discover', DB::getDefault('items', 'discover')),
1249		'type' => getRequest('type', 0),
1250		'interfaceid' => getRequest('interfaceid', 0),
1251		'value_type' => getRequest('value_type', ITEM_VALUE_TYPE_UINT64),
1252		'trapper_hosts' => getRequest('trapper_hosts', ''),
1253		'units' => getRequest('units', ''),
1254		'authtype' => getRequest('authtype', ''),
1255		'jmx_endpoint' => getRequest('jmx_endpoint', ''),
1256		'username' => getRequest('username', ''),
1257		'password' => getRequest('password', ''),
1258		'publickey' => getRequest('publickey', ''),
1259		'privatekey' => getRequest('privatekey', ''),
1260		'valuemapid' => getRequest('valuemapid', 0),
1261		'trends' => getRequest('trends', DB::getDefault('items', 'trends')),
1262		'applications' => [],
1263		'application_prototypes' => [],
1264		'logtimefmt' => getRequest('logtimefmt', ''),
1265		'preprocessing' => getRequest('preprocessing', []),
1266		'initial_item_type' => null,
1267		'multiple_interface_types' => false,
1268		'visible' => getRequest('visible', []),
1269		'master_itemid' => getRequest('master_itemid', 0),
1270		'url' =>  getRequest('url', ''),
1271		'post_type' => getRequest('post_type', DB::getDefault('items', 'post_type')),
1272		'posts' => getRequest('posts', ''),
1273		'headers' => getRequest('headers', []),
1274		'allow_traps' => getRequest('allow_traps', HTTPCHECK_ALLOW_TRAPS_OFF),
1275		'massupdate_app_action' => getRequest('massupdate_app_action', ZBX_ACTION_ADD),
1276		'massupdate_app_prot_action' => getRequest('massupdate_app_prot_action', ZBX_ACTION_ADD),
1277		'preprocessing_test_type' => CControllerPopupItemTestEdit::ZBX_TEST_TYPE_ITEM_PROTOTYPE,
1278		'preprocessing_types' => CItemPrototype::$supported_preprocessing_types,
1279		'preprocessing_script_maxlength' => DB::getFieldLength('item_preproc', 'params')
1280	];
1281
1282	foreach ($data['preprocessing'] as &$step) {
1283		$step += [
1284			'error_handler' => ZBX_PREPROC_FAIL_DEFAULT,
1285			'error_handler_params' => ''
1286		];
1287	}
1288	unset($step);
1289
1290	if (hasRequest('applications')) {
1291		$applicationids = [];
1292
1293		foreach (getRequest('applications') as $application) {
1294			if (is_array($application) && array_key_exists('new', $application)) {
1295				$data['applications'][] = [
1296					'id' => $application['new'],
1297					'name' => $application['new'].' ('._x('new', 'new element in multiselect').')',
1298					'isNew' => true
1299				];
1300			}
1301			else {
1302				$applicationids[] = $application;
1303			}
1304		}
1305
1306		$data['applications'] = array_merge($data['applications'], $applicationids
1307			? CArrayHelper::renameObjectsKeys(API::Application()->get([
1308				'output' => ['applicationid', 'name'],
1309				'applicationids' => $applicationids
1310			]), ['applicationid' => 'id'])
1311			: []);
1312	}
1313
1314	if (hasRequest('application_prototypes')) {
1315		$application_prototypeids = [];
1316
1317		foreach (getRequest('application_prototypes') as $application_prototype) {
1318			if (is_array($application_prototype) && array_key_exists('new', $application_prototype)) {
1319				$data['application_prototypes'][] = [
1320					'id' => $application_prototype['new'],
1321					'name' => $application_prototype['new'].' ('._x('new', 'new element in multiselect').')',
1322					'isNew' => true
1323				];
1324			}
1325			else {
1326				$application_prototypeids[] = $application_prototype;
1327			}
1328		}
1329
1330		$data['application_prototypes'] = array_merge($data['application_prototypes'], $application_prototypeids
1331			? CArrayHelper::renameObjectsKeys(
1332				DBfetchArray(DBselect(
1333					'SELECT ap.application_prototypeid,ap.name'.
1334					' FROM application_prototype ap'.
1335					' WHERE '.dbConditionId('ap.application_prototypeid', $application_prototypeids)
1336				)), ['application_prototypeid' => 'id'])
1337			: []);
1338	}
1339
1340	if ($data['headers']) {
1341		$headers = [];
1342
1343		foreach ($data['headers']['name'] as $index => $key) {
1344			if (array_key_exists($index, $data['headers']['value'])) {
1345				$headers[] = [$key => $data['headers']['value'][$index]];
1346			}
1347		}
1348
1349		// Ignore single row if it is empty.
1350		if (count($headers) == 1 && $key === '' && $data['headers']['value'][$index] === '') {
1351			$headers = [];
1352		}
1353
1354		$data['headers'] = $headers;
1355	}
1356
1357	// hosts
1358	$data['hosts'] = API::Host()->get([
1359		'output' => ['hostid'],
1360		'itemids' => $data['item_prototypeids'],
1361		'selectInterfaces' => ['interfaceid', 'main', 'type', 'useip', 'ip', 'dns', 'port', 'details']
1362	]);
1363
1364	$data['display_interfaces'] = true;
1365
1366	$templates = API::Template()->get([
1367		'output' => ['templateid'],
1368		'itemids' => $data['item_prototypeids']
1369	]);
1370
1371	if ($templates) {
1372		$data['display_interfaces'] = false;
1373
1374		if ($data['hostid'] == 0) {
1375			// If selected from filter without 'hostid'.
1376			$templates = reset($templates);
1377			$data['hostid'] = $templates['templateid'];
1378		}
1379	}
1380
1381	if ($data['display_interfaces']) {
1382		$data['hosts'] = reset($data['hosts']);
1383
1384		// Sort interfaces to be listed starting with one selected as 'main'.
1385		CArrayHelper::sort($data['hosts']['interfaces'], [
1386			['field' => 'main', 'order' => ZBX_SORT_DOWN]
1387		]);
1388
1389		// If selected from filter without 'hostid'.
1390		if ($data['hostid'] == 0) {
1391			$data['hostid'] = $data['hosts']['hostid'];
1392		}
1393
1394		// Set the initial chosen interface to one of the interfaces the items use.
1395		$item_prototypes = API::ItemPrototype()->get([
1396			'output' => ['itemid', 'type', 'name'],
1397			'itemids' => $data['item_prototypeids']
1398		]);
1399		$used_interface_types = [];
1400
1401		foreach ($item_prototypes as $item_prototype) {
1402			$used_interface_types[$item_prototype['type']] = itemTypeInterface($item_prototype['type']);
1403		}
1404
1405		$initial_type = min(array_keys($used_interface_types));
1406		$data['type'] = (getRequest('type') !== null) ? $data['type'] : $initial_type;
1407		$data['initial_item_type'] = $initial_type;
1408		$data['multiple_interface_types'] = (count(array_unique($used_interface_types)) > 1);
1409	}
1410
1411	if ($data['master_itemid'] != 0) {
1412		$master_prototypes = API::Item()->get([
1413			'output' => ['itemid', 'hostid', 'name', 'key_'],
1414			'selectHosts' => ['name'],
1415			'itemids' => [$data['master_itemid']],
1416			'hostids' => [$data['hostid']],
1417			'webitems' => true
1418		])
1419		+ API::ItemPrototype()->get([
1420			'output' => ['itemid', 'hostid', 'name', 'key_'],
1421			'selectHosts' => ['name'],
1422			'itemids' => getRequest('master_itemid', $data['master_itemid'])
1423		]);
1424
1425		if ($master_prototypes) {
1426			$data['master_itemname'] = $master_prototypes[0]['name'];
1427			$data['master_hostname'] = $master_prototypes[0]['hosts'][0]['name'];
1428		}
1429		else {
1430			$data['master_itemid'] = 0;
1431			show_messages(false, '', _('No permissions to referred object or it does not exist!'));
1432		}
1433	}
1434
1435	// item types
1436	$data['itemTypes'] = item_type2str();
1437	unset($data['itemTypes'][ITEM_TYPE_HTTPTEST]);
1438
1439	// valuemap
1440	$data['valuemaps'] = API::ValueMap()->get([
1441		'output' => ['valuemapid', 'name']
1442	]);
1443	CArrayHelper::sort($data['valuemaps'], ['name']);
1444
1445	if (!$data['delay_flex']) {
1446		$data['delay_flex'][] = ['delay' => '', 'period' => '', 'type' => ITEM_DELAY_FLEXIBLE];
1447	}
1448
1449	$data['jmx_endpoint'] = ZBX_DEFAULT_JMX_ENDPOINT;
1450
1451	$history_in_seconds = timeUnitToSeconds($data['history']);
1452	if (!getRequest('form_refresh') && $history_in_seconds !== null && $history_in_seconds == ITEM_NO_STORAGE_VALUE) {
1453		$data['history_mode'] = getRequest('history_mode', ITEM_STORAGE_OFF);
1454		$data['history'] = DB::getDefault('items', 'history');
1455	}
1456	else {
1457		$data['history_mode'] = getRequest('history_mode', ITEM_STORAGE_CUSTOM);
1458	}
1459
1460	$trends_in_seconds = timeUnitToSeconds($data['trends']);
1461	if (!getRequest('form_refresh') && $trends_in_seconds !== null && $trends_in_seconds == ITEM_NO_STORAGE_VALUE) {
1462		$data['trends_mode'] = getRequest('trends_mode', ITEM_STORAGE_OFF);
1463		$data['trends'] = DB::getDefault('items', 'trends');
1464	}
1465	else {
1466		$data['trends_mode'] = getRequest('trends_mode', ITEM_STORAGE_CUSTOM);
1467	}
1468
1469	echo (new CView('configuration.item.prototype.massupdate', $data))->getOutput();
1470}
1471else {
1472	$sortField = getRequest('sort', CProfile::get('web.'.$page['file'].'.sort', 'name'));
1473	$sortOrder = getRequest('sortorder', CProfile::get('web.'.$page['file'].'.sortorder', ZBX_SORT_UP));
1474
1475	CProfile::update('web.'.$page['file'].'.sort', $sortField, PROFILE_TYPE_STR);
1476	CProfile::update('web.'.$page['file'].'.sortorder', $sortOrder, PROFILE_TYPE_STR);
1477
1478	$config = select_config();
1479
1480	$data = [
1481		'form' => getRequest('form'),
1482		'parent_discoveryid' => getRequest('parent_discoveryid'),
1483		'hostid' => $discoveryRule['hostid'],
1484		'sort' => $sortField,
1485		'sortorder' => $sortOrder
1486	];
1487
1488	$data['items'] = API::ItemPrototype()->get([
1489		'discoveryids' => $data['parent_discoveryid'],
1490		'output' => API_OUTPUT_EXTEND,
1491		'editable' => true,
1492		'selectApplications' => API_OUTPUT_EXTEND,
1493		'sortfield' => $sortField,
1494		'limit' => $config['search_limit'] + 1
1495	]);
1496
1497	$data['items'] = expandItemNamesWithMasterItems($data['items'], 'itemprototypes');
1498
1499	switch ($sortField) {
1500		case 'delay':
1501			orderItemsByDelay($data['items'], $sortOrder, ['usermacros' => true, 'lldmacros' => true]);
1502			break;
1503
1504		case 'history':
1505			orderItemsByHistory($data['items'], $sortOrder);
1506			break;
1507
1508		case 'trends':
1509			orderItemsByTrends($data['items'], $sortOrder);
1510			break;
1511
1512		default:
1513			order_result($data['items'], $sortField, $sortOrder);
1514	}
1515
1516	// pager
1517	if (hasRequest('page')) {
1518		$page_num = getRequest('page');
1519	}
1520	elseif (isRequestMethod('get') && !hasRequest('cancel')) {
1521		$page_num = 1;
1522	}
1523	else {
1524		$page_num = CPagerHelper::loadPage($page['file']);
1525	}
1526
1527	CPagerHelper::savePage($page['file'], $page_num);
1528
1529	$data['paging'] = CPagerHelper::paginate($page_num, $data['items'], $sortOrder,
1530		(new CUrl('disc_prototypes.php'))->setArgument('parent_discoveryid', $data['parent_discoveryid'])
1531	);
1532
1533	$data['parent_templates'] = getItemParentTemplates($data['items'], ZBX_FLAG_DISCOVERY_PROTOTYPE);
1534
1535	// render view
1536	echo (new CView('configuration.item.prototype.list', $data))->getOutput();
1537}
1538
1539require_once dirname(__FILE__).'/include/page_footer.php';
1540