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
22class CControllerPopupTriggerExpr extends CController {
23	private $metrics = [];
24	private $param1SecCount = [];
25	private $param1Sec = [];
26	private $param1Str = [];
27	private $param2SecCount = [];
28	private $param2SecMode = [];
29	private $param3SecVal = [];
30	private $param3SecPercent = [];
31	private $paramSecIntCount = [];
32	private $paramForecast = [];
33	private $paramTimeleft = [];
34	private $allowedTypesAny = [];
35	private $allowedTypesNumeric = [];
36	private $allowedTypesStr = [];
37	private $allowedTypesLog = [];
38	private $allowedTypesInt = [];
39	private $functions = [];
40	private $operators = [];
41
42	protected function init() {
43		$this->disableSIDvalidation();
44
45		$this->metrics = [
46			PARAM_TYPE_TIME => _('Time'),
47			PARAM_TYPE_COUNTS => _('Count')
48		];
49
50		/*
51		 * C - caption
52		 * T - type
53		 * M - metrics
54		 * A - asterisk
55		 */
56		$this->param1SecCount = [
57			'last' => [
58				'C' => _('Last of').' (T)',
59				'T' => T_ZBX_INT,
60				'M' => $this->metrics,
61				'A' => true
62			],
63			'shift' => [
64				'C' => _('Time shift'),
65				'T' => T_ZBX_INT,
66				'A' => false
67			]
68		];
69
70		$this->param1Sec = [
71			'last' => [
72				'C' => _('Last of').' (T)',
73				'T' => T_ZBX_INT,
74				'A' => true
75			]
76		];
77
78		$this->param1Str = [
79			'pattern' => [
80				'C' => 'T',
81				'T' => T_ZBX_STR,
82				'A' => false
83			]
84		];
85
86		$this->param2SecCount = [
87			'pattern' => [
88				'C' => 'V',
89				'T' => T_ZBX_STR,
90				'A' => false
91			],
92			'last' => [
93				'C' => _('Last of').' (T)',
94				'T' => T_ZBX_INT,
95				'M' => $this->metrics,
96				'A' => false
97			]
98		];
99
100		$this->param2SecMode = [
101			'last' => [
102				'C' => _('Last of').' (T)',
103				'T' => T_ZBX_INT,
104				'A' => true
105			],
106			'mode' => [
107				'C' => 'Mode',
108				'T' => T_ZBX_STR,
109				'A' => false
110			]
111		];
112
113		$this->param3SecVal = [
114			'last' => [
115				'C' => _('Last of').' (T)',
116				'T' => T_ZBX_INT,
117				'M' => $this->metrics,
118				'A' => true
119			],
120			'v' => [
121				'C' => 'V',
122				'T' => T_ZBX_STR,
123				'A' => false
124			],
125			'o' => [
126				'C' => 'O',
127				'T' => T_ZBX_STR,
128				'A' => false
129			],
130			'shift' => [
131				'C' => _('Time shift'),
132				'T' => T_ZBX_INT,
133				'A' => false
134			]
135		];
136
137		$this->param3SecPercent = [
138			'last' => [
139				'C' => _('Last of').' (T)',
140				'T' => T_ZBX_INT,
141				'M' => $this->metrics,
142				'A' => true
143			],
144			'shift' => [
145				'C' => _('Time shift'),
146				'T' => T_ZBX_INT,
147				'A' => false
148			],
149			'p' => [
150				'C' => _('Percentage').' (P)',
151				'T' => T_ZBX_DBL,
152				'A' => true
153			]
154		];
155
156		$this->paramSecIntCount = [
157			'last' => [
158				'C' => _('Last of').' (T)',
159				'T' => T_ZBX_INT,
160				'M' => $this->metrics,
161				'A' => true
162			],
163			'mask' => [
164				'C' => _('Mask'),
165				'T' => T_ZBX_STR,
166				'A' => true
167			],
168			'shift' => [
169				'C' => _('Time shift'),
170				'T' => T_ZBX_INT,
171				'A' => false
172			]
173		];
174
175		$this->paramForecast = [
176			'last' => [
177				'C' => _('Last of').' (T)',
178				'T' => T_ZBX_INT,
179				'M' => $this->metrics,
180				'A' => true
181			],
182			'shift' => [
183				'C' => _('Time shift'),
184				'T' => T_ZBX_INT,
185				'A' => false
186			],
187			'time' => [
188				'C' => _('Time').' (t)',
189				'T' => T_ZBX_INT,
190				'A' => true
191			],
192			'fit' => [
193				'C' => _('Fit'),
194				'T' => T_ZBX_STR,
195				'A' => false
196			],
197			'mode' => [
198				'C' => _('Mode'),
199				'T' => T_ZBX_STR,
200				'A' => false
201			]
202		];
203
204		$this->paramTimeleft = [
205			'last' => [
206				'C' => _('Last of').' (T)',
207				'T' => T_ZBX_INT,
208				'M' => $this->metrics,
209				'A' => true
210			],
211			'shift' => [
212				'C' => _('Time shift'),
213				'T' => T_ZBX_INT,
214				'A' => false
215			],
216			't' => [
217				'C' => _('Threshold'),
218				'T' => T_ZBX_DBL,
219				'A' => true
220			],
221			'fit' => [
222				'C' => _('Fit'),
223				'T' => T_ZBX_STR,
224				'A' => false
225			]
226		];
227
228		$this->allowedTypesAny = [
229			ITEM_VALUE_TYPE_FLOAT => 1,
230			ITEM_VALUE_TYPE_STR => 1,
231			ITEM_VALUE_TYPE_LOG => 1,
232			ITEM_VALUE_TYPE_UINT64 => 1,
233			ITEM_VALUE_TYPE_TEXT => 1
234		];
235
236		$this->allowedTypesNumeric = [
237			ITEM_VALUE_TYPE_FLOAT => 1,
238			ITEM_VALUE_TYPE_UINT64 => 1
239		];
240
241		$this->allowedTypesStr = [
242			ITEM_VALUE_TYPE_STR => 1,
243			ITEM_VALUE_TYPE_LOG => 1,
244			ITEM_VALUE_TYPE_TEXT => 1
245		];
246
247		$this->allowedTypesLog = [
248			ITEM_VALUE_TYPE_LOG => 1
249		];
250
251		$this->allowedTypesInt = [
252			ITEM_VALUE_TYPE_UINT64 => 1
253		];
254
255		$this->functions = [
256			'abschange' => [
257				'description' => _('abschange() - Absolute difference between last and previous value'),
258				'allowed_types' => $this->allowedTypesAny,
259				'operators' => ['=', '<>', '>', '<', '>=', '<=']
260			],
261			'avg' => [
262				'description' => _('avg() - Average value of a period T'),
263				'params' => $this->param1SecCount,
264				'allowed_types' => $this->allowedTypesNumeric,
265				'operators' => ['=', '<>', '>', '<', '>=', '<=']
266			],
267			'delta' => [
268				'description' => _('delta() - Difference between MAX and MIN value of a period T'),
269				'params' => $this->param1SecCount,
270				'allowed_types' => $this->allowedTypesNumeric,
271				'operators' => ['=', '<>', '>', '<', '>=', '<=']
272			],
273			'change' => [
274				'description' => _('change() - Difference between last and previous value'),
275				'allowed_types' => $this->allowedTypesAny,
276				'operators' => ['=', '<>', '>', '<', '>=', '<=']
277			],
278			'count' => [
279				'description' => _('count() - Number of successfully retrieved values V (which fulfill operator O) for period T'),
280				'params' => $this->param3SecVal,
281				'allowed_types' => $this->allowedTypesAny,
282				'operators' => ['=', '<>', '>', '<', '>=', '<=']
283			],
284			'diff' => [
285				'description' => _('diff() - Difference between last and preceding values (1 - true, 0 - false)'),
286				'allowed_types' => $this->allowedTypesAny,
287				'operators' => ['=', '<>']
288			],
289			'last' => [
290				'description' => _('last() - Last (most recent) T value'),
291				'params' => $this->param1SecCount,
292				'allowed_types' => $this->allowedTypesAny,
293				'operators' => ['=', '<>', '>', '<', '>=', '<=']
294			],
295			'max' => [
296				'description' => _('max() - Maximum value for period T'),
297				'params' => $this->param1SecCount,
298				'allowed_types' => $this->allowedTypesNumeric,
299				'operators' => ['=', '<>', '>', '<', '>=', '<=']
300			],
301			'min' => [
302				'description' => _('min() - Minimum value for period T'),
303				'params' => $this->param1SecCount,
304				'allowed_types' => $this->allowedTypesNumeric,
305				'operators' => ['=', '<>', '>', '<', '>=', '<=']
306			],
307			'percentile' => [
308				'description' => _('percentile() - Percentile P of a period T'),
309				'params' => $this->param3SecPercent,
310				'allowed_types' => $this->allowedTypesNumeric,
311				'operators' => ['=', '<>', '>', '<', '>=', '<=']
312			],
313			'prev' => [
314				'description' => _('prev() - Previous value'),
315				'allowed_types' => $this->allowedTypesAny,
316				'operators' => ['=', '<>', '>', '<', '>=', '<=']
317			],
318			'str' => [
319				'description' => _('str() - Find string V in last (most recent) value (1 - found, 0 - not found)'),
320				'params' => $this->param2SecCount,
321				'allowed_types' => $this->allowedTypesStr,
322				'operators' => ['=', '<>']
323			],
324			'strlen' => [
325				'description' => _('strlen() - Length of last (most recent) T value in characters'),
326				'params' => $this->param1SecCount,
327				'allowed_types' => $this->allowedTypesStr,
328				'operators' => ['=', '<>', '>', '<', '>=', '<=']
329			],
330			'sum' => [
331				'description' => _('sum() - Sum of values of a period T'),
332				'params' => $this->param1SecCount,
333				'allowed_types' => $this->allowedTypesNumeric,
334				'operators' => ['=', '<>', '>', '<', '>=', '<=']
335			],
336			'date' => [
337				'description' => _('date() - Current date'),
338				'allowed_types' => $this->allowedTypesAny,
339				'operators' => ['=', '<>', '>', '<', '>=', '<=']
340			],
341			'dayofweek' => [
342				'description' => _('dayofweek() - Day of week'),
343				'allowed_types' => $this->allowedTypesAny,
344				'operators' => ['=', '<>', '>', '<', '>=', '<=']
345			],
346			'dayofmonth' => [
347				'description' => _('dayofmonth() - Day of month'),
348				'allowed_types' => $this->allowedTypesAny,
349				'operators' => ['=', '<>', '>', '<', '>=', '<=']
350			],
351			'fuzzytime' => [
352				'description' => _('fuzzytime() - Difference between item value (as timestamp) and Zabbix server timestamp is less than or equal to T seconds (1 - true, 0 - false)'),
353				'params' => $this->param1Sec,
354				'allowed_types' => $this->allowedTypesNumeric,
355				'operators' => ['=', '<>']
356			],
357			'regexp' => [
358				'description' => _('regexp() - Regular expression V matching last value in period T (1 - match, 0 - no match)'),
359				'params' => $this->param2SecCount,
360				'allowed_types' => $this->allowedTypesStr,
361				'operators' => ['=', '<>']
362			],
363			'iregexp' => [
364				'description' => _('iregexp() - Regular expression V matching last value in period T (non case-sensitive; 1 - match, 0 - no match)'),
365				'params' => $this->param2SecCount,
366				'allowed_types' => $this->allowedTypesStr,
367				'operators' => ['=', '<>']
368			],
369			'logeventid' => [
370				'description' => _('logeventid() - Event ID of last log entry matching regular expression T (1 - match, 0 - no match)'),
371				'params' => $this->param1Str,
372				'allowed_types' => $this->allowedTypesLog,
373				'operators' => ['=', '<>']
374			],
375			'logseverity' => [
376				'description' => _('logseverity() - Log severity of the last log entry'),
377				'allowed_types' => $this->allowedTypesLog,
378				'operators' => ['=', '<>', '>', '<', '>=', '<=']
379			],
380			'logsource' => [
381				'description' => _('logsource() - Log source of the last log entry matching parameter T (1 - match, 0 - no match)'),
382				'params' => $this->param1Str,
383				'allowed_types' => $this->allowedTypesLog,
384				'operators' => ['=', '<>']
385			],
386			'now' => [
387				'description' => _('now() - Number of seconds since the Epoch'),
388				'allowed_types' => $this->allowedTypesAny,
389				'operators' => ['=', '<>', '>', '<', '>=', '<=']
390			],
391			'time' => [
392				'description' => _('time() - Current time'),
393				'allowed_types' => $this->allowedTypesAny,
394				'operators' => ['=', '<>', '>', '<', '>=', '<=']
395			],
396			'nodata' => [
397				'description' => _('nodata() - No data received during period of time T (1 - true, 0 - false), Mode (strict - ignore proxy time delay in sending data)'),
398				'params' => $this->param2SecMode,
399				'allowed_types' => $this->allowedTypesAny,
400				'operators' => ['=', '<>']
401			],
402			'band' => [
403				'description' => _('band() - Bitwise AND of last (most recent) T value and mask'),
404				'params' => $this->paramSecIntCount,
405				'allowed_types' => $this->allowedTypesInt,
406				'operators' => ['=', '<>']
407			],
408			'forecast' => [
409				'description' => _('forecast() - Forecast for next t seconds based on period T'),
410				'params' => $this->paramForecast,
411				'allowed_types' => $this->allowedTypesNumeric,
412				'operators' => ['=', '<>', '>', '<', '>=', '<=']
413			],
414			'timeleft' => [
415				'description' => _('timeleft() - Time to reach threshold estimated based on period T'),
416				'params' => $this->paramTimeleft,
417				'allowed_types' => $this->allowedTypesNumeric,
418				'operators' => ['=', '<>', '>', '<', '>=', '<=']
419			]
420		];
421
422		CArrayHelper::sort($this->functions, ['description']);
423
424		foreach ($this->functions as $function) {
425			foreach ($function['operators'] as $operator) {
426				$this->operators[$operator] = true;
427			}
428		}
429	}
430
431	protected function checkInput() {
432		$fields = [
433			'dstfrm' =>				'string|fatal',
434			'dstfld1' =>			'string|not_empty',
435			'expression' =>			'string',
436			'itemid' =>				'db items.itemid',
437			'parent_discoveryid' =>	'db items.itemid',
438			'function' =>			'in '.implode(',', array_keys($this->functions)),
439			'operator' =>			'in '.implode(',', array_keys($this->operators)),
440			'params' =>				'',
441			'paramtype' =>			'in '.implode(',', [PARAM_TYPE_TIME, PARAM_TYPE_COUNTS]),
442			'value' =>				'string|not_empty',
443			'hostid' =>				'db hosts.hostid',
444			'groupid' =>			'db hosts_groups.hostgroupid',
445			'add' =>				'in 1'
446		];
447
448		$ret = $this->validateInput($fields);
449
450		if (!$ret) {
451			$output = [];
452			if (($messages = getMessages()) !== null) {
453				$output['errors'] = $messages->toString();
454			}
455
456			if ($this->hasInput('add')) {
457				$this->setResponse(
458					(new CControllerResponseData(['main_block' => json_encode($output)]))->disableView()
459				);
460			}
461			else {
462				$ret = true;
463			}
464		}
465
466		return $ret;
467	}
468
469	protected function checkPermissions() {
470		return true;
471	}
472
473	protected function doAction() {
474		$itemid = $this->getInput('itemid', 0);
475		$function = $this->getInput('function', 'last');
476		$operator = $this->getInput('operator', '=');
477		$param_type = $this->getInput('paramtype', PARAM_TYPE_TIME);
478		$dstfld1 = $this->getInput('dstfld1');
479		$expression = $this->getInput('expression', '');
480		$params = $this->getInput('params', []);
481		$value = $this->getInput('value', 0);
482
483		// Opening the popup when editing an expression in the trigger constructor.
484		if (($dstfld1 === 'expr_temp' || $dstfld1 === 'recovery_expr_temp') && $expression !== '') {
485			$expression = utf8RawUrlDecode($expression);
486
487			$expression_data = new CTriggerExpression();
488			$result = $expression_data->parse($expression);
489
490			if ($result) {
491				$function_macro_tokens = $result->getTokensByType(
492					CTriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO
493				);
494
495				if ($function_macro_tokens) {
496					$function_macro_token = $function_macro_tokens[0];
497					$function = $function_macro_token['data']['functionName'];
498
499					// Determine param type.
500					$params = $function_macro_token['data']['functionParams'];
501					$param_number = in_array($function, ['regexp', 'iregexp', 'str']) ? 1 : 0;
502					if (array_key_exists($param_number, $params) && is_string($params[$param_number])
503							&& $params[$param_number] !== '' && $params[$param_number][0] === '#'
504							&& !in_array($function, ['fuzzytime', 'nodata'])) {
505						$param_type = PARAM_TYPE_COUNTS;
506						$params[$param_number] = substr($params[$param_number], 1);
507					}
508					else {
509						$param_type = PARAM_TYPE_TIME;
510					}
511
512					/*
513					 * Try to find an operator and a value.
514					 * The value and operator can be extracted only if they immediately follow the item function macro.
515					 */
516					$tokens = $result->getTokens();
517					foreach ($tokens as $key => $token) {
518						if ($token['type'] == CTriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO) {
519							if (array_key_exists($key + 2, $tokens)
520									&& $tokens[$key + 1]['type'] == CTriggerExprParserResult::TOKEN_TYPE_OPERATOR
521									&& array_key_exists($function, $this->functions)
522									&& in_array($tokens[$key + 1]['value'],
523										$this->functions[$function]['operators'])) {
524								$operator = $tokens[$key + 1]['value'];
525
526								$value = '';
527								$i = 2;
528
529								if (array_key_exists($key + 3, $tokens)
530										&& $tokens[$key + 2]['type'] == CTriggerExprParserResult::TOKEN_TYPE_OPERATOR) {
531									$value .= $tokens[$key + 2]['value'];
532									$i++;
533								}
534
535								$value .= ($tokens[$key + $i]['type'] == CTriggerExprParserResult::TOKEN_TYPE_STRING)
536									? $tokens[$key + $i]['data']['string']
537									: $tokens[$key + $i]['value'];
538							}
539							else {
540								break;
541							}
542						}
543					}
544
545					// Find the item.
546					$item = API::Item()->get([
547						'output' => ['itemid', 'hostid', 'name', 'key_', 'value_type'],
548						'selectHosts' => ['name'],
549						'webitems' => true,
550						'filter' => [
551							'host' => $function_macro_token['data']['host'],
552							'key_' => $function_macro_token['data']['item'],
553							'flags' => null
554						]
555					]);
556
557					if (($item = reset($item)) !== false) {
558						$itemid = $item['itemid'];
559					}
560					else {
561						error(_('Unknown host item, no such item in selected host'));
562					}
563				}
564			}
565		}
566		// Opening an empty form or switching a function.
567		else {
568			$item = API::Item()->get([
569				'output' => ['itemid', 'hostid', 'name', 'key_', 'value_type'],
570				'selectHosts' => ['host', 'name'],
571				'itemids' => $itemid,
572				'webitems' => true,
573				'filter' => ['flags' => null]
574			]);
575
576			$item = reset($item);
577		}
578
579		if ($itemid) {
580			$items = CMacrosResolverHelper::resolveItemNames([$item]);
581			$item = $items[0];
582
583			$item_value_type = $item['value_type'];
584			$item_key = $item['key_'];
585			$item_host_data = reset($item['hosts']);
586			$description = $item_host_data['name'].NAME_DELIMITER.$item['name_expanded'];
587		}
588		else {
589			$item_key = '';
590			$description = '';
591			$item_value_type = null;
592		}
593
594		if ($param_type === null && array_key_exists($function, $this->functions)
595				&& array_key_exists('params', $this->functions[$function])
596				&& array_key_exists('M', $this->functions[$function]['params'])) {
597			$param_type = is_array($this->functions[$function]['params']['M'])
598				? reset($this->functions[$function]['params']['M'])
599				: $this->functions[$function]['params']['M'];
600		}
601		elseif ($param_type === null) {
602			$param_type = PARAM_TYPE_TIME;
603		}
604
605		$data = [
606			'parent_discoveryid' => $this->getInput('parent_discoveryid', ''),
607			'dstfrm' => $this->getInput('dstfrm'),
608			'dstfld1' => $dstfld1,
609			'itemid' => $itemid,
610			'value' => $value,
611			'params' => $params,
612			'paramtype' => $param_type,
613			'item_description' => $description,
614			'functions' => $this->functions,
615			'function' => $function,
616			'operator' => $operator,
617			'item_key' => $item_key,
618			'itemValueType' => $item_value_type,
619			'selectedFunction' => null,
620			'groupid' => $this->getInput('groupid', 0),
621			'hostid' => $this->getInput('hostid', 0)
622		];
623
624		// Check if submitted function is usable with selected item.
625		foreach ($data['functions'] as $id => $f) {
626			if (($data['itemValueType'] === null || array_key_exists($item_value_type, $f['allowed_types']))
627					&& $id === $function) {
628				$data['selectedFunction'] = $id;
629				break;
630			}
631		}
632
633		if ($data['selectedFunction'] === null) {
634			$data['selectedFunction'] = 'last';
635			$data['function'] = 'last';
636		}
637
638		// Remove functions that not correspond to chosen item.
639		foreach ($data['functions'] as $id => $f) {
640			if ($data['itemValueType'] !== null && !array_key_exists($data['itemValueType'], $f['allowed_types'])) {
641				unset($data['functions'][$id]);
642
643				// Take first available function from list and change to first available operator for that function.
644				if ($id === $data['function']) {
645					$data['function'] = key($data['functions']);
646					$data['operator'] = reset($data['functions'][$data['function']]['operators']);
647				}
648			}
649		}
650
651		// Create and validate trigger expression before inserting it into textarea field.
652		if ($this->getInput('add', false)) {
653			try {
654				if ($data['item_description']) {
655					if ($data['paramtype'] == PARAM_TYPE_COUNTS
656							&& array_key_exists('last', $data['params'])
657							&& $data['params']['last'] !== '') {
658						$data['params']['last'] = zbx_is_int($data['params']['last'])
659							? '#'.$data['params']['last']
660							: $data['params']['last'];
661					}
662					elseif ($data['paramtype'] == PARAM_TYPE_TIME && in_array($function, ['last', 'band', 'strlen'])) {
663						$data['params']['last'] = '';
664					}
665
666					// Quote function param.
667					$quoted_params = [];
668					foreach ($data['params'] as $param) {
669						$quoted_params[] = quoteFunctionParam($param);
670					}
671
672					$data['expression'] = sprintf('{%s:%s.%s(%s)}%s%s',
673						$item_host_data['host'],
674						$data['item_key'],
675						$function,
676						rtrim(implode(',', $quoted_params), ','),
677						$operator,
678						CTriggerExpression::quoteString($data['value'])
679					);
680
681					// Validate trigger expression.
682					$trigger_expression = new CTriggerExpression();
683
684					if ($trigger_expression->parse($data['expression'])) {
685						$expression_data = reset($trigger_expression->expressions);
686
687						// Validate trigger function.
688						$trigger_function_validator = new CFunctionValidator();
689						$is_valid = $trigger_function_validator->validate([
690							'function' => $expression_data['function'],
691							'functionName' => $expression_data['functionName'],
692							'functionParamList' => $expression_data['functionParamList'],
693							'valueType' => $data['itemValueType']
694						]);
695
696						if ($is_valid === false) {
697							error($trigger_function_validator->getError());
698						}
699					}
700					else {
701						error($trigger_expression->error);
702					}
703
704					// Quote function param.
705					if (array_key_exists('insert', $data)) {
706						foreach ($data['params'] as $pnum => $param) {
707							$data['params'][$pnum] = quoteFunctionParam($param);
708						}
709					}
710				}
711				else {
712					error(_('Item not selected'));
713				}
714			}
715			catch (Exception $e) {
716				error($e->getMessage());
717				error(_('Cannot insert trigger expression'));
718			}
719
720			if (($messages = getMessages()) !== null) {
721				$output = [
722					'errors' => $messages->toString()
723				];
724			}
725			else {
726				$output = [
727					'expression' => $data['expression'],
728					'dstfld1' => $data['dstfld1'],
729					'dstfrm' => $data['dstfrm']
730				];
731			}
732
733			$this->setResponse(
734				(new CControllerResponseData(['main_block' => json_encode($output)]))->disableView()
735			);
736		}
737		else {
738			$this->setResponse(new CControllerResponseData(
739				$data + [
740					'title' => _('Condition'),
741					'errors' => hasErrorMesssages() ? getMessages() : null,
742					'user' => [
743						'debug_mode' => $this->getDebugMode()
744					]
745				]
746			));
747		}
748	}
749}
750