public function convert($value)
 {
     $keys = ['tcp', 'ftp', 'http', 'imap', 'ldap', 'nntp', 'ntp', 'pop', 'smtp', 'ssh'];
     $perfKeys = ['tcp_perf', 'ftp_perf', 'http_perf', 'imap_perf', 'ldap_perf', 'nntp_perf', 'ntp_perf', 'pop_perf', 'smtp_perf', 'ssh_perf'];
     $parts = explode(',', $value);
     if (count($parts) <= 2) {
         $key = $parts[0];
         if (isset($parts[1]) && $parts[1] !== '') {
             if ($this->user_macro_parser->parse($parts[1] == CParser::PARSE_SUCCESS)) {
                 $port = ',,' . $parts[1];
             } else {
                 $pos = 0;
                 while (isset($parts[1][$pos]) && $parts[1][$pos] > '0' && $parts[1][$pos] < '9') {
                     $pos++;
                 }
                 if (isset($parts[1][$pos])) {
                     return $value;
                 }
                 $port = ',,' . $parts[1];
             }
         } else {
             $port = '';
         }
         if (in_array($key, $keys)) {
             return 'net.tcp.service[' . $key . $port . ']';
         } elseif (in_array($key, $perfKeys)) {
             list($key, $perfSuffix) = explode('_', $key);
             return 'net.tcp.service.perf[' . $key . $port . ']';
         }
     }
     return $value;
 }
 /**
  * Validate trigger function like last(0), time(), etc.
  * Examples:
  *	array(
  *		'function' => last("#15"),
  *		'functionName' => 'last',
  *		'functionParamList' => array(0 => '#15'),
  *		'valueType' => 3
  *	)
  *
  * @param string $value['function']
  * @param string $value['functionName']
  * @param array  $value['functionParamList']
  * @param int    $value['valueType']
  *
  * @return bool
  */
 public function validate($value)
 {
     $this->setError('');
     if (!isset($this->allowed[$value['functionName']])) {
         $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $value['function']) . ' ' . _('Unknown function.'));
         return false;
     }
     if (!isset($this->allowed[$value['functionName']]['value_types'][$value['valueType']])) {
         $this->setError(_s('Incorrect item value type "%1$s" provided for trigger function "%2$s".', itemValueTypeString($value['valueType']), $value['function']));
         return false;
     }
     if (count($this->allowed[$value['functionName']]['args']) < count($value['functionParamList'])) {
         $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $value['function']) . ' ' . _('Invalid number of parameters.'));
         return false;
     }
     $paramLabels = [_('Invalid first parameter.'), _('Invalid second parameter.'), _('Invalid third parameter.'), _('Invalid fourth parameter.')];
     $user_macro_parser = new CUserMacroParser();
     foreach ($this->allowed[$value['functionName']]['args'] as $aNum => $arg) {
         // mandatory check
         if (isset($arg['mandat']) && $arg['mandat'] && !isset($value['functionParamList'][$aNum])) {
             $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $value['function']) . ' ' . _('Mandatory parameter is missing.'));
             return false;
         }
         if (!isset($value['functionParamList'][$aNum])) {
             continue;
         }
         if (isset($arg['can_be_empty']) && $value['functionParamList'][$aNum] == '') {
             continue;
         }
         // user macro
         if ($user_macro_parser->parse($value['functionParamList'][$aNum]) == CParser::PARSE_SUCCESS) {
             continue;
         }
         if (!$this->validateParameter($value['functionParamList'][$aNum], $arg['type'])) {
             $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $value['function']) . ' ' . $paramLabels[$aNum]);
             return false;
         }
     }
     return true;
 }
