rror', '>', '"', ' & '); // Set to TRUE or FALSE once it has been calculated var $e_highlighting; // Highlight query var $e_query; public $thumbWidth = 100; public $thumbHeight = 0; public $thumbCrop = 0; private $thumbEncode = 0; // Set up the defaults var $e_optDefault = array( // default context: reflects legacy settings (many items enabled) 'context' => 'OLDDEFAULT', // 'fromadmin' => FALSE, // Enable emote display 'emotes' => TRUE, // Convert defines(constants) within text. 'defs' => FALSE, // replace all {e_XXX} constants with their e107 value - 'rel' or 'abs' 'constants' => FALSE, // Enable hooked parsers 'hook' => TRUE, // Allow scripts through (new for 0.8) 'scripts' => TRUE, // Make links clickable 'link_click' => TRUE, // Substitute on clickable links (only if link_click == TRUE) 'link_replace' => TRUE, // Parse shortcodes - TRUE enables parsing 'parse_sc' => FALSE, // remove HTML tags. 'no_tags' => FALSE, // Restore entity form of quotes and such to single characters - TRUE disables 'value' => FALSE, // Line break compression - TRUE removes newline characters 'nobreak' => FALSE, // Retain newlines - wraps to \n instead of
if TRUE (for non-HTML email text etc) 'retain_nl' => FALSE ); // Super modifiers override default option values var $e_SuperMods = array( //text is part of a title (e.g. news title) 'TITLE' => array( 'nobreak'=>TRUE, 'retain_nl'=>TRUE, 'link_click' => FALSE, 'emotes'=>FALSE, 'defs'=>TRUE, 'parse_sc'=>TRUE ), 'TITLE_PLAIN' => array( 'nobreak'=>TRUE, 'retain_nl'=>TRUE, 'link_click' => FALSE, 'emotes'=>FALSE, 'defs'=>TRUE, 'parse_sc'=>TRUE, 'no_tags' => TRUE ), //text is user-entered (i.e. untrusted) and part of a title (e.g. forum title) 'USER_TITLE' => array( 'nobreak'=>TRUE, 'retain_nl'=>TRUE, 'link_click' => FALSE, 'scripts' => FALSE, 'emotes'=>FALSE, 'hook'=>FALSE ), // text is 'body' of email or similar - being sent 'off-site' so don't rely on server availability 'E_TITLE' => array( 'nobreak'=>TRUE, 'retain_nl'=>TRUE, 'defs'=>TRUE, 'parse_sc'=>TRUE, 'emotes'=>FALSE, 'scripts' => FALSE, 'link_click' => FALSE ), // text is part of the summary of a longer item (e.g. content summary) 'SUMMARY' => array( 'defs'=>TRUE, 'constants'=>'full', 'parse_sc'=>TRUE ), // text is the description of an item (e.g. download, link) 'DESCRIPTION' => array( 'defs'=>TRUE, 'constants'=>'full', 'parse_sc'=>TRUE ), // text is 'body' or 'bulk' text (e.g. custom page body, content body) 'BODY' => array( 'defs'=>TRUE, 'constants'=>'full', 'parse_sc'=>TRUE ), 'WYSIWYG' => array( 'defs'=>FALSE, 'constants'=>'full', 'parse_sc'=>FALSE, 'wysiwyg'=>TRUE ), // text is user-entered (i.e. untrusted)'body' or 'bulk' text (e.g. custom page body, content body) 'USER_BODY' => array( 'constants'=>'full', 'scripts' => FALSE, 'nostrip'=>FALSE ), // text is 'body' of email or similar - being sent 'off-site' so don't rely on server availability 'E_BODY' => array( 'defs'=>TRUE, 'constants'=>'full', 'parse_sc'=>TRUE, 'emotes'=>FALSE, 'scripts' => FALSE, 'link_click' => FALSE ), // text is text-only 'body' of email or similar - being sent 'off-site' so don't rely on server availability 'E_BODY_PLAIN' => array( 'defs'=>TRUE, 'constants'=>'full', 'parse_sc'=>TRUE, 'emotes'=>FALSE, 'scripts' => FALSE, 'link_click' => FALSE, 'retain_nl' => TRUE, 'no_tags' => TRUE ), // text is the 'content' of a link (A tag, etc) 'LINKTEXT' => array( 'nobreak'=>TRUE, 'retain_nl'=>TRUE, 'link_click' => FALSE, 'emotes'=>FALSE, 'hook'=>FALSE, 'defs'=>TRUE, 'parse_sc'=>TRUE ), // text is used (for admin edit) without fancy conversions or html. 'RAWTEXT' => array( 'nobreak'=>TRUE, 'retain_nl'=>TRUE, 'link_click' => FALSE, 'emotes'=>FALSE, 'hook'=>FALSE, 'no_tags'=>TRUE ) ); // Individual modifiers change the current context var $e_Modifiers = array( 'emotes_off' => array('emotes' => FALSE), 'emotes_on' => array('emotes' => TRUE), 'no_hook' => array('hook' => FALSE), 'do_hook' => array('hook' => TRUE), // New for 0.8 'scripts_off' => array('scripts' => FALSE), // New for 0.8 'scripts_on' => array('scripts' => TRUE), 'no_make_clickable' => array('link_click' => FALSE), 'make_clickable' => array('link_click' => TRUE), 'no_replace' => array('link_replace' => FALSE), // Replace text of clickable links (only if make_clickable option set) 'replace' => array('link_replace' => TRUE), // No path replacement 'consts_off' => array('constants' => FALSE), // Relative path replacement 'consts_rel' => array('constants' => 'rel'), // Absolute path replacement 'consts_abs' => array('constants' => 'abs'), // Full path replacement 'consts_full' => array('constants' => 'full'), // No shortcode parsing 'scparse_off' => array('parse_sc' => FALSE), 'scparse_on' => array('parse_sc' => TRUE), // Strip tags 'no_tags' => array('no_tags' => TRUE), // Leave tags 'do_tags' => array('no_tags' => FALSE), 'fromadmin' => array('fromadmin' => TRUE), 'notadmin' => array('fromadmin' => FALSE), // entity replacement 'er_off' => array('value' => FALSE), 'er_on' => array('value' => TRUE), // Decode constant if exists 'defs_off' => array('defs' => FALSE), 'defs_on' => array('defs' => TRUE), 'dobreak' => array('nobreak' => FALSE), 'nobreak' => array('nobreak' => TRUE), // Line break using \n 'lb_nl' => array('retain_nl' => TRUE), // Line break using
'lb_br' => array('retain_nl' => FALSE), // Legacy option names below here - discontinue later 'retain_nl' => array('retain_nl' => TRUE), 'defs' => array('defs' => TRUE), 'parse_sc' => array('parse_sc' => TRUE), 'constants' => array('constants' => 'rel'), 'value' => array('value' => TRUE), 'wysiwyg' => array('wysiwyg'=>TRUE) ); /** * Constructor - keep it public for backward compatibility still some new e_parse() in the core * * @return void */ public function __construct() { // initialise the type of UTF-8 processing methods depending on PHP version and mb string extension $this->init(); $this->initCharset(); // Preprocess the supermods to be useful default arrays with all values foreach ($this->e_SuperMods as $key => $val) { // precalculate super defaults $this->e_SuperMods[$key] = array_merge($this->e_optDefault , $this->e_SuperMods[$key]); $this->e_SuperMods[$key]['context'] = $key; } } /** * Initialise the type of UTF-8 processing methods depending on PHP version and mb string extension. * * NOTE: can't be called until CHARSET is known but we all know that it is UTF-8 now * * @return void */ private function initCharset() { // Start by working out what, if anything, we do about utf-8 handling. // 'Do nothing' is the simple option $this->utfAction = 0; // CHARSET is utf-8 // if(strtolower(CHARSET) == 'utf-8') // { if(version_compare(PHP_VERSION, '6.0.0') < 1) { // Need to do something here if(extension_loaded('mbstring')) { // Check for function overloading $temp = ini_get('mbstring.func_overload'); // Just check the string functions - will be non-zero if overloaded if(($temp & MB_OVERLOAD_STRING) == 0) { // Can use the mb_string routines $this->utfAction = 1; } // Set the default encoding, so we don't have to specify every time mb_internal_encoding('UTF-8'); } else { // Must use emulation - will probably be slow! $this->utfAction = 2; require_once(E_UTF8_PACK.'utils/unicode.php'); // Always load the core routines - bound to need some of them! require_once(E_UTF8_PACK.'native/core.php'); } } // } } /** * Unicode (UTF-8) analogue of standard @link http://php.net/strlen strlen PHP function. * Returns the length of the given string. * * @param string $str The UTF-8 encoded string being measured for length. * @return integer The length (amount of UTF-8 characters) of the string on success, and 0 if the string is empty. */ public function ustrlen($str) { switch($this->utfAction) { case 0: return strlen($str); case 1: return mb_strlen($str); } // Default case shouldn't happen often // Save a call - invoke the function directly return strlen(utf8_decode($str)); } /** * Unicode (UTF-8) analogue of standard @link http://php.net/strtolower strtolower PHP function. * Make a string lowercase. * * @param string $str The UTF-8 encoded string to be lowercased. * @return string Specified string with all alphabetic characters converted to lowercase. */ public function ustrtolower($str) { switch($this->utfAction) { case 0: return strtolower($str); case 1: return mb_strtolower($str); } // Default case shouldn't happen often return utf8_strtolower($str); } /** * Unicode (UTF-8) analogue of standard @link http://php.net/strtoupper strtoupper PHP function. * Make a string uppercase. * * @param string $str The UTF-8 encoded string to be uppercased. * @return string Specified string with all alphabetic characters converted to uppercase. */ public function ustrtoupper($str) { switch($this->utfAction) { case 0: return strtoupper($str); case 1: return mb_strtoupper($str); } // Default case shouldn't happen often return utf8_strtoupper($str); } /** * Unicode (UTF-8) analogue of standard @link http://php.net/strpos strpos PHP function. * Find the position of the first occurrence of a case-sensitive UTF-8 encoded string. * Returns the numeric position (offset in amount of UTF-8 characters) * of the first occurrence of needle in the haystack string. * * @param string $haystack The UTF-8 encoded string being searched in. * @param integer $needle The UTF-8 encoded string being searched for. * @param integer $offset [optional] The optional offset parameter allows you to specify which character in haystack to start searching. * The position returned is still relative to the beginning of haystack. * @return integer|boolean Returns the position as an integer. If needle is not found, the function will return boolean FALSE. */ public function ustrpos($haystack, $needle, $offset = 0) { switch($this->utfAction) { case 0: return strpos($haystack, $needle, $offset); case 1: return mb_strpos($haystack, $needle, $offset); } return utf8_strpos($haystack, $needle, $offset); } /** * Unicode (UTF-8) analogue of standard @link http://php.net/strrpos strrpos PHP function. * Find the position of the last occurrence of a case-sensitive UTF-8 encoded string. * Returns the numeric position (offset in amount of UTF-8 characters) * of the last occurrence of needle in the haystack string. * * @param string $haystack The UTF-8 encoded string being searched in. * @param integer $needle The UTF-8 encoded string being searched for. * @param integer $offset [optional] - The optional offset parameter allows you to specify which character in haystack to start searching. * The position returned is still relative to the beginning of haystack. * @return integer|boolean Returns the position as an integer. If needle is not found, the function will return boolean FALSE. */ public function ustrrpos($haystack, $needle, $offset = 0) { switch($this->utfAction) { case 0: return strrpos($haystack, $needle, $offset); case 1: return mb_strrpos($haystack, $needle, $offset); } return utf8_strrpos($haystack, $needle, $offset); } /** * Unicode (UTF-8) analogue of standard @link http://php.net/stristr stristr PHP function. * Returns all of haystack starting from and including the first occurrence of needle to the end. * * @param string $haystack The UTF-8 encoded string to search in. * @param mixed $needle If needle is not a string, it is converted to an integer and applied as the ordinal value of a character. * @param integer $length [optional] (PHP 5.3+) If TRUE, returns the part of the haystack before the first occurrence of the needle (excluding needle). * @return string Returns the matched substring. If needle is not found, returns FALSE. */ public function ustristr($haystack, $needle, $before_needle = false) { switch($this->utfAction) { case 0: return stristr($haystack, $needle, $before_needle); case 1: //return mb_substr($haystack, $needle, $before_needle); return mb_stristr($haystack, $needle, $before_needle); } // No utf8 pack backup return stristr($haystack, $needle, $before_needle); } /** * Unicode (UTF-8) analogue of standard @link http://php.net/substr substr PHP function. * Returns the portion of string specified by the start and length parameters. * * NOTE: May be subtle differences in return values dependent on which routine is used. * Native substr() routine can return FALSE. mb_substr() and utf8_substr() just return an empty string. * * @param string $str The UTF-8 encoded string. * @param integer $start Start of portion to be returned. Position is counted in amount of UTF-8 characters from the beginning of str. * First character's position is 0. Second character position is 1, and so on. * @param integer $length [optional] If length is given, the string returned will contain at most length characters beginning from start * (depending on the length of string). If length is omitted, the rest of string from start will be returned. * @return string The extracted UTF-8 encoded part of input string. */ public function usubstr($str, $start, $length = NULL) { switch($this->utfAction) { case 0: return substr($str, $start, $length); case 1: if(is_null($length)) { return mb_substr($str, $start); } else { return mb_substr($str, $start, $length); } } return utf8_substr($str, $start, $length); } /** * Converts the supplied text (presumed to be from user input) to a format suitable for storing in a database table. * * @param string $data * @param boolean $nostrip [optional] Assumes all data is GPC ($_GET, $_POST, $_COOKIE) unless indicate otherwise by setting this var to TRUE. * If magic quotes is enabled on the server and you do not tell toDB() that the data is non GPC then slashes will be stripped when they should not be. * @param boolean $no_encode [optional] This parameter should nearly always be FALSE. It is used by the save_prefs() function to preserve HTML content within prefs even when * the save_prefs() function has been called by a non admin user / user without html posting permissions. * @param boolean $mod [optional] The 'no_html' and 'no_php' modifiers blanket prevent HTML and PHP posting regardless of posting permissions. (used in logging) * The 'pReFs' value is for internal use only, when saving prefs, to prevent sanitisation of HTML. * @param boolean $original_author [optional] * @return string * @todo complete the documentation of this essential method */ public function toDB($data, $nostrip =false, $no_encode = false, $mod = false, $original_author = false) { $core_pref = e107::getConfig(); if (is_array($data)) { $ret = array(); foreach ($data as $key => $var) { //Fix - sanitize keys as well $ret[$this->toDB($key, $nostrip, $no_encode, $mod, $original_author)] = $this->toDB($var, $nostrip, $no_encode, $mod, $original_author); } return $ret; } if (MAGIC_QUOTES_GPC == true && $nostrip == false) { $data = stripslashes($data); } if ($mod != 'pReFs') //XXX We're not saving prefs. { $data = $this->preFilter($data); // used by bb_xxx.php toDB() functions. bb_code.php toDB() allows us to properly bypass HTML cleaning below. // if(strlen($data) != strlen(strip_tags($data))) // html tags present. // strip_tags() doesn't function doesnt look for unclosed '>'. if(($this->isHtml($data)) && strpos($mod, 'no_html') === false) { $this->isHtml = true; $data = $this->cleanHtml($data); // sanitize all html. $data = str_replace(array('%7B','%7D'),array('{','}'),$data); // fix for {e_XXX} paths. // $data = urldecode($data); //XXX Commented out : NO LONGER REQUIRED. symptom of cleaning the HTML - urlencodes src attributes containing { and } .eg. {e_BASE} } else // caused double-encoding of '&' { //$data = str_replace('<','<',$data); //$data = str_replace('>','>',$data); } if (!check_class($core_pref->get('post_html', e_UC_MAINADMIN))) { $data = strip_tags($data); // remove tags from cleaned html. $data = str_replace(array('[html]','[/html]'),'',$data); } // $data = html_entity_decode($data, ENT_QUOTES, 'utf-8'); // Prevent double-entities. Fix for [code] - see bb_code.php toDB(); } if (check_class($core_pref->get('post_html'))) /*$core_pref->is('post_html') && XXX preformecd by cleanHtml() */ { $no_encode = true; } if (is_numeric($original_author) && !check_class($core_pref->get('post_html'), '', $original_author)) { $no_encode = false; } if ($no_encode === true && strpos($mod, 'no_html') === false) { $search = array('$', '"', "'", '\\', 'get('php_bbcode'))) { $ret = preg_replace("#\[(php)#i", "[\\1", $ret); } return $ret; } /** * Check for umatched 'dangerous' HTML tags * (these can destroy page layout where users are able to post HTML) * @DEPRECATED * @param string $data * @param string $tagList - if empty, uses default list of input tags. Otherwise a CSV list of tags to check (any type) * * @return boolean TRUE if an unopened closing tag found * FALSE if nothing found */ function htmlAbuseFilter($data, $tagList = '') { if ($tagList == '') { $checkTags = array('textarea', 'input', 'td', 'tr', 'table'); } else { $checkTags = explode(',', $tagList); } $tagArray = array_flip($checkTags); foreach ($tagArray as &$v) { $v = 0; }; // Data fields become zero; keys are tag names. $data = strtolower(preg_replace('#\[code\].*?\[\/code\]#i', '', $data)); // Ignore code blocks. All lower case simplifies the rest $matches = array(); if (!preg_match_all('#<(\/|)([^<>]*?[^\/])>#', $data, $matches, PREG_SET_ORDER)) { //echo "No tags found
"; return TRUE; // No tags found; so all OK } //print_a($matches); foreach ($matches as $m) { // $m[0] is the complete tag; $m[1] is '/' or empty; $m[2] is the tag and any attributes list ($tag) = explode(' ', $m[2], 2); if (!isset($tagArray[$tag])) continue; // Not a tag of interest if ($m[1] == '/') { // Closing tag if ($tagArray[$tag] == 0) { //echo "Close before open: {$tag}
"; return TRUE; // Closing tag before we've had an opening tag } $tagArray[$tag]--; // Obviously had at least one opening tag } else { // Opening tag $tagArray[$tag]++; } } //print_a($tagArray); foreach ($tagArray as $t) { if ($t > 0) return TRUE; // More opening tags than closing tags } return FALSE; // OK now } /** * @DEPRECATED XXX TODO Remove this horrible thing which adds junk to a db. * Checks a string for potentially dangerous HTML tags, including malformed tags * */ public function dataFilter($data, $mode='bbcode') { $ans = ''; $vetWords = array('dataFilter($decode)).$match[3]; } // XXX REmove ME. private function modTag($match) { $ans = ''; if (isset($match[1])) { $chop = intval(strlen($match[1]) / 2); $ans = substr($match[1], 0, $chop).'##xss##'.substr($match[1], $chop); } else { $ans = '?????'; } return '[sanitised]'.$ans.'[/sanitised]'; } /** * Processes data as needed before its written to the DB. * Currently gives bbcodes the opportunity to do something * * @param $data string - data about to be written to DB * @return string - modified data */ public function preFilter($data) { if (!is_object($this->e_bb)) { require_once(e_HANDLER.'bbcode_handler.php'); $this->e_bb = new e_bbcode; } $ret = $this->e_bb->parseBBCodes($data, USERID, 'default', 'PRE'); // $postID = logged in user here return $ret; } function toForm($text) { if(empty($text)) // fix - handle proper 0, Space etc values. { return $text; } if(is_string($text) && substr($text,0,6) == '[html]') { // $text = $this->toHtml($text,true); $search = array('"',''','\', '&',); // '&' must be last. $replace = array('"',"'","\\", '&'); // return htmlspecialchars_decode($text); $text = str_replace($search,$replace,$text); // return $text; //$text = htmlentities($text,ENT_NOQUOTES, "UTF-8"); // return $text; } // return htmlentities($text); $search = array('$', '"', '<', '>'); $replace = array('$', '"', '<', '>'); $text = str_replace($search, $replace, $text); if (e107::wysiwyg() !== true) { // fix for utf-8 issue with html_entity_decode(); ??? // $text = str_replace(" ", " ", $text); } return $text; } function post_toForm($text) { if(is_array($text)) { foreach ($text as $key=>$value) { $text[$this->post_toForm($key)] = $this->post_toForm($value); } return $text; } if(MAGIC_QUOTES_GPC == TRUE) { $text = stripslashes($text); } return str_replace(array("'", '"', "<", ">"), array("'", """, "<", ">"), $text); } function post_toHTML($text, $original_author = FALSE, $extra = '', $mod = FALSE) { $text = $this->toDB($text, FALSE, FALSE, $mod, $original_author); return $this->toHTML($text, TRUE, $extra); } /** * @param $text - template to parse. * @param boolean $parseSCFiles - parse core 'single' shortcodes * @param array $extraCodes - support legacy shortcode content (eg. content within .sc) as well as simpleParse array format. * @param object $eVars - XXX more info needed. * @return string */ function parseTemplate($text, $parseSCFiles = true, $extraCodes = null, $eVars = null) { if(!empty($extraCodes) && $this->isSimpleParse($extraCodes)) // support for a combined simple and standard template parse. - (eg. used by signup email template.) { $text = $this->simpleParse($text, $extraCodes, false); } if(!is_bool($parseSCFiles)) { trigger_error("\$parseSCFiles in parseTemplate() was given incorrect data"); } return e107::getScParser()->parseCodes($text, $parseSCFiles, $extraCodes, $eVars); } /** * Check if we are using the simple-Parse array format, or a legacy .sc format which contains 'return ' * @param array $extraCodes */ private function isSimpleParse($extraCodes) { if(!is_array($extraCodes)) { return false; } foreach ($extraCodes as $sc => $code) { if(preg_match('/return(.*);/',$code)) // still problematic. 'return;' Might be used in common speech. { return false; } else { return true; } /* if(!strpos($code, 'return ')) { return true; } else { return false; }*/ } } /** * Simple parser * * @param string $template * @param e_vars|array $vars * @param string $replaceUnset string to be used if replace variable is not set, false - don't replace * @return string parsed content */ function simpleParse($template, $vars, $replaceUnset='') { $this->replaceVars = $vars; $this->replaceUnset = $replaceUnset; return preg_replace_callback("#\{([a-zA-Z0-9_]+)\}#", array($this, 'simpleReplace'), $template); } protected function simpleReplace($tmp) { $unset = ($this->replaceUnset !== false ? $this->replaceUnset : $tmp[0]); if(is_array($this->replaceVars)) { $this->replaceVars = new e_vars($this->replaceVars); //return ($this->replaceVars[$key] !== null ? $this->replaceVars[$key]: $unset); } $key = $tmp[1]; // PHP7 fix. return ($this->replaceVars->$key !== null ? $this->replaceVars->$key : $unset); // Doesn't work. } function htmlwrap($str, $width, $break = "\n", $nobreak = "a", $nobr = "pre", $utf = FALSE) { /* Pretty well complete rewrite to try and handle utf-8 properly. Breaks each utf-8 'word' every $width characters max. If possible, breaks after 'safe' characters. $break is the character inserted to flag the break. $nobreak is a list of tags within which word wrap is to be inactive */ //TODO handle htmlwrap somehow //return $str; // Don't wrap if non-numeric width $width = intval($width); // And trap stupid wrap counts if ($width < 6) return $str; // Transform protected element lists into arrays $nobreak = explode(" ", strtolower($nobreak)); // Variable setup $intag = FALSE; $innbk = array(); $drain = ""; // List of characters it is "safe" to insert line-breaks at // It is not necessary to add < and > as they are automatically implied $lbrks = "/?!%)-}]\\\"':;&"; // Is $str a UTF8 string? if ($utf || strtolower(CHARSET) == 'utf-8') { // 0x1680, 0x180e, 0x2000-0x200a, 0x2028, 0x205f, 0x3000 are 'non-ASCII' Unicode UCS-4 codepoints - see http://www.unicode.org/Public/UNIDATA/UnicodeData.txt // All convert to 3-byte utf-8 sequences: // 0x1680 0xe1 0x9a 0x80 // 0x180e 0xe1 0xa0 0x8e // 0x2000 0xe2 0x80 0x80 // - // 0x200a 0xe2 0x80 0x8a // 0x2028 0xe2 0x80 0xa8 // 0x205f 0xe2 0x81 0x9f // 0x3000 0xe3 0x80 0x80 $utf8 = 'u'; $whiteSpace = '#([\x20|\x0c]|[\xe1][\x9a][\x80]|[\xe1][\xa0][\x8e]|[\xe2][\x80][\x80-\x8a,\xa8]|[\xe2][\x81][\x9f]|[\xe3][\x80][\x80]+)#'; // Have to explicitly enumerate the whitespace chars, and use non-utf-8 mode, otherwise regex fails on badly formed utf-8 } else { $utf8 = ''; // For non-utf-8, can use a simple match string $whiteSpace = '#(\s+)#'; } // Start of the serious stuff - split into HTML tags and text between $content = preg_split('#(<.*?'.'>)#mis', $str, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE ); foreach($content as $value) { if ($value[0] == "<") { // We are within an HTML tag // Create a lowercase copy of this tag's contents $lvalue = strtolower(substr($value, 1, -1)); if ($lvalue) { // Tag of non-zero length // If the first character is not a / then this is an opening tag if ($lvalue[0] != "/") { // Collect the tag name preg_match("/^(\w*?)(\s|$)/", $lvalue, $t); // If this is a protected element, activate the associated protection flag if(in_array($t[1], $nobreak)) array_unshift($innbk, $t[1]); } else { // Otherwise this is a closing tag // If this is a closing tag for a protected element, unset the flag if (in_array(substr($lvalue, 1), $nobreak)) { reset($innbk); while (list($key, $tag) = each($innbk)) { if (substr($lvalue, 1) == $tag) { unset($innbk[$key]); break; } } $innbk = array_values($innbk); } } } else { // Eliminate any empty tags altogether $value = ''; } // Else if we're outside any tags, and with non-zero length string... } elseif ($value) { // If unprotected... if (!count($innbk)) { // Use the ACK (006) ASCII symbol to replace all HTML entities temporarily $value = str_replace("\x06", "", $value); preg_match_all("/&([a-z\d]{2,7}|#\d{2,5});/i", $value, $ents); $value = preg_replace("/&([a-z\d]{2,7}|#\d{2,5});/i", "\x06", $value); // echo "Found block length ".strlen($value).': '.substr($value,20).'
'; // Split at spaces - note that this will fail if presented with invalid utf-8 when doing the regex whitespace search // $split = preg_split('#(\s)#'.$utf8, $value, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE ); $split = preg_split($whiteSpace, $value, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE ); $value = ''; foreach ($split as $sp) { // echo "Split length ".strlen($sp).': '.substr($sp,20).'
'; $loopCount = 0; while (strlen($sp) > $width) { // Enough characters that we may need to do something. $pulled = ''; if ($utf8) { // Pull out a piece of the maximum permissible length if (preg_match('#^((?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,'.$width.'})(.{0,1}).*#s',$sp,$matches) == 0) { // Make any problems obvious for now $value .= '[!invalid utf-8: '.$sp.'!]'; $sp = ''; } elseif (empty($matches[2])) { // utf-8 length is less than specified - treat as a special case $value .= $sp; $sp = ''; } else { // Need to find somewhere to break the string for($i = strlen($matches[1]) - 1; $i >= 0; $i--) { if(strpos($lbrks, $matches[1][$i]) !== FALSE) break; } if($i < 0) { // No 'special' break character found - break at the word boundary $pulled = $matches[1]; } else { $pulled = substr($sp, 0, $i + 1); } } $loopCount++; if ($loopCount > 20) { // Make any problems obvious for now $value .= '[!loop count exceeded: '.$sp.'!]'; $sp = ''; } } else { for ($i = min($width, strlen($sp)); $i > 0; $i--) { // No speed advantage to defining match character if (strpos($lbrks, $sp[$i-1]) !== FALSE) break; } if ($i == 0) { // No 'special' break boundary character found - break at the word boundary $pulled = substr($sp, 0, $width); } else { $pulled = substr($sp, 0, $i); } } if ($pulled) { $value .= $pulled.$break; // Shorten $sp by whatever we've processed (will work even for utf-8) $sp = substr($sp, strlen($pulled)); } } // Add in any residue $value .= $sp; } // Put captured HTML entities back into the string foreach ($ents[0] as $ent) $value = preg_replace("/\x06/", $ent, $value, 1); } } // Send the modified segment down the drain $drain .= $value; } // Return contents of the drain return $drain; } /** * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) * * Truncate a HTML string * * Cuts a string to the length of $length and adds the value of $ending if the text is longer than length. * * @param string $text String to truncate. * @param integer $length Length of returned string, including ellipsis. * @param string $ending It will be used as Ending and appended to the trimmed string. * @param boolean $exact If false, $text will not be cut mid-word * @return string Trimmed string. */ function html_truncate($text, $length = 100, $ending = '...', $exact = true) { if($this->ustrlen(preg_replace('/<.*?>/', '', $text)) <= $length) { return $text; } $totalLength = 0; $openTags = array(); $truncate = ''; preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER); foreach($tags as $tag) { if(!$tag[2] || !preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/si', $tag[2])) { if(preg_match('/<[\w]+[^>]*>/s', $tag[0])) { array_unshift($openTags, $tag[2]); } else if(preg_match('/<\/([\w]+)[^>]*>/s', $tag[0], $closeTag)) { $pos = array_search($closeTag[1], $openTags); if($pos !== false) { array_splice($openTags, $pos, 1); } } } $truncate .= $tag[1]; $contentLength = $this->ustrlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $tag[3])); if($contentLength + $totalLength > $length) { $left = $length - $totalLength; $entitiesLength = 0; if(preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $tag[3], $entities, PREG_OFFSET_CAPTURE)) { foreach($entities[0] as $entity) { if($entity[1] + 1 - $entitiesLength <= $left) { $left--; $entitiesLength += $this->ustrlen($entity[0]); } else { break; } } } $truncate .= $this->usubstr($tag[3], 0, $left + $entitiesLength); break; } else { $truncate .= $tag[3]; $totalLength += $contentLength; } if($totalLength >= $length) { break; } } if(!$exact) { $spacepos = $this->ustrrpos($truncate, ' '); if(isset($spacepos)) { $bits = $this->usubstr($truncate, $spacepos); preg_match_all('/<\/([a-z]+)>/i', $bits, $droppedTags, PREG_SET_ORDER); if(!empty($droppedTags)) { foreach($droppedTags as $closingTag) { if(!in_array($closingTag[1], $openTags)) { array_unshift($openTags, $closingTag[1]); } } } $truncate = $this->usubstr($truncate, 0, $spacepos); } } $truncate .= $ending; foreach($openTags as $tag) { $truncate .= ''; } return $truncate; } /** * Truncate a HTML string to a maximum length $len ­ append the string $more if it was truncated * * @param string $text String to process * @param integer $len [optional] Length of characters to be truncated - default 200 * @param string $more [optional] String which will be added if truncation - default ' ... ' * @return string */ public function html_truncate_old ($text, $len = 200, $more = ' ... ') { $pos = 0; $curlen = 0; $tmp_pos = 0; $intag = FALSE; while($curlen < $len && $curlen < strlen($text)) { switch($text {$pos} ) { case "<": if($text {$pos + 1} == "/") { $closing_tag = TRUE; } $intag = TRUE; $tmp_pos = $pos - 1; $pos++; break; case ">": if($text {$pos - 1} == "/") { $closing_tag = TRUE; } if($closing_tag == TRUE) { $tmp_pos = 0; $closing_tag = FALSE; } $intag = FALSE; $pos++; break; case "&": if($text {$pos + 1} == "#") { $end = strpos(substr($text, $pos, 7), ";"); if($end !== FALSE) { $pos += ($end + 1); if(!$intag) { $curlen++; } break; } } else { $pos++; if(!$intag) { $curlen++; } break; } default: $pos++; if(!$intag) { $curlen++; } break; } } $ret = ($tmp_pos > 0 ? substr($text, 0, $tmp_pos+1) : substr($text, 0, $pos)); if($pos < strlen($text)) { $ret = $ret.$more; } return $ret; } /** * Truncate a string of text to a maximum length $len ­ append the string $more if it was truncated * Uses current CHARSET ­ for utf-8, returns $len characters rather than $len bytes * * @param string $text ­ string to process * @param integer $len ­ length of characters to be truncated * @param string $more ­ string which will be added if truncation * @return string */ public function text_truncate($text, $len = 200, $more = ' ... ') { // Always valid if($this->ustrlen($text) <= $len) { return $text; } $ret = $this->usubstr($text, 0, $len); // search for possible broken html entities // - if an & is in the last 8 chars, removing it and whatever follows shouldn't hurt // it should work for any characters encoding // FIXME - INVESTIGATE this one, switch to utf8 aware methods $leftAmp = $this->ustrrpos($this->usubstr($ret, -8), '&'); if($leftAmp) { $ret = $this->usubstr($ret, 0, $this->ustrlen($ret) - 8 + $leftAmp); } return $ret.$more; } function textclean ($text, $wrap = 100) { $text = str_replace("\n\n\n", "\n\n", $text); $text = $this->htmlwrap($text, $wrap); $text = str_replace(array('
', '
', '
'), '
', $text); /* we can remove any linebreaks added by htmlwrap function as any \n's will be converted later anyway */ return $text; } // Test for text highlighting, and determine the text highlighting transformation // Returns TRUE if highlighting is active for this page display function checkHighlighting() { global $pref; if (!defined('e_SELF')) { // Still in startup, so can't calculate highlighting return FALSE; } if(!isset($this->e_highlighting)) { $this->e_highlighting = FALSE; $shr = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ""); if($pref['search_highlight'] && (strpos(e_SELF, 'search.php') === FALSE) && ((strpos($shr, 'q=') !== FALSE) || (strpos($shr, 'p=') !== FALSE))) { $this->e_highlighting = TRUE; if(!isset($this->e_query)) { $query = preg_match('#(q|p)=(.*?)(&|$)#', $shr, $matches); $this->e_query = str_replace(array('+', '*', '"', ' '), array('', '.*?', '', '\b|\b'), trim(urldecode($matches[2]))); } } } return $this->e_highlighting; } /** * Replace text represenation of website urls and email addresses with clickable equivalents. * @param string $text * @param string $type email|url * @param array $opts options. (see below) * @param string $opts['sub'] substitute text within links * @param bool $opts['ext'] load link in new window (not for email) * @return string */ private function makeClickable($text='', $type='email', $opts=array()) { if(empty($text)) { return ''; } $textReplace = (!empty($opts['sub'])) ? $opts['sub'] : ''; if(substr($textReplace,-6) == '.glyph') { $textReplace = $this->toGlyph($textReplace,''); } switch($type) { default: case "email": preg_match_all("#(?:[\n\r ]|^)?([a-z0-9\-_.]+?)@([\w\-]+\.([\w\-\.]+\.)*[\w]+)#i", $text, $match); if(!empty($match[0])) { $srch = array(); $repl = array(); foreach($match[0] as $eml) { $email = trim($eml); $srch[] = $email; $repl[] = $this->emailObfuscate($email,$textReplace); } $text = str_replace($srch,$repl,$text); } break; case "url": $linktext = (!empty($textReplace)) ? $textReplace : '\\2'; $external = (!empty($opts['ext'])) ? 'rel="external"' : ''; $text = preg_replace("#(^|[\s]| )([\w]+?:\/\/(?:[\w-%]+?)(?:\.[\w-%]+?)+.*?)(?=$|[\s[\]<]|\.\s|\.$|,\s|,$| )#is", "\\1".$linktext."", $text); $text = preg_replace("#(^|[\s])((?:www|ftp)(?:\.[\w-%]+?){2}.*?)(?=$|[\s[\]<]|\.\s|\.$|,\s|,$)#is", "\\1".$linktext."", $text); break; } return $text; } /** * Converts the text (presumably retrieved from the database) for HTML output. * * @param string $text * @param boolean $parseBB [optional] * @param string $modifiers [optional] TITLE|SUMMARY|DESCRIPTION|BODY|RAW|LINKTEXT etc. * Comma-separated list, no spaces allowed * first modifier must be a CONTEXT modifier, in UPPER CASE. * subsequent modifiers are lower case - see $this->e_Modifiers for possible values * @param mixed $postID [optional] * @param boolean $wrap [optional] * @return string * @todo complete the documentation of this essential method */ public function toHTML($text, $parseBB = FALSE, $modifiers = '', $postID = '', $wrap = FALSE) { if($text == '') { return $text; } $pref = e107::getPref(); global $fromadmin; // Set default modifiers to start $opts = $this->e_optDefault; // Now process any modifiers that are specified if ($modifiers) { $aMods = explode(',', $modifiers); // If there's a supermodifier, it must be first, and in uppercase $psm = trim($aMods[0]); if (isset($this->e_SuperMods[$psm])) { // Supermodifier found - override default values where necessary $opts = array_merge($opts,$this->e_SuperMods[$psm]); $opts['context'] = $psm; unset($aMods[0]); } // Now find any regular modifiers; use them to modify the context // (there should only be one or two out of the list of possibles) foreach ($aMods as $mod) { // Slight concession to varying coding styles - stripping spaces is a waste of CPU cycles! $mod = trim($mod); if (isset($this->e_Modifiers[$mod])) { // This is probably quicker than array_merge // - especially as usually only one or two loops foreach ($this->e_Modifiers[$mod] as $k => $v) { // Update our context-specific options $opts[$k] = $v; } } } } // Turn off a few things if not enabled in options if(!vartrue($pref['smiley_activate'])) { $opts['emotes'] = FALSE; } if(!vartrue($pref['make_clickable'])) { $opts['link_click'] = FALSE; } if(!vartrue($pref['link_replace'])) { $opts['link_replace'] = FALSE; } $fromadmin = $opts['fromadmin']; // Convert defines(constants) within text. eg. Lan_XXXX - must be the entire text string (i.e. not embedded) // The check for '::' is a workaround for a bug in the Zend Optimiser 3.3.0 and PHP 5.2.4 combination // - causes crashes if '::' in site name if($opts['defs'] && (strlen($text) < 35) && ((strpos($text, '::') === FALSE) && defined(trim($text)))) { $text = constant(trim($text)); // don't return yet, words could be hooked with linkwords etc. } if ($opts['no_tags']) { $text = strip_tags($text); } if (MAGIC_QUOTES_GPC == TRUE) // precaution for badly saved data. { $text = stripslashes($text); } // Make sure we have a valid count for word wrapping if (!$wrap && !empty($pref['main_wordwrap'])) { $wrap = $pref['main_wordwrap']; } // $text = " ".$text; // Now get on with the parsing $ret_parser = ''; $last_bbcode = ''; // So we can change them on each loop $saveOpts = $opts; if ($parseBB == FALSE) { $content = array($text); } else { // Split each text block into bits which are either within one of the 'key' bbcodes, or outside them // (Because we have to match end words, the 'extra' capturing subpattern gets added to output array. We strip it later) $content = preg_split('#(\[(table|html|php|code|scode|hide).*?\[/(?:\\2)\])#mis', $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE ); } // Use $full_text variable so its available to special bbcodes if required foreach ($content as $full_text) { $proc_funcs = TRUE; $convertNL = TRUE; // We may have 'captured' a bbcode word - strip it if so if ($last_bbcode == $full_text) { $last_bbcode = ''; $proc_funcs = FALSE; $full_text = ''; } else { // Set the options for this pass $opts = $saveOpts; // Have to have a good test in case a 'non-key' bbcode starts the block // - so pull out the bbcode parameters while we're there if (($parseBB !== FALSE) && preg_match('#(^\[(table|html|php|code|scode|hide)(.*?)\])(.*?)(\[/\\2\]$)#is', $full_text, $matches )) { // It's one of the 'key' bbcodes // Usually don't want 'normal' processing if its a 'special' bbcode $proc_funcs = FALSE; // $matches[0] - complete block from opening bracket of opening tag to closing bracket of closing tag // $matches[1] - complete opening tag (inclusive of brackets) // $matches[2] - bbcode word // $matches[3] - parameter, including '=' // $matches[4] - bit between the tags (i.e. text to process) // $matches[5] - closing tag // In case we decide to load a file $bbPath = e_CORE.'bbcodes/'; $bbFile = strtolower(str_replace('_', '', $matches[2])); $bbcode = ''; $className = ''; $full_text = ''; $code_text = $matches[4]; $parm = $matches[3] ? substr($matches[3],1) : ''; $last_bbcode = $matches[2]; switch ($matches[2]) { case 'php' : // Probably run the output through the normal processing functions - but put here so the PHP code can disable if desired $proc_funcs = TRUE; // This is just the contents of the php.bb file pulled in - its short, so will be quicker // $search = array(""", "'", "$", '
', E_NL, "->", "<br />"); // $replace = array('"', "'", "$", "\n", "\n", "->", "
"); // Shouldn't have any parameter on this bbcode // Not sure whether checks are necessary now we've reorganised // if (!$matches[3]) $bbcode = str_replace($search, $replace, $matches[4]); // Because we're bypassing most of the initial parser processing, we should be able to just reverse the effects of toDB() and execute the code // [SecretR] - avoid php code injections, missing php.bb will completely disable user posted php blocks $bbcode = file_get_contents($bbPath.$bbFile.'.bb'); if (!$matches[3]) { $code_text = html_entity_decode($matches[4], ENT_QUOTES, 'UTF-8'); } break; case 'html' : // This overrides and deprecates html.bb $proc_funcs = TRUE; $noBreak = TRUE; // $code_text = str_replace("\r\n", " ", $code_text); // $code_text = html_entity_decode($code_text, ENT_QUOTES, CHARSET); // $code_text = str_replace('&','&',$code_text); // validation safe. $html_start = ""; // markers for html-to-bbcode replacement. $html_end = ""; $full_text = str_replace(array("[html]","[/html]"), "",$code_text); // quick fix.. security issue? $full_text =$this->replaceConstants($full_text,'abs'); $full_text = $html_start.$full_text.$html_end; $full_text = $this->parseBBTags($full_text); // strip tags. $opts['nobreak'] = true; break; case 'table' : // strip
from inside of $convertNL = FALSE; // break; case 'hide' : $proc_funcs = TRUE; default : // Most bbcodes will just execute their normal file // @todo should we cache these bbcodes? require_once should make class-related codes quite efficient if (file_exists($bbPath.'bb_'.$bbFile.'.php')) { // Its a bbcode class file require_once($bbPath.'bb_'.$bbFile.'.php'); $className = 'bb_'.$last_bbcode; $this->bbList[$last_bbcode] = new $className(); } elseif(file_exists($bbPath.$bbFile.'.bb')) { $bbcode = file_get_contents($bbPath.$bbFile.'.bb'); } } // end - switch ($matches[2]) if ($className) { $tempCode = new $className(); $full_text = $tempCode->bbPreDisplay($matches[4], $parm); } elseif ($bbcode) { // Execute the file $full_text = eval($bbcode); // Require output of bbcode to be returned // added to remove possibility of nested bbcode exploits ... // (same as in bbcode_handler - is it right that it just operates on $bbcode_return and not on $bbcode_output? - QUERY XXX-02 } if(strpos($full_text, '[') !== FALSE) { $exp_search = array('eval', 'expression'); $exp_replace = array('eval', 'expression'); $bbcode_return = str_replace($exp_search, $exp_replace, $full_text); } } } // Do the 'normal' processing - in principle, as previously - but think about the order. if ($proc_funcs && !empty($full_text)) // some more speed { // Split out and ignore any scripts and style blocks. With just two choices we can match the closing tag in the regex $subcon = preg_split('#((?:]+>.*?|tyle[^>]+>.*?))#mis', $full_text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE ); foreach ($subcon as $sub_blk) { if(substr($sub_blk, 0, 7) == '"; } $ret_parser .= $sub_blk; } else { // Do 'normal' processing on a chunk // Could put tag stripping in here /* // Line break compression - filter white space after HTML tags - among other things, ensures HTML tables display properly // Hopefully now achieved by other means if ($convertNL && !$opts['nobreak']) { $sub_blk = preg_replace("#>\s*[\r]*\n[\r]*#", ">", $sub_blk); } */ // Link substitution // Convert URL's to clickable links, unless modifiers or prefs override if ($opts['link_click']) { if ($opts['link_replace'] && ADMIN_AREA !== true) { $link_text = $pref['link_text']; $email_text = ($pref['email_text']) ? $this->replaceConstants($pref['email_text']) : LAN_EMAIL_SUBS; $sub_blk = $this->makeClickable($sub_blk, 'url', array('sub'=> $link_text,'ext'=>$pref['links_new_window'])); $sub_blk = $this->makeClickable($sub_blk, 'email', array('sub'=> $email_text)); } else { $sub_blk = $this->makeClickable($sub_blk, 'url', array('ext'=>true)); $sub_blk = $this->makeClickable($sub_blk, 'email'); } } // Convert emoticons to graphical icons, if enabled if ($opts['emotes']) { if (!is_object($this->e_emote)) { // require_once(e_HANDLER.'emote_filter.php'); $this->e_emote = new e_emoteFilter; } $sub_blk = $this->e_emote->filterEmotes($sub_blk); } // Reduce newlines in all forms to a single newline character (finds '\n', '\r\n', '\n\r') if (!$opts['nobreak']) { if ($convertNL && substr($sub_blk,0,6) != '[html]') //XXX Quick Fix, find a cleaner way. { // We may need to convert to
later $sub_blk = preg_replace("#[\r]*\n[\r]*#", E_NL, $sub_blk); } else { // Not doing any more - its HTML so keep \n so HTML is formatted $sub_blk = preg_replace("#[\r]*\n[\r]*#", "\n", $sub_blk); } } // Entity conversion // Restore entity form of quotes and such to single characters, except for text destined for tag attributes or JS. if($opts['value']) { // output used for attribute values. $sub_blk = str_replace($this->replace, $this->search, $sub_blk); } else { // output not used for attribute values. $sub_blk = str_replace($this->search, $this->replace, $sub_blk); } // BBCode processing (other than the four already done, which shouldn't appear at all in the text) if ($parseBB !== FALSE) { if (!is_object($this->e_bb)) { require_once(e_HANDLER.'bbcode_handler.php'); $this->e_bb = new e_bbcode; } if ($parseBB === TRUE) { // 'Normal' or 'legacy' processing if($modifiers == "WYSIWYG") { $sub_blk = $this->e_bb->parseBBCodes($sub_blk, $postID, 'wysiwyg'); } else { $sub_blk = $this->e_bb->parseBBCodes($sub_blk, $postID); } } elseif ($parseBB === 'STRIP') { // Need to strip all BBCodes $sub_blk = $this->e_bb->parseBBCodes($sub_blk, $postID, 'default', TRUE); } else { // Need to strip just some BBCodes $sub_blk = $this->e_bb->parseBBCodes($sub_blk, $postID, 'default', $parseBB); } } // replace all {e_XXX} constants with their e107 value. modifier determines relative/absolute conversion // (Moved to after bbcode processing by Cameron) if ($opts['constants']) { $sub_blk = $this->replaceConstants($sub_blk, $opts['constants']); // Now decodes text values } // profanity filter if (!empty($pref['profanity_filter'])) { if (!is_object($this->e_pf)) { // require_once(e_HANDLER."profanity_filter.php"); $this->e_pf = new e_profanityFilter; } $sub_blk = $this->e_pf->filterProfanities($sub_blk); } // Shortcodes // Optional short-code conversion if ($opts['parse_sc']) { $sub_blk = $this->parseTemplate($sub_blk, TRUE); } //Run any hooked in parsers if ($opts['hook']) { if ( varset($pref['tohtml_hook'])) { //Process the older tohtml_hook pref (deprecated) foreach(explode(",", $pref['tohtml_hook']) as $hook) { if (!is_object($this->e_hook[$hook])) { if(is_readable(e_PLUGIN.$hook."/".$hook.".php")) { require_once(e_PLUGIN.$hook."/".$hook.".php"); $hook_class = "e_".$hook; $this->e_hook[$hook] = new $hook_class; } } $sub_blk = $this->e_hook[$hook]->$hook($sub_blk,$opts['context']); } } if(isset($pref['e_tohtml_list']) && is_array($pref['e_tohtml_list'])) { foreach($pref['e_tohtml_list'] as $hook) { if (!is_object($this->e_hook[$hook])) { if(is_readable(e_PLUGIN.$hook."/e_tohtml.php")) { require_once(e_PLUGIN.$hook."/e_tohtml.php"); $hook_class = "e_tohtml_".$hook; $this->e_hook[$hook] = new $hook_class; } } $sub_blk = $this->e_hook[$hook]->to_html($sub_blk, $opts['context']); } } } // Word wrap if ($wrap && !$opts['nobreak']) { $sub_blk = $this->textclean($sub_blk, $wrap); } // Search highlighting if ($opts['emotes']) // Why?? { if ($this->checkHighlighting()) { $sub_blk = $this->e_highlight($sub_blk, $this->e_query); } } if($convertNL == true) { // Default replaces all \n with
for HTML display $nl_replace = '
'; if ($opts['nobreak']) { $nl_replace = ''; } elseif ($opts['retain_nl']) { $nl_replace = "\n"; } $sub_blk = str_replace(E_NL, $nl_replace, $sub_blk); } $ret_parser .= $sub_blk; } // End of 'normal' processing for a block of text } // End of 'foreach() on each block of non-script text } // End of 'normal' parsing (non-script text) else { // Text block that needed no processing at all $ret_parser .= $full_text; } } // Quick Fix - Remove trailing
on block-level elements (eg. div, pre, table, etc. ) $srch = array(); $repl = array(); foreach($this->blockTags as $val) { $srch[] = "
"; $repl[] = ""; } $ret_parser = str_replace($srch, $repl, $ret_parser); return trim($ret_parser); } /** * Use it on html attributes to avoid breaking markup . * @example echo "Hello"; */ function toAttribute($text) { // URLs posted without HTML access may have an & in them. // Xhtml compliance. $text = htmlspecialchars($text, ENT_QUOTES, 'UTF-8'); if(!preg_match('/&#|\'|"|<|>/s', $text)) { $text = $this->replaceConstants($text); return $text; } else { return ''; } } /** * Convert text blocks which are to be embedded within JS * * @param string|array $stringarray * @return string */ public function toJS($stringarray) { $search = array("\r\n", "\r", "
", "'"); $replace = array("\\n", "", "\\n", "\'"); $stringarray = str_replace($search, $replace, $stringarray); $stringarray = strip_tags($stringarray); $trans_tbl = get_html_translation_table(HTML_ENTITIES); $trans_tbl = array_flip($trans_tbl); return strtr($stringarray, $trans_tbl); } /** * Converts a PHP variable into its JavaScript equivalent. * We use HTML-safe strings, with several characters escaped. * * @param mixed $var * @return string */ public function toJSON($var) { // The PHP version cannot change within a request. static $php530; if(!isset($php530)) { $php530 = version_compare(PHP_VERSION, '5.3.0', '>='); } if($php530) { // Encode <, >, ', &, and " using the json_encode() options parameter. return json_encode($var, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); } return $this->toJSONhelper($var); } /** * Encodes a PHP variable to HTML-safe JSON for PHP versions below 5.3.0. * * @param mixed $var * @return string */ public function toJSONhelper($var) { switch(gettype($var)) { case 'boolean': return $var ? 'true' : 'false'; // Lowercase necessary! case 'integer': case 'double': return $var; case 'resource': case 'string': // Always use Unicode escape sequences (\u0022) over JSON escape // sequences (\") to prevent browsers interpreting these as // special characters. $replace_pairs = array( // ", \ and U+0000 - U+001F must be escaped according to RFC 4627. '\\' => '\u005C', '"' => '\u0022', "\x00" => '\u0000', "\x01" => '\u0001', "\x02" => '\u0002', "\x03" => '\u0003', "\x04" => '\u0004', "\x05" => '\u0005', "\x06" => '\u0006', "\x07" => '\u0007', "\x08" => '\u0008', "\x09" => '\u0009', "\x0a" => '\u000A', "\x0b" => '\u000B', "\x0c" => '\u000C', "\x0d" => '\u000D', "\x0e" => '\u000E', "\x0f" => '\u000F', "\x10" => '\u0010', "\x11" => '\u0011', "\x12" => '\u0012', "\x13" => '\u0013', "\x14" => '\u0014', "\x15" => '\u0015', "\x16" => '\u0016', "\x17" => '\u0017', "\x18" => '\u0018', "\x19" => '\u0019', "\x1a" => '\u001A', "\x1b" => '\u001B', "\x1c" => '\u001C', "\x1d" => '\u001D', "\x1e" => '\u001E', "\x1f" => '\u001F', // Prevent browsers from interpreting these as as special. "'" => '\u0027', '<' => '\u003C', '>' => '\u003E', '&' => '\u0026', // Prevent browsers from interpreting the solidus as special and // non-compliant JSON parsers from interpreting // as a comment. '/' => '\u002F', // While these are allowed unescaped according to ECMA-262, section // 15.12.2, they cause problems in some JSON parsers. "\xe2\x80\xa8" => '\u2028', // U+2028, Line Separator. "\xe2\x80\xa9" => '\u2029', // U+2029, Paragraph Separator. ); return '"' . strtr($var, $replace_pairs) . '"'; case 'array': // Arrays in JSON can't be associative. If the array is empty or if it // has sequential whole number keys starting with 0, it's not associative // so we can go ahead and convert it as an array. if(empty($var) || array_keys($var) === range(0, sizeof($var) - 1)) { $output = array(); foreach($var as $v) { $output[] = $this->toJSONhelper($v); } return '[ ' . implode(', ', $output) . ' ]'; } break; // Otherwise, fall through to convert the array as an object. case 'object': $output = array(); foreach($var as $k => $v) { $output[] = $this->toJSONhelper(strval($k)) . ':' . $this->toJSONhelper($v); } return '{' . implode(', ', $output) . '}'; default: return 'null'; } } /** * Convert Text for RSS/XML use. * * @param string $text * @param boolean $tags [optional] * @return string */ function toRss($text, $tags = false) { if($tags != true) { $text = $this -> toHTML($text, true); $text = strip_tags($text); } $text = $this->toEmail($text); $search = array("&#039;", "&#036;", "'", "$", e_BASE, "href='request.php","",""); $replace = array("'", '$', "'", '$', SITEURL, "href='".SITEURL."request.php", '', '' ); $text = str_replace($search, $replace, $text); $text = $this->ampEncode($text); if($tags == true && ($text)) { $text = ""; } return $text; } /** * Clean and Encode Ampersands '&' for output to browser. * @param string $text * @return mixed|string */ function ampEncode($text='') { // Fix any left-over '&' $text = str_replace('&', '&', $text); //first revert any previously converted. $text = str_replace('&', '&', $text); return $text; } /** * Convert any string back to plain text. * @param $text * @return mixed|string */ function toText($text) { if($this->isHtml($text)==true) { $text = $this->toHtml($text,true); $text = strip_tags($text); } $search = array("&#039;", "&#036;", "'", "$", "\", "&#092;"); $replace = array("'", '$', "'", '$', "\\", "\\"); $text = str_replace($search, $replace, $text); return $text; } /** * Set the dimensions of a thumbNail (generated by thumbUrl) */ public function setThumbSize($w=null,$h=null,$crop=null) { if($w !== null) { $this->thumbWidth = intval($w); } if($h !== null) { $this->thumbHeight = intval($h); } if($crop !== null) { $this->thumbCrop = intval($crop); } } public function thumbEncode($val = null) { if($val !== null) { $this->thumbEncode = intval($val); return null; } return $this->thumbEncode; } /** * Retrieve img tag width and height attributes for current thumbnail. * @return string */ public function thumbDimensions($type = 'single') { if(!empty($this->thumbCrop) && !empty($this->thumbWidth) && !empty($this->thumbHeight)) // dimensions are known. { return ($type == 'double') ? 'width="'.$this->thumbWidth.'" height="'.$this->thumbHeight.'"' : "width='".$this->thumbWidth."' height='".$this->thumbHeight."'"; } return null; } /** * Set or Get the value of the thumbNail Width. * @param $width (optional) */ public function thumbWidth($width=null) { if($width !== null) { $this->thumbWidth = intval($width); } return $this->thumbWidth; } /** * Set or Get the value of the thumbNailbCrop. * @param bool $status = true/false */ public function thumbCrop($status=false) { if($status !== false) { $this->thumbCrop = intval($status); } return $this->thumbCrop; } /** * Set or Get the value of the thumbNail height. * @param $height (optional) */ public function thumbHeight($height= null) { if($height !== null) { $this->thumbHeight = intval($height); } return $this->thumbHeight; } /** * Generate an auto-sized Image URL. * @param $url - path to image or leave blank for a placeholder. * @param $options - width and height, but leaving this empty and using $this->thumbWidth() and $this->thumbHeight() is preferred. ie. {SETWIDTH: w=x&y=x} * @param $raw ?? * @param $full */ public function thumbUrl($url=null, $options = array(), $raw = false, $full = false) { if(substr($url,0,3)=="{e_") // Fix for broken links that use {e_MEDIA} etc. { //$url = $this->replaceConstants($url,'abs'); // always switch to 'nice' urls when SC is used $url = str_replace($this->getUrlConstants('sc'), $this->getUrlConstants('raw'), $url); } if(!is_array($options)) { parse_str($options, $options); } if(strstr($url,e_MEDIA) || strstr($url,e_SYSTEM)) // prevent disclosure of 'hashed' path. { $raw = true; } if($raw) $url = $this->createConstants($url, 'mix'); $baseurl = ($full ? SITEURL : e_HTTP).'thumb.php?'; $thurl = 'src='.urlencode($url).'&'; // e107::getDebug()->log("Thumb: ".basename($url). print_a($options,true), E107_DBG_BASIC); if(!empty($options)) { $options['w'] = varset($options['w']); $options['h'] = varset($options['h']); $options['crop'] = (isset($options['aw']) || isset($options['ah'])) ? 1 : varset($options['crop']); $options['aw'] = varset($options['aw']); $options['ah'] = varset($options['ah']); $options['x'] = varset($options['x']); } else { $options['w'] = $this->thumbWidth; $options['h'] = $this->thumbHeight; $options['crop'] = $this->thumbCrop; $options['aw'] = null; $options['ah'] = null; $options['x'] = $this->thumbEncode; } if(!empty($options['crop'])) { if(!empty($options['aw']) || !empty($options['ah'])) { $options['w'] = $options['aw'] ; $options['h'] = $options['ah'] ; } $thurl .= 'aw='.intval($options['w']).'&ah='.intval($options['h']); } else { $thurl .= 'w='.intval($options['w']).'&h='.intval($options['h']); } if(e_MOD_REWRITE_MEDIA == true && empty($options['nosef']))// Experimental SEF URL support. { $options['full'] = $full; $options['ext'] = substr($url,-3); $options['thurl'] = $thurl; // $options['x'] = $this->thumbEncode(); if($sefUrl = $this->thumbUrlSEF($url,$options)) { return $sefUrl; } } if(!empty($options['x'] ))//base64 encode url { $thurl = 'id='.base64_encode($thurl); } return $baseurl.$thurl; } /** * Experimental: Generate a Thumb URL for use in the img srcset attribute. * @param string $src eg. {e_MEDIA_IMAGE}myimage.jpg * @param int|str $width - desired size in px or '2x' or '3x' or null for all or array ( * @return string */ function thumbSrcSet($src='', $width=null) { if(is_array($width)) { $parm = $width; $multiply = $width['size']; $encode = $width['x']; $width = $width['size']; } // $encode = $this->thumbEncode();; if($width == null || $width=='all') { $links = array(); $mag = ($width == null) ? array(1, 2) : array(160,320,460,600,780,920,1100); foreach($mag as $v) { $w = ($this->thumbWidth * $v); $h = ($this->thumbHeight * $v); $att = (!empty($this->thumbCrop)) ? array('aw' => $w, 'ah' => $h) : array('w' => $w, 'h' => $h); $att['x'] = $encode; $add = ($width == null) ? " ".$v."x" : " ".$v."w"; $links[] = $this->thumbUrl($src, $att).$add; // " w".$width; // } return implode(", ",$links); } elseif($multiply == '2x' || $multiply == '3x' || $multiply == '4x') { if(empty($parm['w']) && isset($parm['h'])) { $parm['h'] = ($parm['h'] * $multiply) ; return $this->thumbUrl($src, $parm)." h".$parm['h']." ".$multiply; } $width = (!empty($parm['w'])) ? ($parm['w'] * $multiply) : ($this->thumbWidth * $multiply); $height = (!empty($parm['h'])) ? ($parm['h'] * $multiply) : ($this->thumbHeight * $multiply); } else { $height = (($this->thumbHeight * $width) / $this->thumbWidth); } $parms = array('w'=>$width,'h'=>$height,'crop'=> $parm['crop'],'x'=>$parm['x'], 'aw'=>$parm['aw'],'ah'=>$parm['ah']); // $parms = !empty($this->thumbCrop) ? array('aw' => $width, 'ah' => $height, 'x'=>$encode) : array('w' => $width, 'h' => $height, 'x'=>$encode ); // $parms['x'] = $encode; return $this->thumbUrl($src, $parms)." ".$width."w"; } /** * Used by thumbUrl when SEF Image URLS is active. @see e107.htaccess * @param $url * @param array $options * @return string */ private function thumbUrlSEF($url='', $options=array()) { if(!empty($options['full'])) { $base = SITEURL; } else { $base = (!empty($options['ebase'])) ? '{e_BASE}' : e_HTTP; } // $base = (!empty($options['full'])) ? SITEURL : e_HTTP; if(!empty($options['x']) && !empty($options['ext'])) // base64 encoded. Build URL for: RewriteRule ^media\/img\/([-A-Za-z0-9+/]*={0,3})\.(jpg|gif|png)?$ thumb.php?id=$1 { $ext = strtolower($options['ext']); return $base.'media/img/'.base64_encode($options['thurl']).'.'.str_replace("jpeg", "jpg", $ext); } elseif(strstr($url, 'e_MEDIA_IMAGE')) // media images. { $sefPath = 'media/img/'; $clean = array('{e_MEDIA_IMAGE}','e_MEDIA_IMAGE/'); } elseif(strstr($url, 'e_AVATAR')) // avatars { $sefPath = 'media/avatar/'; $clean = array('{e_AVATAR}','e_AVATAR/'); } elseif(strstr($url, 'e_THEME')) // theme folder images. { $sefPath = 'theme/img/'; $clean = array('{e_THEME}','e_THEME/'); } else { // e107::getDebug()->log("SEF URL False: ".$url); return false; } // Build URL for ReWriteRule ^media\/img\/(a)?([\d]*)x(a)?([\d]*)\/(.*)?$ thumb.php?src=e_MEDIA_IMAGE/$5&$1w=$2&$3h=$4 $sefUrl = $base.$sefPath; if(vartrue($options['aw']) || vartrue($options['ah'])) { $sefUrl .= 'a'.intval($options['aw']) .'xa'. intval($options['ah']); } elseif(!empty($options['crop'])) { $sefUrl .= 'a'.intval($options['w']) .'xa'. intval($options['h']); } else { $sefUrl .= intval($options['w']) .'x'. intval($options['h']); } $sefUrl .= '/'; $sefUrl .= str_replace($clean,'',$url); return $sefUrl; } /** * Help for converting to more safe URLs * e.g. {e_MEDIA_FILE}path/to/video.flv => e_MEDIA_FILE/path/to/video.flv * * @todo support for ALL URL shortcodes (replacement methods) * @param string $type sc|raw|rev|all * @return array */ public function getUrlConstants($type = 'sc') { // sub-folders first! static $array = array( 'e_MEDIA_FILE/' => '{e_MEDIA_FILE}', 'e_MEDIA_VIDEO/' => '{e_MEDIA_VIDEO}', 'e_MEDIA_IMAGE/' => '{e_MEDIA_IMAGE}', 'e_MEDIA_ICON/' => '{e_MEDIA_ICON}', 'e_AVATAR/' => '{e_AVATAR}', 'e_AVATAR_DEFAULT/' => '{e_AVATAR_DEFAULT}', 'e_AVATAR_UPLOAD/' => '{e_AVATAR_UPLOAD}', 'e_WEB_JS/' => '{e_WEB_JS}', 'e_WEB_CSS/' => '{e_WEB_CSS}', 'e_WEB_IMAGE/' => '{e_WEB_IMAGE}', 'e_IMPORT/' => '{e_IMPORT}', // 'e_WEB_PACK/' => '{e_WEB_PACK}', 'e_BASE/' => '{e_BASE}', 'e_ADMIN/' => '{e_ADMIN}', 'e_IMAGE/' => '{e_IMAGE}', 'e_THEME/' => '{e_THEME}', 'e_PLUGIN/' => '{e_PLUGIN}', 'e_HANDLER/' => '{e_HANDLER}', // BC 'e_MEDIA/' => '{e_MEDIA}', 'e_WEB/' => '{e_ADMIN}', // 'THEME/' => '{THEME}', ); switch ($type) { case 'sc': return array_values($array); break; case 'raw': return array_keys($array); break; case 'rev': return array_reverse($array, true); break; case 'all': return $array; break; } return array(); } function getEmotes() { return $this->e_emote->emotes; } /** * Replace e107 path constants * Note: only an ADMIN user can convert {e_ADMIN} * TODO - runtime cache of search/replace arrays (object property) when $mode !== '' * @param string $text * @param string $mode [optional] abs|full "full" = produce absolute URL path, e.g. http://sitename.com/e107_plugins/etc * 'abs' = produce truncated URL path, e.g. e107plugins/etc * "" (default) = URL's get relative path e.g. ../e107_plugins/etc * @param mixed $all [optional] if TRUE, then when $mode is "full" or TRUE, USERID is also replaced... * when $mode is "" (default), ALL other e107 constants are replaced * @return string */ public function replaceConstants($text, $mode = '', $all = FALSE) { if($mode != "") { $e107 = e107::getInstance(); $replace_relative = array( $e107->getFolder('media_files'), $e107->getFolder('media_video'), $e107->getFolder('media_image'), $e107->getFolder('media_icon'), $e107->getFolder('avatars'), $e107->getFolder('web_js'), $e107->getFolder('web_css'), $e107->getFolder('web_image'), //$e107->getFolder('web_pack'), e_IMAGE_ABS, e_THEME_ABS, $e107->getFolder('images'), $e107->getFolder('plugins'), $e107->getFolder('files'), $e107->getFolder('themes'), // $e107->getFolder('downloads'), $e107->getFolder('handlers'), $e107->getFolder('media'), $e107->getFolder('web'), $e107->site_theme ? $e107->getFolder('themes').$e107->site_theme.'/' : '', defset('THEME_ABS'), (ADMIN ? $e107->getFolder('admin') : ''), '', $e107->getFolder('core'), $e107->getFolder('system'), ); switch ($mode) { case 'abs': $replace_absolute = array( e_MEDIA_FILE_ABS, e_MEDIA_VIDEO_ABS, e_MEDIA_IMAGE_ABS, e_MEDIA_ICON_ABS, e_AVATAR_ABS, e_JS_ABS, e_CSS_ABS, e_WEB_IMAGE_ABS, // e_PACK_ABS, e_IMAGE_ABS, e_THEME_ABS, e_IMAGE_ABS, e_PLUGIN_ABS, e_FILE_ABS, e_THEME_ABS, // e_DOWNLOAD_ABS, //impossible when download is done via php. '', // handlers - no ABS path available e_MEDIA_ABS, e_WEB_ABS, defset('THEME_ABS'), defset('THEME_ABS'), (ADMIN ? e_ADMIN_ABS : ''), $e107->server_path, '', // no e_CORE absolute path '', // no e_SYSTEM absolute path ); break; case 'full': $replace_absolute = array( SITEURLBASE.e_MEDIA_FILE_ABS, SITEURLBASE.e_MEDIA_VIDEO_ABS, SITEURLBASE.e_MEDIA_IMAGE_ABS, SITEURLBASE.e_MEDIA_ICON_ABS, SITEURLBASE.e_AVATAR_ABS, SITEURLBASE.e_JS_ABS, SITEURLBASE.e_CSS_ABS, SITEURLBASE.e_WEB_IMAGE_ABS, // SITEURLBASE.e_PACK_ABS, SITEURLBASE.e_IMAGE_ABS, SITEURLBASE.e_THEME_ABS, SITEURLBASE.e_IMAGE_ABS, SITEURLBASE.e_PLUGIN_ABS, SITEURLBASE.e_FILE_ABS, // deprecated SITEURLBASE.e_THEME_ABS, //SITEURL.$e107->getFolder('downloads'), '', // handlers - no ABS path available SITEURLBASE.e_MEDIA_ABS, SITEURLBASE.e_WEB_ABS, defset('THEME_ABS') ? SITEURLBASE.THEME_ABS : '', defset('THEME_ABS') ? SITEURLBASE.THEME_ABS : '', (ADMIN ? SITEURLBASE.e_ADMIN_ABS : ''), SITEURL, '', // no e_CORE absolute path '', // no e_SYSTEM absolute path ); break; } // sub-folders first! $search = array( '{e_MEDIA_FILE}', '{e_MEDIA_VIDEO}', '{e_MEDIA_IMAGE}', '{e_MEDIA_ICON}', '{e_AVATAR}', '{e_WEB_JS}', '{e_WEB_CSS}', '{e_WEB_IMAGE}', // '{e_WEB_PACK}', "{e_IMAGE_ABS}", "{e_THEME_ABS}", "{e_IMAGE}", "{e_PLUGIN}", "{e_FILE}", "{e_THEME}", //,"{e_DOWNLOAD}" "{e_HANDLER}", "{e_MEDIA}", "{e_WEB}", "{THEME}", "{THEME_ABS}", "{e_ADMIN}", "{e_BASE}", "{e_CORE}", "{e_SYSTEM}", ); /*if (ADMIN) { $replace_relative[] = $e107->getFolder('admin'); $replace_absolute[] = SITEURL.$e107->getFolder('admin'); $search[] = "{e_ADMIN}"; }*/ if ($all) { if (USER) { // Can only replace with valid number for logged in users $replace_relative[] = USERID; $replace_absolute[] = USERID; } else { $replace_relative[] = ''; $replace_absolute[] = ''; } $search[] = "{USERID}"; } // current THEME /*if(!defined('THEME')) { //if not already parsed by doReplace $text = str_replace(array('{THEME}', '{THEME_ABS}'), '', $text); } else { $replace_relative[] = THEME; $replace_absolute[] = THEME_ABS; $search[] = "{THEME}"; $replace_relative[] = THEME; $replace_absolute[] = THEME_ABS; $search[] = "{THEME_ABS}"; }*/ $replace = ((string)$mode == "full" || (string)$mode=='abs' ) ? $replace_absolute : $replace_relative; return str_replace($search,$replace,$text); } // $pattern = ($all ? "#\{([A-Za-z_0-9]*)\}#s" : "#\{(e_[A-Z]*)\}#s"); $pattern = ($all ? '#\{([A-Za-z_0-9]*)\}#s' : '#\{(e_[A-Z]*(?:_IMAGE|_VIDEO|_FILE|_CONTENT|_ICON|_AVATAR|_JS|_CSS|_PACK|_DB|_ABS){0,1})\}#s'); $text = preg_replace_callback($pattern, array($this, 'doReplace'), $text); if(!defined('THEME')) { //if not already parsed by doReplace $text = str_replace(array('{THEME}', '{THEME_ABS}'), '', $text); } else { $srch = array('{THEME}', '{THEME_ABS}'); $repl = array(THEME, THEME_ABS); $text = str_replace($srch, $repl, $text); } return $text; } function doReplace($matches) { if(defined($matches[1]) && (deftrue('ADMIN') || strpos($matches[1], 'ADMIN') === FALSE)) { return constant($matches[1]); } return $matches[1]; } /** * Create and substitute e107 constants in passed URL * * @param string $url * @param integer $mode 0-folders, 1-relative ('rel'), 2-absolute ('abs'), 3-full ('full') (with domain), 4-absolute & relative ('mix') (combination of 1,2,3) * @return string */ public function createConstants($url, $mode = 0) { //FIXME - create constants for absolute paths and site URL's if (!is_numeric($mode)) { switch ($mode) { case 'rel' : $mode = 1; break; case 'abs' : $mode = 2; break; case 'full' : $mode = 3; break; case 'mix' : $mode = 4; break; case 'nice': $mode = 5; break; } } $e107 = e107::getInstance(); switch($mode) { case 0: // folder name only. $tmp = array( '{e_MEDIA_FILE}' => $e107->getFolder('media_files'), '{e_MEDIA_VIDEO}' => $e107->getFolder('media_videos'), '{e_MEDIA_IMAGE}' => $e107->getFolder('media_images'), '{e_MEDIA_ICON}' => $e107->getFolder('media_icons'), '{e_AVATAR}' => $e107->getFolder('avatars'), '{e_WEB_JS}' => $e107->getFolder('web_js'), '{e_WEB_CSS}' => $e107->getFolder('web_css'), '{e_WEB_IMAGE}' => $e107->getFolder('web_images'), // '{e_WEB_PACK}' => $e107->getFolder('web_packs'), '{e_IMAGE}' => $e107->getFolder('images'), '{e_PLUGIN}' => $e107->getFolder('plugins'), '{e_FILE}' => $e107->getFolder('files'), '{e_THEME}' => $e107->getFolder('themes'), '{e_DOWNLOAD}' => $e107->getFolder('downloads'), '{e_ADMIN}' => $e107->getFolder('admin'), '{e_HANDLER}' => $e107->getFolder('handlers'), '{e_MEDIA}' => $e107->getFolder('media'), '{e_WEB}' => $e107->getFolder('web'), '{e_UPLOAD}' => $e107->getFolder('uploads'), ); break; case 1: // relative path only $tmp = array( '{e_MEDIA_FILE}' => e_MEDIA_FILE, '{e_MEDIA_VIDEO}' => e_MEDIA_VIDEO, '{e_MEDIA_IMAGE}' => e_MEDIA_IMAGE, '{e_MEDIA_ICON}' => e_MEDIA_ICON, '{e_AVATAR}' => e_AVATAR, '{e_IMPORT}' => e_IMPORT, '{e_WEB_JS}' => e_WEB_JS, '{e_WEB_CSS}' => e_WEB_CSS, '{e_WEB_IMAGE}' => e_WEB_IMAGE, // '{e_WEB_PACK}' => e_WEB_PACK, '{e_IMAGE}' => e_IMAGE, '{e_PLUGIN}' => e_PLUGIN, '{e_FILE}' => e_FILE, '{e_THEME}' => e_THEME, '{e_DOWNLOAD}' => e_DOWNLOAD, '{e_ADMIN}' => e_ADMIN, '{e_HANDLER}' => e_HANDLER, '{e_MEDIA}' => e_MEDIA, '{e_WEB}' => e_WEB, '{e_UPLOAD}' => e_UPLOAD, ); break; case 2: // absolute path only $tmp = array( '{e_MEDIA_FILE}' => e_MEDIA_FILE_ABS, '{e_MEDIA_VIDEO}' => e_MEDIA_VIDEO_ABS, '{e_MEDIA_IMAGE}' => e_MEDIA_IMAGE_ABS, '{e_MEDIA_ICON}' => e_MEDIA_ICON_ABS, '{e_AVATAR}' => e_AVATAR_ABS, '{e_WEB_JS}' => e_JS_ABS, '{e_WEB_CSS}' => e_CSS_ABS, '{e_WEB_IMAGE}' => e_WEB_IMAGE_ABS, // '{e_WEB_PACK}' => e_PACK_ABS, '{e_IMAGE}' => e_IMAGE_ABS, '{e_PLUGIN}' => e_PLUGIN_ABS, '{e_FILE}' => e_FILE_ABS, // deprecated '{e_THEME}' => e_THEME_ABS, '{e_DOWNLOAD}' => e_HTTP.'request.php?',// FIXME - we need solution! '{e_ADMIN}' => e_ADMIN_ABS, //'{e_HANDLER}' => e_HANDLER_ABS, - no ABS path available '{e_MEDIA}' => e_MEDIA_ABS, '{e_WEB}' => e_WEB_ABS, '{e_BASE}' => e_HTTP, ); break; case 3: // full path (e.g http://domain.com/e107_images/) $tmp = array( '{e_MEDIA_FILE}' => SITEURLBASE.e_MEDIA_FILE_ABS, '{e_MEDIA_VIDEO}' => SITEURLBASE.e_MEDIA_VIDEO_ABS, '{e_MEDIA_IMAGE}' => SITEURLBASE.e_MEDIA_IMAGE_ABS, '{e_MEDIA_ICON}' => SITEURLBASE.e_MEDIA_ICON_ABS, '{e_AVATAR}' => SITEURLBASE.e_AVATAR_ABS, '{e_WEB_JS}' => SITEURLBASE.e_JS_ABS, '{e_WEB_CSS}' => SITEURLBASE.e_CSS_ABS, '{e_WEB_IMAGE}' => SITEURLBASE.e_WEB_IMAGE_ABS, // '{e_WEB_PACK}' => SITEURLBASE.e_PACK_ABS, '{e_IMAGE}' => SITEURLBASE.e_IMAGE_ABS, '{e_PLUGIN}' => SITEURLBASE.e_PLUGIN_ABS, '{e_FILE}' => SITEURLBASE.e_FILE_ABS, // deprecated '{e_THEME}' => SITEURLBASE.e_THEME_ABS, '{e_DOWNLOAD}' => SITEURLBASE.e_HTTP.'request.php?',// FIXME - we need solution! '{e_ADMIN}' => SITEURLBASE.e_ADMIN_ABS, //'{e_HANDLER}' => e_HANDLER_ABS, - no ABS path available '{e_MEDIA}' => SITEURLBASE.e_MEDIA_ABS, '{e_WEB}' => SITEURLBASE.e_WEB_ABS, '{e_BASE}' => SITEURL, ); break; case 4: // absolute & relative paths $url = $this->createConstants($url, 3); $url = $this->createConstants($url, 2); $url = $this->createConstants($url, 1); return $url; break; case 5: // nice urls - e.g. e_MEDIA_VIDEO/mystream.flv $url = $this->createConstants($url, 4); return str_replace($this->getUrlConstants('sc'), $this->getUrlConstants('raw'), $url); break; default: $tmp = array(); break; } $hasCDN = strpos($url, '//') === 0; foreach($tmp as $key=>$val) { // Fix - don't break the CDN '//cdn.com' URLs if ($hasCDN && $val == '/') { continue; } $len = strlen($val); if(substr($url, 0, $len) == $val) { // replace the first instance only return substr_replace($url, $key, 0, $len); } } return $url; } //FIXME - $match not used? function e_highlight($text, $match) { $tags = array(); preg_match_all('#<[^>]+>#', $text, $tags); $text = preg_replace('#<[^>]+>#', '<|>', $text); $text = preg_replace('#(\b".$match."\b)#i', '\\1', $text); foreach ($tags[0] as $tag) { $text = preg_replace('#<\|>#', $tag, $text, 1); } return $text; } /** * Convert Text to a suitable format for use in emails. eg. relative links will be replaced with full links etc. * @param string $text * @param boolean $posted - if the text has been posted. (uses stripslashes etc) * @param string $mods - flags for text transformation. */ public function toEmail($text, $posted = "", $mods = "parse_sc, no_make_clickable") { if ($posted === TRUE) { if (MAGIC_QUOTES_GPC) { $text = stripslashes($text); } $text = preg_replace('#\[(php)#i', '[\\1', $text); } $text = (strtolower($mods) != "rawtext") ? $this->replaceConstants($text, "full") : $text; if($this->isHtml($text)) { $text = str_replace(array("[html]","[/html]"), "", $text); $text = html_entity_decode( $text, ENT_COMPAT, 'UTF-8'); } else { $text = $this->toHTML($text, true, $mods); } return $text; } /** * Given an email address, returns a link including with obfuscated text. * e-email css in e107.css inserts the user/domain data for display. * * @param string $email * @param string $words [optional] text to display * @param null $subject [optional] default subject for email. * @return string */ function emailObfuscate($email, $words = null, $subject =null) { if(strpos($email, '@') === false) { return ''; } if ($subject) { $subject = '?subject='.$subject; } list($name, $address) = explode('@', $email, 2); if(empty($words)) { $words = "@"; $user = "data-user='".$this->obfuscate($name)."'"; $dom = "data-dom='".$this->obfuscate($address)."'"; } else { $user = ''; $dom = ''; } $url = "mailto:".$email.$subject; $safe = $this->obfuscate($url); return "".$words.''; } /** * Obfuscate text from bots using Randomized encoding. * @param $text * @return string */ public function obfuscate($text) { $ret = ''; foreach (str_split($text) as $letter) { switch (rand(1, 3)) { // HTML entity code case 1: $ret .= '&#'.ord($letter).';'; break; // Hex character code case 2: $ret .= '&#x'.dechex(ord($letter)).';'; break; // Raw (no) encoding case 3: $ret .= $letter; } } return $ret; } public function __get($name) { switch($name) { case 'e_sc': $ret = e107::getScParser(); break; default: trigger_error('$e107->$'.$name.' not defined', E_USER_WARNING); return NULL; break; } $this->$name = $ret; return $ret; } } /** * New v2 Parser * Start Fresh and Build on it over time to become eventual replacement to e_parse. * Cameron's DOM-based parser. */ class e_parser { /** * @var DOMDocument */ public $domObj = null; public $isHtml = false; protected $removedList = array(); protected $nodesToDelete = array(); protected $nodesToConvert = array(); protected $nodesToDisableSC = array(); protected $pathList = array(); protected $allowedAttributes = array( 'default' => array('id', 'style', 'class'), 'img' => array('id', 'src', 'style', 'class', 'alt', 'title', 'width', 'height'), 'a' => array('id', 'href', 'style', 'class', 'title', 'target'), 'script' => array('type', 'src', 'language'), 'iframe' => array('id', 'src', 'frameborder', 'class', 'width', 'height', 'style'), 'input' => array('type','name','value','class','style'), 'form' => array('action','method','target'), 'audio' => array('src','controls', 'autoplay', 'loop', 'muted', 'preload' ), 'video' => array('autoplay', 'controls', 'height', 'loop', 'muted', 'poster', 'preload', 'src', 'width'), 'td' => array('id', 'style', 'class', 'colspan', 'rowspan'), 'th' => array('id', 'style', 'class', 'colspan', 'rowspan'), 'col' => array('id', 'span', 'class','style') ); protected $badAttrValues = array('javascript[\s]*?:','alert\(','vbscript[\s]*?:','data:text\/html', 'mhtml[\s]*?:', 'data:[\s]*?image'); protected $replaceAttrValues = array( 'default' => array() ); protected $allowedTags = array('html', 'body','div','a','img','table','tr', 'td', 'th', 'tbody', 'thead', 'colgroup', 'b', 'i', 'pre','code', 'strong', 'u', 'em','ul', 'ol', 'li','img','h1','h2','h3','h4','h5','h6','p', 'div','pre','section','article', 'blockquote','hgroup','aside','figure','figcaption', 'abbr','span', 'audio', 'video', 'br', 'small', 'caption', 'noscript', 'hr', 'section', 'iframe' ); protected $scriptTags = array('script','applet','form','input','button'); //allowed when $pref['post_script'] is enabled. protected $blockTags = array('pre','div','h1','h2','h3','h4','h5','h6','blockquote'); // element includes its own line-break. private $scriptAccess = false; // nobody. public function __construct() { $this->init(); /* $meths = get_class_methods('DomDocument'); sort($meths); print_a($meths); */ } /** * Used by e_parse to start */ function init() { $this->domObj = new DOMDocument(); } /** * Add Allowed Tags. * @param string */ public function addAllowedTag($tag) { $this->allowedTags[] = $tag; } /** * @param $tag - html tag. * @param $attArray - array of attributes. eg. array('style', 'id', 'class') etc. */ public function addAllowedAttribute($tag, $attArray) { $this->allowedAttributes[$tag] = (array) $attArray; } /** * Set Allowed Tags. * @param $array */ public function setAllowedTags($array=array()) { $this->allowedTags = $array; } /** * Set Script Access * @param $val int e_UC_MEMBER, e_UC_NOBODY, e_UC_MAINADMIN or userclass number. */ public function setScriptAccess($val) { $this->scriptAccess = $val; } public function getAllowedTags() { return $this->allowedTags; } public function getScriptAccess() { return $this->scriptAccess; } /** * Set Allowed Attributes. * @param $array */ public function setAllowedAttributes($array=array()) { $this->allowedAttributes = $array; } /** * Set Script Tags. * @param $array */ public function setScriptTags($array=array()) { $this->scriptTags = $array; } /** * Add leading zeros to a number. eg. 3 might become 000003 * @param $num integer * @param $numDigits - total number of digits * @return number with leading zeros. */ public function leadingZeros($num,$numDigits) { return sprintf("%0".$numDigits."d",$num); } /** * Generic variable translator for LAN definitions. * @param $lan - string LAN * @param string | array $vals - either a single value, which will replace '[x]' or an array with key=>value pairs. * @example $tp->lanVars("My name is [x] and I own a [y]", array('x'=>"John", 'y'=>"Cat")); * @example $tp->lanVars("My name is [x] and I own a [y]", array("John","Cat")); * @return string */ function lanVars($lan, $vals, $bold=false) { $array = (!is_array($vals)) ? array('x'=>$vals) : $vals; $search = array(); $replace = array(); $defaults = array('x', 'y', 'z'); foreach($array as $k=>$v) { if(is_numeric($k)) // convert array of numeric to x,y,z { $k = $defaults[$k]; } $search[] = "[".$k."]"; $replace[] = ($bold===true) ? "".$v."" : $v; } return str_replace($search, $replace, $lan); } /** * Return an Array of all specific tags found in an HTML document and their attributes. * @param $html - raw html code * @param $taglist - comma separated list of tags to search or '*' for all. * @param $header - if the $html includes the html head or body tags - it should be set to true. */ public function getTags($html, $taglist='*', $header = false) { if($header == false) { $html = "".$html.""; } $doc = $this->domObj; $doc->preserveWhiteSpace = true; libxml_use_internal_errors(true); $doc->loadHTML($html); $tg = explode(",", $taglist); $ret = array(); foreach($tg as $find) { $tmp = $doc->getElementsByTagName($find); foreach($tmp as $k=>$node) { $tag = $node->nodeName; $inner = $node->C14N(); $inner = str_replace(" ","",$inner); foreach ($node->attributes as $attr) { $name = $attr->nodeName; $value = $attr->nodeValue; $ret[$tag][$k][$name] = $value; } $ret[$tag][$k]['@value'] = $inner; } } if($header == false) { unset($ret['html'],$ret['body']); } return $ret; } /** * Parse xxxxx.glyph file to bootstrap glyph format. * @param string $text * @param array of $parms * @example $tp->toGlyph('fa-spinner', 'spin=1'); * @example $tp->toGlyph('fa-spinner', array('spin'=>1)); * @example $tp->toGlyph('fa-shield', array('rotate'=>90, 'size'=>'2x')); */ public function toGlyph($text, $space=" ") { if(!deftrue('BOOTSTRAP') || empty($text)) { return false; } if(is_array($space)) { $parm = $space; $space = varset($parm['space'],''); } elseif(strpos($space,'=')) { parse_str($space,$parm); $space = varset($parm['space'],''); } else { $parm = array(); } if(substr($text,0,2) == 'e-') // e107 admin icon. { $size = (substr($text,-3) == '-32') ? 'S32' : 'S16'; return ""; } // Get Glyph names. $bs3 = e107::getMedia()->getGlyphs('bs3',''); $fa4 = e107::getMedia()->getGlyphs('fa4',''); list($cls) = explode('.glyph',$text,2); // list($type, $tmp2) = explode("-",$text,2); // return $cls; $removePrefix = array('glyphicon-','icon-','fa-'); $id = str_replace($removePrefix, "", $cls); $spin = null; $rotate = null; $fixedW = null; $prefix = null; $size = null; $tag = 'span'; // return print_r($fa4,true); if(deftrue('FONTAWESOME') && in_array($id ,$fa4)) // Contains FontAwesome 3 set also. { $prefix = 'fa fa-'; $size = (vartrue($parm['size'])) ? ' fa-'.$parm['size'] : ''; $tag = 'i'; $spin = !empty($parm['spin']) ? ' fa-spin' : ''; $rotate = !empty($parm['rotate']) ? ' fa-rotate-'.intval($parm['rotate']) : ''; $fixedW = !empty($parm['fw']) ? ' fa-fw' : ""; } elseif(deftrue("BOOTSTRAP")) { if(BOOTSTRAP === 3 && in_array($id ,$bs3)) { $prefix = 'glyphicon glyphicon-'; $tag = 'span'; } else { $prefix = 'icon-'; $tag = 'i'; } $size = ''; } $idAtt = (!empty($parm['id'])) ? "id='".$parm['id']."' " : ''; $text = "<".$tag." {$idAtt}class='".$prefix.$id.$size.$spin.$rotate.$fixedW."'>" ; $text .= ($space !== false) ? $space : ""; return $text; //$text = preg_replace('/\[(i_[\w]*)\]/',"", $text); // return $text; } /** * Render an avatar based on supplied user data or current user when missing. * @param @array - user data from e107_user. * @return tag of avatar. */ public function toAvatar($userData=null, $options=array()) { $tp = e107::getParser(); $width = !empty($options['w']) ? intval($options['w']) : $tp->thumbWidth; $height = ($tp->thumbHeight !== 0) ? $tp->thumbHeight : ""; $linkStart = ''; $linkEnd = ''; if(!isset($userData['user_image']) && USERID) { $userData['user_id'] = USERID; $userData['user_image'] = USERIMAGE; $userData['user_name'] = USERNAME; } $image = (!empty($userData['user_image'])) ? varset($userData['user_image']) : null; $genericImg = $tp->thumbUrl(e_IMAGE."generic/blank_avatar.jpg","w=".$width."&h=".$height,true); if (!empty($image)) { if(strpos($image,"://")!==false) // Remove Image { $img = $image; } elseif(substr($image,0,8) == "-upload-") { $image = substr($image,8); // strip the -upload- from the beginning. $img = (file_exists(e_AVATAR_UPLOAD.$image)) ? $tp->thumbUrl(e_AVATAR_UPLOAD.$image,"w=".$width."&h=".$height) : $genericImg; } elseif(file_exists(e_AVATAR_DEFAULT.$image)) // User-Uplaoded Image { $img = $tp->thumbUrl(e_AVATAR_DEFAULT.$image,"w=".$width."&h=".$height); } else // Image Missing. { $img = $genericImg; } } else // No image provided - so send generic. { $img = $genericImg; } if(($img == $genericImg) && ($userData['user_id'] == USERID) && !empty($options['link'])) { $linkStart = ""; $linkEnd = ""; } $title = (ADMIN) ? $image : $tp->toAttribute($userData['user_name']); $shape = (!empty($options['shape'])) ? "img-".$options['shape'] : "img-rounded"; if(!empty($options['type']) && $options['type'] == 'url') { return $img; } $heightInsert = empty($height) ? '' : "height='".$height."'"; $id = (!empty($options['id'])) ? "id='".$options['id']."' " : ""; $text = $linkStart; $text .= "\"".$title."\""; $text .= $linkEnd; // return $img; return $text; } /** * Display an icon. * @param string $icon * @example $tp->toIcon("{e_IMAGES}icons/something.png"); */ public function toIcon($icon='',$parm = array()) { if(!vartrue($icon)) { return; } if(strpos($icon,'e_MEDIA_IMAGE')!==false) { // return "
Use \$tp->toImage() instead of toIcon() for ".$icon."
"; // debug info only. } if(substr($icon,0,3) == 'toGlyph($icon,$parm); } if(strpos($icon,'e_MEDIA')!==FALSE) { $path = $this->thumbUrl($icon); $dimensions = $this->thumbDimensions(); } elseif($icon[0] == '{') { $path = $this->replaceConstants($icon,'full'); } elseif(vartrue($parm['legacy'])) { $legacyPath = $parm['legacy'].$icon; $filePath = $this->replaceConstants($legacyPath,'rel'); if(is_readable($filePath)) { $path = $this->replaceConstants($legacyPath,'full'); } else { $log = e107::getAdminLog(); $log->addDebug('Broken Icon Path: '.$legacyPath."\n".print_r(debug_backtrace(null,2), true), false)->save('IMALAN_00'); } } else { $path = $icon; } return "".basename($path).""; } /** * Render an tag. * @param string $file * @param array $parm legacy|w|h|alt|class|id|crop * @return string * @example $tp->toImage('welcome.png', array('legacy'=>{e_IMAGE}newspost_images/','w'=>200)); */ public function toImage($file, $parm=array()) { if(empty($file)) { return null; } if(strpos($file,'e_AVATAR')!==false) { return "
Use \$tp->toAvatar() instead of toImage() for ".$file."
"; // debug info only. } $srcset = null; $path = null; $file = trim($file); $ext = pathinfo($file, PATHINFO_EXTENSION); $accepted = array('jpg','gif','png','jpeg'); $tp = $this; if(!in_array($ext,$accepted)) { return null; } if(strpos($file,'e_MEDIA')!==false || strpos($file,'e_THEME')!==false || strpos($file,'e_PLUGIN')!==false) //v2.x path. { if(!isset($parm['w']) && !isset($parm['h'])) { $parm['w'] = $tp->thumbWidth(); $parm['h'] = $tp->thumbHeight(); $parm['crop'] = $tp->thumbCrop(); $parm['x'] = $tp->thumbEncode(); } unset($parm['src']); $path = $tp->thumbUrl($file,$parm); $srcSetParm = $parm; $srcSetParm['size'] = ($parm['w'] < 100) ? '4x' : '2x'; $parm['srcset'] = $tp->thumbSrcSet($file, $srcSetParm); } elseif($file[0] == '{') // Legacy v1.x path. Example: {e_PLUGIN}myplugin/images/fixedimage.png { $path = $tp->replaceConstants($file,'abs'); } elseif(!empty($parm['legacy'])) // Search legacy path for image in a specific folder. No path, only file name provided. { $legacyPath = $parm['legacy'].$file; $filePath = $tp->replaceConstants($legacyPath,'rel'); if(is_readable($filePath)) { $path = $tp->replaceConstants($legacyPath,'abs'); } else { $log = e107::getAdminLog(); $log->addDebug('Broken Image Path: '.$legacyPath."\n".print_r(debug_backtrace(null,2), true), false)->save('IMALAN_00'); } } else // usually http://.... { $path = $file; } $id = (!empty($parm['id'])) ? "id=\"".$parm['id']."\" " : "" ; $class = (!empty($parm['class'])) ? $parm['class'] : "img-responsive"; $alt = (!empty($parm['alt'])) ? $tp->toAttribute($parm['alt']) : basename($file); $style = (!empty($parm['style'])) ? "style=\"".$parm['style']."\" " : "" ; $srcset = (!empty($parm['srcset'])) ? "srcset=\"".$parm['srcset']."\" " : ""; $width = (!empty($parm['w'])) ? "width=\"".intval($parm['w'])."\" " : ""; $height = (!empty($parm['h'])) ? "height=\"".intval($parm['h'])."\" " : ""; return "\"".$alt."\""; } /** * Check if a string contains bbcode. * @param $text * @return bool */ function isBBcode($text) { $bbsearch = array('[/h]','[/b]','[/link]', '[/right]'); if(str_replace($bbsearch,'',$text)) { return true; } else { return false; } } /** * Check if a string is HTML * @param $text * @return bool */ function isHtml($text) { if(strpos($text,'[html]') !== false || (htmlentities($text, ENT_NOQUOTES,'UTF-8') != $text && $this->isBBcode($text) === false ) || preg_match('#(?<=<)\w+(?=[^<]*?>)#', $text)) { return true; } else { return false; } } /** * Check if a file is an video or not. * @param $file string * @return boolean */ function isVideo($file) { $ext = pathinfo($file,PATHINFO_EXTENSION); return ($ext == 'youtube' || $ext == 'youtubepl') ? true : false; } /** * Check if a file is an image or not. * @param $file string * @return boolean */ function isImage($file) { if(substr($file,0,3)=="{e_") { $file = e107::getParser()->replaceConstants($file); } $ext = pathinfo($file,PATHINFO_EXTENSION); return ($ext == 'jpg' || $ext == 'png' || $ext == 'gif' || $ext == 'jpeg') ? true : false; } /** * Display a Video file. * @param string $file - format: id.type eg. x123dkax.youtube * @param boolean $thumbnail - set to 'tag' to return an image thumbnail and 'src' to return the src url or 'video' for a small video thumbnail. */ function toVideo($file, $parm=array()) { if(empty($file)) { return false; } list($id,$type) = explode(".",$file,2); $thumb = vartrue($parm['thumb']); $pref = e107::getPref(); $ytpref = array(); foreach($pref as $k=>$v) // Find all Youtube Prefs. { if(substr($k,0,8) == 'youtube_') { $key = substr($k,8); $ytpref[$key] = $v; } } unset($ytpref['bbcode_responsive']); // do not include in embed code. if(!empty($ytpref['cc_load_policy'])) { $ytpref['cc_lang_pref'] = e_LAN; // switch captions with chosen user language. } $ytqry = http_build_query($ytpref); $defClass = (deftrue('BOOTSTRAP')) ? "embed-responsive embed-responsive-16by9" : "video-responsive"; // levacy backup. if($type == 'youtube') { // $thumbSrc = "https://i1.ytimg.com/vi/".$id."/0.jpg"; $thumbSrc = "http://i1.ytimg.com/vi/".$id."/mqdefault.jpg"; $video = ''; if($thumb == 'tag') { return "Youtube Video"; } if($thumb == 'email') { $thumbSrc = "http://i1.ytimg.com/vi/".$id."/maxresdefault.jpg"; // 640 x 480 $filename = 'temp/yt-thumb-'.md5($id).".jpg"; $filepath = e_MEDIA.$filename; $url = 'http://youtu.be/'.$id; if(!file_exists($filepath)) { e107::getFile()->getRemoteFile($thumbSrc, $filename,'media'); } return "Youtube Video
Click to watch video
"; } if($thumb == 'src') { return $thumbSrc; } if($thumb == 'video') { return '
'.$video.'
'; } return '
'.$video.'
'; } if($type =='youtubepl') { if($thumb == 'tag') { $thumbSrc = e107::getMedia()->getThumb($id); if(empty($thumbSrc)) { $thumbSrc = e_IMAGE_ABS."generic/playlist_120.png"; } return "Youtube Video Playlist"; } if($thumb == 'src') { $thumb = e107::getMedia()->getThumb($id); if(!empty($thumb)) { return $thumb; } else { // return "https://cdn0.iconfinder.com/data/icons/internet-2-2/64/youtube_playlist_videos_vid_web_online_internet-256.png"; return e_IMAGE_ABS."generic/playlist_120.png"; } } $video = ''; return '
'.$video.'
'; } if($type == 'mp4') //TODO FIXME { return '
'; } return false; } /** * Display a Date in the browser. * Includes support for 'livestamp' (http://mattbradley.github.io/livestampjs/) * @param integer $datestamp - unix timestamp * @param string $format - short | long | relative * @return HTML with converted date. */ public function toDate($datestamp = null, $format='short') { if(!is_numeric($datestamp)){ return; } return ''.e107::getDate()->convert($datestamp, $format).''; } /** * Parse new tags into bbcode output. * @param $retainTags : when you want to replace html and retain the tags wrapping it. * @return html */ function parseBBTags($text,$retainTags = false) { $bbcodes = $this->getTags($text, 'bbcode'); foreach($bbcodes as $v) { foreach($v as $val) { $tag = urldecode($val['alt']); $repl = ($retainTags == true) ? '$1'.$tag.'$2' : $tag; $text = preg_replace('/(]*>).*(<\/bbcode>)/s',$repl, $text); //FIXME - handle multiple instances of bbcodes. } } return $text; } /** * Perform and render XSS Test Comparison */ public function test($text='',$advanced = false) { // $tp = e107::getParser(); $sql = e107::getDb(); $tp = e107::getParser(); if(empty($text)) { $text = <<bold print

