Location: PHPKode > projects > Lanius CMS > includes/dracon.php
<?php if(!defined('_VALID')){header('Status: 404 Not Found');die;}
## Secure initialization functions
# @author legolas558
#
# these functions provide a strong input validation layer to Lanius CMS
# functions are supplied in different flavours to ease coding

	define('__RAW', 0);				// no changes will be performed to input
	define('__SQL', 1);				// en/decoded to be safe for the current database
	//TODO: replace all __XHTML with __HTMLAREA!!! deprecate __XHTML
	define('__XHTML', 2);			// html en/decoded
	define('__PATH', 4);				// input will be safe for path usage
	define('__NUM', 16);			// positive integer number or null
	define('__UNUSED', 32);			// previously assigned to RFC2045 validation
	define('__ARR', 64);			// Array type (to be recursively scanned)
	define('__ARR0', 128);		// Same as above, but only the first element will be considered
	/*NOTE: __ARR0 can be considered transitional (it is preferred not to use it)
		it is currently used in the backend and will one day be deprecated */
	define('__NOHTML', 256);		// strip HTML tags
	define('__SIGNED', 512);		// signed number (can have a minus sign in front of it)
	define('__CHECKBOX', 1024);		// form checkbox value
	define('__HTMLAREA', 2048);		// a special case of __XHTML
//	define('__HTMLENC', 2048);	// HTML encoding fix - used to fix javascript-encoded HTML

	/**
	 * trims a string length to a specified length.
	 *
	 * @param string $data the string to trim
	 * @param null|int $max maximum length for the string
	 */
	function croplen(&$data, $max) {
		if ($max) {
			if (strlen($data)>$max)
				$data = substr($data, 0, $max);
		}
	}

	/**
	 * encode a string for SQL statements inclusion
	 * note: global slashes quoting (gp magic quotes) is not
	 * inherent to this function
	 * @return The \var $data variable with proper quoting
	 * done through the adoDB qstr() function
	 */
	function sql_encode($data, $max=null) {
    	global $conn;
		croplen($data, $max);
	    $r = $conn->Quote($data, false);
		return $r;
	}

	// private function called for removal of PHPSESSID (or any other session name) poisoning
	function _sid_safe(&$data) {
		global $d__sid_cleanup;
		if (!isset($d__sid_cleanup)) {
			if (ini_get('session.use_only_cookies') || !ini_get('session.use_trans_sid'))
				$d__sid_cleanup = false;
			else
				$d__sid_cleanup = true;
		}
		if ($d__sid_cleanup) {
			$sid = session_id();
			if (strlen($sid))
				$data = preg_replace('/(\\?|&amp;|&)'.preg_quote(session_name()).'='.preg_quote($sid).'/', '', $data);
		}
	}

	function xhtml_filter($data, $max=null) {
		global $d_root;
		_sid_safe($data);
		require_once $d_root.'includes/xhtml.php';
		croplen($data, $max);
		return htmLawed($data);
	}
	
	// workaround for PHP bug #43896
	$d__utf8_unsafe = true;
	if (@htmlspecialchars('A'.chr(160), ENT_COMPAT | ENT_IGNORE, 'UTF-8') == '') {
		// UTF-8 support broken, convert to default encoding
		//TODO: double re-encoding
		function safe_htmlspecialchars($s) {
			return htmlspecialchars($s, ENT_COMPAT);
		}
	} else {
		function safe_htmlspecialchars($s) {
			return htmlspecialchars($s, ENT_COMPAT | ENT_IGNORE, 'UTF-8');
		}
	}
	$d__utf8_unsafe = false;

	// $data must be properly encoded!
	function xhtml_safe($data, $max=null) {
		croplen($data, $max);
		return dbg_htmlspecialchars($data);
	}

	function dbg_htmlspecialchars($s, $strict = true) {
		$s = @safe_htmlspecialchars($s);
		if ($s === FALSE) {
			global $d;
			if ($strict && isset($d))
				$d->log(1,
					sprintf("Cannot apply htmlspecialchars() to \"%s\"",
					str_replace(array('<', '>'), '', (strlen($s>255) ? substr($s, 0, 255).'...' : $s))));
			// fallback to iso-8859-1
			return htmlspecialchars($s, ENT_COMPAT);
		}
		return $s;
	}