Example #3
0
 /**
  * Parse key and parameters and put them into $this->parameters array.
  *
  * @param string	$data
  * @param int		$offset
  */
 public function parse($data, $offset = 0)
 {
     $this->length = 0;
     $this->match = '';
     $this->key = '';
     $this->parameters = [];
     for ($p = $offset; isset($data[$p]) && $this->isKeyChar($data[$p]); $p++) {
         // Code is not missing here.
     }
     // is key empty?
     if ($p == $offset) {
         $this->error = isset($data[$p]) ? $this->errorMessage(substr($data, $offset), 0) : _('key is empty');
         return self::PARSE_FAIL;
     }
     $_18_simple_check = false;
     // old-style simple checks
     if ($this->options['18_simple_checks'] && isset($data[$p]) && $data[$p] === ',') {
         $p++;
         $user_macro_parser = new CUserMacroParser();
         if ($user_macro_parser->parse($data, $p) != CParser::PARSE_FAIL) {
             $p += $user_macro_parser->getLength();
         } else {
             for (; isset($data[$p]) && $data[$p] > '0' && $data[$p] < '9'; $p++) {
                 // Code is not missing here.
             }
         }
         $_18_simple_check = true;
     }
     $this->key = substr($data, $offset, $p - $offset);
     $p2 = $p;
     if (!$_18_simple_check) {
         // Zapcat compatibility.
         for (; isset($data[$p2]) && $data[$p2] == '['; $p = $p2) {
             $_parameters = ['type' => self::PARAM_ARRAY, 'raw' => '', 'pos' => $p2 - $offset, 'parameters' => []];
             if (!$this->parseKeyParameters($data, $p2, $_parameters['parameters'])) {
                 break;
             }
             $_parameters['raw'] = substr($data, $p, $p2 - $p);
             $this->parameters[] = $_parameters;
         }
     }
     $this->length = $p - $offset;
     $this->match = substr($data, $offset, $this->length);
     if (!isset($data[$p])) {
         $this->error = '';
         return self::PARSE_SUCCESS;
     }
     $this->error = !isset($data[$p2]) ? _('unexpected end of key') : $this->errorMessage(substr($data, $offset), $p2 - $offset);
     return self::PARSE_SUCCESS_CONT;
 }
Example #4
0
/**
 * Merge list of inherited and host-level macros.
 *
 * Returns an array like:
 *   array(
 *       '{$MACRO}' => array(
 *           'macro' => '{$MACRO}',
 *           'type' => 0x03,						<- MACRO_TYPE_INHERITED, MACRO_TYPE_HOSTMACRO or MACRO_TYPE_BOTH
 *           'value' => 'effective value',
 *           'hostmacroid' => 7532,                 <- optional
 *           'template' => array(                   <- optional
 *               'value' => 'template-level value'
 *               'templateid' => 10001,
 *               'name' => 'Template OS Linux'
 *           ),
 *           'global' => array(                     <- optional
 *               'value' => 'global-level value'
 *           )
 *       )
 *   )
 *
 * @param array $host_macros		the list of host macros
 * @param array $inherited_macros	the list of inherited macros (the output of the getInheritedMacros() function)
 *
 * @return array
 */