<a href='#'>Something</a>

Some text's and things.

 

link

日本語 简体中文

 

[/html] TMPL; } // $text .= '[code=inline]Something[/code]日本語 '; // -------------------- Encoding ---------------- $acc = $this->getScriptAccess(); $accName = e107::getUserclass()->uc_get_classname($acc); echo "

e107 Parser Test with script access by ".$accName."

"; echo"

User-input (eg. from \$_POST)

"; print_a($text); $dbText = $tp->toDB($text,true); echo "

User-input ≫ toDB() "; if($this->isHtml == true) { echo "detected as HTML"; } else { echo "detected as Plain text"; } echo "

"; print_a($dbText); if(!empty($advanced)) { echo "
"; $dbText2 = $tp->toDB($text, true, false, 'no_html'); echo "

User-input ≫ toDb(\$text, true, false, 'no_html')

"; print_a($dbText2); // toClean $filter3 = $tp->filter($text, 'wds'); echo "

User-input ≫ filter(\$text, 'wds')

"; print_a( $filter3); // Filter by String. $filter1 = $tp->filter($text,'str'); echo "

User-input ≫ filter(\$text, 'str')

"; print_a($filter1); // Filter by Encoded. $filter2 = $tp->filter($text,'enc'); echo "

User-input ≫ filter(\$text, 'enc')

