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 21class CValidationRule { 22 const STATE_BEGIN = 0; 23 const STATE_END = 1; 24 25 /** 26 * An error message if validation rule is not valid. 27 * 28 * @var string 29 */ 30 private $error = ''; 31 32 /** 33 * Parse validation rule. Returns array of rules or fail if $buffer is not valid. 34 * 35 * @param string $buffer 36 * 37 * @return array|bool 38 */ 39 public function parse($buffer) { 40 $this->error = ''; 41 42 $pos = 0; 43 $state = self::STATE_BEGIN; 44 $rules = []; 45 $is_empty = true; 46 47 while (isset($buffer[$pos])) { 48 switch ($state) { 49 case self::STATE_BEGIN: 50 switch ($buffer[$pos]) { 51 case ' ': 52 $pos++; 53 break; 54 55 default: 56 $is_empty = false; 57 $rule = []; 58 59 if (!$this->parseString($buffer, $pos, $rule) // string 60 && !$this->parseRangeTime($buffer, $pos, $rule) // range time 61 && !$this->parseTimePeriods($buffer, $pos, $rule) // time periods 62 && !$this->parseRgb($buffer, $pos, $rule) // rgb 63 && !$this->parseRequired($buffer, $pos, $rule) // required 64 && !$this->parseNotEmpty($buffer, $pos, $rule) // not_empty 65 && !$this->parseLE($buffer, $pos, $rule) // le 66 && !$this->parseJson($buffer, $pos, $rule) // json 67 && !$this->parseInt32($buffer, $pos, $rule) // int32 68 && !$this->parseIn($buffer, $pos, $rule) // in 69 && !$this->parseId($buffer, $pos, $rule) // id 70 && !$this->parseGE($buffer, $pos, $rule) // ge 71 && !$this->parseFatal($buffer, $pos, $rule) // fatal 72 && !$this->parseDB($buffer, $pos, $rule) // db 73 && !$this->parseArrayId($buffer, $pos, $rule) // array_id 74 && !$this->parseArrayDB($buffer, $pos, $rule) // array_db 75 && !$this->parseArray($buffer, $pos, $rule) // array 76 && !$this->parseFlags($buffer, $pos, $rule)) { // flags 77 // incorrect validation rule 78 break 3; 79 } 80 81 if (array_key_exists(key($rule), $rules)) { 82 // the message can be not translated because it is an internal error 83 $this->error = 'Validation rule "'.key($rule).'" already exists.'; 84 return false; 85 } 86 87 $rules = array_merge($rules, $rule); 88 $state = self::STATE_END; 89 break; 90 } 91 break; 92 93 case self::STATE_END: 94 switch ($buffer[$pos]) { 95 case ' ': 96 $pos++; 97 break; 98 case '|': 99 $state = self::STATE_BEGIN; 100 $pos++; 101 break; 102 default: 103 // incorrect validation rule 104 break 3; 105 } 106 break; 107 } 108 } 109 110 if (isset($buffer[$pos])) { 111 // the message can be not translated because it is an internal error 112 $this->error = 'Cannot parse validation rules "'.$buffer.'" at position '.$pos.'.'; 113 return false; 114 } 115 116 return $rules; 117 } 118 119 /** 120 * Returns the error message if validation rule is invalid. 121 * 122 * @return string 123 */ 124 public function getError() { 125 return $this->error; 126 } 127 128 /** 129 * fatal 130 * 131 * 'fatal' => true 132 */ 133 private function parseFatal($buffer, &$pos, &$rule) { 134 if (strncmp(substr($buffer, $pos), 'fatal', 5) != 0) { 135 return false; 136 } 137 138 $pos += 5; 139 $rule['fatal'] = true; 140 141 return true; 142 } 143 144 /** 145 * string 146 * 147 * 'string' => true 148 */ 149 private function parseString($buffer, &$pos, &$rules) { 150 if (strncmp(substr($buffer, $pos), 'string', 6) != 0) { 151 return false; 152 } 153 154 $pos += 6; 155 $rules['string'] = true; 156 157 return true; 158 } 159 160 /** 161 * range_time 162 * 163 * 'range_time' => true 164 */ 165 private function parseRangeTime($buffer, &$pos, &$rules) { 166 if (strncmp(substr($buffer, $pos), 'range_time', 10) != 0) { 167 return false; 168 } 169 170 $pos += 10; 171 $rules['range_time'] = true; 172 173 return true; 174 } 175 176 /** 177 * range_time 178 * 179 * 'time_periods' => true 180 */ 181 private function parseTimePeriods($buffer, &$pos, &$rules) { 182 if (strncmp(substr($buffer, $pos), 'time_periods', 12) != 0) { 183 return false; 184 } 185 186 $pos += 12; 187 $rules['time_periods'] = true; 188 189 return true; 190 } 191 192 /** 193 * rgb 194 * 195 * 'rgb' => true 196 */ 197 private function parseRgb($buffer, &$pos, &$rules) { 198 if (strncmp(substr($buffer, $pos), 'rgb', 3) != 0) { 199 return false; 200 } 201 202 $pos += 3; 203 $rules['rgb'] = true; 204 205 return true; 206 } 207 208 /** 209 * required 210 * 211 * 'required' => true 212 */ 213 private function parseRequired($buffer, &$pos, &$rules) { 214 if (strncmp(substr($buffer, $pos), 'required', 8) != 0) { 215 return false; 216 } 217 218 $pos += 8; 219 $rules['required'] = true; 220 221 return true; 222 } 223 224 /** 225 * not_empty 226 * 227 * 'not_empty' => true 228 */ 229 private function parseNotEmpty($buffer, &$pos, &$rules) { 230 if (strncmp(substr($buffer, $pos), 'not_empty', 9) != 0) { 231 return false; 232 } 233 234 $pos += 9; 235 $rules['not_empty'] = true; 236 237 return true; 238 } 239 240 /** 241 * le <value> 242 * 243 * 'le' => '<value>' 244 */ 245 private function parseLE($buffer, &$pos, &$rules) { 246 $i = $pos; 247 248 if (0 != strncmp(substr($buffer, $i), 'le ', 3)) { 249 return false; 250 } 251 252 $i += 3; 253 $value = ''; 254 255 while (isset($buffer[$i]) && $buffer[$i] != '|') { 256 $value .= $buffer[$i++]; 257 } 258 259 if (!CNewValidator::is_int32($value)) { 260 return false; 261 } 262 263 $pos = $i; 264 $rules['le'] = $value; 265 266 return true; 267 } 268 269 /** 270 * json 271 * 272 * 'json' => true 273 */ 274 private function parseJson($buffer, &$pos, &$rules) { 275 if (strncmp(substr($buffer, $pos), 'json', 4) != 0) { 276 return false; 277 } 278 279 $pos += 4; 280 $rules['json'] = true; 281 282 return true; 283 } 284 285 /** 286 * int32 287 * 288 * 'int32' => true 289 */ 290 private function parseInt32($buffer, &$pos, &$rules) { 291 if (strncmp(substr($buffer, $pos), 'int32', 5) != 0) { 292 return false; 293 } 294 295 $pos += 5; 296 $rules['int32'] = true; 297 298 return true; 299 } 300 301 /** 302 * in <value1>[,...,<valueN>] 303 * 304 * 'in' => array('<value1>', ..., '<valueN>') 305 */ 306 private function parseIn($buffer, &$pos, &$rules) { 307 $i = $pos; 308 309 if (strncmp(substr($buffer, $i), 'in ', 3) != 0) { 310 return false; 311 } 312 313 $i += 3; 314 315 while (isset($buffer[$i]) && $buffer[$i] == ' ') { 316 $i++; 317 } 318 319 $values = []; 320 321 if (!$this->parseValues($buffer, $i, $values)) { 322 return false; 323 } 324 325 $pos = $i; 326 $rules['in'] = $values; 327 328 return true; 329 } 330 331 /** 332 * id 333 * 334 * 'id' => true 335 */ 336 private function parseId($buffer, &$pos, &$rules) { 337 if (strncmp(substr($buffer, $pos), 'id', 2) != 0) { 338 return false; 339 } 340 341 $pos += 2; 342 $rules['id'] = true; 343 344 return true; 345 } 346 347 /** 348 * ge <value> 349 * 350 * 'ge' => '<value>' 351 */ 352 private function parseGE($buffer, &$pos, &$rules) { 353 $i = $pos; 354 355 if (0 != strncmp(substr($buffer, $i), 'ge ', 3)) { 356 return false; 357 } 358 359 $i += 3; 360 $value = ''; 361 362 while (isset($buffer[$i]) && $buffer[$i] != '|') { 363 $value .= $buffer[$i++]; 364 } 365 366 if (!CNewValidator::is_int32($value)) { 367 return false; 368 } 369 370 $pos = $i; 371 $rules['ge'] = $value; 372 373 return true; 374 } 375 376 /** 377 * db <table>.<field> 378 * 379 * 'db' => array( 380 * 'table' => '<table>', 381 * 'field' => '<field>' 382 * ) 383 */ 384 private function parseDB($buffer, &$pos, &$rules) { 385 $i = $pos; 386 387 if (strncmp(substr($buffer, $i), 'db ', 3) != 0) { 388 return false; 389 } 390 391 $i += 3; 392 393 while (isset($buffer[$i]) && $buffer[$i] == ' ') { 394 $i++; 395 } 396 397 $table = ''; 398 399 if (!$this->parseField($buffer, $i, $table) || !isset($buffer[$i]) || $buffer[$i++] != '.') { 400 return false; 401 } 402 403 $field = ''; 404 405 if (!$this->parseField($buffer, $i, $field)) { 406 return false; 407 } 408 409 $pos = $i; 410 $rules['db'] = [ 411 'table' => $table, 412 'field' => $field 413 ]; 414 415 return true; 416 } 417 418 /** 419 * array 420 * 421 * 'array' => true 422 */ 423 private function parseArray($buffer, &$pos, &$rules) { 424 if (strncmp(substr($buffer, $pos), 'array', 5) != 0) { 425 return false; 426 } 427 428 $pos += 5; 429 $rules['array'] = true; 430 431 return true; 432 } 433 434 /** 435 * array_id 436 * 437 * 'array_id' => true 438 */ 439 private function parseArrayId($buffer, &$pos, &$rules) { 440 if (strncmp(substr($buffer, $pos), 'array_id', 8) != 0) { 441 return false; 442 } 443 444 $pos += 8; 445 $rules['array_id'] = true; 446 447 return true; 448 } 449 450 /** 451 * array_db <table>.<field> 452 * 453 * 'array_db' => array( 454 * 'table' => '<table>', 455 * 'field' => '<field>' 456 * ) 457 */ 458 private function parseArrayDB($buffer, &$pos, &$rules) { 459 $i = $pos; 460 461 if (strncmp(substr($buffer, $i), 'array_db ', 9) != 0) { 462 return false; 463 } 464 465 $i += 9; 466 467 while (isset($buffer[$i]) && $buffer[$i] == ' ') { 468 $i++; 469 } 470 471 $table = ''; 472 473 if (!$this->parseField($buffer, $i, $table) || !isset($buffer[$i]) || $buffer[$i++] != '.') { 474 return false; 475 } 476 477 $field = ''; 478 479 if (!$this->parseField($buffer, $i, $field)) { 480 return false; 481 } 482 483 $pos = $i; 484 $rules['array_db'] = [ 485 'table' => $table, 486 'field' => $field 487 ]; 488 489 return true; 490 } 491 492 /** 493 * flags <value1> | <value2> | ... | <valueN> 494 * 495 * 'flags' => <value1> | <value2> | ... | <valueN> 496 */ 497 private function parseFlags($buffer, &$pos, &$rules) { 498 $i = $pos; 499 500 if (0 != strncmp(substr($buffer, $i), 'flags ', 6)) { 501 return false; 502 } 503 504 $i += 6; 505 506 $value = 0x00; 507 508 if (!$this->parseValue($buffer, $i, $value)) { 509 return false; 510 } 511 512 $pos = $i; 513 $rules['flags'] = $value; 514 515 return true; 516 } 517 518 /** 519 * <field> 520 */ 521 private function parseField($buffer, &$pos, &$field) { 522 $matches = []; 523 if (1 != preg_match('/^[A-Za-z0-9_]+/', substr($buffer, $pos), $matches)) 524 return false; 525 526 $pos += strlen($matches[0]); 527 $field = $matches[0]; 528 529 return true; 530 } 531 532 /** 533 * <value1>[,...,<valueN>] 534 */ 535 private function parseValues($buffer, &$pos, array &$values) { 536 $i = $pos; 537 538 while (true) { 539 $value = ''; 540 541 if (!isset($buffer[$i]) || !$this->parseValue($buffer, $i, $value)) { 542 return false; 543 } 544 545 $values[] = $value; 546 547 if (!isset($buffer[$i]) || $buffer[$i] == ' ' || $buffer[$i] == '|') { 548 break; 549 } 550 551 $i++; 552 } 553 554 $pos = $i; 555 556 return true; 557 } 558 559 /** 560 * <value> 561 */ 562 private function parseValue($buffer, &$pos, &$value) { 563 $i = $pos; 564 565 while (isset($buffer[$i]) && $buffer[$i] != ',' && $buffer[$i] != ' ' && $buffer[$i] != '|') { 566 $i++; 567 } 568 569 if ($pos == $i) { 570 return false; 571 } 572 573 $value = substr($buffer, $pos, $i - $pos); 574 $pos = $i; 575 576 return true; 577 } 578} 579