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/forms.inc.php';
24require_once dirname(__FILE__).'/include/discovery.inc.php';
25
26$page['title'] = _('Configuration of discovery rules');
27$page['file'] = 'discoveryconf.php';
28$page['type'] = detect_page_type();
29
30require_once dirname(__FILE__).'/include/page_header.php';
31
32// VAR	TYPE	OPTIONAL	FLAGS	VALIDATION	EXCEPTION
33$fields = [
34	'druleid' =>		[T_ZBX_INT, O_OPT, P_SYS,	DB_ID,		'isset({form}) && {form} == "update"'],
35	'name' =>			[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,	'isset({add}) || isset({update})'],
36	'proxy_hostid' =>	[T_ZBX_INT, O_OPT, null,	DB_ID,		'isset({add}) || isset({update})'],
37	'iprange' =>		[T_ZBX_STR, O_OPT, P_CRLF,	null,		'isset({add}) || isset({update})'],
38	'delay' =>			[T_ZBX_INT, O_OPT, null,	BETWEEN(1, SEC_PER_WEEK), 'isset({add}) || isset({update})'],
39	'status' =>			[T_ZBX_INT, O_OPT, null,	IN('0,1'),	null],
40	'uniqueness_criteria' => [T_ZBX_STR, O_OPT, null, null,	'isset({add}) || isset({update})', _('Device uniqueness criteria')],
41	'g_druleid' =>		[T_ZBX_INT, O_OPT, null,	DB_ID,		null],
42	'dchecks' =>		[null, O_OPT, null,		null,		null],
43	// actions
44	'action' =>			[T_ZBX_STR, O_OPT, P_SYS|P_ACT,
45							IN('"drule.massdelete","drule.massdisable","drule.massenable"'),
46							null
47						],
48	'add' =>			[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
49	'update' =>			[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
50	'delete' =>			[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
51	'cancel' =>			[T_ZBX_STR, O_OPT, P_SYS,	null,		null],
52	'form' =>			[T_ZBX_STR, O_OPT, P_SYS,	null,		null],
53	'form_refresh' =>	[T_ZBX_INT, O_OPT, null,	null,		null],
54	'output' =>			[T_ZBX_STR, O_OPT, P_ACT,	null,		null],
55	'ajaxaction' =>		[T_ZBX_STR, O_OPT, P_ACT,	null,		null],
56	'ajaxdata' =>		[T_ZBX_STR, O_OPT, P_ACT,	null,		null],
57	// sort and sortorder
58	'sort' =>			[T_ZBX_STR, O_OPT, P_SYS, IN('"name"'),								null],
59	'sortorder' =>		[T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'),	null]
60];
61check_fields($fields);
62
63$_REQUEST['status'] = isset($_REQUEST['status']) ? DRULE_STATUS_ACTIVE : DRULE_STATUS_DISABLED;
64$_REQUEST['dchecks'] = getRequest('dchecks', []);
65
66/*
67 * Permissions
68 */
69if (isset($_REQUEST['druleid'])) {
70	$dbDRule = API::DRule()->get([
71		'druleids' => getRequest('druleid'),
72		'output' => ['name', 'proxy_hostid', 'iprange', 'delay', 'status'],
73		'selectDChecks' => [
74			'type', 'key_', 'snmp_community', 'ports', 'snmpv3_securityname', 'snmpv3_securitylevel',
75			'snmpv3_authpassphrase', 'snmpv3_privpassphrase', 'uniq', 'snmpv3_authprotocol', 'snmpv3_privprotocol',
76			'snmpv3_contextname'
77		],
78		'editable' => true
79	]);
80	if (empty($dbDRule)) {
81		access_deny();
82	}
83}
84
85// ajax
86if (isset($_REQUEST['output']) && $_REQUEST['output'] == 'ajax') {
87	$ajaxResponse = new CAjaxResponse;
88
89	if (isset($_REQUEST['ajaxaction']) && $_REQUEST['ajaxaction'] == 'validate') {
90		$ajaxData = getRequest('ajaxdata', []);
91		$item_key_parser = new CItemKey();
92
93		foreach ($ajaxData as $check) {
94			switch ($check['field']) {
95				case 'port':
96					if (!validate_port_list($check['value'])) {
97						$ajaxResponse->error(_('Incorrect port range.'));
98					}
99					break;
100				case 'itemKey':
101					if ($item_key_parser->parse($check['value']) != CParser::PARSE_SUCCESS) {
102						$ajaxResponse->error(
103							_s('Invalid key "%1$s": %2$s.', $check['value'], $item_key_parser->getError())
104						);
105					}
106					break;
107			}
108		}
109	}
110
111	$ajaxResponse->send();
112
113	require_once dirname(__FILE__).'/include/page_footer.php';
114	exit;
115}
116
117/*
118 * Action
119 */
120if (hasRequest('add') || hasRequest('update')) {
121	$dChecks = getRequest('dchecks', []);
122	$uniq = getRequest('uniqueness_criteria', 0);
123
124	foreach ($dChecks as $dcnum => $check) {
125		if (substr($check['dcheckid'], 0, 3) === 'new') {
126			unset($dChecks[$dcnum]['dcheckid']);
127		}
128
129		$dChecks[$dcnum]['uniq'] = ($uniq == $dcnum) ? 1 : 0;
130	}
131
132	$discoveryRule = [
133		'name' => getRequest('name'),
134		'proxy_hostid' => getRequest('proxy_hostid'),
135		'iprange' => getRequest('iprange'),
136		'delay' => getRequest('delay'),
137		'status' => getRequest('status'),
138		'dchecks' => $dChecks
139	];
140
141	DBStart();
142
143	if (hasRequest('update')) {
144		$discoveryRule['druleid'] = getRequest('druleid');
145		$result = API::DRule()->update($discoveryRule);
146
147		$messageSuccess = _('Discovery rule updated');
148		$messageFailed = _('Cannot update discovery rule');
149		$auditAction = AUDIT_ACTION_UPDATE;
150	}
151	else {
152		$result = API::DRule()->create($discoveryRule);
153
154		$messageSuccess = _('Discovery rule created');
155		$messageFailed = _('Cannot create discovery rule');
156		$auditAction = AUDIT_ACTION_ADD;
157	}
158
159	if ($result) {
160		$druleid = reset($result['druleids']);
161		add_audit($auditAction, AUDIT_RESOURCE_DISCOVERY_RULE, '['.$druleid.'] '.$discoveryRule['name']);
162		unset($_REQUEST['form']);
163	}
164
165	$result = DBend($result);
166
167	if ($result) {
168		uncheckTableRows();
169	}
170	show_messages($result, $messageSuccess, $messageFailed);
171}
172elseif (isset($_REQUEST['delete']) && isset($_REQUEST['druleid'])) {
173	$result = API::DRule()->delete([$_REQUEST['druleid']]);
174
175	if ($result) {
176		unset($_REQUEST['form'], $_REQUEST['druleid']);
177		uncheckTableRows();
178	}
179	show_messages($result, _('Discovery rule deleted'), _('Cannot delete discovery rule'));
180}
181elseif (hasRequest('action') && str_in_array(getRequest('action'), ['drule.massenable', 'drule.massdisable']) && hasRequest('g_druleid')) {
182	$result = true;
183	$enable = (getRequest('action') == 'drule.massenable');
184	$status = $enable ? DRULE_STATUS_ACTIVE : DRULE_STATUS_DISABLED;
185	$auditAction = $enable ? AUDIT_ACTION_ENABLE : AUDIT_ACTION_DISABLE;
186	$updated = 0;
187
188	DBStart();
189
190	foreach (getRequest('g_druleid') as $druleId) {
191		$result &= DBexecute('UPDATE drules SET status='.zbx_dbstr($status).' WHERE druleid='.zbx_dbstr($druleId));
192
193		if ($result) {
194			$druleData = get_discovery_rule_by_druleid($druleId);
195			add_audit($auditAction, AUDIT_RESOURCE_DISCOVERY_RULE, '['.$druleId.'] '.$druleData['name']);
196		}
197
198		$updated++;
199	}
200
201	$messageSuccess = $enable
202		? _n('Discovery rule enabled', 'Discovery rules enabled', $updated)
203		: _n('Discovery rule disabled', 'Discovery rules disabled', $updated);
204	$messageFailed = $enable
205		? _n('Cannot enable discovery rule', 'Cannot enable discovery rules', $updated)
206		: _n('Cannot disable discovery rule', 'Cannot disable discovery rules', $updated);
207
208	$result = DBend($result);
209
210	if ($result) {
211		uncheckTableRows();
212	}
213	show_messages($result, $messageSuccess, $messageFailed);
214}
215elseif (hasRequest('action') && getRequest('action') == 'drule.massdelete' && hasRequest('g_druleid')) {
216	$result = API::DRule()->delete(getRequest('g_druleid'));
217
218	if ($result) {
219		uncheckTableRows();
220	}
221	show_messages($result, _('Discovery rules deleted'), _('Cannot delete discovery rules'));
222}
223
224/*
225 * Display
226 */
227if (isset($_REQUEST['form'])) {
228	$data = [
229		'druleid' => getRequest('druleid'),
230		'drule' => [],
231		'form' => getRequest('form'),
232		'form_refresh' => getRequest('form_refresh', 0)
233	];
234
235	// get drule
236	if (isset($data['druleid']) && !isset($_REQUEST['form_refresh'])) {
237		$data['drule'] = reset($dbDRule);
238		$data['drule']['uniqueness_criteria'] = -1;
239
240		if (!empty($data['drule']['dchecks'])) {
241			foreach ($data['drule']['dchecks'] as $id => $dcheck) {
242				if ($dcheck['uniq']) {
243					$data['drule']['uniqueness_criteria'] = $dcheck['dcheckid'];
244				}
245			}
246		}
247	}
248	else {
249		$data['drule']['proxy_hostid'] = getRequest('proxy_hostid', 0);
250		$data['drule']['name'] = getRequest('name', '');
251		$data['drule']['iprange'] = getRequest('iprange', '192.168.0.1-254');
252		$data['drule']['delay'] = getRequest('delay', SEC_PER_HOUR);
253		$data['drule']['status'] = getRequest('status', DRULE_STATUS_ACTIVE);
254		$data['drule']['dchecks'] = getRequest('dchecks', []);
255		$data['drule']['nextcheck'] = getRequest('nextcheck', 0);
256		$data['drule']['uniqueness_criteria'] = getRequest('uniqueness_criteria', -1);
257	}
258
259	if (!empty($data['drule']['dchecks'])) {
260		foreach ($data['drule']['dchecks'] as $id => $dcheck) {
261			$data['drule']['dchecks'][$id]['name'] = discovery_check2str(
262				$dcheck['type'],
263				isset($dcheck['key_']) ? $dcheck['key_'] : '',
264				isset($dcheck['ports']) ? $dcheck['ports'] : ''
265			);
266		}
267
268		order_result($data['drule']['dchecks'], 'name');
269	}
270
271	// get proxies
272	$data['proxies'] = API::Proxy()->get([
273		'output' => API_OUTPUT_EXTEND
274	]);
275	order_result($data['proxies'], 'host');
276
277	// render view
278	$discoveryView = new CView('configuration.discovery.edit', $data);
279	$discoveryView->render();
280	$discoveryView->show();
281}
282else {
283	$sortField = getRequest('sort', CProfile::get('web.'.$page['file'].'.sort', 'name'));
284	$sortOrder = getRequest('sortorder', CProfile::get('web.'.$page['file'].'.sortorder', ZBX_SORT_UP));
285
286	CProfile::update('web.'.$page['file'].'.sort', $sortField, PROFILE_TYPE_STR);
287	CProfile::update('web.'.$page['file'].'.sortorder', $sortOrder, PROFILE_TYPE_STR);
288
289	$config = select_config();
290
291	$data = [
292		'sort' => $sortField,
293		'sortorder' => $sortOrder
294	];
295
296	// get drules
297	$data['drules'] = API::DRule()->get([
298		'output' => ['proxy_hostid', 'name', 'status', 'iprange', 'delay'],
299		'selectDChecks' => ['type'],
300		'editable' => true,
301		'sortfield' => $sortField,
302		'limit' => $config['search_limit'] + 1
303	]);
304
305	if ($data['drules']) {
306		foreach ($data['drules'] as $key => $drule) {
307			// checks
308			$checks = [];
309
310			foreach ($drule['dchecks'] as $check) {
311				$checks[$check['type']] = discovery_check_type2str($check['type']);
312			}
313
314			order_result($checks);
315
316			$data['drules'][$key]['checks'] = $checks;
317
318			// description
319			$data['drules'][$key]['description'] = [];
320
321			if ($drule['proxy_hostid']) {
322				$proxy = get_host_by_hostid($drule['proxy_hostid']);
323
324				array_push($data['drules'][$key]['description'], $proxy['host'].NAME_DELIMITER);
325			}
326		}
327
328		order_result($data['drules'], $sortField, $sortOrder);
329	}
330
331	// get paging
332	$data['paging'] = getPagingLine($data['drules'], $sortOrder, new CUrl('discoveryconf.php'));
333
334	// render view
335	$discoveryView = new CView('configuration.discovery.list', $data);
336	$discoveryView->render();
337	$discoveryView->show();
338}
339
340require_once dirname(__FILE__).'/include/page_footer.php';
341