"; print_a($filter2); // toAttribute $toAtt = $tp->toAttribute($text); echo "

User-input ≫ toAttribute(\$text)

"; print_a($toAtt); // toEmail $toEmail = $tp->toEmail($dbText); echo "

User-input ≫ toEmail(\$text) from DB

"; print_a($toEmail); // toEmail $toRss = $tp->toRss($text); echo "

User-input ≫ toRss(\$text)

"; print_a($toRss); echo "
"; } echo "

toDB() ≫ toHtml()

"; $html = $tp->toHtml($dbText,true); print_a($html); echo "

toDB ≫ toHtml() (rendered)

"; echo $html; echo "

toDB ≫ toForm()

"; $toForm = $tp->toForm($dbText); $toFormRender = e107::getForm()->open('test'); $toFormRender .= ""; $toFormRender .= e107::getForm()->close(); echo $toFormRender; echo "

toDB ≫ bbarea

"; echo e107::getForm()->bbarea('name',$toForm); if(!empty($advanced)) { echo "

Allowed Tags

"; print_a($this->allowedTags); echo "

Converted Paths

"; print_a($this->pathList); echo "

Removed Tags and Attributes

"; print_a($this->removedList); echo "

Nodes to Convert

"; print_a($this->nodesToConvert); echo "

