]*))?([^\s?#<>]*)(\?([^\s#<>]*))?(#[^\s?#<>]+)?'; ////////////////////////////////////////////////////////////////////////////// // Escape a string for use in HTML ////////////////////////////////////////////////////////////////////////////// public static function escapeHtml($string) { if (defined('ENT_SUBSTITUTE')) { // PHP5.4 allows us to substitute invalid UTF8 sequences return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } else { return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); } } ////////////////////////////////////////////////////////////////////////////// // Escape a string for use in a URL ////////////////////////////////////////////////////////////////////////////// public static function escapeUrl($string) { return rawurlencode($string); } ////////////////////////////////////////////////////////////////////////////// // Escape a string for use in Javascript ////////////////////////////////////////////////////////////////////////////// public static function escapeJs($string) { return preg_replace_callback('/[^A-Za-z0-9,. _]/Su', function($x) { if (strlen($x[0]) == 1) { return sprintf('\\x%02X', ord($x[0])); } elseif (function_exists('iconv')) { return sprintf('\\u%04s', strtoupper(bin2hex(iconv('UTF-8', 'UTF-16BE', $x[0])))); } elseif (function_exists('mb_convert_encoding')) { return sprintf('\\u%04s', strtoupper(bin2hex(mb_convert_encoding($x[0], 'UTF-16BE', 'UTF-8')))); } else { return $x[0]; } }, $string); } ////////////////////////////////////////////////////////////////////////////// // Unescape an HTML string, giving just the literal text ////////////////////////////////////////////////////////////////////////////// public static function unescapeHtml($string) { return html_entity_decode(strip_tags($string), ENT_QUOTES, 'UTF-8'); } ////////////////////////////////////////////////////////////////////////////// // Format block-level text such as notes or transcripts, etc. ////////////////////////////////////////////////////////////////////////////// public static function formatText($text, WT_Tree $WT_TREE) { switch ($WT_TREE->preference('FORMAT_TEXT')) { case 'markdown': return '
' . WT_Filter::markdown($text) . '
'; break; case '': default: return '
' . WT_Filter::expandUrls($text) . '
'; break; } } ////////////////////////////////////////////////////////////////////////////// // Escape a string for use in HTML, and additionally convert URLs to links. ////////////////////////////////////////////////////////////////////////////// public static function expandUrls($text) { return preg_replace_callback( '/' . addcslashes('(?!>)' . WT_Filter::URL_REGEX . '(?!)', '/') . '/i', function ($m) { return '' . $m[0] . ''; }, WT_Filter::escapeHtml($text) ); } ////////////////////////////////////////////////////////////////////////////// // Format a block of text, using "Markdown". ////////////////////////////////////////////////////////////////////////////// public static function markdown($text) { $parser = new MarkdownExtra; $parser->empty_element_suffix = '>'; $parser->no_markup = true; $text = $parser->transform($text); // HTMLPurifier needs somewhere to write temporary files $HTML_PURIFIER_CACHE_DIR = WT_DATA_DIR . 'html_purifier_cache'; if (!is_dir($HTML_PURIFIER_CACHE_DIR)) { mkdir($HTML_PURIFIER_CACHE_DIR); } $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.SerializerPath', $HTML_PURIFIER_CACHE_DIR); $purifier = new HTMLPurifier($config); $text = $purifier->purify($text); return $text; } ////////////////////////////////////////////////////////////////////////////// // Validate INPUT requests ////////////////////////////////////////////////////////////////////////////// private static function _input($source, $variable, $regexp=null, $default=null) { if ($regexp) { return filter_input( $source, $variable, FILTER_VALIDATE_REGEXP, array( 'options' => array( 'regexp' => '/^(' . $regexp . ')$/u', 'default' => $default, ), ) ); } else { $tmp = filter_input( $source, $variable, FILTER_CALLBACK, array( 'options' => function($x) { return !function_exists('mb_convert_encoding') || mb_check_encoding($x, 'UTF-8') ? $x : false; }, ) ); return ($tmp===null || $tmp===false) ? $default : $tmp; } } private static function _inputArray($source, $variable, $regexp=null, $default=null) { if ($regexp) { // PHP5.3 requires the $tmp variable $tmp = filter_input_array( $source, array( $variable => array( 'flags' => FILTER_REQUIRE_ARRAY, 'filter' => FILTER_VALIDATE_REGEXP, 'options' => array( 'regexp' => '/^(' . $regexp . ')$/u', 'default' => $default, ), ), ) ); return $tmp[$variable] ?: array(); } else { // PHP5.3 requires the $tmp variable $tmp = filter_input_array( $source, array( $variable => array( 'flags' => FILTER_REQUIRE_ARRAY, 'filter' => FILTER_CALLBACK, 'options' => function($x) { return !function_exists('mb_convert_encoding') || mb_check_encoding($x, 'UTF-8') ? $x : false; } ), ) ); return $tmp[$variable] ?: array(); } } ////////////////////////////////////////////////////////////////////////////// // Validate GET requests ////////////////////////////////////////////////////////////////////////////// public static function get($variable, $regexp=null, $default=null) { return self::_input(INPUT_GET, $variable, $regexp, $default); } public static function getArray($variable, $regexp=null, $default=null) { return self::_inputArray(INPUT_GET, $variable, $regexp, $default); } public static function getBool($variable) { return (bool)filter_input(INPUT_GET, $variable, FILTER_VALIDATE_BOOLEAN); } public static function getInteger($variable, $min=0, $max=PHP_INT_MAX, $default=0) { return filter_input(INPUT_GET, $variable, FILTER_VALIDATE_INT, array('options'=>array('min_range'=>$min, 'max_range'=>$max, 'default'=>$default))); } public static function getEmail($variable, $default=null) { return filter_input(INPUT_GET, $variable, FILTER_VALIDATE_EMAIL) ?: $default; } public static function getUrl($variable, $default=null) { return filter_input(INPUT_GET, $variable, FILTER_VALIDATE_URL) ?: $default; } ////////////////////////////////////////////////////////////////////////////// // Validate POST requests ////////////////////////////////////////////////////////////////////////////// public static function post($variable, $regexp=null, $default=null) { return self::_input(INPUT_POST, $variable, $regexp, $default); } public static function postArray($variable, $regexp=null, $default=null) { return self::_inputArray(INPUT_POST, $variable, $regexp, $default); } public static function postBool($variable) { return (bool)filter_input(INPUT_POST, $variable, FILTER_VALIDATE_BOOLEAN); } public static function postInteger($variable, $min=0, $max=PHP_INT_MAX, $default=0) { return filter_input(INPUT_POST, $variable, FILTER_VALIDATE_INT, array('options'=>array('min_range'=>$min, 'max_range'=>$max, 'default'=>$default))); } public static function postEmail($variable, $default=null) { return filter_input(INPUT_POST, $variable, FILTER_VALIDATE_EMAIL) ?: $default; } public static function postUrl($variable, $default=null) { return filter_input(INPUT_POST, $variable, FILTER_VALIDATE_URL) ?: $default; } ////////////////////////////////////////////////////////////////////////////// // Cross-Site Request Forgery tokens - ensure that the user is submitting // a form that was generated by the current session. ////////////////////////////////////////////////////////////////////////////// public static function getCsrfToken() { global $WT_SESSION; if ($WT_SESSION->CSRF_TOKEN === null) { $charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcedfghijklmnopqrstuvwxyz0123456789'; for ($n=0; $n<32; ++$n) { $WT_SESSION->CSRF_TOKEN .= substr($charset, mt_rand(0, 61), 1); } } return $WT_SESSION->CSRF_TOKEN; } // Generate an element - to protect the current form from CSRF attacks. public static function getCsrf() { return ''; } // Check that the POST request contains the CSRF token generated above. public static function checkCsrf() { if (WT_Filter::post('csrf') !== WT_Filter::getCsrfToken()) { // Oops. Something is not quite right AddToLog('CSRF mismatch - session expired or malicious attack', 'auth'); WT_FlashMessages::addMessage(WT_I18N::translate('This form has expired. Try again.')); return false; } return true; } }