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 * @var CView $this
24 */
25?>
26
27<script type="text/x-jquery-tmpl" id="dcheck-row-tmpl">
28	<?= (new CRow([
29			(new CCol(
30				(new CDiv('#{name}'))->addClass(ZBX_STYLE_WORDWRAP)
31			))->setId('dcheckCell_#{dcheckid}'),
32			(new CHorList([
33				(new CButton(null, _('Edit')))
34					->addClass(ZBX_STYLE_BTN_LINK)
35					->setAttribute('data-action', 'edit'),
36				(new CButton(null, _('Remove')))
37					->addClass(ZBX_STYLE_BTN_LINK)
38					->onClick("removeDCheckRow('#{dcheckid}');")
39			]))->addClass(ZBX_STYLE_NOWRAP)
40		]))
41			->setId('dcheckRow_#{dcheckid}')
42			->toString()
43	?>
44</script>
45<script type="text/x-jquery-tmpl" id="unique-row-tmpl">
46	<?= (new CListItem([
47			(new CInput('radio', 'uniqueness_criteria', '#{dcheckid}'))
48				->addClass(ZBX_STYLE_CHECKBOX_RADIO)
49				->setId('uniqueness_criteria_#{dcheckid}'),
50			(new CLabel([new CSpan(), '#{name}'], 'uniqueness_criteria_#{dcheckid}'))->addClass(ZBX_STYLE_WORDWRAP)
51		]))
52			->setId('uniqueness_criteria_row_#{dcheckid}')
53			->toString()
54	?>
55</script>
56<script type="text/x-jquery-tmpl" id="host-source-row-tmpl">
57	<?= (new CListItem([
58			(new CInput('radio', 'host_source', '_#{dcheckid}'))
59				->addClass(ZBX_STYLE_CHECKBOX_RADIO)
60				->setAttribute('data-id', '#{dcheckid}')
61				->setId('host_source_#{dcheckid}'),
62			(new CLabel([new CSpan(), '#{name}'], 'host_source_#{dcheckid}'))->addClass(ZBX_STYLE_WORDWRAP)
63		]))
64			->setId('host_source_row_#{dcheckid}')
65			->toString()
66	?>
67</script>
68<script type="text/x-jquery-tmpl" id="name-source-row-tmpl">
69	<?= (new CListItem([
70			(new CInput('radio', 'name_source', '_#{dcheckid}'))
71				->addClass(ZBX_STYLE_CHECKBOX_RADIO)
72				->setAttribute('data-id', '#{dcheckid}')
73				->setId('name_source_#{dcheckid}'),
74			(new CLabel([new CSpan(), '#{name}'], 'name_source_#{dcheckid}'))->addClass(ZBX_STYLE_WORDWRAP)
75		]))
76			->setId('name_source_row_#{dcheckid}')
77			->toString()
78	?>
79</script>
80
81<script type="text/javascript">
82	var ZBX_CHECKLIST = {};
83
84	function addDCheck(list) {
85		var available_device_types = [<?= SVC_AGENT ?>, <?= SVC_SNMPv1 ?>, <?= SVC_SNMPv2c ?>, <?= SVC_SNMPv3 ?>];
86
87		var addNewValue = function(value) {
88			ZBX_CHECKLIST[value.dcheckid] = value;
89
90			jQuery('#dcheckListFooter').before(new Template(jQuery('#dcheck-row-tmpl').html()).evaluate(value));
91
92			for (var field_name in value) {
93				if (value.hasOwnProperty(field_name)) {
94					var $input = jQuery('<input>', {
95						name: 'dchecks[' + value.dcheckid + '][' + field_name + ']',
96						type: 'hidden',
97						value: value[field_name]
98					});
99
100					jQuery('#dcheckCell_' + value.dcheckid).append($input);
101				}
102			}
103		}
104
105		var updateNewValue = function(value) {
106			ZBX_CHECKLIST[value.dcheckid] = value;
107
108			var ignore_names = ['druleid', 'dcheckid', 'name', 'ports', 'type', 'uniq'];
109
110			// Clean values.
111			jQuery('#dcheckCell_' + value.dcheckid + ' input').each(function(i, item) {
112				var $item = jQuery(item);
113
114				var name = $item
115					.attr('name')
116					.replace('dchecks[' + value.dcheckid + '][', '');
117				name = name.substring(0, name.length - 1);
118
119				if (jQuery.inArray(name, ignore_names) == -1) {
120					$item.remove();
121				}
122			});
123
124			// Set values.
125			for (var field_name in value) {
126				if (value.hasOwnProperty(field_name)) {
127					var $obj = jQuery('input[name="dchecks[' + value.dcheckid + '][' + field_name + ']"]');
128
129					// If the input exists, update the value or create it otherwise.
130					if ($obj.length) {
131						$obj.val(value[field_name]);
132					}
133					else {
134						var $input = jQuery('<input>', {
135							name: 'dchecks[' + value.dcheckid + '][' + field_name + ']',
136							type: 'hidden',
137							value: value[field_name]
138						});
139
140						jQuery('#dcheckCell_' + value.dcheckid).append($input);
141					}
142				}
143			}
144
145			// Update check name.
146			jQuery('#dcheckCell_' + value.dcheckid + ' .wordwrap').text(value['name']);
147		}
148
149		for (var i = 0; i < list.length; i++) {
150			if (empty(list[i])) {
151				continue;
152			}
153
154			var value = list[i];
155
156			if (typeof value.dcheckid === 'undefined') {
157				for (;;) {
158					value.dcheckid = getUniqueId();
159
160					if (typeof ZBX_CHECKLIST[value.dcheckid] === 'undefined') {
161						break;
162					}
163				}
164			}
165
166			if (typeof ZBX_CHECKLIST[value.dcheckid] === 'undefined') {
167				addNewValue(value);
168			}
169			else {
170				updateNewValue(value);
171			}
172
173			var elements = {
174				uniqueness_criteria: ['ip',  new Template(jQuery('#unique-row-tmpl').html()).evaluate(value)],
175				host_source: ['chk_dns', new Template(jQuery('#host-source-row-tmpl').html()).evaluate(value)],
176				name_source: ['chk_host', new Template(jQuery('#name-source-row-tmpl').html()).evaluate(value)]
177			};
178
179			jQuery.each(elements, function(key, param) {
180				var $obj = jQuery('#' + key + '_row_' + value.dcheckid);
181
182				if (jQuery.inArray(parseInt(value.type, 10), available_device_types) > -1) {
183					var new_obj = param[1];
184					if ($obj.length) {
185						var checked_id = jQuery('input:radio[name=' + key + ']:checked').attr('id');
186						$obj.replaceWith(new_obj);
187						jQuery('#' + checked_id).prop('checked', true);
188					}
189					else {
190						jQuery('#' + key).append(new_obj);
191					}
192				}
193				else {
194					if ($obj.length) {
195						$obj.remove();
196						jQuery('#' + key + '_' + param[0]).prop('checked', true);
197					}
198				}
199			});
200		}
201	}
202
203	function removeDCheckRow(dcheckid) {
204		jQuery('#dcheckRow_' + dcheckid).remove();
205
206		delete(ZBX_CHECKLIST[dcheckid]);
207
208		var elements = {
209			uniqueness_criteria_: 'ip',
210			host_source_: 'chk_dns',
211			name_source_: 'chk_host'
212		};
213
214		jQuery.each(elements, function(key, def) {
215			var $obj = jQuery('#' + key + dcheckid);
216
217			if ($obj.length) {
218				if ($obj.is(':checked')) {
219					jQuery('#' + key + def).prop('checked', true);
220				}
221				jQuery('#' + key + 'row_' + dcheckid).remove();
222			}
223		});
224	}
225
226	jQuery(function() {
227		addDCheck(<?= json_encode(array_values($data['drule']['dchecks'])) ?>);
228
229		jQuery('input:radio[name="uniqueness_criteria"][value=<?= json_encode($data['drule']['uniqueness_criteria']) ?>]').attr('checked', 'checked');
230		jQuery('input:radio[name="host_source"][value=<?= json_encode($data['drule']['host_source']) ?>]').attr('checked', 'checked');
231		jQuery('input:radio[name="name_source"][value=<?= json_encode($data['drule']['name_source']) ?>]').attr('checked', 'checked');
232
233		jQuery('#clone').click(function() {
234			jQuery('#update')
235				.text(t('Add'))
236				.val('discovery.create')
237				.attr({id: 'add'});
238			jQuery('#druleid, #delete, #clone').remove();
239			jQuery('#form').val('clone');
240			jQuery('#name').focus();
241		});
242
243		jQuery('#host_source, #name_source').on('change', 'input', function() {
244			var $elem = jQuery(this),
245				name = $elem.attr('name');
246
247			if ($elem.data('id')) {
248				jQuery('[name^=dchecks][name$="[' + name + ']"]')
249					.val((name === 'name_source') ? <?= ZBX_DISCOVERY_UNSPEC ?> : <?= ZBX_DISCOVERY_DNS ?>);
250				jQuery('[name="dchecks[' + $elem.data('id') + '][' + name + ']"]').val(<?= ZBX_DISCOVERY_VALUE ?>);
251			}
252			else {
253				jQuery('[name^=dchecks][name$="[' + name + ']"]').val($elem.val());
254			}
255		});
256
257		jQuery('#dcheckList').on('click', '[data-action]', function() {
258			var $btn = jQuery(this),
259				$rows = jQuery('#dcheckList > table > tbody > tr'),
260				params;
261
262			switch ($btn.data('action')) {
263				case 'add':
264					PopUp('popup.discovery.check', {}, null, $btn);
265					break;
266
267				case 'edit':
268					var $row = $btn.closest('tr');
269
270					params = {
271						update: 1
272					};
273
274					$row.find('input[type="hidden"]').each(function() {
275						var $input = jQuery(this),
276							name = $input.attr('name').match(/\[([^\]]+)]$/);
277
278						if (name) {
279							params[name[1]] = $input.val();
280						}
281					});
282
283					PopUp('popup.discovery.check', params, null, $btn);
284					break;
285			}
286		});
287	});
288
289	/**
290	 * Returns a default port number for the specified discovery check type.
291	 *
292	 * @param {string} dcheck_type  Discovery check type.
293	 *
294	 * @returns {string}
295	 */
296	function getDCheckDefaultPort(dcheck_type) {
297		var default_ports = {
298			<?= SVC_SSH ?>: '22',
299			<?= SVC_LDAP ?>: '389',
300			<?= SVC_SMTP ?>: '25',
301			<?= SVC_FTP ?>:  '21',
302			<?= SVC_HTTP ?>: '80',
303			<?= SVC_POP ?>: '110',
304			<?= SVC_NNTP ?>: '119',
305			<?= SVC_IMAP ?>: '143',
306			<?= SVC_AGENT ?>: '10050',
307			<?= SVC_SNMPv1 ?>: '161',
308			<?= SVC_SNMPv2c ?>: '161',
309			<?= SVC_SNMPv3 ?>: '161',
310			<?= SVC_HTTPS ?>: '443',
311			<?= SVC_TELNET ?>: '23'
312		};
313
314		return default_ports.hasOwnProperty(dcheck_type) ? default_ports[dcheck_type] : '0';
315	}
316
317	/**
318	 * Set default discovery check port to input.
319	 *
320	 * @return {object}
321	 */
322	function setDCheckDefaultPort() {
323		return jQuery('#ports').val(getDCheckDefaultPort(jQuery('#type-select').val()));
324	}
325
326	/**
327	 * Sends discovery check form data to the server for validation before adding it to the main form.
328	 *
329	 * @param {Overlay} overlay
330	 */
331	function submitDCheck(overlay) {
332		var $form = overlay.$dialogue.find('form');
333
334		$form.trimValues([
335			'#ports', '#key_', '#snmp_community', '#snmp_oid', '#snmpv3_contextname', '#snmpv3_securityname',
336			'#snmpv3_authpassphrase', '#snmpv3_privpassphrase'
337		]);
338
339		var data = $form
340				.find('#type, #ports, input[type=hidden], input[type=text]:visible, input[type=radio]:checked:visible')
341				.serialize(),
342			dialogueid = $form
343				.closest("[data-dialogueid]")
344				.data('dialogueid');
345
346		if (!dialogueid) {
347			return false;
348		}
349
350		overlay.setLoading();
351		overlay.xhr = sendAjaxData('zabbix.php', {
352			data: data,
353			dataType: 'json',
354			method: 'POST',
355			complete: function() {
356				overlay.unsetLoading();
357			}
358		}).done(function(response) {
359			$form
360				.parent()
361				.find('.<?= ZBX_STYLE_MSG_BAD ?>')
362				.remove();
363
364			if (typeof response.errors !== 'undefined') {
365				return jQuery(response.errors).insertBefore($form);
366			}
367			else {
368				var dcheck = response.params;
369
370				if (typeof dcheck.ports !== 'undefined' && dcheck.ports != getDCheckDefaultPort(dcheck.type)) {
371					dcheck.name += ' (' + dcheck.ports + ')';
372				}
373				if (dcheck.key_) {
374					dcheck.name += ' "' + dcheck.key_ + '"';
375				}
376				dcheck.host_source = jQuery('[name="host_source"]:checked:not([data-id])').val()
377					|| '<?= ZBX_DISCOVERY_DNS ?>';
378				dcheck.name_source = jQuery('[name="name_source"]:checked:not([data-id])').val()
379					|| '<?= ZBX_DISCOVERY_UNSPEC ?>';
380
381				if (hasDCheckDuplicates()) {
382					jQuery(makeMessageBox('bad', <?= json_encode(_('Check already exists.')) ?>, null, true, false))
383						.insertBefore($form);
384
385					return false;
386				}
387
388				addDCheck([dcheck]);
389				overlayDialogueDestroy(overlay.dialogueid);
390			}
391		});
392	}
393
394	/**
395	 * Check for duplicates.
396	 *
397	 * @return {boolean}
398	 */
399	function hasDCheckDuplicates() {
400		var $form = jQuery(document.forms['dcheck_form']),
401			dcheckid = jQuery('#dcheckid').val(),
402			dcheck = $form
403				.find('#ports, >input[type=hidden], input[type=text]:visible, input[type=radio]:checked:visible')
404				.serializeJSON(),
405			fields = ['type', 'ports', 'snmp_community', 'key_', 'snmpv3_contextname', 'snmpv3_securityname',
406				'snmpv3_securitylevel', 'snmpv3_authprotocol', 'snmpv3_authpassphrase', 'snmpv3_privprotocol',
407				'snmpv3_privpassphrase'
408			];
409
410		dcheck['type'] = $form.find('z-select').val();
411		dcheck.dcheckid = dcheckid ? dcheckid : getUniqueId();
412
413		if (dcheck['type'] == <?= SVC_SNMPv1 ?> || dcheck['type'] == <?= SVC_SNMPv2c ?>
414				|| dcheck['type'] == <?= SVC_SNMPv3 ?>) {
415			dcheck['key_'] = dcheck['snmp_oid'];
416		}
417
418		for (var zbx_dcheckid in ZBX_CHECKLIST) {
419			if (ZBX_CHECKLIST[zbx_dcheckid]['type'] !== dcheck['type']) {
420				continue;
421			}
422
423			if (typeof dcheckid === 'undefined' || dcheckid != zbx_dcheckid) {
424				var duplicate_fields = fields
425					.map(function(value) {
426						return typeof dcheck[value] === 'undefined'
427							|| dcheck[value] === ''
428							|| ZBX_CHECKLIST[zbx_dcheckid][value] === dcheck[value];
429					})
430					.filter(function(value) {
431						return !!value;
432					});
433
434				if (duplicate_fields.length === fields.length) {
435					return true;
436				}
437			}
438		}
439
440		return false;
441	}
442
443	$(() => {
444		const $form = $(document.forms['discoveryForm']);
445		$form.on('submit', () => $form.trimValues(['#name', '#iprange', '#delay']));
446	});
447</script>
448