Nodes to Disable SC

"; print_a($this->nodesToDisableSC); } similar_text($text, html_entity_decode( $toForm, ENT_COMPAT, 'UTF-8'),$perc); $scoreStyle = ($perc > 98) ? 'label-success' : 'label-danger'; echo "

Similarity: ".number_format($perc)."%

"; echo "
User-input toForm() output
".print_a($text,true)." ". $toFormRender."
"; /* ".print_a(json_encode($text),true)." ". print_a(json_encode(html_entity_decode( $toForm, ENT_COMPAT, 'UTF-8')),true)." */ // print_a($text); return; //return; // --------------------------------- $html = $text; // $html = $this->getXss(); echo "

Unprocessed XSS

"; // echo $html; // Remove Comment for a real mess! print_a($html); echo "

Standard v2 Parser

"; echo "

\$tp->dataFilter()

"; // echo $tp->dataFilter($html); // Remove Comment for a real mess! $sql->db_Mark_Time('------ Start Parser Test -------'); print_a($tp->dataFilter($html)); $sql->db_Mark_Time('tp->dataFilter'); echo "

\$tp->toHtml()

"; // echo $tp->dataFilter($html); // Remove Comment for a real mess! print_a($tp->toHTML($html)); $sql->db_Mark_Time('tp->toHtml'); echo "

