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			'selectUsers'				=> null,
72			'countOutput'				=> false,
73			'groupCount'				=> false,
74			'preservekeys'				=> false,
75			'sortfield'					=> '',
76			'sortorder'					=> '',
77			'limit'						=> null
78		];
79		$options = zbx_array_merge($defOptions, $options);
80
81		// permission check
82		if (self::$userData['type'] == USER_TYPE_SUPER_ADMIN) {
83		}
84		elseif (!$options['editable'] && self::$userData['type'] == USER_TYPE_ZABBIX_ADMIN) {
85		}
86		elseif ($options['editable'] || self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
87			return [];
88		}
89
90		// mediatypeids
91		if (!is_null($options['mediatypeids'])) {
92			zbx_value2array($options['mediatypeids']);
93			$sqlParts['where'][] = dbConditionInt('mt.mediatypeid', $options['mediatypeids']);
94		}
95
96		// mediaids
97		if (!is_null($options['mediaids'])) {
98			zbx_value2array($options['mediaids']);
99
100			$sqlParts['from']['media'] = 'media m';
101			$sqlParts['where'][] = dbConditionInt('m.mediaid', $options['mediaids']);
102			$sqlParts['where']['mmt'] = 'm.mediatypeid=mt.mediatypeid';
103		}
104
105		// userids
106		if (!is_null($options['userids'])) {
107			zbx_value2array($options['userids']);
108
109			$sqlParts['from']['media'] = 'media m';
110			$sqlParts['where'][] = dbConditionInt('m.userid', $options['userids']);
111			$sqlParts['where']['mmt'] = 'm.mediatypeid=mt.mediatypeid';
112		}
113
114		// filter
115		if (is_array($options['filter'])) {
116			$this->dbFilter('media_type mt', $options, $sqlParts);
117		}
118
119		// search
120		if (is_array($options['search'])) {
121			zbx_db_search('media_type mt', $options, $sqlParts);
122		}
123
124		// limit
125		if (zbx_ctype_digit($options['limit']) && $options['limit']) {
126			$sqlParts['limit'] = $options['limit'];
127		}
128
129		$sqlParts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts);
130		$sqlParts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts);
131		$res = DBselect(self::createSelectQueryFromParts($sqlParts), $sqlParts['limit']);
132		while ($mediatype = DBfetch($res)) {
133			if ($options['countOutput']) {
134				if ($options['groupCount']) {
135					$result[] = $mediatype;
136				}
137				else {
138					$result = $mediatype['rowscount'];
139				}
140			}
141			else {
142				$result[$mediatype['mediatypeid']] = $mediatype;
143			}
144		}
145
146		if ($options['countOutput']) {
147			return $result;
148		}
149
150		if ($result) {
151			$result = $this->addRelatedObjects($options, $result);
152		}
153
154		// removing keys (hash -> array)
155		if (!$options['preservekeys']) {
156			$result = zbx_cleanHashes($result);
157		}
158		return $result;
159	}
160
161	/**
162	 * Validates the input parameters for the create() method.
163	 *
164	 * @param array $mediatypes
165	 *
166	 * @throws APIException if the input is invalid.
167	 */
168	protected function validateCreate(array $mediatypes) {
169		if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
170			self::exception(ZBX_API_ERROR_PERMISSIONS, _('Only Super Admins can create media types.'));
171		}
172
173		if (!$mediatypes) {
174			self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.'));
175		}
176
177		$required_fields = ['type', 'description'];
178
179		foreach ($mediatypes as $mediatype) {
180			if (!is_array($mediatype)) {
181				self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
182			}
183
184			// Check required parameters.
185			$missing_keys = array_diff($required_fields, array_keys($mediatype));
186
187			if ($missing_keys) {
188				self::exception(ZBX_API_ERROR_PARAMETERS,
189					_s('Media type is missing parameters: %1$s', implode(', ', $missing_keys))
190				);
191			}
192			else {
193				foreach ($required_fields as $field) {
194					if ($mediatype[$field] === '' || $mediatype[$field] === null) {
195						self::exception(ZBX_API_ERROR_PARAMETERS, _s(
196							'Field "%1$s" is missing a value for media type "%2$s".',
197							$field,
198							$mediatype['description']
199						));
200					}
201				}
202			}
203		}
204
205		// Check for duplicate names.
206		$duplicate_name = CArrayHelper::findDuplicate($mediatypes, 'description');
207		if ($duplicate_name) {
208			self::exception(ZBX_API_ERROR_PARAMETERS,
209				_s('Duplicate "description" value "%1$s" for media type.', $duplicate_name['description'])
210			);
211		}
212
213		$simple_interval_parser = new CSimpleIntervalParser();
214
215		foreach ($mediatypes as $mediatype) {
216			// Check if media type already exists.
217			$db_mediatype = API::getApiService()->select('media_type', [
218				'output' => ['description'],
219				'filter' => ['description' => $mediatype['description']],
220				'limit' => 1
221			]);
222
223			if ($db_mediatype) {
224				self::exception(ZBX_API_ERROR_PARAMETERS,
225					_s('Media type "%1$s" already exists.', $mediatype['description'])
226				);
227			}
228
229			// Check additional fields and values depending on media type.
230			$this->checkRequiredFieldsByType($mediatype);
231
232			switch ($mediatype['type']) {
233				case MEDIA_TYPE_EZ_TEXTING:
234					$message_text_limit_validator = new CLimitedSetValidator([
235						'values' => [EZ_TEXTING_LIMIT_USA, EZ_TEXTING_LIMIT_CANADA]
236					]);
237
238					if (!$message_text_limit_validator->validate($mediatype['exec_path'])) {
239						self::exception(ZBX_API_ERROR_PARAMETERS, _s(
240							'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
241							$mediatype['exec_path'],
242							'exec_path',
243							$mediatype['description']
244						));
245					}
246					break;
247
248				case MEDIA_TYPE_EMAIL:
249					if (array_key_exists('smtp_authentication', $mediatype)) {
250						$smtp_authentication_validator = new CLimitedSetValidator([
251							'values' => [SMTP_AUTHENTICATION_NONE, SMTP_AUTHENTICATION_NORMAL]
252						]);
253
254						if (!$smtp_authentication_validator->validate($mediatype['smtp_authentication'])) {
255							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
256								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
257								$mediatype['smtp_authentication'],
258								'smtp_authentication',
259								$mediatype['description']
260							));
261						}
262					}
263
264					// Validate optional 'smtp_port' field.
265					if (array_key_exists('smtp_port', $mediatype) && !validatePortNumber($mediatype['smtp_port'])) {
266						self::exception(ZBX_API_ERROR_PARAMETERS, _s(
267							'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
268							$mediatype['smtp_port'],
269							'smtp_port',
270							$mediatype['description']
271						));
272					}
273
274					// Validate optional field 'smtp_security'.
275					if (array_key_exists('smtp_security', $mediatype)) {
276						$smtp_security_validator = new CLimitedSetValidator([
277							'values' => [
278								SMTP_CONNECTION_SECURITY_NONE,
279								SMTP_CONNECTION_SECURITY_STARTTLS,
280								SMTP_CONNECTION_SECURITY_SSL_TLS
281							]
282						]);
283
284						if (!$smtp_security_validator->validate($mediatype['smtp_security'])) {
285							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
286								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
287								$mediatype['smtp_security'],
288								'smtp_security',
289								$mediatype['description']
290							));
291						}
292					}
293
294					// Validate optional field 'smtp_verify_peer'.
295					if (array_key_exists('smtp_verify_peer', $mediatype)) {
296						$smtp_verify_peer_validator = new CLimitedSetValidator([
297							'values' => [0, 1]
298						]);
299
300						if (!$smtp_verify_peer_validator->validate($mediatype['smtp_verify_peer'])) {
301							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
302								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
303								$mediatype['smtp_verify_peer'],
304								'smtp_verify_peer',
305								$mediatype['description']
306							));
307						}
308					}
309
310					// Validate optional field 'smtp_verify_host'.
311					if (array_key_exists('smtp_verify_host', $mediatype)) {
312						$smtp_verify_host_validator = new CLimitedSetValidator([
313							'values' => [0, 1]
314						]);
315
316						if (!$smtp_verify_host_validator->validate($mediatype['smtp_verify_host'])) {
317							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
318								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
319								$mediatype['smtp_verify_host'],
320								'smtp_verify_host',
321								$mediatype['description']
322							));
323						}
324					}
325					break;
326
327				case MEDIA_TYPE_EXEC:
328					if (array_key_exists('exec_params', $mediatype) && $mediatype['exec_params'] !== '') {
329						$pos = strrpos($mediatype['exec_params'], "\n");
330
331						if ($pos === false || strlen($mediatype['exec_params']) != $pos + 1) {
332							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
333								'Script parameters "%1$s" are missing the last new line feed for media type "%2$s".',
334								$mediatype['exec_params'],
335								$mediatype['description']
336							));
337						}
338					}
339					break;
340			}
341
342			// Validate optional 'status' field.
343			if (array_key_exists('status', $mediatype)) {
344				$status_validator = new CLimitedSetValidator([
345					'values' => [MEDIA_TYPE_STATUS_ACTIVE, MEDIA_TYPE_STATUS_DISABLED]
346				]);
347
348				if (!$status_validator->validate($mediatype['status'])) {
349					self::exception(ZBX_API_ERROR_PARAMETERS, _s(
350						'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
351						$mediatype['status'],
352						'status',
353						$mediatype['description']
354					));
355				}
356			}
357
358			// Validate optional 'maxsessions' field.
359			if (array_key_exists('maxsessions', $mediatype)) {
360				if ($mediatype['maxsessions'] === '') {
361					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
362						'maxsessions', _('cannot be empty')
363					));
364				}
365
366				$min = ($mediatype['type'] == MEDIA_TYPE_SMS) ? 1 : 0;
367				$max = ($mediatype['type'] == MEDIA_TYPE_SMS) ? 1 : 100;
368
369				if (!ctype_digit((string) $mediatype['maxsessions']) || $mediatype['maxsessions'] > $max
370						|| $mediatype['maxsessions'] < $min) {
371					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
372						'maxsessions', _s('must be between "%1$s" and "%2$s"', $min, $max)
373					));
374				}
375			}
376
377			// Validate optional 'maxattempts' field.
378			if (array_key_exists('maxattempts', $mediatype)) {
379				if ($mediatype['maxattempts'] === '') {
380					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
381						'maxattempts', _('cannot be empty')
382					));
383				}
384
385				if (!ctype_digit((string) $mediatype['maxattempts']) || $mediatype['maxattempts'] > 10
386						|| $mediatype['maxattempts'] < 1) {
387					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
388						'maxattempts', _s('must be between "%1$s" and "%2$s"', 1, 10)
389					));
390				}
391			}
392
393			// Validate optional 'attempt_interval' field.
394			if (array_key_exists('attempt_interval', $mediatype)) {
395				if ($mediatype['attempt_interval'] === '') {
396					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
397						'attempt_interval', _('cannot be empty')
398					));
399				}
400
401				if ($simple_interval_parser->parse($mediatype['attempt_interval']) == CParser::PARSE_SUCCESS) {
402					$attempt_interval = timeUnitToSeconds($mediatype['attempt_interval']);
403
404					if ($attempt_interval < 0 || $attempt_interval > 60) {
405						self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
406							'attempt_interval', _s('must be between "%1$s" and "%2$s"', 0, 60)
407						));
408					}
409				}
410				else {
411					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
412						'attempt_interval', _s('must be between "%1$s" and "%2$s"', 0, 60)
413					));
414				}
415			}
416		}
417	}
418
419	/**
420	 * Validates the input parameters for the update() method.
421	 *
422	 * @param array $mediatypes
423	 *
424	 * @throws APIException if the input is invalid.
425	 */
426	protected function validateUpdate(array $mediatypes) {
427		if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
428			self::exception(ZBX_API_ERROR_PERMISSIONS, _('Only Super Admins can edit media types.'));
429		}
430
431		if (!$mediatypes) {
432			self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.'));
433		}
434
435		// Validate given IDs.
436		$this->checkObjectIds($mediatypes, 'mediatypeid',
437			_('No "%1$s" given for media type.'),
438			_('Empty media type ID.'),
439			_('Incorrect media type ID.')
440		);
441
442		$mediatypeids = zbx_objectValues($mediatypes, 'mediatypeid');
443
444		// Check value map names.
445		$db_mediatypes = API::getApiService()->select('media_type', [
446			'output' => ['mediatypeid', 'type', 'description', 'exec_path', 'status', 'smtp_port', 'smtp_verify_peer',
447				'smtp_verify_host', 'smtp_authentication', 'maxsessions', 'maxattempts', 'attempt_interval'
448			],
449			'mediatypeids' => $mediatypeids,
450			'preservekeys' => true
451		]);
452
453		$check_names = [];
454		$simple_interval_parser = new CSimpleIntervalParser();
455
456		foreach ($mediatypes as $mediatype) {
457			// Check if this media type exists.
458			if (!array_key_exists($mediatype['mediatypeid'], $db_mediatypes)) {
459				self::exception(ZBX_API_ERROR_PERMISSIONS,
460					_('No permissions to referred object or it does not exist!')
461				);
462			}
463
464			// Validate "description" field.
465			if (array_key_exists('description', $mediatype)) {
466				if (is_array($mediatype['description'])) {
467					self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
468				}
469				elseif ($mediatype['description'] === '' || $mediatype['description'] === null
470						|| $mediatype['description'] === false) {
471					self::exception(ZBX_API_ERROR_PARAMETERS,
472						_s('Incorrect value for field "%1$s": %2$s.', 'description', _('cannot be empty'))
473					);
474				}
475
476				$check_names[$mediatype['description']] = true;
477			}
478		}
479
480		if ($check_names) {
481			$db_mediatype_names = API::getApiService()->select('media_type', [
482				'output' => ['mediatypeid', 'description'],
483				'filter' => ['name' => array_keys($check_names)]
484			]);
485			$db_mediatype_names = zbx_toHash($db_mediatype_names, 'description');
486
487			foreach ($mediatypes as $mediatype) {
488				if (array_key_exists('description', $mediatype)
489						&& array_key_exists($mediatype['description'], $db_mediatype_names)
490						&& !idcmp($db_mediatype_names[$mediatype['description']]['mediatypeid'],
491							$mediatype['mediatypeid'])) {
492					self::exception(ZBX_API_ERROR_PARAMETERS,
493						_s('Media type "%1$s" already exists.', $mediatype['description'])
494					);
495				}
496			}
497		}
498
499		// Populate "description" field, if not set. Type field should not be populated at this point.
500		$mediatypes = $this->extendFromObjects(zbx_toHash($mediatypes, 'mediatypeid'), $db_mediatypes, ['description']);
501
502		$duplicate_name = CArrayHelper::findDuplicate($mediatypes, 'description');
503		if ($duplicate_name) {
504			self::exception(ZBX_API_ERROR_PARAMETERS,
505				_s('Duplicate "description" value "%1$s" for media type.', $duplicate_name['description'])
506			);
507		}
508
509		foreach ($mediatypes as $mediatype) {
510			$db_mediatype = $db_mediatypes[$mediatype['mediatypeid']];
511
512			// Recheck mandatory fields if type changed.
513			if (array_key_exists('type', $mediatype) && $db_mediatype['type'] != $mediatype['type']) {
514				$this->checkRequiredFieldsByType($mediatype);
515			}
516			else {
517				$optional_fields_by_type = [
518					MEDIA_TYPE_EMAIL => ['smtp_server', 'smtp_helo', 'smtp_email'],
519					MEDIA_TYPE_EXEC => ['exec_path'],
520					MEDIA_TYPE_SMS => ['gsm_modem'],
521					MEDIA_TYPE_JABBER => ['username', 'passwd'],
522					MEDIA_TYPE_EZ_TEXTING => ['exec_path', 'username', 'passwd']
523				];
524
525				foreach ($optional_fields_by_type[$db_mediatype['type']] as $field) {
526					if (array_key_exists($field, $mediatype)
527							&& ($mediatype[$field] === '' || $mediatype[$field] === null)) {
528						self::exception(ZBX_API_ERROR_PARAMETERS, _s(
529							'Field "%1$s" is missing a value for media type "%2$s".',
530							$field,
531							$mediatype['description']
532						));
533					}
534				}
535
536				// Populate "type" field from DB, since it is not set and is required for further validation.
537				$mediatype['type'] = $db_mediatype['type'];
538			}
539
540			switch ($mediatype['type']) {
541				case MEDIA_TYPE_EZ_TEXTING:
542					if (array_key_exists('exec_path', $mediatype)) {
543						$message_text_limit_validator = new CLimitedSetValidator([
544							'values' => [EZ_TEXTING_LIMIT_USA, EZ_TEXTING_LIMIT_CANADA]
545						]);
546
547						if ($db_mediatype['exec_path'] !== $mediatype['exec_path']
548								&& !$message_text_limit_validator->validate($mediatype['exec_path'])) {
549							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
550								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
551								$mediatype['exec_path'],
552								'exec_path',
553								$mediatype['description']
554							));
555						}
556					}
557					break;
558
559				case MEDIA_TYPE_EMAIL:
560					if (array_key_exists('smtp_authentication', $mediatype)) {
561						$smtp_authentication_validator = new CLimitedSetValidator([
562							'values' => [SMTP_AUTHENTICATION_NONE, SMTP_AUTHENTICATION_NORMAL]
563						]);
564
565						if (!$smtp_authentication_validator->validate($mediatype['smtp_authentication'])) {
566							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
567								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
568								$mediatype['smtp_authentication'],
569								'smtp_authentication',
570								$mediatype['description']
571							));
572						}
573					}
574
575					// Validate optional 'smtp_port' field.
576					if (array_key_exists('smtp_port', $mediatype)
577							&& $db_mediatype['smtp_port'] != $mediatype['smtp_port']
578							&& !validatePortNumber($mediatype['smtp_port'])) {
579						self::exception(ZBX_API_ERROR_PARAMETERS, _s(
580							'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
581							$mediatype['smtp_port'],
582							'smtp_port',
583							$mediatype['description']
584						));
585					}
586
587					// Validate optional field 'smtp_security'.
588					if (array_key_exists('smtp_security', $mediatype)) {
589						$smtp_security_validator = new CLimitedSetValidator([
590							'values' => [
591								SMTP_CONNECTION_SECURITY_NONE,
592								SMTP_CONNECTION_SECURITY_STARTTLS,
593								SMTP_CONNECTION_SECURITY_SSL_TLS
594							]
595						]);
596
597						if (!$smtp_security_validator->validate($mediatype['smtp_security'])) {
598							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
599								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
600								$mediatype['smtp_security'],
601								'smtp_security',
602								$mediatype['description']
603							));
604						}
605					}
606
607					// Validate optional field 'smtp_verify_peer'.
608					if (array_key_exists('smtp_verify_peer', $mediatype)
609							&& $db_mediatype['smtp_verify_peer'] != $mediatype['smtp_verify_peer']) {
610						$smtp_verify_peer_validator = new CLimitedSetValidator([
611							'values' => [0, 1]
612						]);
613
614						if (!$smtp_verify_peer_validator->validate($mediatype['smtp_verify_peer'])) {
615							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
616								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
617								$mediatype['smtp_verify_peer'],
618								'smtp_verify_peer',
619								$mediatype['description']
620							));
621						}
622					}
623
624					// Validate optional field 'smtp_verify_host'.
625					if (array_key_exists('smtp_verify_host', $mediatype)
626							&& $db_mediatype['smtp_verify_host'] != $mediatype['smtp_verify_host']) {
627						$smtp_verify_host_validator = new CLimitedSetValidator([
628							'values' => [0, 1]
629						]);
630
631						if (!$smtp_verify_host_validator->validate($mediatype['smtp_verify_host'])) {
632							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
633								'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
634								$mediatype['smtp_verify_host'],
635								'smtp_verify_host',
636								$mediatype['description']
637							));
638						}
639					}
640					break;
641
642				case MEDIA_TYPE_EXEC:
643					if (array_key_exists('exec_params', $mediatype) && $mediatype['exec_params'] !== '') {
644						$pos = strrpos($mediatype['exec_params'], "\n");
645
646						if ($pos === false || strlen($mediatype['exec_params']) != $pos + 1) {
647							self::exception(ZBX_API_ERROR_PARAMETERS, _s(
648								'Script parameters "%1$s" are missing the last new line feed for media type "%2$s".',
649								$mediatype['exec_params'],
650								$mediatype['description']
651							));
652						}
653					}
654					break;
655			}
656
657			// Validate optional 'status' field and only when status is changed.
658			if (array_key_exists('status', $mediatype) && $db_mediatype['status'] != $mediatype['status']) {
659				$status_validator = new CLimitedSetValidator([
660					'values' => [MEDIA_TYPE_STATUS_ACTIVE, MEDIA_TYPE_STATUS_DISABLED]
661				]);
662
663				if (!$status_validator->validate($mediatype['status'])) {
664					self::exception(ZBX_API_ERROR_PARAMETERS, _s(
665						'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
666						$mediatype['status'],
667						'status',
668						$mediatype['description']
669					));
670				}
671			}
672
673			// Validate optional 'maxsessions' field.
674			if (array_key_exists('maxsessions', $mediatype)
675					&& $db_mediatype['maxsessions'] != $mediatype['maxsessions']) {
676				if ($mediatype['maxsessions'] === '') {
677					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
678						'maxsessions', _('cannot be empty')
679					));
680				}
681
682				$min = ($mediatype['type'] == MEDIA_TYPE_SMS) ? 1 : 0;
683				$max = ($mediatype['type'] == MEDIA_TYPE_SMS) ? 1 : 100;
684
685				if (!ctype_digit((string) $mediatype['maxsessions']) || $mediatype['maxsessions'] > $max
686						|| $mediatype['maxsessions'] < $min) {
687					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
688						'maxsessions', _s('must be between "%1$s" and "%2$s"', $min, $max)
689					));
690				}
691			}
692			elseif ($mediatype['type'] == MEDIA_TYPE_SMS && $mediatype['type'] != $db_mediatype['type']
693						&& $db_mediatype['maxsessions'] != 1) {
694				self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
695					'maxsessions', _s('must be between "%1$s" and "%2$s"', 1, 1)
696				));
697			}
698
699			// Validate optional 'maxattempts' field.
700			if (array_key_exists('maxattempts', $mediatype)
701					&& $db_mediatype['maxattempts'] != $mediatype['maxattempts']) {
702				if ($mediatype['maxattempts'] === '') {
703					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
704						'maxattempts', _('cannot be empty')
705					));
706				}
707
708				if (!ctype_digit((string) $mediatype['maxattempts']) || $mediatype['maxattempts'] > 10
709						|| $mediatype['maxattempts'] < 1) {
710					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
711						'maxattempts', _s('must be between "%1$s" and "%2$s"', 1, 10)
712					));
713				}
714			}
715
716			// Validate optional 'attempt_interval' field.
717			if (array_key_exists('attempt_interval', $mediatype)
718					&& $db_mediatype['attempt_interval'] != $mediatype['attempt_interval']) {
719				if ($mediatype['attempt_interval'] === '') {
720					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
721						'attempt_interval', _('cannot be empty')
722					));
723				}
724
725				if ($simple_interval_parser->parse($mediatype['attempt_interval']) == CParser::PARSE_SUCCESS) {
726					$attempt_interval = timeUnitToSeconds($mediatype['attempt_interval']);
727
728					if ($attempt_interval < 0 || $attempt_interval > 60) {
729						self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
730							'attempt_interval', _s('must be between "%1$s" and "%2$s"', 0, 60)
731						));
732					}
733				}
734				else {
735					self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
736						'attempt_interval', _s('must be between "%1$s" and "%2$s"', 0, 60)
737					));
738				}
739			}
740		}
741	}
742
743	/**
744	 * Add Media types.
745	 *
746	 * @param array		$mediatypes							multidimensional array with media types data
747	 * @param int		$mediatypes['type']					type
748	 * @param string	$mediatypes['description']			description
749	 * @param string	$mediatypes['smtp_server']			SMTP server
750	 * @param int		$mediatypes['smtp_port']			SMTP port
751	 * @param string	$mediatypes['smtp_helo']			SMTP hello
752	 * @param string	$mediatypes['smtp_email']			SMTP email
753	 * @param int		$mediatypes['smtp_security']		SMTP connection security
754	 * @param int		$mediatypes['smtp_verify_peer']		SMTP verify peer
755	 * @param int		$mediatypes['smtp_verify_host']		SMTP verify host
756	 * @param int		$mediatypes['smtp_authentication']	SMTP authentication
757	 * @param string	$mediatypes['exec_path']			script name/message text limit
758	 * @param string	$mediatypes['exec_params']			script parameters
759	 * @param string	$mediatypes['gsm_modem']			GSM modem
760	 * @param string	$mediatypes['username']				username
761	 * @param string	$mediatypes['passwd']				password
762	 * @param int		$mediatypes['status']				media type status
763	 * @param int		$mediatypes['maxsessions']			Limit of simultaneously processed alerts.
764	 * @param int		$mediatypes['maxattempts']			Maximum attempts to deliver alert successfully.
765	 * @param string	$mediatypes['attempt_interval']		Interval between alert delivery attempts.
766	 *
767	 * @return array
768	 */
769	public function create($mediatypes) {
770		$mediatypes = zbx_toArray($mediatypes);
771
772		$this->validateCreate($mediatypes);
773
774		$mediatypeids = DB::insert('media_type', $mediatypes);
775
776		return ['mediatypeids' => $mediatypeids];
777	}
778
779	/**
780	 * Update Media types.
781	 *
782	 * @param array		$mediatypes							multidimensional array with media types data
783	 * @param int		$mediatypes['mediatypeid']			id
784	 * @param int		$mediatypes['type']					type
785	 * @param string	$mediatypes['description']			description
786	 * @param string	$mediatypes['smtp_server']			SMTP server
787	 * @param int		$mediatypes['smtp_port']			SMTP port
788	 * @param string	$mediatypes['smtp_helo']			SMTP hello
789	 * @param string	$mediatypes['smtp_email']			SMTP email
790	 * @param int		$mediatypes['smtp_security']		SMTP connection security
791	 * @param int		$mediatypes['smtp_verify_peer']		SMTP verify peer
792	 * @param int		$mediatypes['smtp_verify_host']		SMTP verify host
793	 * @param int		$mediatypes['smtp_authentication']	SMTP authentication
794	 * @param string	$mediatypes['exec_path']			script name/message text limit
795	 * @param string	$mediatypes['exec_params']			script parameters
796	 * @param string	$mediatypes['gsm_modem']			GSM modem
797	 * @param string	$mediatypes['username']				username
798	 * @param string	$mediatypes['passwd']				password
799	 * @param int		$mediatypes['status']				media type status
800	 * @param int		$mediatypes['maxsessions']			Limit of simultaneously processed alerts.
801	 * @param int		$mediatypes['maxattempts']			Maximum attempts to deliver alert successfully.
802	 * @param string	$mediatypes['attempt_interval']		Interval between alert delivery attempts.
803	 *
804	 * @return array
805	 */
806	public function update($mediatypes) {
807		$mediatypes = zbx_toArray($mediatypes);
808
809		$this->validateUpdate($mediatypes);
810
811		$update = [];
812		foreach ($mediatypes as $mediatype) {
813			$mediatypeid = $mediatype['mediatypeid'];
814			unset($mediatype['mediatypeid']);
815
816			if (!empty($mediatype)) {
817				$update[] = [
818					'values' => $mediatype,
819					'where' => ['mediatypeid' => $mediatypeid]
820				];
821			}
822		}
823
824		DB::update('media_type', $update);
825		$mediatypeids = zbx_objectValues($mediatypes, 'mediatypeid');
826
827		return ['mediatypeids' => $mediatypeids];
828	}
829
830	/**
831	 * Delete Media types.
832	 *
833	 * @param array $mediatypeids
834	 *
835	 * @return array
836	 */
837	public function delete(array $mediatypeids) {
838		if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
839			self::exception(ZBX_API_ERROR_PERMISSIONS, _('Only Super Admins can delete media types.'));
840		}
841
842		$actions = API::Action()->get([
843			'mediatypeids' => $mediatypeids,
844			'output' => API_OUTPUT_EXTEND,
845			'preservekeys' => true
846		]);
847		if (!empty($actions)) {
848			$action = reset($actions);
849			self::exception(ZBX_API_ERROR_PARAMETERS, _s('Media types used by action "%s".', $action['name']));
850		}
851
852		DB::delete('media_type', ['mediatypeid' => $mediatypeids]);
853
854		return ['mediatypeids' => $mediatypeids];
855	}
856
857	/**
858	 * Check required fields by type. Values for fields must not be empty.
859	 *
860	 * @param array		$mediatype							An array of media type data.
861	 * @param string	$mediatype['description']			Name of the media type.
862	 * @param string	$mediatype['type']					E-mail, Script, SMS, Jabber and Ez Texting.
863	 *
864	 * @throws APIException if the input is invalid.
865	 */
866	protected function checkRequiredFieldsByType(array $mediatype) {
867		$type_validator = new CLimitedSetValidator([
868			'values' => array_keys(media_type2str())
869		]);
870
871		if (!$type_validator->validate($mediatype['type'])) {
872			self::exception(ZBX_API_ERROR_PARAMETERS, _s(
873				'Incorrect value "%1$s" in field "%2$s" for media type "%3$s".',
874				$mediatype['type'],
875				'type',
876				$mediatype['description']
877			));
878		}
879
880		$required_fields_by_type = [
881			MEDIA_TYPE_EMAIL => ['smtp_server', 'smtp_helo', 'smtp_email'],
882			MEDIA_TYPE_EXEC => ['exec_path'],
883			MEDIA_TYPE_SMS => ['gsm_modem'],
884			MEDIA_TYPE_JABBER => ['username', 'passwd'],
885			MEDIA_TYPE_EZ_TEXTING => ['exec_path', 'username', 'passwd']
886		];
887
888		foreach ($required_fields_by_type[$mediatype['type']] as $field) {
889			// Check if fields set on Create method. For update method they are checked when type is changed.
890			if (!array_key_exists($field, $mediatype)) {
891				self::exception(ZBX_API_ERROR_PARAMETERS,
892					_s('Field "%1$s" is required for media type "%2$s".', $field, $mediatype['description'])
893				);
894			}
895			elseif (array_key_exists($field, $mediatype)
896					&& ($mediatype[$field] === '' || $mediatype[$field] === null)) {
897				self::exception(ZBX_API_ERROR_PARAMETERS,
898					_s('Field "%1$s" is missing a value for media type "%2$s".', $field, $mediatype['description'])
899				);
900			}
901		}
902	}
903
904	protected function addRelatedObjects(array $options, array $result) {
905		$result = parent::addRelatedObjects($options, $result);
906
907		// adding users
908		if ($options['selectUsers'] !== null && $options['selectUsers'] != API_OUTPUT_COUNT) {
909			$relationMap = $this->createRelationMap($result, 'mediatypeid', 'userid', 'media');
910			$users = API::User()->get([
911				'output' => $options['selectUsers'],
912				'userids' => $relationMap->getRelatedIds(),
913				'preservekeys' => true
914			]);
915			$result = $relationMap->mapMany($result, $users, 'users');
916		}
917
918		return $result;
919	}
920}
921