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 * Class containing methods for operations media types.
24 */
25class CMediatype extends CApiService {
26
27	protected $tableName = 'media_type';
28	protected $tableAlias = 'mt';
29	protected $sortColumns = ['mediatypeid'];
30
31	/**
32	 * Get Media types data
33	 *
34	 * @param array $options
35	 * @param array $options['mediatypeids'] filter by Mediatype IDs
36	 * @param boolean $options['type'] filter by Mediatype type [ USER_TYPE_ZABBIX_USER: 1, USER_TYPE_ZABBIX_ADMIN: 2, USER_TYPE_SUPER_ADMIN: 3 ]
37	 * @param boolean $options['output'] output only Mediatype IDs if not set.
38	 * @param boolean $options['count'] output only count of objects in result. ( result returned in property 'rowscount' )
39	 * @param string $options['pattern'] filter by Host name containing only give pattern
40	 * @param int $options['limit'] output will be limited to given number
41	 * @param string $options['sortfield'] output will be sorted by given property [ 'mediatypeid', 'alias' ]
42	 * @param string $options['sortorder'] output will be sorted in given order [ 'ASC', 'DESC' ]
43	 * @return array
44	 */
45	public function get($options = []) {
46		$result = [];
47
48		$sqlParts = [
49			'select'	=> ['media_type' => 'mt.mediatypeid'],
50			'from'		=> ['media_type' => 'media_type mt'],
51			'where'		=> [],
52			'group'		=> [],
53			'order'		=> [],
54			'limit'		=> null
55		];
56
57		$defOptions = [
58			'mediatypeids'				=> null,
59			'mediaids'					=> null,
60			'userids'					=> null,
61			'editable'					=> false,
62			// filter
63			'filter'					=> null,
64			'search'					=> null,
65			'searchByAny'				=> null,
66			'startSearch'				=> false,
67			'excludeSearch'				=> false,
68			'searchWildcardsEnabled'	=> null,
69			// output
70			'output'					=> API_OUTPUT_EXTEND,
71			'selectMessageTemplates'	=> null,
72			'selectUsers'				=> null,
73			'countOutput'				=> false,
74			'groupCount'				=> false,
75			'preservekeys'				=> false,
76			'sortfield'					=> '',
77			'sortorder'					=> '',
78			'limit'						=> null
79		];
80		$options = zbx_array_merge($defOptions, $options);
81
82		// permission check
83		if (self::$userData['type'] == USER_TYPE_SUPER_ADMIN) {
84		}
85		elseif (!$options['editable'] && self::$userData['type'] == USER_TYPE_ZABBIX_ADMIN) {
86		}
87		elseif ($options['editable'] || self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
88			return [];
89		}
90
91		// mediatypeids
92		if (!is_null($options['mediatypeids'])) {
93			zbx_value2array($options['mediatypeids']);
94			$sqlParts['where'][] = dbConditionInt('mt.mediatypeid', $options['mediatypeids']);
95		}
96
97		// mediaids
98		if (!is_null($options['mediaids'])) {
99			zbx_value2array($options['mediaids']);
100
101			$sqlParts['from']['media'] = 'media m';
102			$sqlParts['where'][] = dbConditionInt('m.mediaid', $options['mediaids']);
103			$sqlParts['where']['mmt'] = 'm.mediatypeid=mt.mediatypeid';
104		}
105
106		// userids
107		if (!is_null($options['userids'])) {
108			zbx_value2array($options['userids']);
109
110			$sqlParts['from']['media'] = 'media m';
111			$sqlParts['where'][] = dbConditionInt('m.userid', $options['userids']);
112			$sqlParts['where']['mmt'] = 'm.mediatypeid=mt.mediatypeid';
113		}
114
115		// filter
116		if (is_array($options['filter'])) {
117			$this->dbFilter('media_type mt', $options, $sqlParts);
118		}
119
120		// search
121		if (is_array($options['search'])) {
122			zbx_db_search('media_type mt', $options, $sqlParts);
123		}
124
125		// limit
126		if (zbx_ctype_digit($options['limit']) && $options['limit']) {
127			$sqlParts['limit'] = $options['limit'];
128		}
129
130		$sqlParts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts);
131		$sqlParts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts);
132		$res = DBselect(self::createSelectQueryFromParts($sqlParts), $sqlParts['limit']);
133		while ($mediatype = DBfetch($res)) {
134			if ($options['countOutput']) {
135				if ($options['groupCount']) {
136					$result[] = $mediatype;
137				}
138				else {
139					$result = $mediatype['rowscount'];
140				}
141			}
142			else {
143				$result[$mediatype['mediatypeid']] = $mediatype;
144			}
145		}
146
147		if ($options['countOutput']) {
148			return $result;
149		}
150
151		if ($result) {
152			$result = $this->addRelatedObjects($options, $result);
153		}
154
155		// removing keys (hash -> array)
156		if (!$options['preservekeys']) {
157			$result = zbx_cleanHashes($result);
158		}
159		return $result;
160	}
161
162	/**
163	 * Validates the input parameters for the create() method.
164	 *
165	 * @param array $mediatypes
166	 *
167	 * @throws APIException if the input is invalid.
168	 */
169	protected function validateCreate(array $mediatypes) {
170		if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
171			self::exception(ZBX_API_ERROR_PERMISSIONS, _('Only Super Admins can create media types.'));
172		}
173
174		if (!$mediatypes) {
175			self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.'));
176		}
177
178		$required_fields = ['type', 'name'];
179
180		foreach ($mediatypes as $mediatype) {
181			if (!is_array($mediatype)) {
182				self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
183			}
184
185			// Check required parameters.
186			$missing_keys = array_diff($required_fields, array_keys($mediatype));
187
188			if ($missing_keys) {
189				self::exception(ZBX_API_ERROR_PARAMETERS,
190					_s('Media type is missing parameters: %1$s', implode(', ', $missing_keys))
191				);
192			}
193			else {
194				foreach ($required_fields as $field) {
195					if ($mediatype[$field] === '' || $mediatype[$field] === null) {
196						self::exception(ZBX_API_ERROR_PARAMETERS,
197							_s('Field "%1$s" is missing a value for media type "%2$s".', $field, $mediatype['name'])
198						);
199					}
200				}
201			}
202		}
203
204		// Check for duplicate names.
205		$duplicate_name = CArrayHelper::findDuplicate($mediatypes, 'name');
206		if ($duplicate_name) {
207			self::exception(ZBX_API_ERROR_PARAMETERS, _s('Media type "%1$s" already exists.', $duplicate_name['name']));
208		}
209
210		$simple_interval_parser = new CSimpleIntervalParser();
211
212		foreach ($mediatypes as $i => $mediatype) {
213			// Check if media type already exists.
214			$db_mediatype = API::getApiService()->select('media_type', [
215				'output' => ['name'],
216				'filter' => ['name' => $mediatype['name']],
217				'limit' => 1
218			]);
219
220			if ($db_mediatype) {
221				self::exception(ZBX_API_ERROR_PARAMETERS, _s('Media type "%1$s" already exists.', $mediatype['name']));
222			}
223
224			// Check additional fields and values depending on media type.
225			$this->checkRequiredFieldsByType($mediatype);
226
227			switch ($mediatype['type']) {
228				case MEDIA_TYPE_EMAIL:
229					if (array_key_exists('smtp_authentication', $mediatype)) {
230						$smtp_authentication_validator = new CLimitedSetValidator([
231							'values' => [SMTP_AUTHENTICATION_NONE, SMTP_AUTHENTICATION_NORMAL]
232						]);
233
234						if (!$smtp_authentication_validator->validate($mediatype['smtp_authentication'])) {
235							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
236								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
237								$mediatype['smtp_authentication'],
238								'smtp_authentication',
239								$mediatype['name']
240							));
241						}
242					}
243
244					// Validate optional 'smtp_port' field.
245					if (array_key_exists('smtp_port', $mediatype) && !validatePortNumber($mediatype['smtp_port'])) {
246						self::exception(ZBX_API_ERROR_PARAMETERS, _s(
247							'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
248							$mediatype['smtp_port'],
249							'smtp_port',
250							$mediatype['name']
251						));
252					}
253
254					// Validate optional field 'smtp_security'.
255					if (array_key_exists('smtp_security', $mediatype)) {
256						$smtp_security_validator = new CLimitedSetValidator([
257							'values' => [
258								SMTP_CONNECTION_SECURITY_NONE,
259								SMTP_CONNECTION_SECURITY_STARTTLS,
260								SMTP_CONNECTION_SECURITY_SSL_TLS
261							]
262						]);
263
264						if (!$smtp_security_validator->validate($mediatype['smtp_security'])) {
265							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
266								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
267								$mediatype['smtp_security'],
268								'smtp_security',
269								$mediatype['name']
270							));
271						}
272					}
273
274					// Validate optional field 'smtp_verify_peer'.
275					if (array_key_exists('smtp_verify_peer', $mediatype)) {
276						$smtp_verify_peer_validator = new CLimitedSetValidator([
277							'values' => [0, 1]
278						]);
279
280						if (!$smtp_verify_peer_validator->validate($mediatype['smtp_verify_peer'])) {
281							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
282								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
283								$mediatype['smtp_verify_peer'],
284								'smtp_verify_peer',
285								$mediatype['name']
286							));
287						}
288					}
289
290					// Validate optional field 'smtp_verify_host'.
291					if (array_key_exists('smtp_verify_host', $mediatype)) {
292						$smtp_verify_host_validator = new CLimitedSetValidator([
293							'values' => [0, 1]
294						]);
295
296						if (!$smtp_verify_host_validator->validate($mediatype['smtp_verify_host'])) {
297							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
298								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
299								$mediatype['smtp_verify_host'],
300								'smtp_verify_host',
301								$mediatype['name']
302							));
303						}
304					}
305
306					// Validate optional field 'content_type'.
307					if (array_key_exists('content_type', $mediatype)) {
308						$content_type_validator = new CLimitedSetValidator([
309							'values' => [
310								SMTP_MESSAGE_FORMAT_PLAIN_TEXT,
311								SMTP_MESSAGE_FORMAT_HTML
312							]
313						]);
314
315						if (!$content_type_validator->validate($mediatype['content_type'])) {
316							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
317								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
318								$mediatype['content_type'],
319								'content_type',
320								$mediatype['name']
321							));
322						}
323					}
324					break;
325
326				case MEDIA_TYPE_EXEC:
327					if (array_key_exists('exec_params', $mediatype) && $mediatype['exec_params'] !== '') {
328						$pos = strrpos($mediatype['exec_params'], "\n");
329
330						if ($pos === false || strlen($mediatype['exec_params']) != $pos + 1) {
331							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
332								'Script parameters "%1$s" are missing the last new line feed for media type "%2$s".',
333								$mediatype['exec_params'],
334								$mediatype['name']
335							));
336						}
337					}
338					break;
339			}
340
341			$api_input_rules = $this->getValidationRules($mediatype['type'], 'create');
342			$validated_data = array_intersect_key($mediatype, $api_input_rules['fields']);
343
344			if (!CApiInputValidator::validate($api_input_rules, $validated_data, '/'.($i + 1), $error)) {
345				self::exception(ZBX_API_ERROR_PARAMETERS, $error);
346			}
347			$mediatype = $validated_data + $mediatype;
348
349			// Validate optional 'status' field.
350			if (array_key_exists('status', $mediatype)) {
351				$status_validator = new CLimitedSetValidator([
352					'values' => [MEDIA_TYPE_STATUS_ACTIVE, MEDIA_TYPE_STATUS_DISABLED]
353				]);
354
355				if (!$status_validator->validate($mediatype['status'])) {
356					self::exception(ZBX_API_ERROR_PARAMETERS, _s(
357						'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
358						$mediatype['status'],
359						'status',
360						$mediatype['name']
361					));
362				}
363			}
364
365			// Validate optional 'maxsessions' field.
366			if (array_key_exists('maxsessions', $mediatype)) {
367				if ($mediatype['maxsessions'] === '') {
368					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
369						'maxsessions', _('cannot be empty')
370					));
371				}
372
373				$min = ($mediatype['type'] == MEDIA_TYPE_SMS) ? 1 : 0;
374				$max = ($mediatype['type'] == MEDIA_TYPE_SMS) ? 1 : 100;
375
376				if (!ctype_digit((string) $mediatype['maxsessions']) || $mediatype['maxsessions'] > $max
377						|| $mediatype['maxsessions'] < $min) {
378					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
379						'maxsessions', _s('must be between "%1$s" and "%2$s"', $min, $max)
380					));
381				}
382			}
383
384			// Validate optional 'maxattempts' field.
385			if (array_key_exists('maxattempts', $mediatype)) {
386				if ($mediatype['maxattempts'] === '') {
387					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
388						'maxattempts', _('cannot be empty')
389					));
390				}
391
392				if (!ctype_digit((string) $mediatype['maxattempts']) || $mediatype['maxattempts'] > 100
393						|| $mediatype['maxattempts'] < 1) {
394					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
395						'maxattempts', _s('must be between "%1$s" and "%2$s"', 1, 100)
396					));
397				}
398			}
399
400			// Validate optional 'attempt_interval' field.
401			if (array_key_exists('attempt_interval', $mediatype)) {
402				if ($mediatype['attempt_interval'] === '') {
403					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
404						'attempt_interval', _('cannot be empty')
405					));
406				}
407
408				if ($simple_interval_parser->parse($mediatype['attempt_interval']) == CParser::PARSE_SUCCESS) {
409					$attempt_interval = timeUnitToSeconds($mediatype['attempt_interval']);
410
411					if ($attempt_interval < 0 || $attempt_interval > 3600) {
412						self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
413							'attempt_interval', _s('must be between "%1$s" and "%2$s"', 0, 3600)
414						));
415					}
416				}
417				else {
418					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
419						'attempt_interval', _s('must be between "%1$s" and "%2$s"', 0, 3600)
420					));
421				}
422			}
423		}
424	}
425
426	/**
427	 * Validates the input parameters for the update() method.
428	 *
429	 * @param array $mediatypes
430	 *
431	 * @throws APIException if the input is invalid.
432	 */
433	protected function validateUpdate(array $mediatypes) {
434		if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
435			self::exception(ZBX_API_ERROR_PERMISSIONS, _('Only Super Admins can edit media types.'));
436		}
437
438		if (!$mediatypes) {
439			self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.'));
440		}
441
442		// Validate given IDs.
443		$this->checkObjectIds($mediatypes, 'mediatypeid',
444			_('No "%1$s" given for media type.'),
445			_('Empty media type ID.'),
446			_('Incorrect media type ID.')
447		);
448
449		$mediatypeids = zbx_objectValues($mediatypes, 'mediatypeid');
450
451		// Check value map names.
452		$db_mediatypes = API::getApiService()->select('media_type', [
453			'output' => ['mediatypeid', 'type', 'name', 'exec_path', 'status', 'smtp_port', 'smtp_verify_peer',
454				'smtp_verify_host', 'smtp_authentication', 'maxsessions', 'maxattempts', 'attempt_interval',
455				'content_type', 'script', 'timeout', 'process_tags', 'show_event_menu', 'event_menu_url',
456				'event_menu_name'
457			],
458			'mediatypeids' => $mediatypeids,
459			'preservekeys' => true
460		]);
461
462		$check_names = [];
463		$simple_interval_parser = new CSimpleIntervalParser();
464
465		foreach ($mediatypes as $mediatype) {
466			// Check if this media type exists.
467			if (!array_key_exists($mediatype['mediatypeid'], $db_mediatypes)) {
468				self::exception(ZBX_API_ERROR_PERMISSIONS,
469					_('No permissions to referred object or it does not exist!')
470				);
471			}
472
473			// Validate "name" field.
474			if (array_key_exists('name', $mediatype)) {
475				if (is_array($mediatype['name'])) {
476					self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
477				}
478				elseif ($mediatype['name'] === '' || $mediatype['name'] === null || $mediatype['name'] === false) {
479					self::exception(ZBX_API_ERROR_PARAMETERS,
480						_s('Incorrect value for field "%1$s": %2$s.', 'name', _('cannot be empty'))
481					);
482				}
483
484				$check_names[$mediatype['name']] = true;
485			}
486		}
487
488		if ($check_names) {
489			$db_mediatype_names = API::getApiService()->select('media_type', [
490				'output' => ['mediatypeid', 'name'],
491				'filter' => ['name' => array_keys($check_names)]
492			]);
493			$db_mediatype_names = zbx_toHash($db_mediatype_names, 'name');
494
495			foreach ($mediatypes as $mediatype) {
496				if (array_key_exists('name', $mediatype)
497						&& array_key_exists($mediatype['name'], $db_mediatype_names)
498						&& !idcmp($db_mediatype_names[$mediatype['name']]['mediatypeid'], $mediatype['mediatypeid'])) {
499					self::exception(ZBX_API_ERROR_PARAMETERS,
500						_s('Media type "%1$s" already exists.', $mediatype['name'])
501					);
502				}
503			}
504		}
505
506		// Populate "name" field, if not set. Type field should not be populated at this point.
507		$mediatypes = $this->extendFromObjects(zbx_toHash($mediatypes, 'mediatypeid'), $db_mediatypes, ['name']);
508
509		$duplicate_name = CArrayHelper::findDuplicate($mediatypes, 'name');
510		if ($duplicate_name) {
511			self::exception(ZBX_API_ERROR_PARAMETERS,
512				_s('Media type "%1$s" already exists.', $duplicate_name['name'])
513			);
514		}
515
516		$i = 0;
517		foreach ($mediatypes as $mediatype) {
518			$i++;
519			$db_mediatype = $db_mediatypes[$mediatype['mediatypeid']];
520
521			// Recheck mandatory fields if type changed.
522			if (array_key_exists('type', $mediatype) && $db_mediatype['type'] != $mediatype['type']) {
523				$this->checkRequiredFieldsByType($mediatype);
524			}
525			else {
526				$optional_fields_by_type = [
527					MEDIA_TYPE_EMAIL => ['smtp_server', 'smtp_helo', 'smtp_email'],
528					MEDIA_TYPE_EXEC => ['exec_path'],
529					MEDIA_TYPE_WEBHOOK => [],
530					MEDIA_TYPE_SMS => ['gsm_modem']
531				];
532
533				foreach ($optional_fields_by_type[$db_mediatype['type']] as $field) {
534					if (array_key_exists($field, $mediatype)
535							&& ($mediatype[$field] === '' || $mediatype[$field] === null)) {
536						self::exception(ZBX_API_ERROR_PARAMETERS, _s(
537							'Field "%1$s" is missing a value for media type "%2$s".',
538							$field,
539							$mediatype['name']
540						));
541					}
542				}
543
544				// Populate "type" field from DB, since it is not set and is required for further validation.
545				$mediatype['type'] = $db_mediatype['type'];
546			}
547
548			switch ($mediatype['type']) {
549				case MEDIA_TYPE_EMAIL:
550					if (array_key_exists('smtp_authentication', $mediatype)) {
551						$smtp_authentication_validator = new CLimitedSetValidator([
552							'values' => [SMTP_AUTHENTICATION_NONE, SMTP_AUTHENTICATION_NORMAL]
553						]);
554
555						if (!$smtp_authentication_validator->validate($mediatype['smtp_authentication'])) {
556							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
557								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
558								$mediatype['smtp_authentication'],
559								'smtp_authentication',
560								$mediatype['name']
561							));
562						}
563					}
564
565					// Validate optional 'smtp_port' field.
566					if (array_key_exists('smtp_port', $mediatype)
567							&& $db_mediatype['smtp_port'] != $mediatype['smtp_port']
568							&& !validatePortNumber($mediatype['smtp_port'])) {
569						self::exception(ZBX_API_ERROR_PARAMETERS, _s(
570							'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
571							$mediatype['smtp_port'],
572							'smtp_port',
573							$mediatype['name']
574						));
575					}
576
577					// Validate optional field 'smtp_security'.
578					if (array_key_exists('smtp_security', $mediatype)) {
579						$smtp_security_validator = new CLimitedSetValidator([
580							'values' => [
581								SMTP_CONNECTION_SECURITY_NONE,
582								SMTP_CONNECTION_SECURITY_STARTTLS,
583								SMTP_CONNECTION_SECURITY_SSL_TLS
584							]
585						]);
586
587						if (!$smtp_security_validator->validate($mediatype['smtp_security'])) {
588							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
589								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
590								$mediatype['smtp_security'],
591								'smtp_security',
592								$mediatype['name']
593							));
594						}
595					}
596
597					// Validate optional field 'smtp_verify_peer'.
598					if (array_key_exists('smtp_verify_peer', $mediatype)
599							&& $db_mediatype['smtp_verify_peer'] != $mediatype['smtp_verify_peer']) {
600						$smtp_verify_peer_validator = new CLimitedSetValidator([
601							'values' => [0, 1]
602						]);
603
604						if (!$smtp_verify_peer_validator->validate($mediatype['smtp_verify_peer'])) {
605							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
606								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
607								$mediatype['smtp_verify_peer'],
608								'smtp_verify_peer',
609								$mediatype['name']
610							));
611						}
612					}
613
614					// Validate optional field 'smtp_verify_host'.
615					if (array_key_exists('smtp_verify_host', $mediatype)
616							&& $db_mediatype['smtp_verify_host'] != $mediatype['smtp_verify_host']) {
617						$smtp_verify_host_validator = new CLimitedSetValidator([
618							'values' => [0, 1]
619						]);
620
621						if (!$smtp_verify_host_validator->validate($mediatype['smtp_verify_host'])) {
622							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
623								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
624								$mediatype['smtp_verify_host'],
625								'smtp_verify_host',
626								$mediatype['name']
627							));
628						}
629					}
630
631					// Validate optional field 'content_type'.
632					if (array_key_exists('content_type', $mediatype)
633							&& $db_mediatype['content_type'] != $mediatype['content_type']) {
634						$content_type_validator = new CLimitedSetValidator([
635							'values' => [
636								SMTP_MESSAGE_FORMAT_PLAIN_TEXT,
637								SMTP_MESSAGE_FORMAT_HTML
638							]
639						]);
640
641						if (!$content_type_validator->validate($mediatype['content_type'])) {
642							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
643								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
644								$mediatype['content_type'],
645								'content_type',
646								$mediatype['name']
647							));
648						}
649					}
650					break;
651
652				case MEDIA_TYPE_EXEC:
653					if (array_key_exists('exec_params', $mediatype) && $mediatype['exec_params'] !== '') {
654						$pos = strrpos($mediatype['exec_params'], "\n");
655
656						if ($pos === false || strlen($mediatype['exec_params']) != $pos + 1) {
657							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
658								'Script parameters "%1$s" are missing the last new line feed for media type "%2$s".',
659								$mediatype['exec_params'],
660								$mediatype['name']
661							));
662						}
663					}
664					break;
665			}
666
667			$api_input_rules = $this->getValidationRules($mediatype['type'], 'update');
668			$validated_data = array_intersect_key($mediatype, $api_input_rules['fields']);
669
670			if (!CApiInputValidator::validate($api_input_rules, $validated_data, '/'.$i, $error)) {
671				self::exception(ZBX_API_ERROR_PARAMETERS, $error);
672			}
673			$mediatype = $validated_data + $mediatype;
674
675			// Validate optional 'status' field and only when status is changed.
676			if (array_key_exists('status', $mediatype) && $db_mediatype['status'] != $mediatype['status']) {
677				$status_validator = new CLimitedSetValidator([
678					'values' => [MEDIA_TYPE_STATUS_ACTIVE, MEDIA_TYPE_STATUS_DISABLED]
679				]);
680
681				if (!$status_validator->validate($mediatype['status'])) {
682					self::exception(ZBX_API_ERROR_PARAMETERS, _s(
683						'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
684						$mediatype['status'],
685						'status',
686						$mediatype['name']
687					));
688				}
689			}
690
691			// Validate optional 'maxsessions' field.
692			if (array_key_exists('maxsessions', $mediatype)
693					&& $db_mediatype['maxsessions'] != $mediatype['maxsessions']) {
694				if ($mediatype['maxsessions'] === '') {
695					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
696						'maxsessions', _('cannot be empty')
697					));
698				}
699
700				$min = ($mediatype['type'] == MEDIA_TYPE_SMS) ? 1 : 0;
701				$max = ($mediatype['type'] == MEDIA_TYPE_SMS) ? 1 : 100;
702
703				if (!ctype_digit((string) $mediatype['maxsessions']) || $mediatype['maxsessions'] > $max
704						|| $mediatype['maxsessions'] < $min) {
705					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
706						'maxsessions', _s('must be between "%1$s" and "%2$s"', $min, $max)
707					));
708				}
709			}
710			elseif ($mediatype['type'] == MEDIA_TYPE_SMS && $mediatype['type'] != $db_mediatype['type']
711						&& $db_mediatype['maxsessions'] != 1) {
712				self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
713					'maxsessions', _s('must be between "%1$s" and "%2$s"', 1, 1)
714				));
715			}
716
717			// Validate optional 'maxattempts' field.
718			if (array_key_exists('maxattempts', $mediatype)
719					&& $db_mediatype['maxattempts'] != $mediatype['maxattempts']) {
720				if ($mediatype['maxattempts'] === '') {
721					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
722						'maxattempts', _('cannot be empty')
723					));
724				}
725
726				if (!ctype_digit((string) $mediatype['maxattempts']) || $mediatype['maxattempts'] > 100
727						|| $mediatype['maxattempts'] < 1) {
728					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
729						'maxattempts', _s('must be between "%1$s" and "%2$s"', 1, 100)
730					));
731				}
732			}
733
734			// Validate optional 'attempt_interval' field.
735			if (array_key_exists('attempt_interval', $mediatype)
736					&& $db_mediatype['attempt_interval'] != $mediatype['attempt_interval']) {
737				if ($mediatype['attempt_interval'] === '') {
738					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
739						'attempt_interval', _('cannot be empty')
740					));
741				}
742
743				if ($simple_interval_parser->parse($mediatype['attempt_interval']) == CParser::PARSE_SUCCESS) {
744					$attempt_interval = timeUnitToSeconds($mediatype['attempt_interval']);
745
746					if ($attempt_interval < 0 || $attempt_interval > 3600) {
747						self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
748							'attempt_interval', _s('must be between "%1$s" and "%2$s"', 0, 3600)
749						));
750					}
751				}
752				else {
753					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
754						'attempt_interval', _s('must be between "%1$s" and "%2$s"', 0, 3600)
755					));
756				}
757			}
758		}
759	}
760
761	/**
762	 * Validates the event_menu_* input parameters.
763	 *
764	 * @param array $mediatype
765	 * @param array $db_mediatype
766	 *
767	 * @throws APIException if the input is invalid.
768	 */
769	private function validateEventMenu(array $mediatype, array $db_mediatype = null) {
770		if ($db_mediatype === null) {
771			$db_mediatype = DB::getDefaults('media_type');
772		}
773
774		foreach (['show_event_menu', 'event_menu_url', 'event_menu_name'] as $field_name) {
775			if (!array_key_exists($field_name, $mediatype)) {
776				$mediatype[$field_name] = $db_mediatype[$field_name];
777			}
778		}
779
780		foreach (['event_menu_url', 'event_menu_name'] as $field_name) {
781			if ($mediatype['show_event_menu'] == ZBX_EVENT_MENU_HIDE) {
782				if ($mediatype[$field_name] !== '') {
783					self::exception(ZBX_API_ERROR_PARAMETERS,
784						_s('Incorrect value for field "%1$s": %2$s.', $field_name, _('should be empty'))
785					);
786				}
787			}
788			else {
789				if ($mediatype[$field_name] === '') {
790					self::exception(ZBX_API_ERROR_PARAMETERS,
791						_s('Incorrect value for field "%1$s": %2$s.', $field_name, _('cannot be empty'))
792					);
793				}
794			}
795		}
796	}
797
798	/**
799	 * Add Media types.
800	 *
801	 * @param array  $mediatypes                           A multidimensional array with media types data.
802	 * @param int    $mediatypes[]['type']                 Transport used by the media type.
803	 * @param string $mediatypes[]['name']                 Name of the media type.
804	 * @param string $mediatypes[]['smtp_server']          SMTP server.
805	 * @param int    $mediatypes[]['smtp_port']            SMTP server port.
806	 * @param string $mediatypes[]['smtp_helo']            SMTP HELO.
807	 * @param string $mediatypes[]['smtp_email']           SMTP email.
808	 * @param int    $mediatypes[]['smtp_security']        SMTP connection security level.
809	 * @param int    $mediatypes[]['smtp_verify_peer']     SMTP verify peer.
810	 * @param int    $mediatypes[]['smtp_verify_host']     SMTP verify host.
811	 * @param int    $mediatypes[]['smtp_authentication']  SMTP authentication.
812	 * @param int    $mediatypes[]['content_type']         Message format.
813	 * @param string $mediatypes[]['exec_path']            Script name.
814	 * @param string $mediatypes[]['exec_params']          Script parameters.
815	 * @param string $mediatypes[]['gsm_modem']            Serial device name of the GSM modem.
816	 * @param string $mediatypes[]['username']             User name used for authentication.
817	 * @param string $mediatypes[]['passwd']               Password used for authentication.
818	 * @param int    $mediatypes[]['status']               Media type status.
819	 * @param int    $mediatypes[]['maxsessions']          Limit of simultaneously processed alerts.
820	 * @param int    $mediatypes[]['maxattempts']          Maximum attempts to deliver alert successfully.
821	 * @param string $mediatypes[]['attempt_interval']     Interval between alert delivery attempts.
822	 * @param string $mediatypes[]['script']               Webhook JavaScript body.
823	 * @param array  $mediatypes[]['parameters']           An array of webhook parameters:
824	 *                                                     ['name' => .., 'value' => ..]
825	 * @param string $mediatypes[]['timeout']              Webhook JavaScript HTTP request timeout.
826	 * @param string $mediatypes[]['process_tags']         Webhook HTTP response should be saved as tags.
827	 * @param string $mediatypes[]['show_event_menu']      Indicates presence of entry in event.get "urls" objects list.
828	 * @param string $mediatypes[]['event_menu_url']       Webhook additional info in frontend, supports received tags.
829	 * @param string $mediatypes[]['event_menu_name']      Webhook 'url' visual name.
830	 * @param string $mediatypes[]['description']          Media type description.
831	 * @param array  $mediatypes[]['message_templates']    An array of media type message templates.
832	 *
833	 * @return array
834	 */
835	public function create(array $mediatypes) {
836		$mediatypes = zbx_toArray($mediatypes);
837
838		$this->validateCreate($mediatypes);
839
840		$mediatypeids = DB::insert('media_type', $mediatypes);
841		$ins_media_type_param = [];
842		$ins_media_type_message = [];
843
844		foreach ($mediatypes as $i => $mediatype) {
845			$mediatypeid = $mediatypeids[$i];
846			$mediatypes[$i]['mediatypeid'] = $mediatypeid;
847
848			if ($mediatype['type'] == MEDIA_TYPE_WEBHOOK) {
849				if (array_key_exists('parameters', $mediatype)) {
850					foreach ($mediatype['parameters'] as $parameter) {
851						$ins_media_type_param[] = ['mediatypeid' => $mediatypeid] + $parameter;
852					}
853				}
854
855				$this->validateEventMenu($mediatype);
856			}
857
858			if (array_key_exists('message_templates', $mediatype)) {
859				foreach ($mediatype['message_templates'] as $message_template) {
860					$ins_media_type_message[] = ['mediatypeid' => $mediatypeid] + $message_template;
861				}
862			}
863		}
864
865		if ($ins_media_type_param) {
866			DB::insertBatch('media_type_param', $ins_media_type_param);
867		}
868
869		if ($ins_media_type_message) {
870			DB::insert('media_type_message', $ins_media_type_message);
871		}
872
873		$this->addAuditBulk(AUDIT_ACTION_ADD, AUDIT_RESOURCE_MEDIA_TYPE, $mediatypes);
874
875		return ['mediatypeids' => $mediatypeids];
876	}
877
878	/**
879	 * Update Media types.
880	 *
881	 * @param array  $mediatypes                           A multidimensional array with media types data.
882	 * @param int    $mediatypes[]['mediatypeid']          Media type ID.
883	 * @param int    $mediatypes[]['type']                 Transport used by the media type.
884	 * @param string $mediatypes[]['name']                 Name of the media type.
885	 * @param string $mediatypes[]['smtp_server']          SMTP server.
886	 * @param int    $mediatypes[]['smtp_port']            SMTP server port.
887	 * @param string $mediatypes[]['smtp_helo']            SMTP HELO.
888	 * @param string $mediatypes[]['smtp_email']           SMTP email.
889	 * @param int    $mediatypes[]['smtp_security']        SMTP connection security level.
890	 * @param int    $mediatypes[]['smtp_verify_peer']     SMTP verify peer.
891	 * @param int    $mediatypes[]['smtp_verify_host']     SMTP verify host.
892	 * @param int    $mediatypes[]['smtp_authentication']  SMTP authentication.
893	 * @param int    $mediatypes[]['content_type']         Message format.
894	 * @param string $mediatypes[]['exec_path']            Script name.
895	 * @param string $mediatypes[]['exec_params']          Script parameters.
896	 * @param string $mediatypes[]['gsm_modem']            Serial device name of the GSM modem.
897	 * @param string $mediatypes[]['username']             User name used for authentication.
898	 * @param string $mediatypes[]['passwd']               Password used for authentication.
899	 * @param int    $mediatypes[]['status']               Media type status.
900	 * @param int    $mediatypes[]['maxsessions']          Limit of simultaneously processed alerts.
901	 * @param int    $mediatypes[]['maxattempts']          Maximum attempts to deliver alert successfully.
902	 * @param string $mediatypes[]['attempt_interval']     Interval between alert delivery attempts.
903	 * @param string $mediatypes[]['script']               Webhook JavaScript body.
904	 * @param array  $mediatypes[]['parameters']           An array of webhook parameters:
905	 *                                                     ['name' => .., 'value' => ..]
906	 * @param string $mediatypes[]['timeout']              Webhook JavaScript HTTP request timeout.
907	 * @param string $mediatypes[]['process_tags']         Webhook HTTP response should be saved as tags.
908	 * @param string $mediatypes[]['show_event_menu']      Indicates presence of entry in event.get "urls" objects list.
909	 * @param string $mediatypes[]['event_menu_url']       Webhook additional info in frontend, supports received tags.
910	 * @param string $mediatypes[]['event_menu_name']      Webhook 'url' visual name.
911	 * @param string $mediatypes[]['description']          Media type description.
912	 * @param array  $mediatypes[]['message_templates']    An array of media type message templates.
913	 *
914	 * @return array
915	 */
916	public function update(array $mediatypes) {
917		$mediatypes = zbx_toArray($mediatypes);
918
919		$this->validateUpdate($mediatypes);
920
921		$update = [];
922		$webhooks_params = [];
923		$message_templates = [];
924		$default_values = DB::getDefaults('media_type');
925		$db_mediatypes = DB::select('media_type', [
926			'output' => ['mediatypeid', 'type', 'name', 'smtp_server', 'smtp_helo', 'smtp_email', 'exec_path',
927				'gsm_modem', 'username', 'passwd', 'status', 'smtp_port', 'smtp_security', 'smtp_verify_peer',
928				'smtp_verify_host', 'smtp_authentication', 'exec_params', 'maxsessions', 'maxattempts',
929				'attempt_interval', 'content_type', 'script', 'timeout', 'process_tags', 'show_event_menu',
930				'event_menu_url', 'event_menu_name', 'description'
931			],
932			'filter' => ['mediatypeid' => zbx_objectValues($mediatypes, 'mediatypeid')],
933			'preservekeys' => true
934		]);
935
936		$type_switch_fields = [
937			MEDIA_TYPE_EMAIL => [
938				'smtp_server', 'smtp_helo', 'smtp_email', 'smtp_port', 'smtp_security', 'smtp_verify_peer',
939				'smtp_verify_host', 'smtp_authentication', 'passwd', 'username', 'content_type'
940			],
941			MEDIA_TYPE_EXEC => [
942				'exec_path', 'exec_params'
943			],
944			MEDIA_TYPE_SMS => [
945				'gsm_modem'
946			],
947			MEDIA_TYPE_WEBHOOK => [
948				'script', 'timeout', 'process_tags', 'show_event_menu', 'event_menu_url', 'event_menu_name', 'parameters'
949			]
950		];
951		$default_values['parameters'] = [];
952
953		foreach ($mediatypes as $mediatype) {
954			$mediatypeid = $mediatype['mediatypeid'];
955			$db_mediatype = $db_mediatypes[$mediatypeid];
956			$db_type = $db_mediatype['type'];
957			$type = array_key_exists('type', $mediatype) ? $mediatype['type'] : $db_type;
958			unset($mediatype['mediatypeid']);
959
960			if ($type == MEDIA_TYPE_WEBHOOK) {
961				if (array_key_exists('parameters', $mediatype)) {
962					$params = [];
963
964					foreach ($mediatype['parameters'] as $param) {
965						$params[$param['name']] = $param['value'];
966					};
967
968					$webhooks_params[$mediatypeid] = $params;
969					unset($mediatype['parameters']);
970				}
971
972				if (array_key_exists('show_event_menu', $mediatype)
973						&& $mediatype['show_event_menu'] == ZBX_EVENT_MENU_HIDE) {
974					$mediatype += ['event_menu_url' => '', 'event_menu_name' => ''];
975				}
976
977				$this->validateEventMenu($mediatype, $db_mediatype);
978			}
979			else if ($db_type == MEDIA_TYPE_WEBHOOK) {
980				$webhooks_params[$mediatypeid] = [];
981			}
982
983			if (array_key_exists('message_templates', $mediatype)) {
984				$message_templates[$mediatypeid] = $mediatype['message_templates'];
985				unset($mediatype['message_templates']);
986			}
987
988			if ($type != $db_type) {
989				$mediatype = array_intersect_key($default_values,
990					array_fill_keys($type_switch_fields[$db_type], '')) + $mediatype;
991			}
992
993			if (!empty($mediatype)) {
994				$update[] = [
995					'values' => $mediatype,
996					'where' => ['mediatypeid' => $mediatypeid]
997				];
998			}
999		}
1000
1001		DB::update('media_type', $update);
1002		$mediatypeids = zbx_objectValues($mediatypes, 'mediatypeid');
1003
1004		if ($webhooks_params) {
1005			$ins_media_type_param = [];
1006			$del_media_type_param = [];
1007			$upd_media_type_param = [];
1008			$db_webhooks_params = DB::select('media_type_param', [
1009				'output' => ['mediatype_paramid', 'mediatypeid', 'name', 'value'],
1010				'filter' => ['mediatypeid' => array_keys($webhooks_params)]
1011			]);
1012
1013			foreach ($db_webhooks_params as $param) {
1014				$mediatypeid = $param['mediatypeid'];
1015
1016				if (!array_key_exists($param['name'], $webhooks_params[$mediatypeid])) {
1017					$del_media_type_param[] = $param['mediatype_paramid'];
1018				}
1019				elseif ($webhooks_params[$mediatypeid][$param['name']] !== $param['value']) {
1020					$upd_media_type_param[] = [
1021						'values' => ['value' => $webhooks_params[$mediatypeid][$param['name']]],
1022						'where' => ['mediatype_paramid' => $param['mediatype_paramid']]
1023					];
1024					unset($webhooks_params[$mediatypeid][$param['name']]);
1025				}
1026				else {
1027					unset($webhooks_params[$mediatypeid][$param['name']]);
1028				}
1029			}
1030
1031			$webhooks_params = array_filter($webhooks_params);
1032
1033			foreach ($webhooks_params as $mediatypeid => $params) {
1034				foreach ($params as $name => $value) {
1035					$ins_media_type_param[] = compact('mediatypeid', 'name', 'value');
1036				}
1037			}
1038
1039			if ($del_media_type_param) {
1040				DB::delete('media_type_param', ['mediatype_paramid' => array_keys(array_flip($del_media_type_param))]);
1041			}
1042
1043			if ($upd_media_type_param) {
1044				DB::update('media_type_param', $upd_media_type_param);
1045			}
1046
1047			if ($ins_media_type_param) {
1048				DB::insert('media_type_param', $ins_media_type_param);
1049			}
1050		}
1051
1052		if ($message_templates) {
1053			$db_media_type_messages = DB::select('media_type_message', [
1054				'output' => ['mediatype_messageid', 'mediatypeid', 'eventsource', 'recovery', 'subject', 'message'],
1055				'filter' => ['mediatypeid' => array_keys($message_templates)],
1056				'preservekeys' => true
1057			]);
1058
1059			$ins_media_type_message = [];
1060			$del_media_type_message = zbx_toHash(array_keys($db_media_type_messages));
1061			$upd_media_type_message = [];
1062
1063			$message_template_defaults = [
1064				'subject' => DB::getDefault('media_type_message', 'subject'),
1065				'message' => DB::getDefault('media_type_message', 'message')
1066			];
1067
1068			foreach ($db_media_type_messages as $mediatype_messageid => $db_message_template) {
1069				$db_mediatypeid = $db_message_template['mediatypeid'];
1070
1071				foreach ($message_templates[$db_mediatypeid] as $idx => $message_template) {
1072					if ($db_message_template['eventsource'] == $message_template['eventsource']
1073							&& $db_message_template['recovery'] == $message_template['recovery']) {
1074						$values = [];
1075						$message_template += $message_template_defaults;
1076
1077						if ($db_message_template['subject'] !== $message_template['subject']) {
1078							$values['subject'] = $message_template['subject'];
1079						}
1080
1081						if ($db_message_template['message'] !== $message_template['message']) {
1082							$values['message'] = $message_template['message'];
1083						}
1084
1085						if ($values) {
1086							$upd_media_type_message[] = [
1087								'values' => $values,
1088								'where' => ['mediatype_messageid' => $mediatype_messageid]
1089							];
1090						}
1091
1092						unset($message_templates[$db_mediatypeid][$idx], $del_media_type_message[$mediatype_messageid]);
1093					}
1094				}
1095			}
1096
1097			foreach ($message_templates as $mediatypeid => $templates) {
1098				foreach ($templates as $template) {
1099					$ins_media_type_message[] = ['mediatypeid' => $mediatypeid] + $template;
1100				}
1101			}
1102
1103			if ($del_media_type_message) {
1104				DB::delete('media_type_message', ['mediatype_messageid' => $del_media_type_message]);
1105			}
1106
1107			if ($upd_media_type_message) {
1108				DB::update('media_type_message', $upd_media_type_message);
1109			}
1110
1111			if ($ins_media_type_message) {
1112				DB::insert('media_type_message', $ins_media_type_message);
1113			}
1114		}
1115
1116		$this->addAuditBulk(AUDIT_ACTION_UPDATE, AUDIT_RESOURCE_MEDIA_TYPE, $mediatypes, $db_mediatypes);
1117
1118		return ['mediatypeids' => $mediatypeids];
1119	}
1120
1121	/**
1122	 * Delete Media types.
1123	 *
1124	 * @param array $mediatypeids
1125	 *
1126	 * @return array
1127	 */
1128	public function delete(array $mediatypeids) {
1129		if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
1130			self::exception(ZBX_API_ERROR_PERMISSIONS, _('Only Super Admins can delete media types.'));
1131		}
1132
1133		$actions = API::Action()->get([
1134			'mediatypeids' => $mediatypeids,
1135			'output' => API_OUTPUT_EXTEND,
1136			'preservekeys' => true
1137		]);
1138		if (!empty($actions)) {
1139			$action = reset($actions);
1140			self::exception(ZBX_API_ERROR_PARAMETERS, _s('Media types used by action "%1$s".', $action['name']));
1141		}
1142
1143		$db_mediatypes = DB::select('media_type', [
1144			'output' => ['mediatypeid', 'name'],
1145			'mediatypeids' => $mediatypeids,
1146			'preservekeys' => true
1147		]);
1148
1149		DB::delete('media_type', ['mediatypeid' => $mediatypeids]);
1150
1151		$this->addAuditBulk(AUDIT_ACTION_DELETE, AUDIT_RESOURCE_MEDIA_TYPE, $db_mediatypes);
1152
1153		return ['mediatypeids' => $mediatypeids];
1154	}
1155
1156	/**
1157	 * Check required fields by type. Values for fields must not be empty.
1158	 *
1159	 * @param array		$mediatype							An array of media type data.
1160	 * @param string	$mediatype['name']					Name of the media type.
1161	 * @param string	$mediatype['type']					E-mail, Script and SMS.
1162	 *
1163	 * @throws APIException if the input is invalid.
1164	 */
1165	protected function checkRequiredFieldsByType(array $mediatype) {
1166		$type_validator = new CLimitedSetValidator([
1167			'values' => array_keys(media_type2str())
1168		]);
1169
1170		if (!$type_validator->validate($mediatype['type'])) {
1171			self::exception(ZBX_API_ERROR_PARAMETERS, _s(
1172				'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
1173				$mediatype['type'],
1174				'type',
1175				$mediatype['name']
1176			));
1177		}
1178
1179		$required_fields_by_type = [
1180			MEDIA_TYPE_EMAIL => ['smtp_server', 'smtp_helo', 'smtp_email'],
1181			MEDIA_TYPE_EXEC => ['exec_path'],
1182			MEDIA_TYPE_WEBHOOK => [],
1183			MEDIA_TYPE_SMS => ['gsm_modem']
1184		];
1185
1186		foreach ($required_fields_by_type[$mediatype['type']] as $field) {
1187			// Check if fields set on Create method. For update method they are checked when type is changed.
1188			if (!array_key_exists($field, $mediatype)) {
1189				self::exception(ZBX_API_ERROR_PARAMETERS,
1190					_s('Field "%1$s" is required for media type "%2$s".', $field, $mediatype['name'])
1191				);
1192			}
1193			elseif (array_key_exists($field, $mediatype)
1194					&& ($mediatype[$field] === '' || $mediatype[$field] === null)) {
1195				self::exception(ZBX_API_ERROR_PARAMETERS,
1196					_s('Field "%1$s" is missing a value for media type "%2$s".', $field, $mediatype['name'])
1197				);
1198			}
1199		}
1200	}
1201
1202	protected function addRelatedObjects(array $options, array $result) {
1203		$result = parent::addRelatedObjects($options, $result);
1204
1205		// adding message templates
1206		if ($options['selectMessageTemplates'] !== null && $options['selectMessageTemplates'] != API_OUTPUT_COUNT) {
1207			$message_templates = [];
1208			$relation_map = $this->createRelationMap($result, 'mediatypeid', 'mediatype_messageid',
1209				'media_type_message'
1210			);
1211			$related_ids = $relation_map->getRelatedIds();
1212
1213			if ($related_ids) {
1214				$message_templates = API::getApiService()->select('media_type_message', [
1215					'output' => $options['selectMessageTemplates'],
1216					'mediatype_messageids' => $related_ids,
1217					'preservekeys' => true
1218				]);
1219				$message_templates = $this->unsetExtraFields($message_templates, ['mediatype_messageid', 'mediatypeid'],
1220					[]
1221				);
1222			}
1223
1224			$result = $relation_map->mapMany($result, $message_templates, 'message_templates');
1225		}
1226
1227		// adding users
1228		if ($options['selectUsers'] !== null && $options['selectUsers'] != API_OUTPUT_COUNT) {
1229			$users = [];
1230			$relationMap = $this->createRelationMap($result, 'mediatypeid', 'userid', 'media');
1231			$related_ids = $relationMap->getRelatedIds();
1232
1233			if ($related_ids) {
1234				$users = API::User()->get([
1235					'output' => $options['selectUsers'],
1236					'userids' => $related_ids,
1237					'preservekeys' => true
1238				]);
1239			}
1240
1241			$result = $relationMap->mapMany($result, $users, 'users');
1242		}
1243
1244		if ($this->outputIsRequested('parameters', $options['output'])) {
1245			foreach ($result as $mediatypeid => $mediatype) {
1246				$result[$mediatypeid]['parameters'] = [];
1247			}
1248
1249			$parameters = DB::select('media_type_param', [
1250				'output' => ['mediatypeid', 'name', 'value'],
1251				'filter' => ['mediatypeid' => array_keys($result)]
1252			]);
1253
1254			foreach ($parameters as $parameter) {
1255				$result[$parameter['mediatypeid']]['parameters'][] = [
1256					'name' => $parameter['name'],
1257					'value' => $parameter['value']
1258				];
1259			}
1260		}
1261
1262		return $result;
1263	}
1264
1265	/**
1266	 * Get incomplete media type validation rules.
1267	 *
1268	 * @param int    $type
1269	 * @param string $method
1270	 *
1271	 * @return array
1272	 */
1273	protected function getValidationRules($type, $method) {
1274		$api_input_rules = ['type' => API_OBJECT, 'fields' => [
1275			'description' =>		['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('media_type', 'description')],
1276			'message_templates' =>	['type' => API_OBJECTS, 'uniq' => [['eventsource', 'recovery']], 'fields' => [
1277				'eventsource' =>		['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [EVENT_SOURCE_TRIGGERS, EVENT_SOURCE_DISCOVERY, EVENT_SOURCE_AUTOREGISTRATION, EVENT_SOURCE_INTERNAL])],
1278				'recovery' =>			['type' => API_MULTIPLE, 'flags' => API_REQUIRED, 'rules' => [
1279											['if' => ['field' => 'eventsource', 'in' => EVENT_SOURCE_TRIGGERS], 'type' => API_INT32, 'in' => implode(',', [ACTION_OPERATION, ACTION_RECOVERY_OPERATION, ACTION_ACKNOWLEDGE_OPERATION])],
1280											['if' => ['field' => 'eventsource', 'in' => implode(',', [EVENT_SOURCE_DISCOVERY, EVENT_SOURCE_AUTOREGISTRATION])], 'type' => API_INT32, 'in' => ACTION_OPERATION],
1281											['if' => ['field' => 'eventsource', 'in' => EVENT_SOURCE_INTERNAL], 'type' => API_INT32, 'in' => implode(',', [ACTION_OPERATION, ACTION_RECOVERY_OPERATION])]
1282				]],
1283				'subject' =>			['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('media_type_message', 'subject')],
1284				'message' =>			['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('media_type_message', 'message')]
1285			]]
1286		]];
1287
1288		if ($type == MEDIA_TYPE_WEBHOOK) {
1289			$api_input_rules['fields'] += [
1290				'script' =>				['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('media_type', 'script')],
1291				'timeout' =>			['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('media_type', 'timeout'), 'in' => '1:60'],
1292				'process_tags' =>		['type' => API_INT32, 'in' => implode(',', [ZBX_MEDIA_TYPE_TAGS_DISABLED, ZBX_MEDIA_TYPE_TAGS_ENABLED])],
1293				'show_event_menu' =>	['type' => API_INT32, 'in' => implode(',', [ZBX_EVENT_MENU_HIDE, ZBX_EVENT_MENU_SHOW])],
1294				// Should be checked as string not as URL because it can contain macros tags.
1295				'event_menu_url' =>		['type' => API_URL, 'flags' => API_ALLOW_EVENT_TAGS_MACRO, 'length' => DB::getFieldLength('media_type', 'event_menu_url')],
1296				'event_menu_name' =>	['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('media_type', 'event_menu_name')],
1297				'parameters' =>			['type' => API_OBJECTS, 'uniq' => [['name']], 'fields' => [
1298					'name' =>				['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('media_type_param', 'name')],
1299					'value' =>				['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('media_type_param', 'value')]
1300				]]
1301			];
1302
1303			if ($method === 'create') {
1304				$api_input_rules['fields']['script']['flags'] |= API_REQUIRED;
1305			}
1306		}
1307
1308		return $api_input_rules;
1309	}
1310}
1311