\$tp->toDB()

"; // echo $tp->dataFilter($html); // Remove Comment for a real mess! $todb = $tp->toDB($html); print_a( $todb); $sql->db_Mark_Time('tp->toDB'); echo "

\$tp->toForm() with toDB input.

"; print_a( $tp->toForm($todb)); echo "

New Parser

"; echo "

Processed

"; $cleaned = $this->cleanHtml($html, true); // false = don't check html pref. print_a($cleaned); $sql->db_Mark_Time('new Parser'); // $sql->db_Mark_Time('------ End Parser Test -------'); echo "

Processed & Rendered

"; echo $cleaned; echo "

New Parser - Data

"; echo "

Converted Paths

"; print_a($this->pathList); echo "

Removed Tags and Attributes

"; print_a($this->removedList); // print_a($p); } /** * Filters/Validates using the PHP5 filter_var() method. * @param $text * @param $type string str|int|email|url|w|wds * @return string | boolean */ function filter($text, $type='str',$validate=false) { if(empty($text)) { return $text; } if($type == 'w') // words only. { return preg_replace('/[^\w]/',"",$text); } if($type == 'wds') // words, digits and spaces only. { return preg_replace('/[^\w\d ]/',"",$text); } if($validate == false) { $filterTypes = array( 'int' => FILTER_SANITIZE_NUMBER_INT, 'str' => FILTER_SANITIZE_STRING, // no html. 'email' => FILTER_SANITIZE_EMAIL, 'url' => FILTER_SANITIZE_URL, 'enc' => FILTER_SANITIZE_ENCODED ); } else { $filterTypes = array( 'int' => FILTER_VALIDATE_INT, 'email' => FILTER_VALIDATE_EMAIL, 'ip' => FILTER_VALIDATE_IP, 'url' => FILTER_VALIDATE_URL, ); } if(is_array($text)) { return filter_var_array($text, $filterTypes[$type]); } return filter_var($text, $filterTypes[$type]); } /** * Process and clean HTML from user input. * TODO Html5 tag support. * @param string $html raw HTML * @param boolean $checkPref * @return string */ public function cleanHtml($html='', $checkPref = true) { if(empty($html)){ return ''; } $html = str_replace(' ', '@nbsp;', $html); // prevent replacement of   with spaces. if(strpos($html, "'; } else // Full HTML page. { // $this->allowedTags[] = 'head'; // $this->allowedTags[] = 'body'; // $this->allowedTags[] = 'title'; //$this->allowedTags[] = 'meta'; } if(!is_object($this->domObj)) { $this->init(); } if($this->scriptAccess === false) { $this->scriptAccess = e107::getConfig()->get('post_script', e_UC_MAINADMIN); // Pref to Allow alert(1)//0
< script>document.getElementById("div2").innerHTML = document.getElementById("div1").innerHTML; Some example text
This is bold
This is italic
Some small text
This is pre-formatted
        
        Bold Stuff
        
