]*))?([^\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;
}
}