You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

719 lines
13 KiB

1 year ago
<?php
/*
** Zabbix
** Copyright (C) 2001-2023 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
class CValidationRule {
const STATE_BEGIN = 0;
const STATE_END = 1;
/**
* An error message if validation rule is not valid.
*
* @var string
*/
private $error = '';
/**
* Parse validation rule. Returns array of rules or fail if $buffer is not valid.
*
* @param string $buffer
*
* @return array|bool
*/
public function parse($buffer) {
$this->error = '';
$pos = 0;
$state = self::STATE_BEGIN;
$rules = [];
while (isset($buffer[$pos])) {
switch ($state) {
case self::STATE_BEGIN:
switch ($buffer[$pos]) {
case ' ':
$pos++;
break;
default:
$rule = [];
if (!$this->parseString($buffer, $pos, $rule)
&& !$this->parseRangeTime($buffer, $pos, $rule)
&& !$this->parseAbsDate($buffer, $pos, $rule)
&& !$this->parseAbsTime($buffer, $pos, $rule)
&& !$this->parseTimePeriods($buffer, $pos, $rule)
&& !$this->parseTimeUnit($buffer, $pos, $rule)
&& !$this->parseRgb($buffer, $pos, $rule)
&& !$this->parseRequired($buffer, $pos, $rule)
&& !$this->parseNotEmpty($buffer, $pos, $rule)
&& !$this->parseLE($buffer, $pos, $rule)
&& !$this->parseJson($buffer, $pos, $rule)
&& !$this->parseInt32($buffer, $pos, $rule)
&& !$this->parseUInt64($buffer, $pos, $rule)
&& !$this->parseIn($buffer, $pos, $rule)
&& !$this->parseId($buffer, $pos, $rule)
&& !$this->parseGE($buffer, $pos, $rule)
&& !$this->parseFatal($buffer, $pos, $rule)
&& !$this->parseDB($buffer, $pos, $rule)
&& !$this->parseArrayId($buffer, $pos, $rule)
&& !$this->parseArrayDB($buffer, $pos, $rule)
&& !$this->parseArray($buffer, $pos, $rule)
&& !$this->parseFlags($buffer, $pos, $rule)
&& !$this->parseBool($buffer, $pos, $rule)
&& !$this->parseCuid($buffer, $pos, $rule)) {
// incorrect validation rule
break 3;
}
if (array_key_exists(key($rule), $rules)) {
// the message can be not translated because it is an internal error
$this->error = 'Validation rule "'.key($rule).'" already exists.';
return false;
}
$rules = array_merge($rules, $rule);
$state = self::STATE_END;
break;
}
break;
case self::STATE_END:
switch ($buffer[$pos]) {
case ' ':
$pos++;
break;
case '|':
$state = self::STATE_BEGIN;
$pos++;
break;
default:
// incorrect validation rule
break 3;
}
break;
}
}
if (isset($buffer[$pos])) {
// the message can be not translated because it is an internal error
$this->error = 'Cannot parse validation rules "'.$buffer.'" at position '.$pos.'.';
return false;
}
return $rules;
}
/**
* Returns the error message if validation rule is invalid.
*
* @return string
*/
public function getError() {
return $this->error;
}
/**
* fatal
*
* 'fatal' => true
*/
private function parseFatal($buffer, &$pos, &$rule) {
if (strncmp(substr($buffer, $pos), 'fatal', 5) != 0) {
return false;
}
$pos += 5;
$rule['fatal'] = true;
return true;
}
/**
* string
*
* 'string' => true
*/
private function parseString($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'string', 6) != 0) {
return false;
}
$pos += 6;
$rules['string'] = true;
return true;
}
/**
* range_time
*
* 'range_time' => true
*/
private function parseRangeTime($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'range_time', 10) != 0) {
return false;
}
$pos += 10;
$rules['range_time'] = true;
return true;
}
/**
* abs_date
*
* 'abs_date' => true
*/
private function parseAbsDate($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'abs_date', 8) != 0) {
return false;
}
$pos += 8;
$rules['abs_date'] = true;
return true;
}
/**
* abs_time
*
* 'abs_time' => true
*/
private function parseAbsTime($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'abs_time', 8) != 0) {
return false;
}
$pos += 8;
$rules['abs_time'] = true;
return true;
}
/**
* range_time
*
* 'time_periods' => true
*/
private function parseTimePeriods($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'time_periods', 12) != 0) {
return false;
}
$pos += 12;
$rules['time_periods'] = true;
return true;
}
/**
* time_unit
*
* 'time_unit' => ['<value1>', ..., '<valueN>']
*/
private function parseTimeUnit($buffer, &$pos, &$rules): bool {
$TIME_UNIT_LENGTH = mb_strlen('time_unit');
$TIME_UNIT_YEAR_LENGTH = mb_strlen('time_unit_year');
$values = [];
$ranges_string = '';
$ranges = [];
if (strncmp(substr($buffer, $pos), 'time_unit_year', $TIME_UNIT_YEAR_LENGTH) === 0) {
$pos += $TIME_UNIT_YEAR_LENGTH;
$values['with_year'] = true;
}
else if (strncmp(substr($buffer, $pos), 'time_unit', $TIME_UNIT_LENGTH) === 0) {
$pos += $TIME_UNIT_LENGTH;
}
else {
return false;
}
while (isset($buffer[$pos]) && $buffer[$pos] === ' ') {
$pos++;
}
while (isset($buffer[$pos]) && $buffer[$pos] !== '|') {
$ranges_string .= $buffer[$pos];
$pos++;
}
foreach (explode(',', $ranges_string) as $range_string) {
if (strpos($range_string, ':') !== false) {
[$from, $to] = explode(':', $range_string);
}
else {
$from = $range_string;
$to = $range_string;
}
if (ctype_digit($from) && ctype_digit($to)) {
$ranges[] = ['from' => $from, 'to' => $to];
}
}
if ($ranges) {
$values['ranges'] = $ranges;
}
$rules['time_unit'] = $values;
return true;
}
/**
* rgb
*
* 'rgb' => true
*/
private function parseRgb($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'rgb', 3) != 0) {
return false;
}
$pos += 3;
$rules['rgb'] = true;
return true;
}
/**
* required
*
* 'required' => true
*/
private function parseRequired($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'required', 8) != 0) {
return false;
}
$pos += 8;
$rules['required'] = true;
return true;
}
/**
* not_empty
*
* 'not_empty' => true
*/
private function parseNotEmpty($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'not_empty', 9) != 0) {
return false;
}
$pos += 9;
$rules['not_empty'] = true;
return true;
}
/**
* le <value>
*
* 'le' => '<value>'
*/
private function parseLE($buffer, &$pos, &$rules) {
$i = $pos;
if (0 != strncmp(substr($buffer, $i), 'le ', 3)) {
return false;
}
$i += 3;
$value = '';
while (isset($buffer[$i]) && $buffer[$i] != '|') {
$value .= $buffer[$i++];
}
if (!CNewValidator::is_int32($value)) {
return false;
}
$pos = $i;
$rules['le'] = $value;
return true;
}
/**
* json
*
* 'json' => true
*/
private function parseJson($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'json', 4) != 0) {
return false;
}
$pos += 4;
$rules['json'] = true;
return true;
}
/**
* int32
*
* 'int32' => true
*/
private function parseInt32($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'int32', 5) != 0) {
return false;
}
$pos += 5;
$rules['int32'] = true;
return true;
}
/**
* uint64
*
* 'uint64' => true
*/
private function parseUInt64($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'uint64', 6) != 0) {
return false;
}
$pos += 6;
$rules['uint64'] = true;
return true;
}
/**
* in <value1>[,...,<valueN>]
*
* 'in' => array('<value1>', ..., '<valueN>')
*/
private function parseIn($buffer, &$pos, &$rules) {
$i = $pos;
if (strncmp(substr($buffer, $i), 'in ', 3) != 0) {
return false;
}
$i += 3;
while (isset($buffer[$i]) && $buffer[$i] == ' ') {
$i++;
}
$values = [];
if (!$this->parseValues($buffer, $i, $values)) {
return false;
}
$pos = $i;
$rules['in'] = $values;
return true;
}
/**
* id
*
* 'id' => true
*/
private function parseId($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'id', 2) != 0) {
return false;
}
$pos += 2;
$rules['id'] = true;
return true;
}
/**
* ge <value>
*
* 'ge' => '<value>'
*/
private function parseGE($buffer, &$pos, &$rules) {
$i = $pos;
if (0 != strncmp(substr($buffer, $i), 'ge ', 3)) {
return false;
}
$i += 3;
$value = '';
while (isset($buffer[$i]) && $buffer[$i] != '|') {
$value .= $buffer[$i++];
}
if (!CNewValidator::is_int32($value)) {
return false;
}
$pos = $i;
$rules['ge'] = $value;
return true;
}
/**
* db <table>.<field>
*
* 'db' => array(
* 'table' => '<table>',
* 'field' => '<field>'
* )
*/
private function parseDB($buffer, &$pos, &$rules) {
$i = $pos;
if (strncmp(substr($buffer, $i), 'db ', 3) != 0) {
return false;
}
$i += 3;
while (isset($buffer[$i]) && $buffer[$i] == ' ') {
$i++;
}
$table = '';
if (!$this->parseField($buffer, $i, $table) || !isset($buffer[$i]) || $buffer[$i++] != '.') {
return false;
}
$field = '';
if (!$this->parseField($buffer, $i, $field)) {
return false;
}
$pos = $i;
$rules['db'] = [
'table' => $table,
'field' => $field
];
return true;
}
/**
* array
*
* 'array' => true
*/
private function parseArray($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'array', 5) != 0) {
return false;
}
$pos += 5;
$rules['array'] = true;
return true;
}
/**
* array_id
*
* 'array_id' => true
*/
private function parseArrayId($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'array_id', 8) != 0) {
return false;
}
$pos += 8;
$rules['array_id'] = true;
return true;
}
/**
* array_db <table>.<field>
*
* 'array_db' => array(
* 'table' => '<table>',
* 'field' => '<field>'
* )
*/
private function parseArrayDB($buffer, &$pos, &$rules) {
$i = $pos;
if (strncmp(substr($buffer, $i), 'array_db ', 9) != 0) {
return false;
}
$i += 9;
while (isset($buffer[$i]) && $buffer[$i] == ' ') {
$i++;
}
$table = '';
if (!$this->parseField($buffer, $i, $table) || !isset($buffer[$i]) || $buffer[$i++] != '.') {
return false;
}
$field = '';
if (!$this->parseField($buffer, $i, $field)) {
return false;
}
$pos = $i;
$rules['array_db'] = [
'table' => $table,
'field' => $field
];
return true;
}
/**
* flags <value1> | <value2> | ... | <valueN>
*
* 'flags' => <value1> | <value2> | ... | <valueN>
*/
private function parseFlags($buffer, &$pos, &$rules) {
$i = $pos;
if (0 != strncmp(substr($buffer, $i), 'flags ', 6)) {
return false;
}
$i += 6;
$value = 0x00;
if (!$this->parseValue($buffer, $i, $value)) {
return false;
}
$pos = $i;
$rules['flags'] = $value;
return true;
}
/**
* <field>
*/
private function parseField($buffer, &$pos, &$field) {
$matches = [];
if (1 != preg_match('/^[A-Za-z0-9_]+/', substr($buffer, $pos), $matches))
return false;
$pos += strlen($matches[0]);
$field = $matches[0];
return true;
}
/**
* <value1>[,...,<valueN>]
*/
private function parseValues($buffer, &$pos, array &$values) {
$i = $pos;
while (true) {
$value = '';
if (!isset($buffer[$i]) || !$this->parseValue($buffer, $i, $value)) {
return false;
}
$values[] = $value;
if (!isset($buffer[$i]) || $buffer[$i] == ' ' || $buffer[$i] == '|') {
break;
}
$i++;
}
$pos = $i;
return true;
}
/**
* <value>
*/
private function parseValue($buffer, &$pos, &$value) {
$i = $pos;
while (isset($buffer[$i]) && $buffer[$i] != ',' && $buffer[$i] != ' ' && $buffer[$i] != '|') {
$i++;
}
if ($pos == $i) {
return false;
}
$value = substr($buffer, $pos, $i - $pos);
$pos = $i;
return true;
}
/**
* bool
*
* 'bool' => true
*/
private function parseBool($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'bool', 4) != 0) {
return false;
}
$pos += 4;
$rules['bool'] = true;
return true;
}
/**
* cuid
*
* 'cuid' => true
*/
private function parseCuid($buffer, &$pos, &$rules) {
if (strncmp(substr($buffer, $pos), 'cuid', 4) != 0) {
return false;
}
$pos += 4;
$rules['cuid'] = true;
return true;
}
}