something
code BOLD function myfunction() { }
function myfunction() { } ><image xlink:href=" // O10.10↓, OM10.0↓, GC6↓, FF // IE6, O10.10↓, OM10.0↓ // IE6, O11.01↓, OM10.1↓
x
[A] "> "> "> [B] "> [C] [D] <% foo> some content without two new line \n\n Content-Type: multipart/related; boundary="******"some content without two new line --****** Content-Location: xss.html Content-Transfer-Encoding: base64 PGlmcmFtZSBuYW1lPWxvIHN0eWxlPWRpc3BsYXk6bm9uZT48L2lmcmFtZT4NCjxzY3JpcHQ+DQp1 cmw9bG9jYXRpb24uaHJlZjtkb2N1bWVudC5nZXRFbGVtZW50c0J5TmFtZSgnbG8nKVswXS5zcmM9 dXJsLnN1YnN0cmluZyg2LHVybC5pbmRleE9mKCcvJywxNSkpO3NldFRpbWVvdXQoImFsZXJ0KGZy YW1lc1snbG8nXS5kb2N1bWVudC5jb29raWUpIiwyMDAwKTsNCjwvc2NyaXB0PiAgICAg --******--
XXX `><img src=xx:x onerror=alert(1)></a> `><img src=xx:x onerror=alert(2)// `><img src=xx:x onerror=alert(3)// // Safari 5.0, Chrome 9, 10 // Safari 5.0 <% %></xmp><img src=xx:x onerror=alert(1)// %>/ alert(2) XXX -->{} *{color:red} X
XXX






