another minor fix to prior commit
[openemr.git] / library / classes / Filtreatment_class.php
blob3781301695834353d2ad1422f2c8d120c974324d
1 <?php
2 /**
3 * FILTREATMENT CLASS FILE
4 *
5 *
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 {
31 var $minval = 0;
32 var $maxval = 0;
33 var $error = '';
35 /**
36 * CONSTRUCTOR
38 * do some settings at init
40 * @param none
41 * @return void
43 function __construct() {
44 if ( get_magic_quotes_gpc() ) {
45 if ( !defined('MAGICQUOTES') ) define ('MAGICQUOTES', TRUE);
46 } else {
47 if ( !defined('MAGICQUOTES') ) define ('MAGICQUOTES', FALSE);
51 //-----------------------------------------------------------------------------
52 /**
53 * CHECKS FOR AN INTEGER
55 * if the $minval and|or $maxval are set, a comparison will be performed
57 * NOTE: because the function can return 0 also as a valid result, check with === the return value
58 * @param int $input - what to check/transform
59 * @return int|bool
61 function ft_integer($input) {
62 $input_c = (int)$input;
63 $mnval = (int)$this->minval;
64 $mxval = (int)$this->maxval;
66 if ( !$mnval && !$mxval ) {
67 return $input_c;
68 } else if ( $mnval && $mxval ) {
69 // check if they are in order (min < max)
70 if ( $mnval > $mxval ) {
71 $temp = $mnval;
72 $mnval = $mxval;
73 $mxval = $temp;
76 // and then check if the value is between these values
77 return (($input >= $mnval) && ($input <= $mxval)) ? $input_c : FALSE;
78 } else {
79 // only one value set
80 if ( $mnval ) return (($input >= $mnval) ? $input_c : FALSE );
81 if ( $mxval ) return (($input <= $mxval) ? $input_c : FALSE );
87 //-----------------------------------------------------------------------------
88 /**
89 * CHECKS FOR A FLOAT
91 * if the $minval and|or $maxval are set, a comparison will be performed
93 * @param int $input - what to check/transform
94 * @return int|bool
96 function ft_float($input) {
97 $input_c = (float)$input;
98 $mnval = (float)$this->minval;
99 $mxval = (float)$this->maxval;
101 if ( !$mnval && !$mxval ) {
102 return $input_c;
103 } else if ( $mnval && $mxval ) {
104 // check if they are in order (min < max)
105 if ( $this->ft_realcmp($mnval, $mxval) > 0 ) {
106 $temp = $mnval;
107 $mnval = $mxval;
108 $mxval = $temp;
111 // and then check if the value is between these values
112 $lt = $this->ft_realcmp($input, $mxval); //-1 or 0 for true
113 if ( $lt === -1 || $lt === 0 ) $lt = $input_c; else $lt = FALSE;
115 $gt = $this->ft_realcmp($input, $mnval); //1 or 0 for true
116 if ( $gt === 1 || $gt === 0 ) $gt = TRUE; else $gt = FALSE;
118 return (( $lt && $gt ) ? $input_c : FALSE);
119 } else {
120 // only one value set
121 if ( $mnval ) {
122 $gt = $this->ft_realcmp($input, $mnval); //1 or 0 for true
123 return ( $gt === 1 || $gt === 0 ) ? $input_c : FALSE;
126 if ( $mxval ) {
127 $lt = $this->ft_realcmp($input, $mxval); //-1 or 0 for true
128 return ( $lt === -1 || $lt === 0 ) ? $input_c : FALSE;
134 //-----------------------------------------------------------------------------
136 * VALIDATES A DATE
138 * must be in YYYY-MM-DD format
140 * @param string $str - date in requested format
141 * @return string|bool - the string itselfs only for valid date
143 function ft_validdate($str) {
144 if ( preg_match("/([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})/", $str) ) {
145 $arr = explode("-",$str); // splitting the array
146 $yy = $arr[0]; // first element of the array is year
147 $mm = $arr[1]; // second element is month
148 $dd = $arr[2]; // third element is days
149 return ( checkdate($mm, $dd, $yy) ? $str : FALSE );
150 } else {
151 return FALSE;
155 //-----------------------------------------------------------------------------
157 * VALIDATES AN EMAIL
159 * implies RFC 2822
161 * @param string $str - email to validate
162 * @return string|bool - the string itselfs only for valid email
164 function ft_email($email) {
165 if (MAGICQUOTES) {
166 $value = stripslashes($email);
169 // check for @ symbol and maximum allowed lengths
170 if (!preg_match("/^[^@]{1,64}@[^@]{1,255}$/", $email)) { return FALSE; }
172 // split for sections
173 $email_array = explode("@", $email);
174 $local_array = explode(".", $email_array[0]);
176 for ($i = 0; $i < sizeof($local_array); $i++) {
177 if ( !preg_match("/^(([A-Za-z0-9!#$%&'*+\/=?^_`{|}~-][A-Za-z0-9!#$%&'*+\/=?^_`{|}~\.-]{0,63})|(\"[^(\\|\")]{0,62}\"))$/", $local_array[$i]) ) {
178 return FALSE;
182 if (!preg_match("/^\[?[0-9\.]+\]?$/", $email_array[1])) {
183 // verify if domain is IP. If not, it must be a valid domain name
184 $domain_array = explode(".", $email_array[1]);
185 if (sizeof($domain_array) < 2) { return FALSE; }
187 for ($i = 0; $i < sizeof($domain_array); $i++) {
188 if (!preg_match("/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]+))$/", $domain_array[$i])) {
189 return false;
192 } // if
194 return $email;
197 //-----------------------------------------------------------------------------
199 * PREPARES THE INPUT FOR DATABASE
201 * works with mysql/postgresql
203 * @param string $value - email to validate
204 * @param string $db_type - allow two constants MYSQL | PGSQL
205 * @return string|bool $value sanitized value
207 function ft_dbsql($value, $db_type = 'MYSQL') {
208 if (MAGICQUOTES) {
209 $value = stripslashes($value);
212 // Quote if not a number or a numeric string
213 if (!is_numeric($value)) {
214 switch ($db_type) {
215 case 'MYSQL': $value = add_escape_custom($value); break;
216 case 'PGSQL': $value = pg_escape_string($value); break;
220 return $value;
224 //-----------------------------------------------------------------------------
226 * WORKS ON A STRING WITH REGEX EXPRESSION
228 * checks a string for specified characters
230 * @param string $value - variable to sanitize
231 * @param string $regex - is in a special form detailed below:
232 * it contains ONLY allowed characters, ANY other characters making invalid string
233 * it must NOT contain begin/end delimitators /[... ]/
234 * eg: 0-9, 0-9A-Za-z, AERS
235 * @param int $cv - 1 or 2
236 * @return string|bool return string if check succeed ($cv = 1) or string with replaced chars
239 function ft_strregex($value, $regex, $cv = 1) {
240 $s = TRUE; //var control
241 $regexfull = "/[^" . $regex . "]/";
243 // function of $cv might be a clean up operation, or just verifying
244 switch ($cv) {
245 // verify the string
246 case '1':
247 $s = ( preg_match($regexfull, $value) ? FALSE : TRUE );
248 break;
250 // cleanup the string
251 case '2':
252 $value = preg_replace($regexfull,'',$value);
253 break;
255 // if $cv is not specified or it's wrong
256 default: if ( preg_match($regexfull, $value) ) $s = FALSE;
259 return ( $s ? $value : FALSE );
264 //-----------------------------------------------------------------------------
266 * CLEANS AGAINST XSS
268 * NOTE all credits goes to codeigniter.com
269 * @param string $str - string to check
270 * @param string $charset - character set (default ISO-8859-1)
271 * @return string|bool $value sanitized string
273 function ft_xss($str, $charset = 'ISO-8859-1') {
275 * Remove Null Characters
277 * This prevents sandwiching null characters
278 * between ascii characters, like Java\0script.
281 $str = preg_replace('/\0+/', '', $str);
282 $str = preg_replace('/(\\\\0)+/', '', $str);
285 * Validate standard character entities
287 * Add a semicolon if missing. We do this to enable
288 * the conversion of entities to ASCII later.
291 $str = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u',"\\1;",$str);
294 * Validate UTF16 two byte encoding (x00)
296 * Just as above, adds a semicolon if missing.
299 $str = preg_replace('#(&\#x*)([0-9A-F]+);*#iu',"\\1\\2;",$str);
302 * URL Decode
304 * Just in case stuff like this is submitted:
306 * <a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>
308 * Note: Normally urldecode() would be easier but it removes plus signs
311 $str = preg_replace("/%u0([a-z0-9]{3})/i", "&#x\\1;", $str);
312 $str = preg_replace("/%([a-z0-9]{2})/i", "&#x\\1;", $str);
315 * Convert character entities to ASCII
317 * This permits our tests below to work reliably.
318 * We only convert entities that are within tags since
319 * these are the ones that will pose security problems.
322 if (preg_match_all("/<(.+?)>/si", $str, $matches)) {
323 for ($i = 0; $i < count($matches['0']); $i++) {
324 $str = str_replace($matches['1'][$i],
325 html_entity_decode($matches['1'][$i], ENT_COMPAT, $charset), $str);
330 * Convert all tabs to spaces
332 * This prevents strings like this: ja vascript
333 * Note: we deal with spaces between characters later.
336 $str = preg_replace("#\t+#", " ", $str);
339 * Makes PHP tags safe
341 * Note: XML tags are inadvertently replaced too:
343 * <?xml
345 * But it doesn't seem to pose a problem.
348 $str = str_replace(array('<?php', '<?PHP', '<?', '?>'), array('&lt;?php', '&lt;?PHP', '&lt;?', '?&gt;'), $str);
351 * Compact any exploded words
353 * This corrects words like: j a v a s c r i p t
354 * These words are compacted back to their correct state.
357 $words = array('javascript', 'vbscript', 'script', 'applet', 'alert', 'document', 'write', 'cookie', 'window');
358 foreach ($words as $word) {
359 $temp = '';
360 for ($i = 0; $i < strlen($word); $i++) {
361 $temp .= substr($word, $i, 1)."\s*";
364 $temp = substr($temp, 0, -3);
365 $str = preg_replace('#'.$temp.'#s', $word, $str);
366 $str = preg_replace('#'.ucfirst($temp).'#s', ucfirst($word), $str);
370 * Remove disallowed Javascript in links or img tags
372 $str = preg_replace("#<a.+?href=.*?(alert\(|alert&\#40;|javascript\:|window\.|document\.|\.cookie|<script|<xss).*?\>.*?</a>#si", "", $str);
373 $str = preg_replace("#<img.+?src=.*?(alert\(|alert&\#40;|javascript\:|window\.|document\.|\.cookie|<script|<xss).*?\>#si","", $str);
374 $str = preg_replace("#<(script|xss).*?\>#si", "", $str);
377 * Remove JavaScript Event Handlers
379 * Note: This code is a little blunt. It removes
380 * the event handler and anything up to the closing >,
381 * but it's unlikely to be a problem.
384 $str = preg_replace('#(<[^>]+.*?)(onblur|onchange|onclick|onfocus|onload|onmouseover|onmouseup|onmousedown|onselect|onsubmit|onunload|onkeypress|onkeydown|onkeyup|onresize)[^>]*>#iU',"\\1>",$str);
387 * Sanitize naughty HTML elements
389 * If a tag containing any of the words in the list
390 * below is found, the tag gets converted to entities.
392 * So this: <blink>
393 * Becomes: &lt;blink&gt;
396 $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);
399 * Sanitize naughty scripting elements
401 * Similar to above, only instead of looking for
402 * tags it looks for PHP and JavaScript commands
403 * that are disallowed. Rather than removing the
404 * code, it simply converts the parenthesis to entities
405 * rendering the code un-executable.
407 * For example: eval('some code')
408 * Becomes: eval&#40;'some code'&#41;
411 $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);
414 * Final clean up
416 * This adds a bit of extra precaution in case
417 * something got through the above filters
421 $bad = array(
422 'document.cookie' => '',
423 'document.write' => '',
424 'window.location' => '',
425 "javascript\s*:" => '',
426 "Redirect\s+302" => '',
427 '<!--' => '&lt;!--',
428 '-->' => '--&gt;'
431 foreach ($bad as $key => $val) {
432 $str = preg_replace("#".$key."#i", $val, $str);
435 return $str;
439 //-----------------------------------------------------------------------------
441 * DISPLAY ERRORS
443 * @param int $mode - if 1 then echo the string; if 2 then echo the string
444 * @return void
446 function display_error($mode = 1) {
447 $errstr = ( $this->error ) ? $this->error : '';
448 if ( $mode == 1 ) {
449 echo '<br />' .$this->ft_xss($errstr) . '<br />';
450 } else {
451 return $this->ft_xss($errstr);
456 //-----------------------------------------------------------------------------
458 * REAL COMPARASION BETWEEN FLOATS
460 * 0 - for ==, 1 for r1 > r2, -1 for r1 '<' r2
462 * @param float $r1
463 * @param float $r2
464 * @return int
466 function ft_realcmp($r1, $r2) {
467 $diff = $r1 - $r2;
469 if ( abs($diff) < EPSILON ) return 0;
470 else return $diff < 0 ? -1 : 1;
474 //-----------------------------------------------------------------------------
475 } // class