function mergeInheritedMacros(array $host_macros, array $inherited_macros)
{
    $user_macro_parser = new CUserMacroParser();
    foreach ($inherited_macros as &$inherited_macro) {
        $inherited_macro['type'] = MACRO_TYPE_INHERITED;
        $inherited_macro['value'] = array_key_exists('template', $inherited_macro) ? $inherited_macro['template']['value'] : $inherited_macro['global']['value'];
    }
    unset($inherited_macro);
    /*
     * Global macros and template macros are overwritten by host macros. Macros with contexts require additional
     * checking for contexts, since {$MACRO:} is the same as {$MACRO:""}.
     */
    foreach ($host_macros as &$host_macro) {
        if (array_key_exists($host_macro['macro'], $inherited_macros)) {
            $host_macro = array_merge($inherited_macros[$host_macro['macro']], $host_macro);
            unset($inherited_macros[$host_macro['macro']]);
        } else {
            /*
             * Cannot use array dereferencing because "$host_macro['macro']" may contain invalid macros
             * which results in empty array.
             */
            if ($user_macro_parser->parse($host_macro['macro']) == CParser::PARSE_SUCCESS) {
                $hst_macro = $user_macro_parser->getMacro();
                $hst_context = $user_macro_parser->getContext();
                if ($hst_context === null) {
                    $host_macro['type'] = 0x0;
                } else {
                    $match_found = false;
                    foreach ($inherited_macros as $inherited_macro => $inherited_values) {
                        // Safe to use array dereferencing since these values come from database.
                        $user_macro_parser->parse($inherited_macro);
                        $inh_macro = $user_macro_parser->getMacro();
                        $inh_context = $user_macro_parser->getContext();
                        if ($hst_macro === $inh_macro && $hst_context === $inh_context) {
                            $match_found = true;
                            $host_macro = array_merge($inherited_macros[$inherited_macro], $host_macro);
                            unset($inherited_macros[$inherited_macro]);
                            break;
                        }
                    }
                    if (!$match_found) {
                        $host_macro['type'] = 0x0;
                    }
                }
            } else {
                $host_macro['type'] = 0x0;
            }
        }
        $host_macro['type'] |= MACRO_TYPE_HOSTMACRO;
    }
    unset($host_macro);
    foreach ($inherited_macros as $inherited_macro) {
        $host_macros[] = $inherited_macro;
    }
    return $host_macros;
}
Example #5
0
 /**
  * Validates the "ip" field.
  *
  * @throws APIException if the field is invalid.
  *
  * @param array $interface
  * @param string $interface['ip']
  */
 protected function checkIp(array $interface)
 {
     if ($interface['ip'] === '') {
         return;
     }
     $user_macro_parser = new CUserMacroParser();
     if (preg_match('/^' . ZBX_PREG_MACRO_NAME_FORMAT . '$/', $interface['ip']) || $user_macro_parser->parse($interface['ip']) == CParser::PARSE_SUCCESS) {
         return;
     }
     $ipValidator = new CIPValidator();
     if (!$ipValidator->validate($interface['ip'])) {
         self::exception(ZBX_API_ERROR_PARAMETERS, $ipValidator->getError());
     }
 }
Example #6
0
 /**
  * Validate http response code range.
  * Range can be empty string, can be set as user macro or be numeric and contain ',' and '-'.
  *
  * Examples: '100-199, 301, 404, 500-550' or '{$USER_MACRO123}'
  *
  * @throws APIException if the status code range is invalid.
  *
  * @param string $statusCodeRange
  *
  * @return bool
  */
 protected function checkStatusCode($statusCodeRange)
 {
     $user_macro_parser = new CUserMacroParser();
     if ($statusCodeRange === '' || $user_macro_parser->parse($statusCodeRange) == CParser::PARSE_SUCCESS) {
         return true;
     } else {
         $ranges = explode(',', $statusCodeRange);
         foreach ($ranges as $range) {
             $range = explode('-', $range);
             if (count($range) > 2) {
                 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid response code "%1$s".', $statusCodeRange));
             }
             foreach ($range as $value) {
                 if (!is_numeric($value)) {
                     self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid response code "%1$s".', $statusCodeRange));
                 }
             }
         }
     }
     return true;
 }
