mysql 8 fixes (#1639)
[openemr.git] / library / classes / Filtreatment_class.php
blobaff39b68a249ef2d97345eea550ef2e8ab01eda6
1 <?php
2 /**
3 * FILTREATMENT CLASS FILE
6 * @author Cristian Năvălici {@link http://www.lemonsoftware.eu} lemonsoftware [at] gmail [.] com
7 * @version 1.31 17 March 2008
8 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
9 * @package Filtreatment
13 //error_reporting(E_ALL);
15 /**
16 * constant used in float comparisions
18 define('EPSILON', 1.0e-8);
20 /**
21 * CLASS DEFINITION
23 * This class can be used to sanitize user inputs and prevent
24 * most of known vulnerabilities
25 * it requires at least PHP 5.0
27 * @package Filtreatment
29 class Filtreatment
32 var $minval = 0;
33 var $maxval = 0;
34 var $error = '';
36 /**
37 * CONSTRUCTOR
39 * do some settings at init
41 * @param none
42 * @return void
44 function __construct()
46 if (get_magic_quotes_gpc()) {
47 if (!defined('MAGICQUOTES')) {
48 define('MAGICQUOTES', true);
50 } else {
51 if (!defined('MAGICQUOTES')) {
52 define('MAGICQUOTES', false);
57 //-----------------------------------------------------------------------------
58 /**
59 * CHECKS FOR AN INTEGER
61 * if the $minval and|or $maxval are set, a comparison will be performed
63 * NOTE: because the function can return 0 also as a valid result, check with === the return value
64 * @param int $input - what to check/transform
65 * @return int|bool
67 function ft_integer($input)
69 $input_c = (int)$input;
70 $mnval = (int)$this->minval;
71 $mxval = (int)$this->maxval;
73 if (!$mnval && !$mxval) {
74 return $input_c;
75 } else if ($mnval && $mxval) {
76 // check if they are in order (min < max)
77 if ($mnval > $mxval) {
78 $temp = $mnval;
79 $mnval = $mxval;
80 $mxval = $temp;
83 // and then check if the value is between these values
84 return (($input >= $mnval) && ($input <= $mxval)) ? $input_c : false;
85 } else {
86 // only one value set
87 if ($mnval) {
88 return (($input >= $mnval) ? $input_c : false );
91 if ($mxval) {
92 return (($input <= $mxval) ? $input_c : false );
98 //-----------------------------------------------------------------------------
99 /**
100 * CHECKS FOR A FLOAT
102 * if the $minval and|or $maxval are set, a comparison will be performed
104 * @param int $input - what to check/transform
105 * @return int|bool
107 function ft_float($input)
109 $input_c = (float)$input;
110 $mnval = (float)$this->minval;
111 $mxval = (float)$this->maxval;
113 if (!$mnval && !$mxval) {
114 return $input_c;
115 } else if ($mnval && $mxval) {
116 // check if they are in order (min < max)
117 if ($this->ft_realcmp($mnval, $mxval) > 0) {
118 $temp = $mnval;
119 $mnval = $mxval;
120 $mxval = $temp;
123 // and then check if the value is between these values
124 $lt = $this->ft_realcmp($input, $mxval); //-1 or 0 for true
125 if ($lt === -1 || $lt === 0) {
126 $lt = $input_c;
127 } else {
128 $lt = false;
131 $gt = $this->ft_realcmp($input, $mnval); //1 or 0 for true
132 if ($gt === 1 || $gt === 0) {
133 $gt = true;
134 } else {
135 $gt = false;
138 return (( $lt && $gt ) ? $input_c : false);
139 } else {
140 // only one value set
141 if ($mnval) {
142 $gt = $this->ft_realcmp($input, $mnval); //1 or 0 for true
143 return ( $gt === 1 || $gt === 0 ) ? $input_c : false;
146 if ($mxval) {
147 $lt = $this->ft_realcmp($input, $mxval); //-1 or 0 for true
148 return ( $lt === -1 || $lt === 0 ) ? $input_c : false;
153 //-----------------------------------------------------------------------------
155 * VALIDATES A DATE
157 * must be in YYYY-MM-DD format
159 * @param string $str - date in requested format
160 * @return string|bool - the string itselfs only for valid date
162 function ft_validdate($str)
164 if (preg_match("/([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})/", $str)) {
165 $arr = explode("-", $str); // splitting the array
166 $yy = $arr[0]; // first element of the array is year
167 $mm = $arr[1]; // second element is month
168 $dd = $arr[2]; // third element is days
169 return ( checkdate($mm, $dd, $yy) ? $str : false );
170 } else {
171 return false;
175 //-----------------------------------------------------------------------------
177 * VALIDATES AN EMAIL
179 * implies RFC 2822
181 * @param string $str - email to validate
182 * @return string|bool - the string itselfs only for valid email
184 function ft_email($email)
186 if (MAGICQUOTES) {
187 $value = stripslashes($email);
190 // check for @ symbol and maximum allowed lengths
191 if (!preg_match("/^[^@]{1,64}@[^@]{1,255}$/", $email)) {
192 return false;
195 // split for sections
196 $email_array = explode("@", $email);
197 $local_array = explode(".", $email_array[0]);
199 for ($i = 0; $i < sizeof($local_array); $i++) {
200 if (!preg_match("/^(([A-Za-z0-9!#$%&'*+\/=?^_`{|}~-][A-Za-z0-9!#$%&'*+\/=?^_`{|}~\.-]{0,63})|(\"[^(\\|\")]{0,62}\"))$/", $local_array[$i])) {
201 return false;
205 if (!preg_match("/^\[?[0-9\.]+\]?$/", $email_array[1])) {
206 // verify if domain is IP. If not, it must be a valid domain name
207 $domain_array = explode(".", $email_array[1]);
208 if (sizeof($domain_array) < 2) {
209 return false;
212 for ($i = 0; $i < sizeof($domain_array); $i++) {
213 if (!preg_match("/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]+))$/", $domain_array[$i])) {
214 return false;
217 } // if
219 return $email;
222 //-----------------------------------------------------------------------------
224 * PREPARES THE INPUT FOR DATABASE
226 * works with mysql/postgresql
228 * @param string $value - email to validate
229 * @param string $db_type - allow two constants MYSQL | PGSQL
230 * @return string|bool $value sanitized value
232 function ft_dbsql($value, $db_type = 'MYSQL')
234 if (MAGICQUOTES) {
235 $value = stripslashes($value);
238 // Quote if not a number or a numeric string
239 if (!is_numeric($value)) {
240 switch ($db_type) {
241 case 'MYSQL':
242 $value = add_escape_custom($value);
243 break;
244 case 'PGSQL':
245 $value = pg_escape_string($value);
246 break;
250 return $value;
254 //-----------------------------------------------------------------------------
256 * WORKS ON A STRING WITH REGEX EXPRESSION
258 * checks a string for specified characters
260 * @param string $value - variable to sanitize
261 * @param string $regex - is in a special form detailed below:
262 * it contains ONLY allowed characters, ANY other characters making invalid string
263 * it must NOT contain begin/end delimitators /[... ]/
264 * eg: 0-9, 0-9A-Za-z, AERS
265 * @param int $cv - 1 or 2
266 * @return string|bool return string if check succeed ($cv = 1) or string with replaced chars
269 function ft_strregex($value, $regex, $cv = 1)
271 $s = true; //var control
272 $regexfull = "/[^" . $regex . "]/";
274 // function of $cv might be a clean up operation, or just verifying
275 switch ($cv) {
276 // verify the string
277 case '1':
278 $s = ( preg_match($regexfull, $value) ? false : true );
279 break;
281 // cleanup the string
282 case '2':
283 $value = preg_replace($regexfull, '', $value);
284 break;
286 // if $cv is not specified or it's wrong
287 default:
288 if (preg_match($regexfull, $value)) {
289 $s = false;
293 return ( $s ? $value : false );
298 //-----------------------------------------------------------------------------
300 * CLEANS AGAINST XSS
302 * NOTE all credits goes to codeigniter.com
303 * @param string $str - string to check
304 * @param string $charset - character set (default ISO-8859-1)
305 * @return string|bool $value sanitized string
307 function ft_xss($str, $charset = 'ISO-8859-1')
310 * Remove Null Characters
312 * This prevents sandwiching null characters
313 * between ascii characters, like Java\0script.
316 $str = preg_replace('/\0+/', '', $str);
317 $str = preg_replace('/(\\\\0)+/', '', $str);
320 * Validate standard character entities
322 * Add a semicolon if missing. We do this to enable
323 * the conversion of entities to ASCII later.
326 $str = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u', "\\1;", $str);
329 * Validate UTF16 two byte encoding (x00)
331 * Just as above, adds a semicolon if missing.
334 $str = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "\\1\\2;", $str);
337 * URL Decode
339 * Just in case stuff like this is submitted:
341 * <a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>
343 * Note: Normally urldecode() would be easier but it removes plus signs
346 $str = preg_replace("/%u0([a-z0-9]{3})/i", "&#x\\1;", $str);
347 $str = preg_replace("/%([a-z0-9]{2})/i", "&#x\\1;", $str);
350 * Convert character entities to ASCII
352 * This permits our tests below to work reliably.
353 * We only convert entities that are within tags since
354 * these are the ones that will pose security problems.
357 if (preg_match_all("/<(.+?)>/si", $str, $matches)) {
358 for ($i = 0; $i < count($matches['0']); $i++) {
359 $str = str_replace(
360 $matches['1'][$i],
361 html_entity_decode($matches['1'][$i], ENT_COMPAT, $charset),
362 $str
368 * Convert all tabs to spaces
370 * This prevents strings like this: ja vascript
371 * Note: we deal with spaces between characters later.
374 $str = preg_replace("#\t+#", " ", $str);
377 * Makes PHP tags safe
379 * Note: XML tags are inadvertently replaced too:
381 * <?xml
383 * But it doesn't seem to pose a problem.
386 $str = str_replace(array('<?php', '<?PHP', '<?', '?>'), array('&lt;?php', '&lt;?PHP', '&lt;?', '?&gt;'), $str);
389 * Compact any exploded words
391 * This corrects words like: j a v a s c r i p t
392 * These words are compacted back to their correct state.
395 $words = array('javascript', 'vbscript', 'script', 'applet', 'alert', 'document', 'write', 'cookie', 'window');
396 foreach ($words as $word) {
397 $temp = '';
398 for ($i = 0; $i < strlen($word); $i++) {
399 $temp .= substr($word, $i, 1)."\s*";
402 $temp = substr($temp, 0, -3);
403 $str = preg_replace('#'.$temp.'#s', $word, $str);
404 $str = preg_replace('#'.ucfirst($temp).'#s', ucfirst($word), $str);
408 * Remove disallowed Javascript in links or img tags
410 $str = preg_replace("#<a.+?href=.*?(alert\(|alert&\#40;|javascript\:|window\.|document\.|\.cookie|<script|<xss).*?\>.*?</a>#si", "", $str);
411 $str = preg_replace("#<img.+?src=.*?(alert\(|alert&\#40;|javascript\:|window\.|document\.|\.cookie|<script|<xss).*?\>#si", "", $str);
412 $str = preg_replace("#<(script|xss).*?\>#si", "", $str);
415 * Remove JavaScript Event Handlers
417 * Note: This code is a little blunt. It removes
418 * the event handler and anything up to the closing >,
419 * but it's unlikely to be a problem.
422 $str = preg_replace('#(<[^>]+.*?)(onblur|onchange|onclick|onfocus|onload|onmouseover|onmouseup|onmousedown|onselect|onsubmit|onunload|onkeypress|onkeydown|onkeyup|onresize)[^>]*>#iU', "\\1>", $str);
425 * Sanitize naughty HTML elements
427 * If a tag containing any of the words in the list
428 * below is found, the tag gets converted to entities.
430 * So this: <blink>
431 * Becomes: &lt;blink&gt;
434 $str = preg_replace('#<(/*\s*)(alert|applet|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|layer|link|meta|object|plaintext|style|script|textarea|title|xml|xss)([^>]*)>#is', "&lt;\\1\\2\\3&gt;", $str);
437 * Sanitize naughty scripting elements
439 * Similar to above, only instead of looking for
440 * tags it looks for PHP and JavaScript commands
441 * that are disallowed. Rather than removing the
442 * code, it simply converts the parenthesis to entities
443 * rendering the code un-executable.
445 * For example: eval('some code')
446 * Becomes: eval&#40;'some code'&#41;
449 $str = preg_replace('#(alert|cmd|passthru|eval|exec|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', "\\1\\2&#40;\\3&#41;", $str);
452 * Final clean up
454 * This adds a bit of extra precaution in case
455 * something got through the above filters
459 $bad = array(
460 'document.cookie' => '',
461 'document.write' => '',
462 'window.location' => '',
463 "javascript\s*:" => '',
464 "Redirect\s+302" => '',
465 '<!--' => '&lt;!--',
466 '-->' => '--&gt;'
469 foreach ($bad as $key => $val) {
470 $str = preg_replace("#".$key."#i", $val, $str);
473 return $str;
476 //-----------------------------------------------------------------------------
478 * DISPLAY ERRORS
480 * @param int $mode - if 1 then echo the string; if 2 then echo the string
481 * @return void
483 function display_error($mode = 1)
485 $errstr = ( $this->error ) ? $this->error : '';
486 if ($mode == 1) {
487 echo '<br />' .$this->ft_xss($errstr) . '<br />';
488 } else {
489 return $this->ft_xss($errstr);
494 //-----------------------------------------------------------------------------
496 * REAL COMPARASION BETWEEN FLOATS
498 * 0 - for ==, 1 for r1 > r2, -1 for r1 '<' r2
500 * @param float $r1
501 * @param float $r2
502 * @return int
504 function ft_realcmp($r1, $r2)
506 $diff = $r1 - $r2;
508 if (abs($diff) < EPSILON) {
509 return 0;
510 } else {
511 return $diff < 0 ? -1 : 1;
516 //-----------------------------------------------------------------------------
517 } // class