...



Some text goes here
EOF; return $html; } } class e_emotefilter { var $search; var $replace; var $emotes; function __construct() /* constructor */ { $pref = e107::getPref(); if(empty($pref['emotepack'])) { $pref['emotepack'] = "default"; e107::getConfig('emote')->clearPrefCache('emote'); e107::getConfig('core')->set('emotepack','default')->save(false,true,false); } $this->emotes = e107::getConfig("emote")->getPref(); if(!vartrue($this->emotes)) { return; } foreach($this->emotes as $key => $value) { $value = trim($value); if ($value) { // Only 'activate' emote if there's a substitution string set $key = preg_replace("#!(\w{3,}?)$#si", ".\\1", $key); // Next two probably to sort out legacy issues - may not be required any more $key = preg_replace("#_(\w{3})$#", ".\\1", $key); $key = str_replace("!", "_", $key); $filename = e_IMAGE."emotes/" . $pref['emotepack'] . "/" . $key; $fileloc = SITEURLBASE.e_IMAGE_ABS."emotes/" . $pref['emotepack'] . "/" . $key; if(file_exists($filename)) { if(strstr($value, " ")) { $tmp = explode(" ", $value); foreach($tmp as $code) { $this->search[] = " ".$code; $this->search[] = "\n".$code; //TODO CSS class? $this->replace[] = " "; $this->replace[] = "\n "; } unset($tmp); } else { if($value) { $this->search[] = " ".$value; $this->search[] = "\n".$value; //TODO CSS class? $this->replace[] = " "; $this->replace[] = "\n "; } } } } else { unset($this->emotes[$key]); } } } function filterEmotes($text) { $text = str_replace($this->search, $this->replace, $text); return $text; } function filterEmotesRev($text) { $text = str_replace($this->replace, $this->search, $text); return $text; } } class e_profanityFilter { var $profanityList; function __construct() { global $pref; $words = explode(",", $pref['profanity_words']); $word_array = array(); foreach($words as $word) { $word = trim($word); if($word != "") { $word_array[] = $word; if (strpos($word, '$') !== FALSE) { $word_array[] = str_replace('$', '\$', $word); // Special case - '$' may be 'in clear' or as entity } } } if(count($word_array)) { $this->profanityList = str_replace('#','\#',implode("\b|\b", $word_array)); // We can get entities in the string - confuse the regex delimiters } unset($words); return TRUE; } function filterProfanities($text) { global $pref; if (!$this->profanityList) { return $text; } if ($pref['profanity_replace']) { return preg_replace("#\b".$this->profanityList."\b#is", $pref['profanity_replace'], $text); } else { return preg_replace_callback("#\b".$this->profanityList."\b#is", array($this, 'replaceProfanities'), $text); } } function replaceProfanities($matches) { /*! @function replaceProfanities callback @abstract replaces vowels in profanity words with stars @param text string - text string to be filtered @result filtered text */ return preg_replace("#a|e|i|o|u#i", "*" , $matches[0]); } }