Example #7
0
 /**
  * Checks if any of the given host global macros already exist. If the macros are updated and
  * the "globalmacroid" field is set, the method will only fail, if a macro with a different globalmacroid exists.
  * Assumes the "macro", "hostmacroid" fields are valid.
  *
  * @param array $globalmacros
  * @param int $globalmacros[]['globalmacroid']
  * @param string $globalmacros[]['macro']
  * @param string $globalmacros[]['value']
  *
  * @throws APIException if any of the given macros already exist.
  */
 protected function checkIfGlobalMacrosDontRepeat(array $globalmacros)
 {
     if (!$globalmacros) {
         return;
     }
     $macro_names = [];
     $user_macro_parser = new CUserMacroParser();
     // Parse each macro, get unique names and, if context exists, narrow down the search.
     foreach ($globalmacros as $globalmacro) {
         $user_macro_parser->parse($globalmacro['macro']);
         $macro_name = $user_macro_parser->getMacro();
         $context = $user_macro_parser->getContext();
         if ($context === null) {
             $macro_names['{$' . $macro_name] = true;
         } else {
             // Narrow down the search for macros with contexts.
             $macro_names['{$' . $macro_name . ':'] = true;
         }
     }
     // When updating with empty array, don't select any data from database.
     $db_macros = API::getApiService()->select('globalmacro', ['output' => ['globalmacroid', 'macro'], 'search' => ['macro' => array_keys($macro_names)], 'searchByAny' => true]);
     $existing_macros = [];
     // Collect existing unique macro names and their contexts.
     foreach ($db_macros as $db_macro) {
         $user_macro_parser->parse($db_macro['macro']);
         $macro_name = $user_macro_parser->getMacro();
         $context = $user_macro_parser->getContext();
         $existing_macros[$macro_name][$db_macro['globalmacroid']] = $context;
     }
     // Compare each macro name and context to existing one.
     foreach ($globalmacros as $globalmacro) {
         $user_macro_parser->parse($globalmacro['macro']);
         $macro_name = $user_macro_parser->getMacro();
         $context = $user_macro_parser->getContext();
         if (array_key_exists($macro_name, $existing_macros) && in_array($context, $existing_macros[$macro_name], true)) {
             foreach ($existing_macros[$macro_name] as $globalmacroid => $existing_macro_context) {
                 if ((!array_key_exists('globalmacroid', $globalmacro) || bccomp($globalmacro['globalmacroid'], $globalmacroid) != 0) && $context === $existing_macro_context) {
                     self::exception(ZBX_API_ERROR_PARAMETERS, _s('Macro "%1$s" already exists.', $globalmacro['macro']));
                 }
             }
         }
     }
 }
Example #8
0
/**
 * Purpose: Replaces host in trigger expression.
 * {localhost:agent.ping.nodata(5m)}  =>  {localhost6:agent.ping.nodata(5m)}
 *
 * @param string $expression	full expression with host names and item keys
 * @param string $src_host
 * @param string $dst_host
 *
 * @return string
 */
