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
22function getRegexp($regexpId) {
23	return DBfetch(DBselect('SELECT re.* FROM regexps re WHERE regexpid='.zbx_dbstr($regexpId)));
24}
25
26function getRegexpExpressions($regexpId) {
27	$expressions = [];
28
29	$dbExpressions = DBselect(
30		'SELECT e.expressionid,e.expression,e.expression_type,e.exp_delimiter,e.case_sensitive'.
31		' FROM expressions e'.
32		' WHERE regexpid='.zbx_dbstr($regexpId)
33	);
34	while ($expression = DBfetch($dbExpressions)) {
35		$expressions[$expression['expressionid']] = $expression;
36	}
37
38	return $expressions;
39}
40
41function addRegexp(array $regexp, array $expressions) {
42	try {
43		// check required fields
44		$dbFields = ['name' => null, 'test_string' => ''];
45
46		validateRegexp($expressions);
47
48		if (!check_db_fields($dbFields, $regexp)) {
49			throw new Exception(_('Incorrect arguments passed to function').' [addRegexp]');
50		}
51
52		// check duplicate name
53		$sql = 'SELECT re.regexpid'.
54				' FROM regexps re'.
55				' WHERE re.name='.zbx_dbstr($regexp['name']);
56
57		if (DBfetch(DBselect($sql))) {
58			throw new Exception(_s('Regular expression "%s" already exists.', $regexp['name']));
59		}
60
61		$regexpIds = DB::insert('regexps', [$regexp]);
62		$regexpId = reset($regexpIds);
63
64		addRegexpExpressions($regexpId, $expressions);
65	}
66	catch (Exception $e) {
67		error($e->getMessage());
68		return false;
69	}
70
71	return true;
72}
73
74function updateRegexp(array $regexp, array $expressions) {
75	try {
76		$regexpId = $regexp['regexpid'];
77		unset($regexp['regexpid']);
78
79		validateRegexp($expressions);
80
81		// check existence
82		if (!getRegexp($regexpId)) {
83			throw new Exception(_('Regular expression does not exist.'));
84		}
85
86		// check required fields
87		$dbFields = ['name' => null];
88		if (!check_db_fields($dbFields, $regexp)) {
89			throw new Exception(_('Incorrect arguments passed to function').' [updateRegexp]');
90		}
91
92		// check duplicate name
93		$dbRegexp = DBfetch(DBselect(
94			'SELECT re.regexpid'.
95			' FROM regexps re'.
96			' WHERE re.name='.zbx_dbstr($regexp['name'])
97		));
98		if ($dbRegexp && bccomp($regexpId, $dbRegexp['regexpid']) != 0) {
99			throw new Exception(_s('Regular expression "%s" already exists.', $regexp['name']));
100		}
101
102		rewriteRegexpExpressions($regexpId, $expressions);
103
104		DB::update('regexps', [
105			'values' => $regexp,
106			'where' => ['regexpid' => $regexpId]
107		]);
108	}
109	catch (Exception $e) {
110		error($e->getMessage());
111		return false;
112	}
113
114	return true;
115}
116
117function validateRegexp($expressions) {
118	$validator = new CRegexValidator([
119		'messageInvalid' => _('Regular expression must be a string'),
120		'messageRegex' => _('Incorrect regular expression "%1$s": "%2$s"')
121	]);
122
123	foreach ($expressions as $expression) {
124		switch ($expression['expression_type']) {
125			case EXPRESSION_TYPE_TRUE:
126			case EXPRESSION_TYPE_FALSE:
127				if (!$validator->validate($expression['expression'])) {
128					throw new Exception($validator->getError());
129				}
130				break;
131
132			case EXPRESSION_TYPE_INCLUDED:
133			case EXPRESSION_TYPE_NOT_INCLUDED:
134				if ($expression['expression'] === '') {
135					throw new Exception(_('Expression cannot be empty'));
136				}
137				break;
138
139			case EXPRESSION_TYPE_ANY_INCLUDED:
140				foreach (explode($expression['exp_delimiter'], $expression['expression']) as $string) {
141					if ($expression['expression'] === '') {
142						throw new Exception(_('Expression cannot be empty'));
143					}
144				}
145				break;
146		}
147	}
148}
149
150/**
151 * Rewrite Zabbix regexp expressions.
152 * If all fields are equal to existing expression, that expression is not touched.
153 * Other expressions are removed and new ones created.
154 *
155 * @param string $regexpId
156 * @param array  $expressions
157 */
158function rewriteRegexpExpressions($regexpId, array $expressions) {
159	$dbExpressions = getRegexpExpressions($regexpId);
160
161	$expressionsToAdd = [];
162	$expressionsToUpdate = [];
163
164	foreach ($expressions as $expression) {
165		if (!isset($expression['expressionid'])) {
166			$expressionsToAdd[] = $expression;
167		}
168		elseif (isset($dbExpressions[$expression['expressionid']])) {
169			$expressionsToUpdate[] = $expression;
170			unset($dbExpressions[$expression['expressionid']]);
171		}
172	}
173
174	if ($dbExpressions) {
175		$dbExpressionIds = zbx_objectValues($dbExpressions, 'expressionid');
176		deleteRegexpExpressions($dbExpressionIds);
177	}
178
179	if ($expressionsToAdd) {
180		addRegexpExpressions($regexpId, $expressionsToAdd);
181	}
182
183	if ($expressionsToUpdate) {
184		updateRegexpExpressions($expressionsToUpdate);
185	}
186}
187
188function addRegexpExpressions($regexpId, array $expressions) {
189	$dbFields = ['expression' => null, 'expression_type' => null];
190
191	foreach ($expressions as &$expression) {
192		if (!check_db_fields($dbFields, $expression)) {
193			throw new Exception(_('Incorrect arguments passed to function').' [add_expression]');
194		}
195
196		$expression['regexpid'] = $regexpId;
197	}
198	unset($expression);
199
200	DB::insert('expressions', $expressions);
201}
202
203function updateRegexpExpressions(array $expressions) {
204	foreach ($expressions as &$expression) {
205		$expressionId = $expression['expressionid'];
206		unset($expression['expressionid']);
207
208		DB::update('expressions', [
209			'values' => $expression,
210			'where' => ['expressionid' => $expressionId]
211		]);
212	}
213	unset($expression);
214}
215
216function deleteRegexpExpressions(array $expressionIds) {
217	DB::delete('expressions', ['expressionid' => $expressionIds]);
218}
219
220function expression_type2str($type = null) {
221	$types = [
222		EXPRESSION_TYPE_INCLUDED => _('Character string included'),
223		EXPRESSION_TYPE_ANY_INCLUDED => _('Any character string included'),
224		EXPRESSION_TYPE_NOT_INCLUDED => _('Character string not included'),
225		EXPRESSION_TYPE_TRUE => _('Result is TRUE'),
226		EXPRESSION_TYPE_FALSE => _('Result is FALSE')
227	];
228
229	if ($type === null) {
230		return $types;
231	}
232	elseif (isset($types[$type])) {
233		return $types[$type];
234	}
235	else {
236		return _('Unknown');
237	}
238}
239
240function expressionDelimiters() {
241	return [
242		',' => ',',
243		'.' => '.',
244		'/' => '/'
245	];
246}
247