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