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 * Class is used to validate and parse the trigger function. 24 */ 25class CFunctionParser extends CParser { 26 27 const STATE_NEW = 0; 28 const STATE_END = 1; 29 const STATE_UNQUOTED = 2; 30 const STATE_QUOTED = 3; 31 const STATE_END_OF_PARAMS = 4; 32 33 const PARAM_ARRAY = 0; 34 const PARAM_UNQUOTED = 1; 35 const PARAM_QUOTED = 2; 36 37 private $function = ''; 38 private $parameters = ''; 39 private $params_raw = []; 40 41 /** 42 * Returns true if the char is allowed in the function name, false otherwise. 43 * 44 * @param string $c 45 * 46 * @return bool 47 */ 48 protected function isFunctionChar($c) { 49 return ($c >= 'a' && $c <= 'z'); 50 } 51 52 /** 53 * Parse a trigger function and parameters and put them into $this->params_raw array. 54 * 55 * @param string $source 56 * @param int $pos 57 */ 58 public function parse($source, $pos = 0) { 59 $this->length = 0; 60 $this->match = ''; 61 $this->function = ''; 62 $this->parameters = ''; 63 $this->params_raw = []; 64 65 for ($p = $pos; isset($source[$p]) && $this->isFunctionChar($source[$p]); $p++) { 66 } 67 68 if ($p == $pos) { 69 return self::PARSE_FAIL; 70 } 71 72 $p2 = $p; 73 74 $params_raw = [ 75 'type' => self::PARAM_ARRAY, 76 'raw' => '', 77 'pos' => $p - $pos, 78 'parameters' => [] 79 ]; 80 if (!$this->parseFunctionParameters($source, $p, $params_raw['parameters'])) { 81 return self::PARSE_FAIL; 82 } 83 84 $params_raw['raw'] = substr($source, $p2, $p - $p2); 85 86 $this->length = $p - $pos; 87 $this->match = substr($source, $pos, $this->length); 88 $this->function = substr($source, $pos, $p2 - $pos); 89 $this->parameters = substr($source, $p2 + 1, $p - $p2 - 2); 90 $this->params_raw = $params_raw; 91 92 return isset($source[$p]) ? self::PARSE_SUCCESS_CONT : self::PARSE_SUCCESS; 93 } 94 95 private function parseFunctionParameters($source, &$pos, array &$parameters) { 96 if (!isset($source[$pos]) || $source[$pos] != '(') { 97 return false; 98 } 99 100 $_parameters = []; 101 $state = self::STATE_NEW; 102 $num = 0; 103 104 for ($p = $pos + 1; isset($source[$p]); $p++) { 105 switch ($state) { 106 // a new parameter started 107 case self::STATE_NEW: 108 switch ($source[$p]) { 109 case ' ': 110 break; 111 112 case ',': 113 $_parameters[$num++] = [ 114 'type' => self::PARAM_UNQUOTED, 115 'raw' => '', 116 'pos' => $p - $pos 117 ]; 118 break; 119 120 case ')': 121 $_parameters[$num] = [ 122 'type' => self::PARAM_UNQUOTED, 123 'raw' => '', 124 'pos' => $p - $pos 125 ]; 126 $state = self::STATE_END_OF_PARAMS; 127 break; 128 129 case '"': 130 $_parameters[$num] = [ 131 'type' => self::PARAM_QUOTED, 132 'raw' => $source[$p], 133 'pos' => $p - $pos 134 ]; 135 $state = self::STATE_QUOTED; 136 break; 137 138 default: 139 $_parameters[$num] = [ 140 'type' => self::PARAM_UNQUOTED, 141 'raw' => $source[$p], 142 'pos' => $p - $pos 143 ]; 144 $state = self::STATE_UNQUOTED; 145 } 146 break; 147 148 // end of parameter 149 case self::STATE_END: 150 switch ($source[$p]) { 151 case ' ': 152 break; 153 154 case ',': 155 $state = self::STATE_NEW; 156 $num++; 157 break; 158 159 case ')': 160 $state = self::STATE_END_OF_PARAMS; 161 break; 162 163 default: 164 break 3; 165 } 166 break; 167 168 // an unquoted parameter 169 case self::STATE_UNQUOTED: 170 switch ($source[$p]) { 171 case ')': 172 $state = self::STATE_END_OF_PARAMS; 173 break; 174 175 case ',': 176 $state = self::STATE_NEW; 177 $num++; 178 break; 179 180 default: 181 $_parameters[$num]['raw'] .= $source[$p]; 182 } 183 break; 184 185 // a quoted parameter 186 case self::STATE_QUOTED: 187 $_parameters[$num]['raw'] .= $source[$p]; 188 189 if ($source[$p] == '"' && $source[$p - 1] != '\\') { 190 $state = self::STATE_END; 191 } 192 break; 193 194 // end of parameters 195 case self::STATE_END_OF_PARAMS: 196 break 2; 197 } 198 } 199 200 if ($state == self::STATE_END_OF_PARAMS) { 201 $parameters = $_parameters; 202 $pos = $p; 203 204 return true; 205 } 206 207 return false; 208 } 209 210 /** 211 * Returns the left part of the trigger function without parameters. 212 * 213 * @return string 214 */ 215 public function getFunction() { 216 return $this->function; 217 } 218 219 /** 220 * Returns the parameters of the function. 221 * 222 * @return string 223 */ 224 public function getParameters() { 225 return $this->parameters; 226 } 227 228 /** 229 * Returns the list of the parameters. 230 * 231 * @return array 232 */ 233 public function getParamsRaw() { 234 return $this->params_raw; 235 } 236 237 /** 238 * Returns the number of the parameters. 239 * 240 * @return int 241 */ 242 public function getParamsNum() { 243 return array_key_exists('parameters', $this->params_raw) ? count($this->params_raw['parameters']) : 0; 244 } 245 246 /* 247 * Unquotes special symbols in item the parameter 248 * 249 * @param string $param 250 * 251 * @return string 252 */ 253 public static function unquoteParam($param) { 254 $unquoted = ''; 255 256 for ($p = 1; isset($param[$p]); $p++) { 257 if ($param[$p] == '\\' && $param[$p + 1] == '"') { 258 continue; 259 } 260 261 $unquoted .= $param[$p]; 262 } 263 264 return substr($unquoted, 0, -1); 265 } 266 267 /** 268 * Returns an unquoted parameter. 269 * 270 * @param int $n the number of the requested parameter 271 * 272 * @return string|null 273 */ 274 public function getParam($n) { 275 $num = 0; 276 277 foreach ($this->params_raw['parameters'] as $param) { 278 if ($num++ == $n) { 279 switch ($param['type']) { 280 case self::PARAM_UNQUOTED: 281 // return parameter without any changes 282 return $param['raw']; 283 case self::PARAM_QUOTED: 284 return $this->unquoteParam($param['raw']); 285 } 286 } 287 } 288 289 return null; 290 } 291} 292