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 * API validator 24 */ 25class CApiInputValidator { 26 27 /** 28 * Base validation function. 29 * 30 * @param array $rule validation rule 31 * @param mixed $data import data 32 * @param string $path data path (for error reporting) 33 * @param string $error 34 * 35 * @return bool 36 */ 37 public static function validate(array $rule, &$data, $path, &$error) { 38 $error = ''; 39 40 return self::validateData($rule, $data, $path, $error) 41 && self::validateDataUniqueness($rule, $data, $path, $error); 42 } 43 44 /** 45 * Base uniqueness validation function. 46 * 47 * @param array $rule validation rule 48 * @param mixed $data import data 49 * @param string $path data path (for error reporting) 50 * @param string $error 51 * 52 * @return bool 53 */ 54 public static function validateUniqueness(array $rule, $data, $path, &$error) { 55 $error = ''; 56 57 return self::validateDataUniqueness($rule, $data, $path, $error); 58 } 59 60 /** 61 * Base data validation function. 62 * 63 * @param array $rule 64 * @param mixed $data 65 * @param string $path 66 * @param string $error 67 * @param array $parent_data 68 * 69 * @return bool 70 */ 71 private static function validateData($rule, &$data, $path, &$error, array $parent_data = null) { 72 switch ($rule['type']) { 73 case API_COLOR: 74 return self::validateColor($rule, $data, $path, $error); 75 76 case API_MULTIPLE: 77 if ($parent_data !== null) { 78 return self::validateMultiple($rule, $data, $path, $error, $parent_data); 79 } 80 break; 81 82 case API_STRING_UTF8: 83 return self::validateStringUtf8($rule, $data, $path, $error); 84 85 case API_STRINGS_UTF8: 86 return self::validateStringsUtf8($rule, $data, $path, $error); 87 88 case API_INT32: 89 return self::validateInt32($rule, $data, $path, $error); 90 91 case API_INTS32: 92 return self::validateInts32($rule, $data, $path, $error); 93 94 case API_UINT64: 95 return self::validateUInt64($rule, $data, $path, $error); 96 97 case API_UINTS64: 98 return self::validateUInts64($rule, $data, $path, $error); 99 100 case API_FLOAT: 101 return self::validateFloat($rule, $data, $path, $error); 102 103 case API_FLOATS: 104 return self::validateFloats($rule, $data, $path, $error); 105 106 case API_ID: 107 return self::validateId($rule, $data, $path, $error); 108 109 case API_BOOLEAN: 110 return self::validateBoolean($rule, $data, $path, $error); 111 112 case API_FLAG: 113 return self::validateFlag($rule, $data, $path, $error); 114 115 case API_OBJECT: 116 return self::validateObject($rule, $data, $path, $error); 117 118 case API_OUTPUT: 119 return self::validateOutput($rule, $data, $path, $error); 120 121 case API_SORTORDER: 122 return self::validateSortOrder($rule, $data, $path, $error); 123 124 case API_IDS: 125 return self::validateIds($rule, $data, $path, $error); 126 127 case API_OBJECTS: 128 return self::validateObjects($rule, $data, $path, $error); 129 130 case API_HG_NAME: 131 return self::validateHostGroupName($rule, $data, $path, $error); 132 133 case API_H_NAME: 134 return self::validateHostName($rule, $data, $path, $error); 135 136 case API_NUMERIC: 137 return self::validateNumeric($rule, $data, $path, $error); 138 139 case API_SCRIPT_NAME: 140 return self::validateScriptName($rule, $data, $path, $error); 141 142 case API_USER_MACRO: 143 return self::validateUserMacro($rule, $data, $path, $error); 144 145 case API_RANGE_TIME: 146 return self::validateRangeTime($rule, $data, $path, $error); 147 148 case API_TIME_PERIOD: 149 return self::validateTimePeriod($rule, $data, $path, $error); 150 151 case API_REGEX: 152 return self::validateRegex($rule, $data, $path, $error); 153 154 case API_HTTP_POST: 155 return self::validateHttpPosts($rule, $data, $path, $error); 156 157 case API_VARIABLE_NAME: 158 return self::validateVariableName($rule, $data, $path, $error); 159 160 case API_TIME_UNIT: 161 return self::validateTimeUnit($rule, $data, $path, $error); 162 163 case API_URL: 164 return self::validateUrl($rule, $data, $path, $error); 165 166 case API_TRIGGER_EXPRESSION: 167 return self::validateTriggerExpression($rule, $data, $path, $error); 168 169 case API_JSONRPC_PARAMS: 170 return self::validateJsonRpcParams($rule, $data, $path, $error); 171 172 case API_JSONRPC_ID: 173 return self::validateJsonRpcId($rule, $data, $path, $error); 174 } 175 176 // This message can be untranslated because warn about incorrect validation rules at a development stage. 177 $error = 'Incorrect validation rules.'; 178 179 return false; 180 } 181 182 /** 183 * Base data uniqueness validation function. 184 * 185 * @param array $rule 186 * @param mixed $data 187 * @param string $path 188 * @param string $error 189 * 190 * @return bool 191 */ 192 private static function validateDataUniqueness($rule, &$data, $path, &$error) { 193 switch ($rule['type']) { 194 case API_COLOR: 195 case API_MULTIPLE: 196 case API_STRING_UTF8: 197 case API_INT32: 198 case API_UINT64: 199 case API_UINTS64: 200 case API_FLOAT: 201 case API_FLOATS: 202 case API_ID: 203 case API_BOOLEAN: 204 case API_FLAG: 205 case API_OUTPUT: 206 case API_SORTORDER: 207 case API_HG_NAME: 208 case API_H_NAME: 209 case API_NUMERIC: 210 case API_SCRIPT_NAME: 211 case API_USER_MACRO: 212 case API_RANGE_TIME: 213 case API_TIME_PERIOD: 214 case API_TIME_UNIT: 215 case API_REGEX: 216 case API_HTTP_POST: 217 case API_VARIABLE_NAME: 218 case API_URL: 219 case API_TRIGGER_EXPRESSION: 220 case API_JSONRPC_PARAMS: 221 case API_JSONRPC_ID: 222 return true; 223 224 case API_OBJECT: 225 foreach ($rule['fields'] as $field_name => $field_rule) { 226 if ($data !== null && array_key_exists($field_name, $data)) { 227 $subpath = ($path === '/' ? $path : $path.'/').$field_name; 228 if (!self::validateDataUniqueness($field_rule, $data[$field_name], $subpath, $error)) { 229 return false; 230 } 231 } 232 } 233 return true; 234 235 case API_IDS: 236 case API_STRINGS_UTF8: 237 case API_INTS32: 238 return self::validateStringsUniqueness($rule, $data, $path, $error); 239 240 case API_OBJECTS: 241 return self::validateObjectsUniqueness($rule, $data, $path, $error); 242 } 243 244 // This message can be untranslated because warn about incorrect validation rules at a development stage. 245 $error = 'Incorrect validation rules.'; 246 247 return false; 248 } 249 250 /** 251 * Generic string validator. 252 * 253 * @param int $flags API_NOT_EMPTY 254 * @param mixed $data 255 * @param string $path 256 * @param string $error 257 * 258 * @return bool 259 */ 260 private static function checkStringUtf8($flags, &$data, $path, &$error) { 261 if (!is_string($data)) { 262 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a character string is expected')); 263 return false; 264 } 265 266 if (mb_check_encoding($data, 'UTF-8') !== true) { 267 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('invalid byte sequence in UTF-8')); 268 return false; 269 } 270 271 if (($flags & API_NOT_EMPTY) && $data === '') { 272 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('cannot be empty')); 273 return false; 274 } 275 276 return true; 277 } 278 279 /** 280 * Color validator. 281 * 282 * @param array $rule 283 * @param int $rule['flags'] (optional) API_NOT_EMPTY 284 * @param mixed $data 285 * @param string $path 286 * @param string $error 287 * 288 * @return bool 289 */ 290 private static function validateColor($rule, &$data, $path, &$error) { 291 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 292 293 if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) { 294 return false; 295 } 296 297 if (($flags & API_NOT_EMPTY) == 0 && $data === '') { 298 return true; 299 } 300 301 if (preg_match('/^[0-9a-f]{6}$/i', $data) !== 1) { 302 $error = _s('Invalid parameter "%1$s": %2$s.', $path, 303 _('a hexadecimal colour code (6 symbols) is expected') 304 ); 305 return false; 306 } 307 308 return true; 309 } 310 311 /** 312 * Multiple data types validator. 313 * 314 * @param array $rule 315 * @param array $rule['rules'] 316 * @param mixed $data 317 * @param string $path 318 * @param string $error 319 * @param array $parent_data 320 * 321 * @return bool 322 */ 323 private static function validateMultiple($rule, &$data, $path, &$error, array $parent_data) { 324 foreach ($rule['rules'] as $field_rule) { 325 if (self::Int32In($parent_data[$field_rule['if']['field']], $field_rule['if']['in'])) { 326 unset($field_rule['if']); 327 328 return self::validateData($field_rule, $data, $path, $error); 329 } 330 } 331 332 // This message can be untranslated because warn about incorrect validation rules at a development stage. 333 $error = 'Incorrect validation rules.'; 334 335 return false; 336 } 337 338 /** 339 * String validator. 340 * 341 * @param array $rule 342 * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_NULL 343 * @param string $rule['in'] (optional) a comma-delimited character string, for example: 'xml,json' 344 * @param int $rule['length'] (optional) 345 * @param mixed $data 346 * @param string $path 347 * @param string $error 348 * 349 * @return bool 350 */ 351 private static function validateStringUtf8($rule, &$data, $path, &$error) { 352 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 353 354 if (($flags & API_ALLOW_NULL) && $data === null) { 355 return true; 356 } 357 358 if (self::checkStringUtf8($flags, $data, $path, $error) === false) { 359 return false; 360 } 361 362 if (array_key_exists('in', $rule) && !in_array($data, explode(',', $rule['in']), true)) { 363 $error = _s('Invalid parameter "%1$s": %2$s.', $path, 364 _s('value must be one of %1$s', str_replace(',', ', ', $rule['in'])) 365 ); 366 return false; 367 } 368 369 if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) { 370 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long')); 371 return false; 372 } 373 374 return true; 375 } 376 377 /** 378 * Array of strings validator. 379 * 380 * @param array $rule 381 * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_NULL, API_NORMALIZE 382 * @param string $rule['in'] (optional) a comma-delimited character string, for example: 'xml,json' 383 * @param mixed $data 384 * @param string $path 385 * @param string $error 386 * 387 * @return bool 388 */ 389 private static function validateStringsUtf8($rule, &$data, $path, &$error) { 390 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 391 392 if (($flags & API_ALLOW_NULL) && $data === null) { 393 return true; 394 } 395 396 if (($flags & API_NORMALIZE) && self::validateStringUtf8([], $data, '', $e)) { 397 $data = [$data]; 398 } 399 unset($e); 400 401 if (!is_array($data)) { 402 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('an array is expected')); 403 return false; 404 } 405 406 if (($flags & API_NOT_EMPTY) && !$data) { 407 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('cannot be empty')); 408 return false; 409 } 410 411 $data = array_values($data); 412 $rules = ['type' => API_STRING_UTF8]; 413 414 if (array_key_exists('in', $rule)) { 415 $rules['in'] = $rule['in']; 416 } 417 418 foreach ($data as $index => &$value) { 419 $subpath = ($path === '/' ? $path : $path.'/').($index + 1); 420 if (!self::validateData($rules, $value, $subpath, $error)) { 421 return false; 422 } 423 } 424 unset($value); 425 426 return true; 427 } 428 429 /** 430 * Integers validator. 431 * 432 * @param array $rule 433 * @param int $rule['flags'] (optional) API_ALLOW_NULL 434 * @param string $rule['in'] (optional) a comma-delimited character string, for example: '0,60:900' 435 * @param mixed $data 436 * @param string $path 437 * @param string $error 438 * 439 * @return bool 440 */ 441 private static function validateInt32($rule, &$data, $path, &$error) { 442 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 443 444 if (($flags & API_ALLOW_NULL) && $data === null) { 445 return true; 446 } 447 448 if ((!is_int($data) && !is_string($data)) || 1 != preg_match('/^\-?[0-9]+$/', strval($data))) { 449 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('an integer is expected')); 450 return false; 451 } 452 453 if (bccomp($data, ZBX_MIN_INT32) < 0 || bccomp($data, ZBX_MAX_INT32) > 0) { 454 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a number is too large')); 455 return false; 456 } 457 458 if (!self::checkInt32In($rule, $data, $path, $error)) { 459 return false; 460 } 461 462 if (is_string($data)) { 463 $data = (int) $data; 464 } 465 466 return true; 467 } 468 469 /** 470 * Unsigned integers validator. 471 * 472 * @param array $rule 473 * @param int $rule['flags'] (optional) API_ALLOW_NULL 474 * @param mixed $data 475 * @param string $path 476 * @param string $error 477 * 478 * @return bool 479 */ 480 private static function validateUInt64($rule, &$data, $path, &$error) { 481 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 482 483 if (($flags & API_ALLOW_NULL) && $data === null) { 484 return true; 485 } 486 487 if (!is_scalar($data) || is_bool($data) || is_double($data) || !ctype_digit(strval($data))) { 488 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('an unsigned integer is expected')); 489 return false; 490 } 491 492 if (bccomp($data, ZBX_MAX_UINT64) > 0) { 493 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a number is too large')); 494 return false; 495 } 496 497 $data = (string) $data; 498 499 if ($data[0] === '0') { 500 $data = ltrim($data, '0'); 501 502 if ($data === '') { 503 $data = '0'; 504 } 505 } 506 507 return true; 508 } 509 510 /** 511 * Array of unsigned integers validator. 512 * 513 * @param array $rule 514 * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_NULL, API_NORMALIZE 515 * @param mixed $data 516 * @param string $path 517 * @param string $error 518 * 519 * @return bool 520 */ 521 private static function validateUInts64($rule, &$data, $path, &$error) { 522 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 523 524 if (($flags & API_ALLOW_NULL) && $data === null) { 525 return true; 526 } 527 528 if (($flags & API_NORMALIZE) && self::validateUInt64([], $data, '', $e)) { 529 $data = [$data]; 530 } 531 unset($e); 532 533 if (!is_array($data)) { 534 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('an array is expected')); 535 return false; 536 } 537 538 if (($flags & API_NOT_EMPTY) && !$data) { 539 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('cannot be empty')); 540 return false; 541 } 542 543 $data = array_values($data); 544 $rules = ['type' => API_UINT64]; 545 546 foreach ($data as $index => &$value) { 547 $subpath = ($path === '/' ? $path : $path.'/').($index + 1); 548 if (!self::validateData($rules, $value, $subpath, $error)) { 549 return false; 550 } 551 } 552 unset($value); 553 554 return true; 555 } 556 557 /** 558 * Floating point number validator. 559 * 560 * @param array $rule 561 * @param int $rule['flags'] (optional) API_ALLOW_NULL 562 * @param mixed $data 563 * @param string $path 564 * @param string $error 565 * 566 * @return bool 567 */ 568 private static function validateFloat($rule, &$data, $path, &$error) { 569 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 570 571 if (($flags & API_ALLOW_NULL) && $data === null) { 572 return true; 573 } 574 575 if (is_int($data) || (is_string($data) && 1 == preg_match('/^-?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?$/', $data))) { 576 $data = (float) $data; 577 } 578 elseif (!is_float($data)) { 579 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a floating point value is expected')); 580 return false; 581 } 582 583 return true; 584 } 585 586 /** 587 * Array of floating point numbers validator. 588 * 589 * @param array $rule 590 * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_NULL, API_NORMALIZE 591 * @param mixed $data 592 * @param string $path 593 * @param string $error 594 * 595 * @return bool 596 */ 597 private static function validateFloats($rule, &$data, $path, &$error) { 598 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 599 600 if (($flags & API_ALLOW_NULL) && $data === null) { 601 return true; 602 } 603 604 if (($flags & API_NORMALIZE) && self::validateFloat([], $data, '', $e)) { 605 $data = [$data]; 606 } 607 unset($e); 608 609 if (!is_array($data)) { 610 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('an array is expected')); 611 return false; 612 } 613 614 if (($flags & API_NOT_EMPTY) && !$data) { 615 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('cannot be empty')); 616 return false; 617 } 618 619 $data = array_values($data); 620 $rules = ['type' => API_FLOAT]; 621 622 foreach ($data as $index => &$value) { 623 $subpath = ($path === '/' ? $path : $path.'/').($index + 1); 624 if (!self::validateData($rules, $value, $subpath, $error)) { 625 return false; 626 } 627 } 628 unset($value); 629 630 return true; 631 } 632 633 private static function Int32In($data, $in) { 634 $valid = false; 635 636 foreach (explode(',', $in) as $in) { 637 if (strpos($in, ':') !== false) { 638 list($from, $to) = explode(':', $in); 639 } 640 else { 641 $from = $in; 642 $to = $in; 643 } 644 645 if ($from <= $data && $data <= $to) { 646 $valid = true; 647 break; 648 } 649 } 650 651 return $valid; 652 } 653 654 /** 655 * @param array $rule 656 * @param int $rule['in'] (optional) 657 * @param int $data 658 * @param string $path 659 * @param string $error 660 * 661 * @return bool 662 */ 663 private static function checkInt32In($rule, $data, $path, &$error) { 664 if (!array_key_exists('in', $rule)) { 665 return true; 666 } 667 668 $valid = self::Int32In($data, $rule['in']); 669 670 if (!$valid) { 671 $error = _s('Invalid parameter "%1$s": %2$s.', $path, 672 _s('value must be one of %1$s', str_replace([',', ':'], [', ', '-'], $rule['in'])) 673 ); 674 } 675 676 return $valid; 677 } 678 679 /** 680 * Array of integers validator. 681 * 682 * @param array $rule 683 * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_NULL, API_NORMALIZE 684 * @param string $rule['in'] (optional) a comma-delimited character string, for example: '0,60:900' 685 * @param mixed $data 686 * @param string $path 687 * @param string $error 688 * 689 * @return bool 690 */ 691 private static function validateInts32($rule, &$data, $path, &$error) { 692 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 693 694 if (($flags & API_ALLOW_NULL) && $data === null) { 695 return true; 696 } 697 698 if (($flags & API_NORMALIZE) && self::validateInt32([], $data, '', $e)) { 699 $data = [$data]; 700 } 701 unset($e); 702 703 if (!is_array($data)) { 704 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('an array is expected')); 705 return false; 706 } 707 708 if (($flags & API_NOT_EMPTY) && !$data) { 709 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('cannot be empty')); 710 return false; 711 } 712 713 $data = array_values($data); 714 $rules = ['type' => API_INT32]; 715 716 if (array_key_exists('in', $rule)) { 717 $rules['in'] = $rule['in']; 718 } 719 720 foreach ($data as $index => &$value) { 721 $subpath = ($path === '/' ? $path : $path.'/').($index + 1); 722 if (!self::validateData($rules, $value, $subpath, $error)) { 723 return false; 724 } 725 } 726 unset($value); 727 728 return true; 729 } 730 731 /** 732 * Identifier validator. 733 * 734 * @param array $rule 735 * @param int $rule['flags'] (optional) API_ALLOW_NULL, API_NOT_EMPTY 736 * @param mixed $data 737 * @param string $path 738 * @param string $error 739 * 740 * @return bool 741 */ 742 private static function validateId($rule, &$data, $path, &$error) { 743 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 744 745 if (($flags & API_ALLOW_NULL) && $data === null) { 746 return true; 747 } 748 749 if (!is_scalar($data) || is_bool($data) || is_double($data) || !ctype_digit(strval($data))) { 750 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a number is expected')); 751 return false; 752 } 753 754 if (($flags & API_NOT_EMPTY) && $data == 0) { 755 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('cannot be empty')); 756 return false; 757 } 758 759 if (bccomp($data, ZBX_DB_MAX_ID) > 0) { 760 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a number is too large')); 761 return false; 762 } 763 764 $data = (string) $data; 765 766 if ($data[0] === '0') { 767 $data = ltrim($data, '0'); 768 769 if ($data === '') { 770 $data = '0'; 771 } 772 } 773 774 return true; 775 } 776 777 /** 778 * Boolean validator. 779 * 780 * @param array $rule 781 * @param mixed $data 782 * @param string $path 783 * @param string $error 784 * 785 * @return bool 786 */ 787 private static function validateBoolean($rule, &$data, $path, &$error) { 788 if (!is_bool($data)) { 789 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a boolean is expected')); 790 return false; 791 } 792 793 return true; 794 } 795 796 /** 797 * Flag validator. 798 * 799 * @param array $rule 800 * @param mixed $data 801 * @param string $path 802 * @param string $error 803 * 804 * @return bool 805 */ 806 private static function validateFlag($rule, &$data, $path, &$error) { 807 if (is_bool($data)) { 808 return true; 809 } 810 811 /** 812 * @deprecated As of version 3.4, use boolean flags only. 813 */ 814 trigger_error(_('Non-boolean flags are deprecated.'), E_USER_NOTICE); 815 816 $data = !is_null($data); 817 818 return true; 819 } 820 821 /** 822 * Object validator. 823 * 824 * @param array $rul 825 * @param int $rule['flags'] (optional) API_ALLOW_NULL 826 * @param array $rule['fields'] 827 * @param int $rule['fields'][<field_name>]['flags'] (optional) API_REQUIRED, API_DEPRECATED 828 * @param mixed $rule['fields'][<field_name>]['default'] (optional) 829 * @param string $rule['fields'][<field_name>]['default_source'] (optional) 830 * @param mixed $data 831 * @param string $path 832 * @param string $error 833 * 834 * @return bool 835 */ 836 private static function validateObject($rule, &$data, $path, &$error) { 837 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 838 839 if (($flags & API_ALLOW_NULL) && $data === null) { 840 return true; 841 } 842 843 if (!is_array($data)) { 844 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('an array is expected')); 845 return false; 846 } 847 848 // unexpected parameter validation 849 foreach ($data as $field_name => $value) { 850 if (!array_key_exists($field_name, $rule['fields'])) { 851 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _s('unexpected parameter "%1$s"', $field_name)); 852 return false; 853 } 854 } 855 856 // validation of the values type 857 foreach ($rule['fields'] as $field_name => $field_rule) { 858 $flags = array_key_exists('flags', $field_rule) ? $field_rule['flags'] : 0x00; 859 860 if (array_key_exists('default', $field_rule) && !array_key_exists($field_name, $data)) { 861 $data[$field_name] = $field_rule['default']; 862 } 863 864 if (array_key_exists('default_source', $field_rule) && !array_key_exists($field_name, $data)) { 865 $data[$field_name] = $data[$field_rule['default_source']]; 866 } 867 868 if (array_key_exists($field_name, $data)) { 869 $subpath = ($path === '/' ? $path : $path.'/').$field_name; 870 if (!self::validateData($field_rule, $data[$field_name], $subpath, $error, $data)) { 871 return false; 872 } 873 if ($flags & API_DEPRECATED) { 874 trigger_error(_s('Parameter "%1$s" is deprecated.', $subpath), E_USER_NOTICE); 875 } 876 } 877 elseif ($flags & API_REQUIRED) { 878 $error = _s('Invalid parameter "%1$s": %2$s.', $path, 879 _s('the parameter "%1$s" is missing', $field_name) 880 ); 881 return false; 882 } 883 } 884 885 return true; 886 } 887 888 /** 889 * API output validator. 890 * 891 * @param array $rule 892 * @param int $rule['flags'] (optional) API_ALLOW_COUNT, API_ALLOW_NULL 893 * @param string $rule['in'] (optional) comma-delimited field names, for example: 'hostid,name' 894 * @param mixed $data 895 * @param string $path 896 * @param string $error 897 * 898 * @return bool 899 */ 900 private static function validateOutput($rule, &$data, $path, &$error) { 901 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 902 903 if (($flags & API_ALLOW_NULL) && $data === null) { 904 return true; 905 } 906 907 if (is_array($data)) { 908 $rules = ['type' => API_STRINGS_UTF8, 'uniq' => true]; 909 910 if (array_key_exists('in', $rule)) { 911 $rules['in'] = $rule['in']; 912 } 913 914 return self::validateData($rules, $data, $path, $error) 915 && self::validateDataUniqueness($rules, $data, $path, $error); 916 } 917 elseif (is_string($data)) { 918 $in = ($flags & API_ALLOW_COUNT) ? implode(',', [API_OUTPUT_EXTEND, API_OUTPUT_COUNT]) : API_OUTPUT_EXTEND; 919 920 return self::validateData(['type' => API_STRING_UTF8, 'in' => $in], $data, $path, $error); 921 } 922 923 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('an array or a character string is expected')); 924 925 return false; 926 } 927 928 /** 929 * API sort order validator. 930 * 931 * @param array $rule 932 * @param mixed $data 933 * @param string $path 934 * @param string $error 935 * 936 * @return bool 937 */ 938 private static function validateSortOrder($rule, &$data, $path, &$error) { 939 $in = ZBX_SORT_UP.','.ZBX_SORT_DOWN; 940 941 if (self::validateStringUtf8(['in' => $in], $data, $path, $e)) { 942 return true; 943 } 944 945 if (is_string($data)) { 946 $error = $e; 947 return false; 948 } 949 unset($e); 950 951 if (!is_array($data)) { 952 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('an array or a character string is expected')); 953 return false; 954 } 955 956 $data = array_values($data); 957 $rules = [ 958 'type' => API_STRING_UTF8, 959 'in' => $in 960 ]; 961 962 foreach ($data as $index => &$value) { 963 $subpath = ($path === '/' ? $path : $path.'/').($index + 1); 964 if (!self::validateData($rules, $value, $subpath, $error)) { 965 return false; 966 } 967 } 968 unset($value); 969 970 return true; 971 } 972 973 /** 974 * Array of ids validator. 975 * 976 * @param array $rule 977 * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_NULL, API_NORMALIZE 978 * @param mixed $data 979 * @param string $path 980 * @param string $error 981 * 982 * @return bool 983 */ 984 private static function validateIds($rule, &$data, $path, &$error) { 985 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 986 987 if (($flags & API_ALLOW_NULL) && $data === null) { 988 return true; 989 } 990 991 if (($flags & API_NORMALIZE) && self::validateId([], $data, '', $e)) { 992 $data = [$data]; 993 } 994 unset($e); 995 996 if (!is_array($data)) { 997 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('an array is expected')); 998 return false; 999 } 1000 1001 if (($flags & API_NOT_EMPTY) && !$data) { 1002 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('cannot be empty')); 1003 return false; 1004 } 1005 1006 $data = array_values($data); 1007 1008 foreach ($data as $index => &$value) { 1009 $subpath = ($path === '/' ? $path : $path.'/').($index + 1); 1010 if (!self::validateId([], $value, $subpath, $error)) { 1011 return false; 1012 } 1013 } 1014 unset($value); 1015 1016 return true; 1017 } 1018 1019 /** 1020 * Array of objects validator. 1021 * 1022 * @param array $rule 1023 * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_NULL, API_NORMALIZE 1024 * @param array $rule['fields'] 1025 * @param mixed $data 1026 * @param string $path 1027 * @param string $error 1028 * 1029 * @return bool 1030 */ 1031 private static function validateObjects($rule, &$data, $path, &$error) { 1032 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 1033 1034 if (($flags & API_ALLOW_NULL) && $data === null) { 1035 return true; 1036 } 1037 1038 if (!is_array($data)) { 1039 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('an array is expected')); 1040 return false; 1041 } 1042 1043 if (($flags & API_NOT_EMPTY) && !$data) { 1044 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('cannot be empty')); 1045 return false; 1046 } 1047 1048 if (($flags & API_NORMALIZE) && $data) { 1049 reset($data); 1050 1051 if (!is_int(key($data))) { 1052 $data = [$data]; 1053 } 1054 } 1055 1056 $data = array_values($data); 1057 1058 foreach ($data as $index => &$value) { 1059 $subpath = ($path === '/' ? $path : $path.'/').($index + 1); 1060 if (!self::validateObject(['fields' => $rule['fields']], $value, $subpath, $error)) { 1061 return false; 1062 } 1063 } 1064 unset($value); 1065 1066 return true; 1067 } 1068 1069 /** 1070 * Host group name validator. 1071 * 1072 * @param array $rule 1073 * @param int $rule['flags'] (optional) API_REQUIRED_LLD_MACRO 1074 * @param int $rule['length'] (optional) 1075 * @param mixed $data 1076 * @param string $path 1077 * @param string $error 1078 * 1079 * @return bool 1080 */ 1081 private static function validateHostGroupName($rule, &$data, $path, &$error) { 1082 if (self::checkStringUtf8(API_NOT_EMPTY, $data, $path, $error) === false) { 1083 return false; 1084 } 1085 1086 if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) { 1087 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long')); 1088 1089 return false; 1090 } 1091 1092 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 1093 $host_group_name_parser = new CHostGroupNameParser(['lldmacros' => ($flags & API_REQUIRED_LLD_MACRO)]); 1094 1095 if ($host_group_name_parser->parse($data) != CParser::PARSE_SUCCESS) { 1096 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('invalid host group name')); 1097 1098 return false; 1099 } 1100 1101 if (($flags & API_REQUIRED_LLD_MACRO) && !$host_group_name_parser->getMacros()) { 1102 $error = _s('Invalid parameter "%1$s": %2$s.', $path, 1103 _('must contain at least one low-level discovery macro') 1104 ); 1105 1106 return false; 1107 } 1108 1109 return true; 1110 } 1111 1112 /** 1113 * Host name validator. 1114 * 1115 * @param array $rule 1116 * @param int $rule['flags'] (optional) API_REQUIRED_LLD_MACRO 1117 * @param int $rule['length'] (optional) 1118 * @param mixed $data 1119 * @param string $path 1120 * @param string $error 1121 * 1122 * @return bool 1123 */ 1124 private static function validateHostName($rule, &$data, $path, &$error) { 1125 if (self::checkStringUtf8(API_NOT_EMPTY, $data, $path, $error) === false) { 1126 return false; 1127 } 1128 1129 if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) { 1130 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long')); 1131 1132 return false; 1133 } 1134 1135 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 1136 $host_name_parser = new CHostNameParser(['lldmacros' => ($flags & API_REQUIRED_LLD_MACRO)]); 1137 1138 // For example, host prototype name MUST contain macros. 1139 if ($host_name_parser->parse($data) != CParser::PARSE_SUCCESS) { 1140 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('invalid host name')); 1141 1142 return false; 1143 } 1144 1145 if (($flags & API_REQUIRED_LLD_MACRO) && !$host_name_parser->getMacros()) { 1146 $error = _s('Invalid parameter "%1$s": %2$s.', $path, 1147 _('must contain at least one low-level discovery macro') 1148 ); 1149 1150 return false; 1151 } 1152 1153 return true; 1154 } 1155 1156 /** 1157 * Validator for numeric data with optional suffix. 1158 * Supported time suffixes: s, m, h, d, w 1159 * Supported metric suffixes: K, M, G, T 1160 * 1161 * @param array $rule 1162 * @param int $rule['flags'] (optional) API_NOT_EMPTY 1163 * @param int $rule['length'] (optional) 1164 * @param mixed $data 1165 * @param string $path 1166 * @param string $error 1167 * 1168 * @return bool 1169 */ 1170 private static function validateNumeric($rule, &$data, $path, &$error) { 1171 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 1172 1173 if (is_int($data)) { 1174 $data = (string) $data; 1175 } 1176 1177 if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) { 1178 return false; 1179 } 1180 1181 if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) { 1182 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long')); 1183 return false; 1184 } 1185 1186 if (($flags & API_NOT_EMPTY) == 0 && $data === '') { 1187 return true; 1188 } 1189 1190 $pattern = '/^(-?)0*(0|[1-9][0-9]*)(\.?[0-9]+)?(['.ZBX_BYTE_SUFFIXES.ZBX_TIME_SUFFIXES.'])?$/'; 1191 1192 if (1 != preg_match($pattern, strval($data))) { 1193 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a number is expected')); 1194 return false; 1195 } 1196 1197 $value = convertFunctionValue($data, ZBX_UNITS_ROUNDOFF_LOWER_LIMIT); 1198 1199 if (bccomp($value, ZBX_MIN_INT64) < 0 || bccomp($value, ZBX_MAX_INT64) > 0) { 1200 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a number is too large')); 1201 return false; 1202 } 1203 1204 // Trim leading zeroes. 1205 $data = preg_replace($pattern, '${1}${2}${3}${4}', (string) $data); 1206 1207 return true; 1208 } 1209 1210 /** 1211 * Global script name validator. 1212 * 1213 * @param array $rule 1214 * @param int $rule['length'] (optional) 1215 * @param mixed $data 1216 * @param string $path 1217 * @param string $error 1218 * 1219 * @return bool 1220 */ 1221 private static function validateScriptName($rule, &$data, $path, &$error) { 1222 if (self::checkStringUtf8(API_NOT_EMPTY, $data, $path, $error) === false) { 1223 return false; 1224 } 1225 1226 if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) { 1227 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long')); 1228 return false; 1229 } 1230 1231 $folders = splitPath($data); 1232 $folders = array_map('trim', $folders); 1233 1234 // folder1/{empty}/name or folder1/folder2/{empty} 1235 foreach ($folders as $folder) { 1236 if ($folder === '') { 1237 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('directory or script name cannot be empty')); 1238 return false; 1239 } 1240 } 1241 1242 return true; 1243 } 1244 1245 /** 1246 * User macro validator. 1247 * 1248 * @param array $rule 1249 * @param int $rule['length'] (optional) 1250 * @param mixed $data 1251 * @param string $path 1252 * @param string $error 1253 * 1254 * @return bool 1255 */ 1256 private static function validateUserMacro($rule, &$data, $path, &$error) { 1257 if (self::checkStringUtf8(API_NOT_EMPTY, $data, $path, $error) === false) { 1258 return false; 1259 } 1260 1261 if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) { 1262 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long')); 1263 return false; 1264 } 1265 1266 if ((new CUserMacroParser())->parse($data) != CParser::PARSE_SUCCESS) { 1267 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a user macro is expected')); 1268 return false; 1269 } 1270 1271 return true; 1272 } 1273 1274 /** 1275 * Time period validator like "1-7,00:00-24:00". 1276 * 1277 * @param array $rule 1278 * @param int $rule['length'] (optional) 1279 * @param mixed $data 1280 * @param string $path 1281 * @param string $error 1282 * 1283 * @return bool 1284 */ 1285 private static function validateRangeTime($rule, &$data, $path, &$error) { 1286 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 1287 1288 if (self::checkStringUtf8(API_NOT_EMPTY, $data, $path, $error) === false) { 1289 return false; 1290 } 1291 1292 if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) { 1293 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long')); 1294 return false; 1295 } 1296 1297 $range_time_parser = new CRangeTimeParser(); 1298 1299 if ($range_time_parser->parse($data) != CParser::PARSE_SUCCESS) { 1300 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a time range is expected')); 1301 return false; 1302 } 1303 1304 return true; 1305 } 1306 1307 /** 1308 * Time period validator like "1-7,00:00-24:00". 1309 * 1310 * @param array $rule 1311 * @param int $rule['flags'] (optional) API_ALLOW_USER_MACRO 1312 * @param int $rule['length'] (optional) 1313 * @param mixed $data 1314 * @param string $path 1315 * @param string $error 1316 * 1317 * @return bool 1318 */ 1319 private static function validateTimePeriod($rule, &$data, $path, &$error) { 1320 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 1321 1322 if (self::checkStringUtf8(API_NOT_EMPTY, $data, $path, $error) === false) { 1323 return false; 1324 } 1325 1326 if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) { 1327 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long')); 1328 return false; 1329 } 1330 1331 $time_period_parser = new CTimePeriodsParser(['usermacros' => ($flags & API_ALLOW_USER_MACRO)]); 1332 1333 if ($time_period_parser->parse($data) != CParser::PARSE_SUCCESS) { 1334 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a time period is expected')); 1335 return false; 1336 } 1337 1338 return true; 1339 } 1340 1341 /** 1342 * Regular expression validator. 1343 * 1344 * @param array $rule 1345 * @param int $rule['flags'] (optional) API_NOT_EMPTY 1346 * @param int $rule['length'] (optional) 1347 * @param mixed $data 1348 * @param string $path 1349 * @param string $error 1350 * 1351 * @return bool 1352 */ 1353 private static function validateRegex($rule, &$data, $path, &$error) { 1354 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 1355 1356 if (self::checkStringUtf8($flags, $data, $path, $error) === false) { 1357 return false; 1358 } 1359 1360 if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) { 1361 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long')); 1362 return false; 1363 } 1364 1365 if ($data !== '' && $data[0] === '@') { 1366 return true; 1367 } 1368 1369 if (@preg_match('/'.str_replace('/', '\/', $data).'/', '') === false) { 1370 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('invalid regular expression')); 1371 return false; 1372 } 1373 1374 return true; 1375 } 1376 1377 /** 1378 * Time unit validator like "10", "20s", "30m", "4h", "{$TIME}" etc. 1379 * 1380 * @param array $rule 1381 * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_USER_MACRO, API_ALLOW_LLD_MACRO 1382 * @param int $rule['in'] (optional) 1383 * @param mixed $data 1384 * @param string $path 1385 * @param string $error 1386 * 1387 * @return bool 1388 */ 1389 private static function validateTimeUnit($rule, &$data, $path, &$error) { 1390 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 1391 1392 /* 1393 * It's possible to enter seconds as integers, but by default now we look for strings. For example: "30m". 1394 * Other rules like emptiness and invalid characters are validated by parsers. 1395 */ 1396 if (is_int($data)) { 1397 $data = (string) $data; 1398 } 1399 1400 if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) { 1401 return false; 1402 } 1403 1404 if (($flags & API_NOT_EMPTY) == 0 && $data === '') { 1405 return true; 1406 } 1407 1408 $simple_interval_parser = new CSimpleIntervalParser([ 1409 'usermacros' => ($flags & API_ALLOW_USER_MACRO), 1410 'lldmacros' => ($flags & API_ALLOW_LLD_MACRO), 1411 'negative' => true 1412 ]); 1413 1414 if ($simple_interval_parser->parse($data) != CParser::PARSE_SUCCESS) { 1415 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a time unit is expected')); 1416 return false; 1417 } 1418 1419 if (($flags & (API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO)) && $data[0] === '{') { 1420 return true; 1421 } 1422 1423 $seconds = timeUnitToSeconds($data); 1424 1425 if (bccomp(ZBX_MIN_INT32, $seconds) > 0 || bccomp($seconds, ZBX_MAX_INT32) > 0) { 1426 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a number is too large')); 1427 return false; 1428 } 1429 1430 return self::checkInt32In($rule, $seconds, $path, $error); 1431 } 1432 1433 /** 1434 * Array of ids, int32 or strings uniqueness validator. 1435 * 1436 * @param array $rule 1437 * @param bool $rule['uniq'] (optional) 1438 * @param array $data 1439 * @param string $path 1440 * @param string $error 1441 * 1442 * @return bool 1443 */ 1444 private static function validateStringsUniqueness($rule, array $data = null, $path, &$error) { 1445 // $data can be NULL when API_ALLOW_NULL is set 1446 if ($data === null) { 1447 return true; 1448 } 1449 1450 if (!array_key_exists('uniq', $rule) || $rule['uniq'] === false) { 1451 return true; 1452 } 1453 1454 $uniq = []; 1455 1456 foreach ($data as $index => $value) { 1457 if (array_key_exists($value, $uniq)) { 1458 $subpath = ($path === '/' ? $path : $path.'/').($index + 1); 1459 $error = _s('Invalid parameter "%1$s": %2$s.', $subpath, 1460 _s('value %1$s already exists', '('.$value.')') 1461 ); 1462 return false; 1463 } 1464 $uniq[$value] = true; 1465 } 1466 1467 return true; 1468 } 1469 1470 /** 1471 * Array of objects uniqueness validator. 1472 * 1473 * @param array $rule 1474 * @param array $rule['uniq'] (optional) subsets of unique fields ([['hostid', 'name'], [...]]) 1475 * @param array $rule['fields'] (optional) 1476 * @param array $data 1477 * @param string $path 1478 * @param string $error 1479 * 1480 * @return bool 1481 */ 1482 private static function validateObjectsUniqueness($rule, array $data = null, $path, &$error) { 1483 // $data can be NULL when API_ALLOW_NULL is set 1484 if ($data === null) { 1485 return true; 1486 } 1487 1488 if (array_key_exists('uniq', $rule)) { 1489 foreach ($rule['uniq'] as $field_names) { 1490 $uniq = []; 1491 1492 foreach ($data as $index => $object) { 1493 $_uniq = &$uniq; 1494 $values = []; 1495 $level = 1; 1496 1497 foreach ($field_names as $field_name) { 1498 if (!array_key_exists($field_name, $object)) { 1499 break; 1500 } 1501 1502 $values[] = $object[$field_name]; 1503 1504 if ($level < count($field_names)) { 1505 if (!array_key_exists($object[$field_name], $_uniq)) { 1506 $_uniq[$object[$field_name]] = []; 1507 } 1508 1509 $_uniq = &$_uniq[$object[$field_name]]; 1510 } 1511 else { 1512 if (array_key_exists($object[$field_name], $_uniq)) { 1513 $subpath = ($path === '/' ? $path : $path.'/').($index + 1); 1514 $error = _s('Invalid parameter "%1$s": %2$s.', $subpath, _s('value %1$s already exists', 1515 '('.implode(', ', $field_names).')=('.implode(', ', $values).')' 1516 )); 1517 return false; 1518 } 1519 1520 $_uniq[$object[$field_name]] = true; 1521 } 1522 1523 $level++; 1524 } 1525 } 1526 } 1527 } 1528 1529 if (array_key_exists('fields', $rule)) { 1530 foreach ($data as $index => $object) { 1531 foreach ($rule['fields'] as $field_name => $field_rule) { 1532 if (array_key_exists($field_name, $object)) { 1533 $subpath = ($path === '/' ? $path : $path.'/').($index + 1).'/'.$field_name; 1534 if (!self::validateDataUniqueness($field_rule, $object[$field_name], $subpath, $error)) { 1535 return false; 1536 } 1537 } 1538 } 1539 } 1540 } 1541 1542 return true; 1543 } 1544 1545 /** 1546 * HTTP POST validator. Posts can be set to string (raw post) or to http pairs (form fields) 1547 * 1548 * @param array $rule 1549 * @param int $rule['length'] (optional) 1550 * @param int $rule['name-length'] (optional) 1551 * @param int $rule['value-length'] (optional) 1552 * @param mixed $data 1553 * @param string $path 1554 * @param string $error 1555 * 1556 * @return bool 1557 */ 1558 private static function validateHttpPosts($rule, &$data, $path, &$error) { 1559 if (is_array($data)) { 1560 $rules = ['type' => API_OBJECTS, 'fields' => [ 1561 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY], 1562 'value' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED] 1563 ]]; 1564 1565 if (array_key_exists('name-length', $rule)) { 1566 $rules['fields']['name']['length'] = $rule['name-length']; 1567 } 1568 1569 if (array_key_exists('value-length', $rule)) { 1570 $rules['fields']['value']['length'] = $rule['value-length']; 1571 } 1572 } 1573 else { 1574 $rules = ['type' => API_STRING_UTF8]; 1575 1576 if (array_key_exists('length', $rule)) { 1577 $rules['length'] = $rule['length']; 1578 } 1579 } 1580 1581 return self::validateData($rules, $data, $path, $error); 1582 } 1583 1584 /** 1585 * HTTP variable validator. 1586 * 1587 * @param array $rule 1588 * @param int $rule['length'] (optional) 1589 * @param mixed $data 1590 * @param string $path 1591 * @param string $error 1592 * 1593 * @return bool 1594 */ 1595 private static function validateVariableName($rule, &$data, $path, &$error) { 1596 if (self::checkStringUtf8(API_NOT_EMPTY, $data, $path, $error) === false) { 1597 return false; 1598 } 1599 1600 if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) { 1601 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long')); 1602 return false; 1603 } 1604 1605 if (preg_match('/^{[^{}]+}$/', $data) !== 1) { 1606 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('is not enclosed in {} or is malformed')); 1607 return false; 1608 } 1609 1610 return true; 1611 } 1612 1613 /** 1614 * URL validator. 1615 * 1616 * @param array $rule 1617 * @param int $rule['length'] (optional) 1618 * @param int $rule['flags'] (optional) API_ALLOW_USER_MACRO, API_NOT_EMPTY 1619 * @param mixed $data 1620 * @param string $path 1621 * @param string $error 1622 * 1623 * @return bool 1624 */ 1625 private static function validateUrl($rule, &$data, $path, &$error) { 1626 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 1627 1628 if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) { 1629 return false; 1630 } 1631 1632 if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) { 1633 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long')); 1634 return false; 1635 } 1636 1637 if ($data !== '' && CHtmlUrlValidator::validate($data, ($flags & API_ALLOW_USER_MACRO)) === false) { 1638 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('unacceptable URL')); 1639 return false; 1640 } 1641 1642 return true; 1643 } 1644 1645 /** 1646 * Trigger expression validator. 1647 * 1648 * @param array $rule 1649 * @param int $rule['length'] (optional) 1650 * @param int $rule['flags'] (optional) API_ALLOW_LLD_MACRO, API_NOT_EMPTY 1651 * @param mixed $data 1652 * @param string $path 1653 * @param string $error 1654 * 1655 * @return bool 1656 */ 1657 private static function validateTriggerExpression($rule, &$data, $path, &$error) { 1658 $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00; 1659 1660 if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) { 1661 return false; 1662 } 1663 1664 if (($flags & API_NOT_EMPTY) == 0 && $data === '') { 1665 return true; 1666 } 1667 1668 if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) { 1669 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long')); 1670 return false; 1671 } 1672 1673 $expression_data = new CTriggerExpression([ 1674 'lldmacros' => ($flags & API_ALLOW_LLD_MACRO), 1675 'lowercase_errors' => true 1676 ]); 1677 1678 if (!$expression_data->parse($data)) { 1679 $error = _s('Invalid parameter "%1$s": %2$s.', $path, $expression_data->error); 1680 return false; 1681 } 1682 1683 if (!$expression_data->expressions) { 1684 $error = _s('Invalid parameter "%1$s": %2$s.', $path, 1685 _('trigger expression must contain at least one host:key reference') 1686 ); 1687 return false; 1688 } 1689 1690 return true; 1691 } 1692 1693 /** 1694 * JSON RPC parameters validator. Parameters MUST contain an array or object value. 1695 * 1696 * @param array $rule 1697 * @param mixed $data 1698 * @param string $path 1699 * @param string $error 1700 * 1701 * @return bool 1702 */ 1703 private static function validateJsonRpcParams($rule, &$data, $path, &$error) { 1704 if (is_array($data)) { 1705 return true; 1706 } 1707 1708 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('an array or object is expected')); 1709 1710 return false; 1711 } 1712 1713 /** 1714 * JSON RPC identifier validator. This identifier MUST contain a String, Number, or NULL value. 1715 * 1716 * @param array $rule 1717 * @param mixed $data 1718 * @param string $path 1719 * @param string $error 1720 * 1721 * @return bool 1722 */ 1723 private static function validateJsonRpcId($rule, &$data, $path, &$error) { 1724 if (is_string($data) || is_int($data) || is_float($data) || $data === null) { 1725 return true; 1726 } 1727 1728 $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a string, number or null value is expected')); 1729 1730 return false; 1731 } 1732} 1733