/*
	function _enc_xhtml_byte($m) {
		$m = $m[0];
		$l=strlen($m);
		$s='';
		for($i=0;$i<$l;++$i) {
			$s.='&#x'.dechex(ord($m[$i])).';';
		}
		return $s;
	}
	
	function xhtml_safe_encoding($data, $max = null) {
		croplen($data, $max);
		// \x1-a
		$data = preg_replace_callback('/["&<>\xc0-\xff]+/', '_enc_xhtml_byte', $data);
		return $data;
	} */
	
	function num_get($data, $def=null) {
		if (!lcms_ctype_digit($data))
			return $def;
		else
			return (int)$data;
	}
	
	function signed_get($data, $def=null) {
		if (!preg_match('/\\s*-?\\s*\\d+\\s*$/', $data))
			return $def;
		return (int)$data;
	}

	function path_safe($data) {
		if (!strlen($data)) return '';
		$data = str_replace(array('/','\\'), '_', $data);
		if (($data[0]=='.') || ($data[0]==chr(0))) $data[0]='_';
		return $data;
	}
	
	## returns true if the path is normalized
	function valid_path($s) {
		if (	preg_match( '/[^a-zA-Z0-9_\\-\\.\\/]/', $s) ||
			preg_match('/^(\\.|\\.\\.|\\/)/', $s))
			return false;
		return true;
	}

	/**
	 * prepare input for sql statements
	 * @return The variable input prepared by sql_encode for sql
	 * usage, not including quoting. Quoting is inherent to the SQL statement
	 */
	function in_sql($name, $arr = null, $def = null, $max = null) {
		if (!isset($arr))
			$arr =& $_REQUEST;
		if (!isset($arr[$name])) return $def;
		$data = $arr[$name];
		//s2: this should now be handled by the adodb driver
		//L: global quoting settings are not inherent to sql_encode()
		if (is_array($data)) return 'Array';
		raw_fix($data);
		return sql_encode($data, $max);
	}

	function in_html($name, $arr = null,$def = null, $max = null) {
		$data = in_raw($name, $arr, $def, $max);
		return xhtml_filter($data, $max);
	}

	function in_num($name, $arr = null,$def = null) {
		if (!isset($arr))
			$arr =& $_REQUEST;
		if (!isset($arr[$name]))	return $def;
		$data = $arr[$name];
		// raw_fix($data) is not needed here
		return num_get($data, $def);
	}
	
	function in_signed($name, $arr = null, $def = null) {
		if (!isset($arr))
			$arr =& $_REQUEST;
		if (!isset($arr[$name]))	return $def;
		$data = $arr[$name];
		// raw_fix($data) is not needed here
		return signed_get($data, $def);
	}
	
	// if the checkbox was not defined, return 0 (which is the correct default value)
	function in_checkbox($name, $arr = null, $def = 0) {
		if (!isset($arr))
			$arr =& $_REQUEST;
		if (!isset($arr[$name]))	return $def;
		return checkbox_get($arr[$name], $def);
	}
	
	function checkbox_get($data, $def=0) {
		if (!$data)	return 0;	// will evaluate '0', 'false' and ''
		// raw_fix($data) is not needed here
		if ($data!='off')
			return 1;
		return 0;
	}

	function in_raw($name, $arr = null, $def = null, $max = null) {
		if (!isset($arr))
			$arr =& $_REQUEST;
		if (!isset($arr[$name])) return $def;
		$data = $arr[$name];
		if (is_array($data)) return 'Array';
		raw_fix($data);
		croplen($data, $max);
		return $data;
	}

	function spam_filter($s) {
		$c = substr_count($s, 'http://');
		$c = max($c, substr_count($s, 'https://'));
		if ($c > 5)
			return preg_replace('/[^:]{3,}:\\/\\/[^\\s]*/', '<http://nospam.laniuscms.org/>', $s);
		return $s;
	}

	// experimental spam-filtering input function
	// might be useful to fix user content snippets broadcast via emails
	function in_nospam($name, $arr = null, $def = null, $max = null) {
		if (!isset($arr))
			$arr =& $_REQUEST;
		if (!isset($arr[$name])) return $def;
		$data = $arr[$name];
		if (is_array($data)) return 'Array';
		raw_fix($data);
		croplen($data, $max);
		return spam_filter($data);
	}

	function in_path($name, $arr = null, $def = null, $max = null) {
		if (!isset($arr))
			$arr =& $_REQUEST;
		if (!isset($arr[$name])) return $def;
		$data = $arr[$name];
		if (is_array($data)) return 'Array';
		raw_fix($data);
		$data = path_safe($data);
		croplen($data, $max);
		return $data;
	}
	
	// inputs array of arrays - always takes care of keys and applies the flags
	function in_arr2($name, $flags = 0, $arr = null, $def = null, $max = null) {
		if (!isset($arr))
			$arr =& $_REQUEST;
		if (!isset($arr[$name])) return $def;
		$data = $arr[$name];
		$ak = array_keys($data);
		foreach($ak as $akey)
			array_validation($data[$akey], $flags , $def, $max);
		return $data;
	}

	// inputs an array of data - elements cannot be arrays
	function in_arr($name, $flags = 0, $arr = null, $def = null, $max = null) {
		if (!isset($arr))
			$arr =& $_REQUEST;
		if (!isset($arr[$name])) return $def;
		$data = $arr[$name];
		// raw_fix($data) is not needed here since it is executed by array_validation()
		if (!is_array($data))
			return $def;
		array_validation($data, $flags, $def, $max);
		return $data;
	}
	
	function array_validation(&$arr, $flags = 0, $def = null, $max = null) {
		if (!$flags) {	// optimization for raw input
			if ($GLOBALS['d__bad_env'])
				$arr = array_map('stripslashes', $arr);
			return;
		}
		//TODO: could optimize in_validation() usage
		$ak = array_keys($arr);
		if (isset($def)) {
			foreach($ak as $akey) {
				in_validation($arr[$akey], $flags , current($def), $max);
				next($def);
			}
		} else {
			foreach($ak as $akey)
				in_validation($arr[$akey], $flags , null, $max);
		}
	}

	if ($GLOBALS['d__bad_env']) {
		function raw_fix(&$data) {
			$data = stripslashes($data);
		}
	} else {
		function raw_fix(&$data) {}
	}

	function in_validation(&$data, $flags, $def, $max) {
		if (is_array($data)) {	// disallow nested arrays
			$data = $def;
			return;
		}
		raw_fix($data);
		if (!$flags) {
			croplen($data, $max);
			return;	// be careful with optimizations
		}
		if ($flags & __NUM) {
			if ($flags & __SIGNED)
				$data = signed_get($data, $def);
			else
				$data = num_get($data, $def);
		}

		if ($flags & __HTMLAREA)
			$data = area_filter($data, $max);
		else if ($flags & __XHTML)
			$data = xhtml_filter($data, $max);
		if ($flags & __NOHTML)
			$data = xhtml_safe($data, $max);
		if ($flags & __SQL)
			$data = sql_encode($data, $max);
		if ($flags & __PATH)
			$data = path_safe($data);
		if ($flags & __CHECKBOX)
			$data = checkbox_get($data, $def);
		//L: skipped because of previous optimization
//		croplen($data, $max);
	}

	// prepare a variable for input from the (wild) external world
	function in($name, $flags = 0, &$arr, $def = null, $max = null) {
		if (!isset($arr))
			$arr =& $_REQUEST;
		if (!isset($arr[$name])) return $def;
		$data = $arr[$name];
		// raw_fix($data) is not needed here since it is executed by in_validation()
		if ($flags & __ARR0) {
			if (!isset($data[0]))
				return $def;
			in_validation($data[0], $flags ^ __ARR0 , $def, $max);
			return $data[0];
		}
		if ($flags & __ARR) {
			if (!is_array($data))
				return $def;

			$c=count($data);
			$data = array_values($data);
			for($i=0;$i<$c;$i++)	// referencing pointers to array items (through '&') is allowed only on PHP5+
				in_validation($data[$i], $flags ^ __ARR , $def, $max);
			return $data;
		}

		in_validation($data, $flags, $def, $max);
		return $data;
	}
	
	function _filter_blank($s) {
		if (strlen($s)>20)
			return $s;
		// filter out some blank contents
		if (preg_match('/^\\s*(<br\\s*\\/?\\s*>)|(<p>(?:&(nbsp|#160);)<\\/p>)\\s*$/', $s))
			return '';
		return $s;
	}

	function _fix_urls($s) {
		if (strlen($s))
			return preg_replace_callback('/<\\w+\\s+[^>]+>/s', '_url_fix_nested', $s);
		return $s;
	}

	function _URL_replace_rel($m) {
		// d_website already contains the subsite path
		global $d_website;
		if (strpos($m[2], $d_website)===0)
			return $m[1].'="'.substr($m[2], strlen($d_website)).'"';
		return $m[0];
	}

	function __fix_space_urls($m) {
		return $m[1].'="'.str_replace(' ', '%20', $m[2]);
	}
	
	function _url_fix_nested($m) {
		$data = preg_replace_callback('/(href|src)="([^"><]+)/i', '__fix_space_urls', $m[0]);
		return preg_replace_callback('/(href|src)="?([^"\\s>]+)"?/i', '_URL_replace_rel', $data);
	}
	
	function area_filter($data, $max) {
		$data = _fix_urls(_filter_blank($data));
		croplen($data, $max);
		return $data;
	}

	function in_area($name, &$arr, $def = null, $max = null) {
		return area_filter(in($name, __XHTML, $arr, $def, $max), $max);
	}
	
	function in_cookie($name, $def = null, $max = null) {
		return in($GLOBALS['d_rand'].'-'.$name, __RAW, $_COOKIE, $def, $max);
	}
	
	function in_session($name, $flags = __RAW, $def = null, $max = null) {
		return in($GLOBALS['d_uid'].'-'.$name, $flags, $GLOBALS['_SESSION'], $def, $max);
	}

	function out_session($name, $value) {
		$_SESSION[$GLOBALS['d_uid'].'-'.$name] = $value;
	}
	
	function unset_session($name) {
		unset($_SESSION[$GLOBALS['d_uid'].'-'.$name]);
	}
	
	function _userid_enc($h, $encode = true) {
		// create the destination map
		global $d_rand;
		$b = $d = '309bc1a2648efd57';
		$l = strlen($d_rand);
		// walk on odd letters
		for ($i=0;$i<$l;$i+=2) {
			$o = $i % 16;
			$p = ord($d_rand[$i]) % 16;
			$t = $d[$p]; $d[$p] = $d[$o]; $d[$o] = $t;
		}
		// walk complement on even letters
		for ($i=1;$i<$l;$i+=2) {
			$o = $i % 16;
			$p = 15 - (ord($d_rand[$i]) % 16);
			$t = $d[$p]; $d[$p] = $d[$o]; $d[$o] = $t;
		}
		if ($encode)
			return strtr($h, $b, $d);
		else
			return strtr($h, $d, $b);
	}
	
	function encode_userid($n) {
		return _userid_enc(dechex($n), true);
	}

	function decode_userid($n) {
		return hexdec(_userid_enc($n, false));
	}

	function in_userid($name, &$arr, $def = null) {
		$data = in_raw($name, $arr);
		if (!isset($data) || !lcms_ctype_xdigit($data))
			return $def;
		return decode_userid($data);
	}
	
	//NOTE: arrays should be used instead of this prefix approach
	function in_prefix($prefix, $flags, &$arr, $def = null) {
		if (!isset($arr))
			$arr =& $_REQUEST;
		$keys = array_keys($arr);
		if (!isset($keys[0])) return $def;
		$data = array();
		foreach($keys as $key) {
			if (strpos($key, $prefix)===0) {
				$dk = substr($key, strlen($prefix));			
				$data[$dk] = in($key, $flags, $arr,
					isset($def[$dk]) ? $def[$dk] : null);
			}
		}
		if (!count($data)) return $def;
		return $data;
	}

// moved here from com_fb for filemanager
function is_safe_path($path) {
	// if path is empty, it's ok
	if (!strlen($path)) return true;
	// if path tries to climb up the directory level, it's bad
	if (strpos($path, '..')!==false) return false;
	// if path contains invalid slashes sequences, it's bad
	if (strpos($path, '//')!==false) return false;
	if (strpos($path, './')!==false) return false;
	return true;
}

?>
Return current item: Lanius CMS