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; }
/** * 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; }
/** * 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; }
/** * 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()); } }
/** * 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; }
/** * 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'])); } } } } }
/** * 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; }
/** * @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; }