function triggerExpressionReplaceHost($expression, $src_host, $dst_host)
{
    $new_expression = '';
    $function_macro_parser = new CFunctionMacroParser();
    $user_macro_parser = new CUserMacroParser();
    $macro_parser = new CMacroParser(['{TRIGGER.VALUE}']);
    $lld_macro_parser = new CLLDMacroParser();
    for ($pos = 0, $pos_left = 0; isset($expression[$pos]); $pos++) {
        if ($function_macro_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
            $host = $function_macro_parser->getHost();
            $item = $function_macro_parser->getItem();
            $function = $function_macro_parser->getFunction();
            if ($host === $src_host) {
                $host = $dst_host;
            }
            $new_expression .= substr($expression, $pos_left, $pos - $pos_left);
            $new_expression .= '{' . $host . ':' . $item . '.' . $function . '}';
            $pos_left = $pos + $function_macro_parser->getLength();
            $pos += $function_macro_parser->getLength() - 1;
        } elseif ($user_macro_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
            $pos += $user_macro_parser->getLength() - 1;
        } elseif ($macro_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
            $pos += $macro_parser->getLength() - 1;
        } elseif ($lld_macro_parser->parse($expression, $pos) != CParser::PARSE_FAIL) {
            $pos += $lld_macro_parser->getLength() - 1;
        }
    }
    $new_expression .= substr($expression, $pos_left, $pos - $pos_left);
    return $new_expression;
}
Example #9
0
 /**
  * @param array $operations
  *
  * @return bool
  */
 public function validateOperationsIntegrity($operations)
 {
     $operations = zbx_toArray($operations);
     foreach ($operations as $operation) {
         if ((isset($operation['esc_step_from']) || isset($operation['esc_step_to'])) && !isset($operation['esc_step_from'], $operation['esc_step_to'])) {
             self::exception(ZBX_API_ERROR_PARAMETERS, _('esc_step_from and esc_step_to must be set together.'));
         }
         if (isset($operation['esc_step_from'], $operation['esc_step_to'])) {
             if ($operation['esc_step_from'] < 1 || $operation['esc_step_to'] < 0) {
                 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation escalation step values.'));
             }
             if ($operation['esc_step_from'] > $operation['esc_step_to'] && $operation['esc_step_to'] != 0) {
                 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation escalation step values.'));
             }
         }
         if (isset($operation['esc_period'])) {
             if (isset($operation['esc_period']) && $operation['esc_period'] != 0 && $operation['esc_period'] < SEC_PER_MIN) {
                 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation step duration.'));
             }
         }
         $hostIdsAll = [];
         $hostGroupIdsAll = [];
         $userIdsAll = [];
         $userGroupIdsAll = [];
         switch ($operation['operationtype']) {
             case OPERATION_TYPE_MESSAGE:
                 $userIds = isset($operation['opmessage_usr']) ? zbx_objectValues($operation['opmessage_usr'], 'userid') : [];
                 $userGroupIds = isset($operation['opmessage_grp']) ? zbx_objectValues($operation['opmessage_grp'], 'usrgrpid') : [];
                 if (empty($userIds) && empty($userGroupIds)) {
                     self::exception(ZBX_API_ERROR_PARAMETERS, _('No recipients for action operation message.'));
                 }
                 $userIdsAll = array_merge($userIdsAll, $userIds);
                 $userGroupIdsAll = array_merge($userGroupIdsAll, $userGroupIds);
                 break;
             case OPERATION_TYPE_COMMAND:
                 if (!isset($operation['opcommand']['type'])) {
                     self::exception(ZBX_API_ERROR_PARAMETERS, _('No command type specified for action operation.'));
                 }
                 if ((!isset($operation['opcommand']['command']) || zbx_empty(trim($operation['opcommand']['command']))) && $operation['opcommand']['type'] != ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT) {
                     self::exception(ZBX_API_ERROR_PARAMETERS, _s('No command specified for action operation.'));
                 }
                 switch ($operation['opcommand']['type']) {
                     case ZBX_SCRIPT_TYPE_IPMI:
                         break;
                     case ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT:
                         if (!isset($operation['opcommand']['execute_on'])) {
                             self::exception(ZBX_API_ERROR_PARAMETERS, _s('No execution target specified for action operation command "%s".', $operation['opcommand']['command']));
                         }
                         break;
                     case ZBX_SCRIPT_TYPE_SSH:
                         if (!isset($operation['opcommand']['authtype']) || zbx_empty($operation['opcommand']['authtype'])) {
                             self::exception(ZBX_API_ERROR_PARAMETERS, _s('No authentication type specified for action operation command "%s".', $operation['opcommand']['command']));
                         }
                         if (!isset($operation['opcommand']['username']) || zbx_empty($operation['opcommand']['username'])) {
                             self::exception(ZBX_API_ERROR_PARAMETERS, _s('No authentication user name specified for action operation command "%s".', $operation['opcommand']['command']));
                         }
                         if ($operation['opcommand']['authtype'] == ITEM_AUTHTYPE_PUBLICKEY) {
                             if (!isset($operation['opcommand']['publickey']) || zbx_empty($operation['opcommand']['publickey'])) {
                                 self::exception(ZBX_API_ERROR_PARAMETERS, _s('No public key file specified for action operation command "%s".', $operation['opcommand']['command']));
                             }
                             if (!isset($operation['opcommand']['privatekey']) || zbx_empty($operation['opcommand']['privatekey'])) {
                                 self::exception(ZBX_API_ERROR_PARAMETERS, _s('No private key file specified for action operation command "%s".', $operation['opcommand']['command']));
                             }
                         }
                         break;
                     case ZBX_SCRIPT_TYPE_TELNET:
                         if (!isset($operation['opcommand']['username']) || zbx_empty($operation['opcommand']['username'])) {
                             self::exception(ZBX_API_ERROR_PARAMETERS, _s('No authentication user name specified for action operation command "%s".', $operation['opcommand']['command']));
                         }
                         break;
                     case ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT:
                         if (!isset($operation['opcommand']['scriptid']) || zbx_empty($operation['opcommand']['scriptid'])) {
                             self::exception(ZBX_API_ERROR_PARAMETERS, _('No script specified for action operation command.'));
                         }
                         $scripts = API::Script()->get(['output' => ['scriptid', 'name'], 'scriptids' => $operation['opcommand']['scriptid'], 'preservekeys' => true]);
                         if (!isset($scripts[$operation['opcommand']['scriptid']])) {
                             self::exception(ZBX_API_ERROR_PARAMETERS, _('Specified script does not exist or you do not have rights on it for action operation command.'));
                         }
                         break;
                     default:
                         self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation command type.'));
                 }
                 if (isset($operation['opcommand']['port']) && !zbx_empty($operation['opcommand']['port'])) {
                     if (zbx_ctype_digit($operation['opcommand']['port'])) {
                         if ($operation['opcommand']['port'] > 65535 || $operation['opcommand']['port'] < 1) {
                             self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect action operation port "%s".', $operation['opcommand']['port']));
                         }
                     } else {
                         $user_macro_parser = new CUserMacroParser();
                         if ($user_macro_parser->parse($operation['opcommand']['port']) != CParser::PARSE_SUCCESS) {
                             self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect action operation port "%s".', $operation['opcommand']['port']));
                         }
                     }
                 }
                 $groupIds = [];
                 if (isset($operation['opcommand_grp'])) {
                     $groupIds = zbx_objectValues($operation['opcommand_grp'], 'groupid');
                 }
                 $hostIds = [];
                 $withoutCurrent = true;
                 if (isset($operation['opcommand_hst'])) {
                     foreach ($operation['opcommand_hst'] as $hstCommand) {
                         if ($hstCommand['hostid'] == 0) {
                             $withoutCurrent = false;
                         } else {
                             $hostIds[$hstCommand['hostid']] = $hstCommand['hostid'];
                         }
                     }
                 }
                 if (empty($groupIds) && empty($hostIds) && $withoutCurrent) {
                     if ($operation['opcommand']['type'] == ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT) {
                         self::exception(ZBX_API_ERROR_PARAMETERS, _s('You did not specify targets for action operation global script "%s".', $scripts[$operation['opcommand']['scriptid']]['name']));
                     } else {
                         self::exception(ZBX_API_ERROR_PARAMETERS, _s('You did not specify targets for action operation command "%s".', $operation['opcommand']['command']));
                     }
                 }
                 $hostIdsAll = array_merge($hostIdsAll, $hostIds);
                 $hostGroupIdsAll = array_merge($hostGroupIdsAll, $groupIds);
                 break;
             case OPERATION_TYPE_GROUP_ADD:
             case OPERATION_TYPE_GROUP_REMOVE:
                 $groupIds = isset($operation['opgroup']) ? zbx_objectValues($operation['opgroup'], 'groupid') : [];
                 if (empty($groupIds)) {
                     self::exception(ZBX_API_ERROR_PARAMETERS, _('Operation has no group to operate.'));
                 }
                 $hostGroupIdsAll = array_merge($hostGroupIdsAll, $groupIds);
                 break;
             case OPERATION_TYPE_TEMPLATE_ADD:
             case OPERATION_TYPE_TEMPLATE_REMOVE:
                 $templateIds = isset($operation['optemplate']) ? zbx_objectValues($operation['optemplate'], 'templateid') : [];
                 if (empty($templateIds)) {
                     self::exception(ZBX_API_ERROR_PARAMETERS, _('Operation has no template to operate.'));
                 }
                 $hostIdsAll = array_merge($hostIdsAll, $templateIds);
                 break;
             case OPERATION_TYPE_HOST_ADD:
             case OPERATION_TYPE_HOST_REMOVE:
             case OPERATION_TYPE_HOST_ENABLE:
             case OPERATION_TYPE_HOST_DISABLE:
                 break;
             case OPERATION_TYPE_HOST_INVENTORY:
                 if (!array_key_exists('opinventory', $operation) || !array_key_exists('inventory_mode', $operation['opinventory'])) {
                     self::exception(ZBX_API_ERROR_PARAMETERS, _('No inventory mode specified for action operation.'));
                 }
                 if ($operation['opinventory']['inventory_mode'] != HOST_INVENTORY_MANUAL && $operation['opinventory']['inventory_mode'] != HOST_INVENTORY_AUTOMATIC) {
                     self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect inventory mode in action operation.'));
                 }
                 break;
             default:
                 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation type.'));
         }
     }
     if (!API::HostGroup()->isWritable($hostGroupIdsAll)) {
         self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation host group. Host group does not exist or you have no access to this host group.'));
     }
     if (!API::Host()->isWritable($hostIdsAll)) {
         self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation host. Host does not exist or you have no access to this host.'));
     }
     if (!API::User()->isReadable($userIdsAll)) {
         self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation user. User does not exist or you have no access to this user.'));
     }
     if (!API::UserGroup()->isReadable($userGroupIdsAll)) {
         self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation user group. User group does not exist or you have no access to this user group.'));
     }
     return true;
 }
 /**
  * Get macros with values.
  *
  * @param array $data
  * @param array $data[<id>]			any identificator
  * @param array $data['hostids']	the list of host ids; [<hostid1>, ...]
  * @param array $data['macros']		the list of user macros to resolve, ['<usermacro1>' => null, ...]
  *
  * @return array
  */
 protected function getUserMacros(array $data)
 {
     // User macros.
     $hostids = [];
     foreach ($data as $element) {
         foreach ($element['hostids'] as $hostid) {
             $hostids[$hostid] = true;
         }
     }
     if (!$hostids) {
         return $data;
     }
     /*
      * @var array $host_templates
      * @var array $host_templates[<hostid>]		array of templates
      */
     $host_templates = [];
     /*
      * @var array  $host_macros
      * @var array  $host_macros[<hostid>]
      * @var array  $host_macros[<hostid>][<macro>]				macro base without curly braces
      * @var string $host_macros[<hostid>][<macro>]['value']		base macro value (without context); can be null
      * @var array  $host_macros[<hostid>][<macro>]['contexts']	context values; ['<context1>' => '<value1>', ...]
      */
     $host_macros = [];
     $user_macro_parser = new CUserMacroParser();
     do {
         $db_hosts = API::Host()->get(['hostids' => array_keys($hostids), 'templated_hosts' => true, 'output' => ['hostid'], 'selectParentTemplates' => ['templateid'], 'selectMacros' => ['macro', 'value']]);
         $hostids = [];
         foreach ($db_hosts as $db_host) {
             $host_templates[$db_host['hostid']] = zbx_objectValues($db_host['parentTemplates'], 'templateid');
             foreach ($db_host['macros'] as $db_macro) {
                 if ($user_macro_parser->parse($db_macro['macro']) != CParser::PARSE_SUCCESS) {
                     continue;
                 }
                 $macro = $user_macro_parser->getMacro();
                 $context = $user_macro_parser->getContext();
                 if (!array_key_exists($db_host['hostid'], $host_macros)) {
                     $host_macros[$db_host['hostid']] = [];
                 }
                 if (!array_key_exists($macro, $host_macros[$db_host['hostid']])) {
                     $host_macros[$db_host['hostid']][$macro] = ['value' => null, 'contexts' => []];
                 }
                 if ($context === null) {
                     $host_macros[$db_host['hostid']][$macro]['value'] = $db_macro['value'];
                 } else {
                     $host_macros[$db_host['hostid']][$macro]['contexts'][$context] = $db_macro['value'];
                 }
             }
         }
         foreach ($db_hosts as $db_host) {
             // Only unprocessed templates will be populated.
             foreach ($host_templates[$db_host['hostid']] as $templateid) {
                 if (!array_key_exists($templateid, $host_templates)) {
                     $hostids[$templateid] = true;
                 }
             }
         }
     } while ($hostids);
     $all_macros_resolved = true;
     $user_macro_parser = new CUserMacroParser();
     foreach ($data as &$element) {
         $hostids = [];
         foreach ($element['hostids'] as $hostid) {
             $hostids[$hostid] = true;
         }
         $hostids = array_keys($hostids);
         natsort($hostids);
         foreach ($element['macros'] as $usermacro => &$value) {
             if ($user_macro_parser->parse($usermacro) == CParser::PARSE_SUCCESS) {
                 $value = $this->getHostUserMacros($hostids, $user_macro_parser->getMacro(), $user_macro_parser->getContext(), $host_templates, $host_macros);
                 if ($value['value'] === null) {
                     $all_macros_resolved = false;
                 }
             } else {
                 // This macro cannot be resolved.
                 $value = ['value' => $usermacro, 'value_default' => null];
             }
         }
         unset($value);
     }
     unset($element);
     if (!$all_macros_resolved) {
         // Global macros.
         $db_global_macros = API::UserMacro()->get(['output' => ['macro', 'value'], 'globalmacro' => true]);
         /*
          * @var array  $global_macros
          * @var array  $global_macros[<macro>]				macro base without curly braces
          * @var string $global_macros[<macro>]['value']		base macro value (without context); can be null
          * @var array  $global_macros[<macro>]['contexts']	context values; ['<context1>' => '<value1>', ...]
          */
         $global_macros = [];
         foreach ($db_global_macros as $db_global_macro) {
             if ($user_macro_parser->parse($db_global_macro['macro']) == CParser::PARSE_SUCCESS) {
                 $macro = $user_macro_parser->getMacro();
                 $context = $user_macro_parser->getContext();
                 if (!array_key_exists($macro, $global_macros)) {
                     $global_macros[$macro] = ['value' => null, 'contexts' => []];
                 }
                 if ($context === null) {
                     $global_macros[$macro]['value'] = $db_global_macro['value'];
                 } else {
                     $global_macros[$macro]['contexts'][$context] = $db_global_macro['value'];
                 }
             }
         }
         foreach ($data as &$element) {
             foreach ($element['macros'] as $usermacro => &$value) {
                 if ($value['value'] === null && $user_macro_parser->parse($usermacro) == CParser::PARSE_SUCCESS) {
                     $macro = $user_macro_parser->getMacro();
                     $context = $user_macro_parser->getContext();
                     if (array_key_exists($macro, $global_macros)) {
                         if ($context !== null && array_key_exists($context, $global_macros[$macro]['contexts'])) {
                             $value['value'] = $global_macros[$macro]['contexts'][$context];
                         } elseif ($global_macros[$macro]['value'] !== null) {
                             if ($context === null) {
                                 $value['value'] = $global_macros[$macro]['value'];
                             } elseif ($value['value_default'] === null) {
                                 $value['value_default'] = $global_macros[$macro]['value'];
                             }
                         }
                     }
                 }
             }
             unset($value);
         }
         unset($element);
     }
     foreach ($data as &$element) {
         foreach ($element['macros'] as $usermacro => &$value) {
             if ($value['value'] !== null) {
                 $value = $value['value'];
             } elseif ($value['value_default'] !== null) {
                 $value = $value['value_default'];
             } else {
                 // Unresolved macro.
                 $value = $usermacro;
             }
         }
         unset($value);
     }
     unset($element);
     return $data;
 }