1<?php declare(strict_types=1);
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 CControllerActionOperationGet extends CController {
23
24	protected function checkInput() {
25		$fields = [
26			'eventsource'   => 'required|in '.implode(',', [EVENT_SOURCE_TRIGGERS, EVENT_SOURCE_DISCOVERY, EVENT_SOURCE_AUTOREGISTRATION, EVENT_SOURCE_INTERNAL]),
27			'recovery'      => 'required|in '.implode(',', [ACTION_OPERATION, ACTION_RECOVERY_OPERATION, ACTION_ACKNOWLEDGE_OPERATION]),
28			'actionid'      => 'db actions.actionid',
29			'operation'     => 'array'
30		];
31
32		$ret = $this->validateInput($fields) && $this->validateInputConstraints();
33
34		if (!$ret) {
35			$output = [];
36			if (($messages = getMessages()) !== null) {
37				$output['errors'] = $messages->toString();
38			}
39
40			$this->setResponse(new CControllerResponseData(['main_block' => json_encode($output)]));
41		}
42
43		return $ret;
44	}
45
46	protected function validateInputConstraints(): bool {
47		$eventsource = $this->getInput('eventsource');
48		$recovery = $this->getInput('recovery');
49
50		$allowed_operations = getAllowedOperations($eventsource);
51
52		if (!array_key_exists($recovery, $allowed_operations)) {
53			error(_('Unsupported operation.'));
54			return false;
55		}
56
57		return true;
58	}
59
60	protected function checkPermissions() {
61		if ($this->getUserType() >= USER_TYPE_ZABBIX_ADMIN) {
62			if (!$this->getInput('actionid', '0')) {
63				return true;
64			}
65
66			return (bool) API::Action()->get([
67				'output' => [],
68				'actionids' => $this->getInput('actionid'),
69				'editable' => true
70			]);
71		}
72
73		return false;
74	}
75
76	protected function doAction() {
77		$operation = $this->getInput('operation', []) + $this->defaultOperationObject();
78
79		$eventsource = (int) $this->getInput('eventsource');
80		$recovery = (int) $this->getInput('recovery');
81
82		$data = [
83			'popup_config' => $this->popupConfig($operation, $eventsource, $recovery),
84			'debug' => null
85		];
86
87		if ($this->getDebugMode() == GROUP_DEBUG_MODE_ENABLED) {
88			CProfiler::getInstance()->stop();
89			$data['debug'] = CProfiler::getInstance()->make()->toString();
90		}
91
92		$this->setResponse(new CControllerResponseData(['main_block' => json_encode($data)]));
93	}
94
95	/**
96	 * Returns empty default operation object.
97	 *
98	 * @return array
99	 */
100	private function defaultOperationObject(): array {
101		return [
102			'opmessage_usr' => [],
103			'opmessage_grp' => [],
104			'opmessage' => [
105				'subject' => '',
106				'message' => '',
107				'mediatypeid' => '0',
108				'default_msg' => '1'
109			],
110			'operationtype' => '0',
111			'esc_step_from' => '1',
112			'esc_step_to' => '1',
113			'esc_period' => '0',
114			'opcommand_hst' => [],
115			'opcommand_grp' => [],
116			'evaltype' => (string) CONDITION_EVAL_TYPE_AND_OR,
117			'opconditions' => [],
118			'opgroup' => [],
119			'optemplate' => [],
120			'opinventory' => [
121				'inventory_mode' => (string) HOST_INVENTORY_MANUAL
122			],
123			'opcommand' => [
124				'type' => (string) ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT,
125				'scriptid' => '0',
126				'execute_on' => (string) ZBX_SCRIPT_EXECUTE_ON_AGENT,
127				'port' => '',
128				'authtype' => (string) ITEM_AUTHTYPE_PASSWORD,
129				'username' => '',
130				'password' => '',
131				'publickey' => '',
132				'privatekey' => '',
133				'command' => ''
134			]
135		];
136	}
137
138	/**
139	 * Transforms operation object into operation config object. Needed meta data is queried.
140	 *
141	 * @param array $operation  Operation object.
142	 * @param int $eventsource  Action event source.
143	 * @param int $recovery     Action event phase.
144	 *
145	 * @return array  Each of fields is nullable, meaning given operation may not have particular configuration domain.
146	 */
147	private function popupConfig(array $operation, int $eventsource, int $recovery): array {
148		$operation_type = $this->popupConfigOperationType($operation, $eventsource, $recovery);
149		$operation_steps = $this->popupConfigOperationSteps($operation, $eventsource, $recovery);
150		$operation_message = $this->popupConfigOperationMessage($operation, $eventsource);
151		$operation_command = $this->popupConfigOperationCommand($operation, $eventsource);
152		$operation_attr = $this->popupConfigOperationAttr($operation, $eventsource);
153		$operation_condition = $this->popupConfigOperationCondition($operation, $eventsource, $recovery);
154
155		return [
156			'operation_type' => $operation_type,
157			'operation_steps' => $operation_steps,
158			'operation_message' => $operation_message,
159			'operation_command' => $operation_command,
160			'operation_attr' => $operation_attr,
161			'operation_condition' => $operation_condition
162		];
163	}
164
165	/**
166	 * Returns "operation type" configuration fields for given operation in given source.
167	 *
168	 * @param array $operation  Operation object.
169	 * @param int $eventsource  Action event source.
170	 * @param int $recovery     Action event phase.
171	 *
172	 * @return array
173	 */
174	private function popupConfigOperationType(array $operation, int $eventsource, int $recovery): array {
175		$operation_type_options = [];
176
177		foreach (getAllowedOperations($eventsource)[$recovery] as $operation_type) {
178			$operation_type_options[] = [
179				'value' => $operation_type,
180				'name' => operation_type2str($operation_type)
181			];
182		}
183
184		return [
185			'options' => $operation_type_options,
186			'selected' => $operation['operationtype']
187		];
188	}
189
190	/**
191	 * Returns populated "escalation steps" domain configuration fields for given operation in given source.
192	 *
193	 * @param array $operation  Operation object.
194	 * @param int $eventsource  Action event source.
195	 * @param int $recovery     Action event phase.
196	 *
197	 * @return array|null
198	 */
199	private function popupConfigOperationSteps(array $operation, int $eventsource, int $recovery): ?array {
200		if ($eventsource == EVENT_SOURCE_TRIGGERS || $eventsource == EVENT_SOURCE_INTERNAL) {
201			if ($recovery == ACTION_OPERATION) {
202				return [
203					'from' => $operation['esc_step_from'],
204					'to' => $operation['esc_step_to'],
205					'duration' => $operation['esc_period']
206				];
207			}
208		}
209
210		return null;
211	}
212
213	/**
214	 * Returns populated "message" domain configuration fields for given operation in given source.
215	 *
216	 * @param array $operation  Operation object.
217	 * @param int $eventsource  Action event source.
218	 *
219	 * @return array|null
220	 */
221	private function popupConfigOperationMessage(array $operation, int $eventsource): ?array {
222		if ($eventsource == EVENT_SOURCE_TRIGGERS || $eventsource == EVENT_SOURCE_DISCOVERY
223				|| $eventsource == EVENT_SOURCE_AUTOREGISTRATION || $eventsource == EVENT_SOURCE_INTERNAL) {
224			$usergroups = [];
225			if ($operation['opmessage_grp']) {
226				$usergroups = API::UserGroup()->get([
227					'output' => ['usergroupid', 'name'],
228					'usrgrpids' => array_column($operation['opmessage_grp'], 'usrgrpid')
229				]);
230			}
231
232			$users = [];
233			if ($operation['opmessage_usr']) {
234				$db_users = API::User()->get([
235					'output' => ['userid', 'alias', 'name', 'surname'],
236					'userids' => array_column($operation['opmessage_usr'], 'userid')
237				]);
238				CArrayHelper::sort($db_users, ['alias']);
239
240				foreach ($db_users as $db_user) {
241					$users[] = [
242						'id' => $db_user['userid'],
243						'name' => getUserFullname($db_user)
244					];
245				}
246			}
247
248			$mediatypes = API::MediaType()->get(['output' => ['mediatypeid', 'name', 'status']]);
249			CArrayHelper::sort($mediatypes, ['name']);
250			$mediatypes = array_values($mediatypes);
251
252			return [
253				'custom_message' => $operation['opmessage']['default_msg'] === '0',
254				'subject' => $operation['opmessage']['subject'],
255				'body' => $operation['opmessage']['message'],
256				'mediatypeid' => $operation['opmessage']['mediatypeid'],
257				'mediatypes' => $mediatypes,
258				'usergroups' => $usergroups,
259				'users' => $users
260			];
261		}
262
263		return null;
264	}
265
266	/**
267	 * Returns populated "command" domain configuration fields for given operation in given context (eventsource).
268	 *
269	 * @param array $operation  Operation object.
270	 * @param int $eventsource  Action event source.
271	 *
272	 * @return array|null
273	 */
274	private function popupConfigOperationCommand(array $operation, int $eventsource): ?array {
275		if ($eventsource == EVENT_SOURCE_TRIGGERS || $eventsource == EVENT_SOURCE_DISCOVERY
276				|| $eventsource == EVENT_SOURCE_AUTOREGISTRATION) {
277			$current_host = false;
278
279			$hostids = [];
280			foreach ($operation['opcommand_hst'] as $hostid) {
281				if ($hostid == '0') {
282					$current_host = true;
283				}
284				else {
285					$hostids[] = $hostid;
286				}
287			}
288
289			$operation_command = [
290				'type' => $operation['opcommand']['type'],
291				'current_host' => $current_host,
292				'execute_on' => $operation['opcommand']['execute_on'],
293				'command' => $operation['opcommand']['command'],
294				'username' => $operation['opcommand']['username'],
295				'password' => $operation['opcommand']['password'],
296				'privatekey' => $operation['opcommand']['privatekey'],
297				'publickey' => $operation['opcommand']['publickey'],
298				'port' => $operation['opcommand']['port'],
299				'authtype' => $operation['opcommand']['authtype'],
300				'global_script' => ['scriptid' => '0', 'name' => ''],
301				'hosts' => [],
302				'groups' => []
303			];
304
305			if ($operation['opcommand']['scriptid']) {
306				$db_scrpt = API::Script()->get([
307					'output' => ['name', 'scriptid'],
308					'scriptids' => (array) $operation['opcommand']['scriptid']
309				]);
310
311				if ($db_scrpt) {
312					$operation_command['global_script'] = $db_scrpt[0];
313				}
314			}
315
316			if ($hostids) {
317				$operation_command['hosts'] = CArrayHelper::renameObjectsKeys(API::Host()->get([
318					'output' => ['hostid', 'name'],
319					'hostids' => $hostids
320				]), ['hostid' => 'id']);
321			}
322
323			if ($operation['opcommand_grp']) {
324				$operation_command['groups'] = CArrayHelper::renameObjectsKeys(API::HostGroup()->get([
325					'output' => ['groupid', 'name'],
326					'groupids' => $operation['opcommand_grp']
327				]), ['groupid' => 'id']);
328			}
329
330			return $operation_command;
331		}
332
333		return null;
334	}
335
336	/**
337	 * Returns populated "attributes" domain configuration fields for given operation in given source.
338	 *
339	 * @param array $operation  Operation object.
340	 * @param int $eventsource  Action event source.
341	 *
342	 * @return array|null
343	 */
344	private function popupConfigOperationAttr(array $operation, int $eventsource): ?array {
345		if ($eventsource == EVENT_SOURCE_DISCOVERY || $eventsource == EVENT_SOURCE_AUTOREGISTRATION) {
346			$operation_attr = [
347				'hostgroups' => [],
348				'templates' => []
349			];
350
351			if ($operation['opgroup']) {
352				$operation_attr['hostgroups'] = CArrayHelper::renameObjectsKeys(API::HostGroup()->get([
353					'output' => ['groupid', 'name'],
354					'groupids' => array_column($operation['opgroup'], 'groupid')
355				]), ['groupid' => 'id']);
356			}
357
358			if ($operation['optemplate']) {
359				$operation_attr['templates'] = CArrayHelper::renameObjectsKeys(API::Template()->get([
360					'output' => ['templateid', 'name'],
361					'templateids' => array_column($operation['optemplate'], 'templateid'),
362					'editable' => true
363				]), ['templateid' => 'id']);
364			}
365
366			if ($operation['opinventory']) {
367				$operation_attr['inventory_mode'] = $operation['opinventory']['inventory_mode'];
368			}
369
370			return $operation_attr;
371		}
372
373		return null;
374	}
375
376	/**
377	 * Returns populated "conditions" domain configuration fields for given operation in given source.
378	 *
379	 * @param array $operation  Operation object.
380	 * @param int $eventsource  Action event source.
381	 *
382	 * @return array|null
383	 */
384	private function popupConfigOperationCondition(array $operation, int $eventsource, int $recovery): ?array {
385		if ($eventsource == EVENT_SOURCE_TRIGGERS) {
386			if ($recovery == ACTION_OPERATION) {
387				$operation_condition = [
388					'conditions' => [],
389					'evaltype' => $operation['evaltype']
390				];
391
392				foreach ($operation['opconditions'] as $index => $opcondition) {
393					$name = getConditionDescription($opcondition['conditiontype'], $opcondition['operator'],
394						$opcondition['value'], ''
395					);
396
397					$operation_condition['conditions'][] = [
398						'formulaid' => num2letter($index),
399						'name' => $name,
400						'conditiontype' => $opcondition['conditiontype'],
401						'operator' => $opcondition['operator'],
402						'value' => $opcondition['value']
403					];
404				}
405
406				return $operation_condition;
407			}
408		}
409
410		return null;
411	}
412}
413