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 * Controller to build preprocessing test dialog. 24 */ 25class CControllerPopupItemTestEdit extends CControllerPopupItemTest { 26 27 protected function checkInput() { 28 $fields = [ 29 'authtype' => 'in '.implode(',', [HTTPTEST_AUTH_NONE, HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST, ITEM_AUTHTYPE_PASSWORD, ITEM_AUTHTYPE_PUBLICKEY]), 30 'data' => 'array', 31 'delay' => 'string', 32 'get_value' => 'in 0,1', 33 'headers' => 'array', 34 'hostid' => 'db hosts.hostid', 35 'http_authtype' => 'in '.implode(',', [HTTPTEST_AUTH_NONE, HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST, ITEM_AUTHTYPE_PASSWORD, ITEM_AUTHTYPE_PUBLICKEY]), 36 'http_password' => 'string', 37 'http_proxy' => 'string', 38 'http_username' => 'string', 39 'follow_redirects' => 'in 0,1', 40 'key' => 'string', 41 'interfaceid' => 'db interface.interfaceid', 42 'ipmi_sensor' => 'string', 43 'itemid' => 'db items.itemid', 44 'item_type' => 'in '.implode(',', [ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_HTTPTEST, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT]), 45 'jmx_endpoint' => 'string', 46 'output_format' => 'in '.implode(',', [HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON]), 47 'params_ap' => 'string', 48 'params_es' => 'string', 49 'params_f' => 'string', 50 'script' => 'string', 51 'password' => 'string', 52 'post_type' => 'in '.implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML]), 53 'posts' => 'string', 54 'privatekey' => 'string', 55 'publickey' => 'string', 56 'query_fields' => 'array', 57 'parameters' => 'array', 58 'request_method' => 'in '.implode(',', [HTTPCHECK_REQUEST_GET, HTTPCHECK_REQUEST_POST, HTTPCHECK_REQUEST_PUT, HTTPCHECK_REQUEST_HEAD]), 59 'retrieve_mode' => 'in '.implode(',', [HTTPTEST_STEP_RETRIEVE_MODE_CONTENT, HTTPTEST_STEP_RETRIEVE_MODE_HEADERS, HTTPTEST_STEP_RETRIEVE_MODE_BOTH]), 60 'show_final_result' => 'in 0,1', 61 'snmp_oid' => 'string', 62 'step_obj' => 'required|int32', 63 'steps' => 'array', 64 'ssl_cert_file' => 'string', 65 'ssl_key_file' => 'string', 66 'ssl_key_password' => 'string', 67 'status_codes' => 'string', 68 'test_type' => 'required|in '.implode(',', [self::ZBX_TEST_TYPE_ITEM, self::ZBX_TEST_TYPE_ITEM_PROTOTYPE, self::ZBX_TEST_TYPE_LLD]), 69 'timeout' => 'string', 70 'username' => 'string', 71 'url' => 'string', 72 'value_type' => 'in '.implode(',', [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT]), 73 'valuemapid' => 'int32', 74 'verify_host' => 'in 0,1', 75 'verify_peer' => 'in 0,1' 76 ]; 77 78 $ret = $this->validateInput($fields); 79 80 if ($ret) { 81 $testable_item_types = self::getTestableItemTypes($this->getInput('hostid', '0')); 82 $this->item_type = $this->hasInput('item_type') ? $this->getInput('item_type') : -1; 83 $this->preproc_item = self::getPreprocessingItemClassInstance($this->getInput('test_type')); 84 $this->is_item_testable = in_array($this->item_type, $testable_item_types); 85 86 // Check if key is valid for item types it's mandatory. 87 if (in_array($this->item_type, $this->item_types_has_key_mandatory)) { 88 $item_key_parser = new CItemKey(); 89 90 if ($item_key_parser->parse($this->getInput('key', '')) != CParser::PARSE_SUCCESS) { 91 error(_s('Incorrect value for field "%1$s": %2$s.', 'key_', $item_key_parser->getError())); 92 $ret = false; 93 } 94 } 95 96 /* 97 * Either the item must be testable or at least one preprocessing test must be passed ("Test" button should 98 * be disabled otherwise). 99 */ 100 $steps = $this->getInput('steps', []); 101 if ($ret && $steps) { 102 $steps_validation_response = $this->preproc_item->validateItemPreprocessingSteps($steps); 103 if ($steps_validation_response !== true) { 104 error($steps_validation_response); 105 $ret = false; 106 } 107 } 108 elseif ($ret && !$this->is_item_testable) { 109 error(_s('Test of "%1$s" items is not supported.', item_type2str($this->item_type))); 110 $ret = false; 111 } 112 } 113 114 if (($messages = getMessages(false, null, false)) !== null) { 115 $this->setResponse( 116 (new CControllerResponseData([ 117 'main_block' => json_encode(['errors' => $messages->toString()]) 118 ]))->disableView() 119 ); 120 } 121 122 return $ret; 123 } 124 125 protected function doAction() { 126 // VMware and icmpping simple checks are not supported. 127 $key = $this->hasInput('key') ? $this->getInput('key') : ''; 128 if ($this->item_type == ITEM_TYPE_SIMPLE 129 && (substr($key, 0, 7) === 'vmware.' || substr($key, 0, 8) === 'icmpping')) { 130 $this->is_item_testable = false; 131 } 132 133 // Get item and host properties and values from cache. 134 $data = $this->getInput('data', []); 135 $inputs = $this->getItemTestProperties($this->getInputAll()); 136 137 // Work with preprocessing steps. 138 $preprocessing_steps_input = $this->getInput('steps', []); 139 $preprocessing_steps = []; 140 foreach ($preprocessing_steps_input as $preproc) { 141 if ($preproc['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) { 142 array_unshift($preprocessing_steps, $preproc); 143 } 144 else { 145 $preprocessing_steps[] = $preproc; 146 } 147 } 148 149 $preprocessing_types = zbx_objectValues($preprocessing_steps, 'type'); 150 $preprocessing_names = get_preprocessing_types(null, false, $preprocessing_types); 151 $support_lldmacros = ($this->preproc_item instanceof CItemPrototype); 152 $show_prev = (count(array_intersect($preprocessing_types, self::$preproc_steps_using_prev_value)) > 0); 153 154 // Collect item texts and macros to later check their usage. 155 $texts_support_macros = []; 156 $texts_support_user_macros = []; 157 $texts_support_lld_macros = []; 158 $supported_macros = []; 159 foreach (array_keys(array_intersect_key($inputs, $this->macros_by_item_props)) as $field) { 160 // Special processing for calculated item formula. 161 if ($field === 'params_f') { 162 $expression_parser = new CExpressionParser([ 163 'usermacros' => true, 164 'lldmacros' => $support_lldmacros, 165 'calculated' => true, 166 'host_macro' => true, 167 'empty_host' => true 168 ]); 169 170 if ($expression_parser->parse($inputs[$field]) == CParser::PARSE_SUCCESS) { 171 $tokens = $expression_parser->getResult()->getTokensOfTypes([ 172 CExpressionParserResult::TOKEN_TYPE_USER_MACRO, 173 CExpressionParserResult::TOKEN_TYPE_LLD_MACRO, 174 CExpressionParserResult::TOKEN_TYPE_STRING, 175 CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION 176 ]); 177 foreach ($tokens as $token) { 178 switch ($token['type']) { 179 case CExpressionParserResult::TOKEN_TYPE_USER_MACRO: 180 $texts_support_user_macros[] = $token['match']; 181 break; 182 183 case CExpressionParserResult::TOKEN_TYPE_LLD_MACRO: 184 $texts_support_lld_macros[] = $token['match']; 185 break; 186 187 case CExpressionParserResult::TOKEN_TYPE_STRING: 188 $text = CExpressionParser::unquoteString($token['match']); 189 $texts_support_user_macros[] = $text; 190 $texts_support_lld_macros[] = $text; 191 break; 192 193 case CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION: 194 foreach ($token['data']['parameters'][0]['data']['filter']['tokens'] as $filter_token) { 195 switch ($filter_token['type']) { 196 case CFilterParser::TOKEN_TYPE_USER_MACRO: 197 $texts_support_user_macros[] = $filter_token['match']; 198 break; 199 200 case CFilterParser::TOKEN_TYPE_LLD_MACRO: 201 $texts_support_lld_macros[] = $filter_token['match']; 202 break; 203 204 case CFilterParser::TOKEN_TYPE_STRING: 205 $text = CFilterParser::unquoteString($filter_token['match']); 206 $texts_support_user_macros[] = $text; 207 $texts_support_lld_macros[] = $text; 208 break; 209 } 210 } 211 break; 212 } 213 } 214 } 215 continue; 216 } 217 218 $macros = $this->macros_by_item_props[$field]; 219 unset($macros['support_lld_macros'], $macros['support_user_macros']); 220 221 if ($field === 'query_fields' || $field === 'headers' || $field === 'parameters') { 222 if (!array_key_exists($field, $inputs) || !$inputs[$field]) { 223 continue; 224 } 225 226 foreach (['name', 'value'] as $key) { 227 $texts_having_macros = array_filter($inputs[$field][$key], function($str) { 228 return (strstr($str, '{') !== false); 229 }); 230 231 if ($texts_having_macros) { 232 $supported_macros = array_merge_recursive($supported_macros, $macros); 233 $texts_support_macros = array_merge($texts_support_macros, $texts_having_macros); 234 $texts_support_user_macros = array_merge($texts_support_user_macros, $texts_having_macros); 235 236 if ($support_lldmacros) { 237 $texts_support_lld_macros = array_merge($texts_support_lld_macros, $texts_having_macros); 238 } 239 } 240 } 241 } 242 elseif (strstr($inputs[$field], '{') !== false) { 243 // Field support macros like {HOST.*}, {ITEM.*} etc. 244 if ($macros) { 245 $supported_macros = array_merge_recursive($supported_macros, $macros); 246 $texts_support_macros[] = $inputs[$field]; 247 } 248 249 // Check if LLD macros are supported in field. 250 if ($support_lldmacros && $this->macros_by_item_props[$field]['support_lld_macros']) { 251 $texts_support_lld_macros[] = $inputs[$field]; 252 } 253 254 // Check if user macros are supported in field. 255 if ($this->macros_by_item_props[$field]['support_user_macros']) { 256 $texts_support_user_macros[] = $inputs[$field]; 257 } 258 } 259 } 260 261 // Unset duplicate macros. 262 foreach ($supported_macros as &$item_macros_type) { 263 $item_macros_type = array_unique($item_macros_type); 264 } 265 unset($item_macros_type); 266 267 // Extract macros and apply effective values for each of them. 268 $usermacros = CMacrosResolverHelper::extractItemTestMacros([ 269 'steps' => $preprocessing_steps, 270 'delay' => $show_prev ? $this->getInput('delay', ZBX_ITEM_DELAY_DEFAULT) : '', 271 'supported_macros' => $supported_macros, 272 'support_lldmacros' => $support_lldmacros, 273 'texts_support_macros' => $texts_support_macros, 274 'texts_support_user_macros' => $texts_support_user_macros, 275 'texts_support_lld_macros' => $texts_support_lld_macros, 276 'hostid' => $this->host ? $this->host['hostid'] : 0, 277 'macros_values' => $this->getSupportedMacros($inputs + ['interfaceid' => $this->getInput('interfaceid', 0)]) 278 ]); 279 280 $show_warning = false; 281 282 if (array_key_exists('interface', $inputs)) { 283 if (array_key_exists('address', $inputs['interface']) 284 && strstr($inputs['interface']['address'], ZBX_SECRET_MASK) !== false) { 285 $inputs['interface']['address'] = ''; 286 $show_warning = true; 287 } 288 289 if (array_key_exists('port', $inputs['interface']) && $inputs['interface']['port'] === ZBX_SECRET_MASK) { 290 $inputs['interface']['port'] = ''; 291 $show_warning = true; 292 } 293 294 if (array_key_exists('details', $inputs['interface'])) { 295 foreach ($inputs['interface']['details'] as $field => $value) { 296 if (strstr($value, ZBX_SECRET_MASK) !== false) { 297 $inputs['interface']['details'][$field] = ''; 298 $show_warning = true; 299 } 300 } 301 } 302 } 303 304 // Set resolved macros to previously specified values. 305 foreach (array_keys($usermacros['macros']) as $macro_name) { 306 if ($usermacros['macros'] && array_key_exists('macros', $data) && is_array($data['macros']) 307 && array_key_exists($macro_name, $data['macros'])) { 308 // Macro values were set by user. Which means those could be intentional asterisks or empty fields. 309 $usermacros['macros'][$macro_name] = $data['macros'][$macro_name]; 310 } 311 elseif ($usermacros['macros'][$macro_name] === ZBX_SECRET_MASK) { 312 /* 313 * Macro values were not set by user, so this means form was opened for the first time. So in this 314 * case check if there are secret macros. If there are, clear the values and show warning message box. 315 */ 316 317 $usermacros['macros'][$macro_name] = ''; 318 $show_warning = true; 319 } 320 } 321 322 // Get previous value and time. 323 $prev_value = ''; 324 $prev_time = ''; 325 if ($show_prev && array_key_exists('prev_value', $data) && $data['prev_value'] !== '') { 326 $prev_value = $data['prev_value']; 327 328 // Get previous value time. 329 if (array_key_exists('prev_time', $data)) { 330 $prev_time = $data['prev_time']; 331 } 332 else { 333 $delay = timeUnitToSeconds($usermacros['delay']); 334 $prev_time = ($delay !== null && $delay > 0) 335 ? 'now-'.$usermacros['delay'] 336 : 'now'; 337 } 338 } 339 340 // Sort macros. 341 ksort($usermacros['macros']); 342 343 // Add step number and name for each preprocessing step. 344 $num = 0; 345 foreach ($preprocessing_steps as &$step) { 346 $step['name'] = $preprocessing_names[$step['type']]; 347 $step['num'] = ++$num; 348 } 349 unset($step); 350 351 $this->setResponse(new CControllerResponseData([ 352 'title' => _('Test item'), 353 'steps' => $preprocessing_steps, 354 'value' => array_key_exists('value', $data) ? $data['value'] : '', 355 'eol' => array_key_exists('eol', $data) ? (int) $data['eol'] : ZBX_EOL_LF, 356 'macros' => $usermacros['macros'], 357 'show_prev' => $show_prev, 358 'prev_value' => $prev_value, 359 'prev_time' => $prev_time, 360 'hostid' => $this->getInput('hostid'), 361 'interfaceid' => $this->getInput('interfaceid', 0), 362 'test_type' => $this->getInput('test_type'), 363 'step_obj' => $this->getInput('step_obj'), 364 'show_final_result' => $this->getInput('show_final_result'), 365 'valuemapid' => $this->getInput('valuemapid', 0), 366 'get_value' => array_key_exists('get_value', $data) 367 ? $data['get_value'] 368 : $this->getInput('get_value', 0), 369 'is_item_testable' => $this->is_item_testable, 370 'inputs' => $inputs, 371 'proxies' => in_array($this->item_type, $this->items_support_proxy) ? $this->getHostProxies() : [], 372 'proxies_enabled' => in_array($this->item_type, $this->items_support_proxy), 373 'interface_address_enabled' => (array_key_exists($this->item_type, $this->items_require_interface) 374 && $this->items_require_interface[$this->item_type]['address'] 375 ), 376 'interface_port_enabled' => (array_key_exists($this->item_type, $this->items_require_interface) 377 && $this->items_require_interface[$this->item_type]['port'] 378 ), 379 'preproc_item' => $this->preproc_item, 380 'show_snmp_form' => ($this->item_type == ITEM_TYPE_SNMP), 381 'show_warning' => $show_warning, 382 'user' => [ 383 'debug_mode' => $this->getDebugMode() 384 ] 385 ])); 386 } 387} 388