* Invision Power Services * IP.Board v3.4.8 * Custom bbcode plugin interfaces * Last Updated: $Date: 2012-05-24 16:33:36 +0100 (Thu, 24 May 2012) $ * * * @author $author$ * @copyright (c) 2001 - 2009 Invision Power Services, Inc. * @license http://www.invisionpower.com/company/standards.php#license * @package IP.Board * @link http://www.invisionpower.com * @version $Rev: 10790 $ */ /** *
* Invision Power Services * IP.Board v3.4.8 * BBCode parser: default custom bbcodes: img, quote, list, size, member, media, url, snapback * Last Updated: $Date: 2012-05-24 16:33:36 +0100 (Thu, 24 May 2012) $ ** * @author $author$ * @copyright (c) 2001 - 2009 Invision Power Services, Inc. * @license http://www.invisionpower.com/company/standards.php#license * @package IP.Board * @link http://www.invisionpower.com * @version $Rev: 10790 $ * */ class bbcode_parent_main_class { /** * Current position in the text document * * @access protected * @var integer */ protected $cur_pos = 0; /** * Stored position of ending quote tag * * @access protected * @var integer */ protected $end_pos = 0; /** * Error message * * @access public * @var string */ public $error = ''; /** * Warning message * * @access public * @var string */ public $warning = ''; /** * This bbcode's data * * @access protected * @var integer */ protected $_bbcode = array(); /** * Registry object * * @access protected * @var object */ protected $registry; /** * Database object * * @access protected * @var object */ protected $DB; /** * Settings object * * @access protected * @var object */ protected $settings; /** * Request object * * @access protected * @var object */ protected $request; /** * Language object * * @access protected * @var object */ protected $lang; /** * Member object * * @access protected * @var object */ protected $member; protected $memberData; /** * Cache object * * @access protected * @var object */ protected $cache; protected $caches; /** * Current bbcode * * @access protected * @var string */ protected $currentBbcode; /** * Mode */ protected $_mode = ''; /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent=null ) { /* Make object */ $this->registry = $registry; $this->DB = $this->registry->DB(); $this->settings =& $this->registry->fetchSettings(); $this->request =& $this->registry->fetchRequest(); $this->lang = $this->registry->getClass('class_localization'); $this->member = $this->registry->member(); $this->memberData =& $this->registry->member()->fetchMemberData(); $this->cache = $this->registry->cache(); $this->caches =& $this->registry->cache()->fetchCaches(); $this->_parentBBcode = $_parent; /* Retrieve bbcode data */ $bbcodeCache = $this->cache->getCache('bbcode'); $this->_bbcode = $bbcodeCache[ $this->currentBbcode ]; } /** * Convert to HTML * * @access public * @param string $txt BBCode/parsed text from database to be displayed * @return string Formatted content, ready for display */ public function run( $txt, $mode='html' ) { $this->cur_pos = 0; $this->end_pos = 0; $this->error = ''; /* Set Mode */ $this->_mode = $mode; return $this->_replaceText( $txt ); } /** * Retrieves the tags used for this bbcode, including aliases * * @access public * @return array Array of tags to check */ protected function _retrieveTags() { $_tags = array( $this->_bbcode['bbcode_tag'] ); //----------------------------------------- // We'll also need to check for any aliases //----------------------------------------- if( $this->_bbcode['bbcode_aliases'] ) { $aliases = explode( ',', trim($this->_bbcode['bbcode_aliases']) ); if( is_array($aliases) AND count($aliases) ) { foreach( $aliases as $alias ) { $_tags[] = trim($alias); } } } return $_tags; } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_plugin_sharedmedia extends bbcode_parent_main_class { /** * Store plugins we've instantiated * * @var array */ protected $plugins = array(); /** * Constructor * * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent='' ) { $this->currentBbcode = 'sharedmedia'; parent::__construct( $registry, $_parent ); } /** * We use this to verify you have permission to share the items you are sharing * * @access public * @param string $txt BBCode text from submission to be stored in database * @return string Formatted content, ready for display */ public function _replaceText( $txt ) { if ( $this->_mode == 'html' ) { $_ignored = preg_replace_callback( '#(\[sharedmedia=(.+?):(.+?):(.+?)\])#is' , array( $this, '_checkPostingPermissions' ), $txt ); } else { $txt = preg_replace_callback( '#(\[sharedmedia=(.+?):(.+?):(.+?)\])#is' , array( $this, '_parseMedia' ), $txt ); } return $txt; } /** * We use this to check for permissions * * @param array preg_replace_callback Matches * @return @e string */ protected function _checkPostingPermissions( $matches ) { $txt = trim( $matches[1] ); $app = trim( $matches[2] ); $plugin = trim( $matches[3] ); if( !$txt OR !$app OR ! IPSLib::appIsInstalled( $app ) OR !$plugin ) { return ''; } if( !isset($this->plugins[ $app ][ $plugin ]) ) { if( is_file( IPSLib::getAppDir( $app ) . '/extensions/sharedmedia/plugin_' . $plugin . '.php' ) ) { $classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir( $app ) . '/extensions/sharedmedia/plugin_' . $plugin . '.php', 'plugin_' . $app . '_' . $plugin, $app ); $this->plugins[ $app ][ $plugin ] = new $classToLoad( $this->registry ); } } if( isset($this->plugins[ $app ][ $plugin ]) ) { if( $error = $this->plugins[ $app ][ $plugin ]->checkPostPermission( $matches[4] ) ) { $this->error = $error; } } //----------------------------------------- // Return the original output (we aren't replacing, just verifying permissions) //----------------------------------------- return $txt; } /** * Callback for shared media preg_replace call * * @param array preg_replace_callback Matches * @return @e string */ protected function _parseMedia( $matches ) { $txt = trim( $matches[1] ); $app = trim( $matches[2] ); $plugin = trim( $matches[3] ); $_orig = $txt; if( !$txt OR !$app OR ! IPSLib::appIsInstalled( $app ) OR !$plugin ) { return ''; } if( !isset($this->plugins[ $app ][ $plugin ]) ) { if( is_file( IPSLib::getAppDir( $app ) . '/extensions/sharedmedia/plugin_' . $plugin . '.php' ) ) { $classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir( $app ) . '/extensions/sharedmedia/plugin_' . $plugin . '.php', 'plugin_' . $app . '_' . $plugin, $app ); $this->plugins[ $app ][ $plugin ] = new $classToLoad( $this->registry ); } } if( isset($this->plugins[ $app ][ $plugin ]) ) { $txt = $this->plugins[ $app ][ $plugin ]->getOutput( $matches[4] ); } //----------------------------------------- // Return the replaced output //----------------------------------------- return $txt; } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_plugin_img extends bbcode_parent_main_class { /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent='' ) { $this->currentBbcode = 'img'; parent::__construct( $registry, $_parent ); } /** * Do the actual replacement * * @access protected * @param string $txt Parsed text from database to be edited * @return string BBCode content, ready for editing */ protected function _replaceText( $txt ) { $_tags = $this->_retrieveTags(); $txt = preg_replace( '#\[img=([^\[]+?)\]#i', '[img]\1[/img]', $txt ); foreach( $_tags as $_tag ) { //----------------------------------------- // Start building open/close tag //----------------------------------------- $open_tag = '[' . $_tag . ']'; $close_tag = '[/' . $_tag . ']'; //----------------------------------------- // Infinite loop catcher //----------------------------------------- $_iteration = 0; //----------------------------------------- // Doz I can haz opin tag? Loopy loo //----------------------------------------- while( ( $this->cur_pos = stripos( $txt, $open_tag, $this->cur_pos ) ) !== false ) { //----------------------------------------- // Stop infinite loops //----------------------------------------- if( $_iteration > $this->settings['max_bbcodes_per_post'] ) { break; } $_iteration++; //----------------------------------------- // Grab the new position to jump to //----------------------------------------- $new_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; //----------------------------------------- // No closing tag //----------------------------------------- if( stripos( $txt, $close_tag, $new_pos ) === false ) { break; } //----------------------------------------- // Grab the content //----------------------------------------- $_content = substr( $txt, ($this->cur_pos + strlen($open_tag)), (stripos( $txt, $close_tag, $this->cur_pos ) - ($this->cur_pos + strlen($open_tag))) ); //----------------------------------------- // If this is a single tag, that's it //----------------------------------------- if( $_content ) { /* Trying to sneak in another URL to auto parse? */ preg_match_all( '#http(s)?://#i', $_content, $_match ); if ( count( $_match[0] ) > 1 ) { /* Make safe */ $txt = preg_replace( "#(http|https|news|ftp)://#i", "\\1://", $txt ); return $txt; } $txt = substr_replace( $txt, $this->_buildOutput( $_content ), $this->cur_pos, (stripos( $txt, $close_tag, $this->cur_pos ) + strlen($close_tag) - $this->cur_pos) ); } else { $txt = substr_replace( $txt, '', $this->cur_pos, (stripos( $txt, $close_tag, $this->cur_pos ) + strlen($close_tag) - $this->cur_pos) ); } //----------------------------------------- // And reset current position to end of open tag //----------------------------------------- $this->cur_pos = stripos( $txt, $open_tag ) ? stripos( $txt, $open_tag ) : $this->cur_pos + 1; //$new_pos; if( $this->cur_pos > strlen($txt) ) { //----------------------------------------- // Need to reset for next "tag" //----------------------------------------- $this->cur_pos = 0; break; } } } return $txt; } /** * Build the actual output to show * * @access protected * @param array $content Image URL to link to * @return string Content to replace bbcode with */ protected function _buildOutput( $content ) { $content = trim($content); //----------------------------------------- // Too many images? //----------------------------------------- $existing = $this->cache->getCache( '_tmp_bbcode_images', false ); $existing = intval($existing) + 1; if ( $this->settings['max_images'] AND $this->caches['_tmp_section'] != 'signatures' ) { if ($existing > $this->settings['max_images']) { $this->error = 'too_many_img'; return $content; } } $this->cache->updateCacheWithoutSaving( '_tmp_bbcode_images', $existing ); //----------------------------------------- // Some security checking //----------------------------------------- $content = preg_replace( '#(https|http|ftp)&\#(058|58);//#', '\1://', $content ); if ( IPSText::xssCheckUrl( $content ) !== TRUE ) { return $content; } foreach( $this->cache->getCache('bbcode') as $bbcode ) { $_tags = $this->_retrieveTags(); foreach( $_tags as $tag ) { if ( stripos( $content, '[' . $tag ) !== false ) { return $content; } } } //----------------------------------------- // Allowed type? //----------------------------------------- /* Load parser */ $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/text/parser.php', 'classes_text_parser' ); $parser = new $classToLoad(); if ( ! $parser->isAllowedImgUrl( $content ) ) { $this->error = 'invalid_ext'; return $content; } //----------------------------------------- // URL filtering? //----------------------------------------- if ( ! $parser->isAllowedUrl( $content ) ) { $this->error = 'domain_not_allowed'; return $content; } if ( stristr( $content, $this->settings['board_url'] . '/' . PUBLIC_DIRECTORY . '/style_emoticons/' ) ) { return "
{$content}
"; } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_plugin_size extends bbcode_parent_main_class { /** * Mapped font sizes * * @access protected * @var array */ protected $font_sizes = array( 1 => 8, 2 => 10, 3 => 12, 4 => 14, 5 => 18, 6 => 24, 7 => 36, 8 => 48 ); /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent='' ) { $this->currentBbcode = 'size'; parent::__construct( $registry, $_parent ); } /** * Do the actual replacement * * @access protected * @param string $txt Parsed text from database to be edited * @return string BBCode content, ready for editing */ protected function _replaceText( $txt ) { $_tags = $this->_retrieveTags(); foreach( $_tags as $_tag ) { //----------------------------------------- // Infinite loop catcher //----------------------------------------- $_iteration = 0; //----------------------------------------- // Start building open tag //----------------------------------------- $open_tag = '[' . $_tag . '='; //----------------------------------------- // Doz I can haz opin tag? Loopy loo //----------------------------------------- while( ( $this->cur_pos = stripos( $txt, $open_tag, $this->cur_pos ) ) !== false ) { //----------------------------------------- // Stop infinite loops //----------------------------------------- if( $_iteration > $this->settings['max_bbcodes_per_post'] ) { break; } $_iteration++; //----------------------------------------- // Grab the new position to jump to //----------------------------------------- $new_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; //----------------------------------------- // Extract the option (like surgery) //----------------------------------------- $_content = ''; $_option = substr( $txt, $this->cur_pos + strlen($open_tag), (strpos( $txt, ']', $this->cur_pos ) - ($this->cur_pos + strlen($open_tag))) ); $close_tag = '[/' . $_tag . ']'; //----------------------------------------- // Protect against XSS //----------------------------------------- $_option = IPSText::getTextClass('bbcode')->xssHtmlClean($_option); /* Make sure it's clean */ $test = str_replace( array( '"', "'", '"', ''' ), "", $_option ); $test1 = IPSText::alphanumericalClean( $test, '.+ ' ); if ( $test1 != $test ) { $_option = false; } //----------------------------------------- // No closing tag //----------------------------------------- if ( $_option !== false AND stripos( $txt, $close_tag, $new_pos ) !== false ) { $_content = substr( $txt, ($this->cur_pos + strlen($open_tag) + strlen($_option) + 1), (stripos( $txt, $close_tag, $this->cur_pos ) - ($this->cur_pos + strlen($open_tag) + strlen($_option) + 1)) ); //----------------------------------------- // If this is a single tag, that's it //----------------------------------------- if( preg_match( '/\S/', $_content ) ) /* Make sure we don't miss this, if there's only a 0 for the content. Bug #21610 */ { $txt = substr_replace( $txt, $this->_buildOutput( $_option, $_content ), $this->cur_pos, (stripos( $txt, $close_tag, $this->cur_pos ) + strlen($close_tag) - $this->cur_pos) ); } else { $txt = substr_replace( $txt, '', $this->cur_pos, (stripos( $txt, $close_tag, $this->cur_pos ) + strlen($close_tag) - $this->cur_pos) ); } } //----------------------------------------- // And reset current position to end of open tag //----------------------------------------- $this->cur_pos = stripos( $txt, $open_tag ) ? stripos( $txt, $open_tag ) : $this->cur_pos + 1; //$new_pos; if( $this->cur_pos > strlen($txt) ) { //----------------------------------------- // Need to reset for next "tag" //----------------------------------------- $this->cur_pos = 0; break; } } } return $txt; } /** * Build the actual output to show * * @access protected * @param integer $option Font size * @param string $content Text * @return string Content to replace bbcode with */ protected function _buildOutput( $option, $content ) { //----------------------------------------- // Strip the optional quote delimiters //----------------------------------------- $option = trim( $option, '"' . "'" ); $option = str_replace( '"', '', $option ); $option = str_replace( ''', '', $option ); $size = $this->font_sizes[ $option ]; return "{$content}"; } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_plugin_url extends bbcode_parent_main_class { /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent='' ) { $this->currentBbcode = 'url'; parent::__construct( $registry, $_parent ); } /** * Do the actual replacement * * @access protected * @param string $txt Parsed text from database to be edited * @return string BBCode content, ready for editing */ protected function _replaceText( $txt ) { $_tags = $this->_retrieveTags(); /* Hack to fix people pasting URLs as BBCode in the RTE and having * purify auto-parse the URls inside breaking this section. */ preg_match_all( '#\[url([^\]]+?)?\]([^\[]+?)\[/url\]()?#', $txt, $matches ); for( $i = 0 ; $i < count( $matches[0] ) ; $i++ ) { $option = $matches[1][$i]; $value = $matches[2][$i]; if ( strstr( $option, 'quot;' ) ) { $option = str_replace( array( '"', '"' ), '"', $option ); $txt = str_replace( $matches[0][$i], '[url' . $option . ']' . $value . '[/url]' . $matches[3][$i], $txt ); } if ( stristr( $option, '', '', $value ); $value = preg_replace( '#]+?)>(.*)$#', '\1', $value ); if ( $option && $value ) { $option = preg_replace( '#^=#', '', $option ); $option = preg_replace( '#^(\'|"|\"|&\#39;)(.*)(\'|"|\"|&\#39;)$#', '\\2', $option ); $option = str_replace( '', '', $option ); $option = preg_replace( '#]+?)>(.*)$#', '\1', $option ); $txt = str_replace( $matches[0][$i], '' . $value . '', $txt ); } else if ( $value ) { $txt = str_replace( $matches[0][$i], '' . $value . '', $txt ); } } } foreach( $_tags as $_tag ) { //----------------------------------------- // Infinite loop catcher //----------------------------------------- $_iteration = 0; //----------------------------------------- // Start building open/close tag //----------------------------------------- $open_tag = '[' . $_tag; $close_tag = '[/' . $_tag . ']'; //----------------------------------------- // Doz I can haz opin tag? Loopy loo //----------------------------------------- while( ( $this->cur_pos = stripos( $txt, $open_tag, $this->cur_pos ) ) !== false ) { //----------------------------------------- // Stop infinite loops //----------------------------------------- if( $_iteration > $this->settings['max_bbcodes_per_post'] ) { break; } $_iteration++; $open_length = strlen($open_tag); //----------------------------------------- // Extract the option (like surgery) //----------------------------------------- $_option = ''; if( $this->_bbcode['bbcode_useoption'] ) { //----------------------------------------- // Is option optional? //----------------------------------------- if( $this->_bbcode['bbcode_optional_option'] ) { //----------------------------------------- // Does we haz it? //----------------------------------------- if( substr( $txt, $this->cur_pos + strlen($open_tag), 1 ) == '=' ) { $open_length += 1; //----------------------------------------- // This is here to try to capture urls with // [ and ] in them, only works if enclosed in quotes //----------------------------------------- $cur_content = ''; if( substr( $txt, $this->cur_pos + $open_length, 6 ) == '"' ) { /* Skip the bbocde if there is more than 2 quotes. Bug #21161 */ if( strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) < 1 OR ( substr_count( $txt, '"', $this->cur_pos, strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) ) ) > 2 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } $end_pos = stripos( $txt, '"', $this->cur_pos + $open_length + 1 ) ? stripos( $txt, '"', $this->cur_pos + $open_length + 1 ) : strpos( $txt, ']', $this->cur_pos + $open_length + 1 ); $cur_content = substr( $txt, $this->cur_pos + $open_length + 6, ($end_pos - ($this->cur_pos + $open_length + 6 ) ) ); $new_content = str_replace( '[', '%5B', str_replace( ']', '%5D', $cur_content ) ); $txt = substr_replace( $txt, $new_content, $this->cur_pos + $open_length + 6, ($end_pos - ($this->cur_pos + $open_length + 6 ) ) ); } else if( substr( $txt, $this->cur_pos + $open_length, 5 ) == "'" ) { /* Skip the bbocde if there is mroe than 2 quotes. Bug #21161 */ if( strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) < 1 OR ( substr_count( $txt, ''', $this->cur_pos, strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) ) ) > 2 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } $end_pos = stripos( $txt, ''', $this->cur_pos + $open_length + 1 ) ? stripos( $txt, ''', $this->cur_pos + $open_length + 1 ) : strpos( $txt, ']', $this->cur_pos + $open_length + 1 ); $cur_content = substr( $txt, $this->cur_pos + $open_length + 5, ($end_pos - ($this->cur_pos + $open_length + 5 )) ); $new_content = str_replace( '[', '%5B', str_replace( ']', '%5D', $cur_content ) ); $txt = substr_replace( $txt, $new_content, $this->cur_pos + $open_length + 5, ($end_pos - ($this->cur_pos + $open_length + 5 ) ) ); } //----------------------------------------- // Need this because HTML on converts the // entities back to quote/apos //----------------------------------------- else if( substr( $txt, $this->cur_pos + $open_length, 1 ) == '"' ) { /* Skip the bbocde if there is mroe than 2 quotes. Bug #21161 */ if( strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) < 1 OR ( substr_count( $txt, '"', $this->cur_pos, strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) ) ) > 2 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } $end_pos = stripos( $txt, '"', $this->cur_pos + $open_length + 1 ) ? stripos( $txt, '"', $this->cur_pos + $open_length + 1 ) : strpos( $txt, ']', $this->cur_pos + $open_length + 1 ); $cur_content = substr( $txt, $this->cur_pos + $open_length + 1, ($end_pos - ($this->cur_pos + $open_length + 1 ) ) ); $new_content = str_replace( '[', '%5B', str_replace( ']', '%5D', $cur_content ) ); $txt = substr_replace( $txt, $new_content, $this->cur_pos + $open_length + 1, ($end_pos - ($this->cur_pos + $open_length + 1 ) ) ); } else if( substr( $txt, $this->cur_pos + $open_length, 1 ) == "'" ) { /* Skip the bbocde if there is mroe than 2 quotes. Bug #21161 */ if( strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) < 1 OR ( substr_count( $txt, "'", $this->cur_pos, strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) ) ) > 2 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } $end_pos = stripos( $txt, "'", $this->cur_pos + $open_length + 1 ) ? stripos( $txt, "'", $this->cur_pos + $open_length + 1 ) : strpos( $txt, ']', $this->cur_pos + $open_length + 1 ); $cur_content = substr( $txt, $this->cur_pos + $open_length + 1, ($end_pos - ($this->cur_pos + $open_length + 1 )) ); $new_content = str_replace( '[', '%5B', str_replace( ']', '%5D', $cur_content ) ); $txt = substr_replace( $txt, $new_content, $this->cur_pos + $open_length + 1, ($end_pos - ($this->cur_pos + $open_length + 1 ) ) ); } $_option = substr( $txt, $this->cur_pos + $open_length, (strpos( $txt, ']', $this->cur_pos ) - ($this->cur_pos + $open_length)) ); } //----------------------------------------- // If not, [u] != [url] (for example) //----------------------------------------- else if( (strpos( $txt, ']', $this->cur_pos ) - ( $this->cur_pos + $open_length )) !== 0 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } } //----------------------------------------- // No? Then just grab it //----------------------------------------- else { $open_length += 1; $_option = substr( $txt, $this->cur_pos + $open_length, (strpos( $txt, ']', $this->cur_pos ) - ($this->cur_pos + $open_length)) ); } } //----------------------------------------- // [img] != [i] (for example) //----------------------------------------- else if( (strpos( $txt, ']', $this->cur_pos ) - ( $this->cur_pos + $open_length )) !== 0 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } //----------------------------------------- // Grab the new position to jump to //----------------------------------------- $new_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; //----------------------------------------- // No closing tag //----------------------------------------- if( stripos( $txt, $close_tag, $new_pos ) === false ) { break; } //----------------------------------------- // Grab the content //----------------------------------------- $_content = substr( $txt, ($this->cur_pos + $open_length + strlen($_option) + 1), ( ( stripos( $txt, $close_tag, $this->cur_pos + $open_length + strlen($_option) + 1 ) ) - ($this->cur_pos + $open_length + strlen($_option) + 1)) ); //----------------------------------------- // If this is a single tag, that's it // @link: http://forums.invisionpower.com/index.php?autocom=tracker&showissue=11909 //----------------------------------------- if( $_content OR $_content === '0' ) { if( $this->_buildOutput( $_content, $_option ? $_option : $_content ) ) { $txt = substr_replace( $txt, $this->_buildOutput( $_content, $_option ? $_option : $_content ), $this->cur_pos, (stripos( $txt, $close_tag, $this->cur_pos ) + strlen($close_tag) - $this->cur_pos) ); } } else { $txt = substr_replace( $txt, '', $this->cur_pos, (stripos( $txt, $close_tag, $this->cur_pos ) + strlen($close_tag) - $this->cur_pos) ); } //----------------------------------------- // And reset current position to end of open tag //----------------------------------------- $this->cur_pos = stripos( $txt, $open_tag ) ? stripos( $txt, $open_tag ) : $this->cur_pos + 1; //$new_pos; if( $this->cur_pos > strlen($txt) ) { //----------------------------------------- // Need to reset for next "tag" //----------------------------------------- $this->cur_pos = 0; break; } } } return $txt; } /** * Build the actual output to show * * @access protected * @param array $content Display text * @param string $option URL to link to * @return string Content to replace bbcode with */ protected function _buildOutput( $content, $option ) { // This is problematic if url contains a ' or " // $option = str_replace( array( '"', "'", ''', '"' ), '', $option ); //----------------------------------------- // Remove " and ' from beginning + end //----------------------------------------- if( substr( $option, 0, 5 ) == ''' ) { $option = substr( $option, 5 ); } else if( substr( $option, 0, 6 ) == '"' ) { $option = substr( $option, 6 ); } else if( substr( $option, 0, 1 ) == "'" ) { $option = substr( $option, 1 ); } else if( substr( $option, 0, 1 ) == '"' ) { $option = substr( $option, 1 ); } if( substr( $option, -5 ) == ''' ) { $option = substr( $option, 0, -5 ); } else if( substr( $option, -6 ) == '"' ) { $option = substr( $option, 0, -6 ); } else if( substr( $option, -1 ) == "'" ) { $option = substr( $option, 0, -1 ); } else if( substr( $option, -1 ) == '"' ) { $option = substr( $option, 0, -1 ); } //----------------------------------------- // Some security checking //----------------------------------------- if ( IPSText::xssCheckUrl( $option ) !== TRUE ) { return $content; } /* Check for mangled or embedded URLs */ if ( stristr( $option, '[attachment' ) OR stristr( $option, '[quote' ) OR stristr( $option, '[url' ) OR stristr( $option, '[/url' ) OR stristr( $content, '[url' ) OR stristr( $content, '[/url' ) ) { return $content; } //----------------------------------------- // Fix quotes in urls //----------------------------------------- $option = str_replace( array( ''', "'" ), '%27', $option ); $option = str_replace( array( '"', '"' ), '%22', $option ); foreach( $this->cache->getCache('bbcode') as $bbcode ) { $_tags = $this->_retrieveTags(); foreach( $_tags as $tag ) { if( strpos( $option, '[' . $tag ) !== false ) { return $content; } } } // ----------------------------------------- // Test URL filtering? // ----------------------------------------- /* Load parser */ $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/text/parser.php', 'classes_text_parser' ); $parser = new $classToLoad(); if ( ! $parser->isAllowedUrl( $option ) ) { $this->warning = 'domain_not_allowed'; return $option; } //----------------------------------------- // Adding rel='nofollow'? //----------------------------------------- $rels = array(); $rel = ''; $_title = ''; /* Fetch actual host for better matching */ $data = @parse_url( $option ); if ( $this->settings['posts_add_nofollow'] ) { if ( ! stristr( $data['host'], $_SERVER['HTTP_HOST'] ) ) { $rels[] = "nofollow"; } } if ( $this->settings['links_external'] ) { if ( ! stristr( $data['host'], $_SERVER['HTTP_HOST'] ) ) { /* Look a little closer */ $rels[] = "external"; $_title = $this->lang->words['bbc_external_link']; } } if ( count($rels) ) { $rel = " rel='" . implode( ' ', $rels ) . "'"; } /* If this is just converting to HTML and not display, return the URL as * filters and such are tested on display */ return "{$content}"; } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_plugin_code extends bbcode_parent_main_class { /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent='' ) { $this->currentBbcode = 'code'; $this->_parent = $_parent; parent::__construct( $registry, $_parent ); } /** * Check and make safe embedded codes * @param array $matches */ protected function _cleanCodeBoxes( $matches ) { $txt = $matches[0]; $map = array(); /* CODE: Fetch paired opening and closing tags */ $data = $this->_parent->getTagPositions( $txt, 'code', array( '[' , ']' ) ); if ( is_array( $data['open'] ) ) { foreach( $data['open'] as $id => $val ) { $o = $data['open'][ $id ]; $c = $data['close'][ $id ] - $o; /* Prevent unclosed tags from breaking this */ if ( $o < 1 || $c < 1 ) { continue; } $slice = substr( $txt, $o, $c ); /* Need to bump up lengths of opening and closing */ $_origLength = strlen( $slice ); /* Extra conversion for BBCODE>HTML mode */ $slice = str_replace( "[", "[", $slice ); $slice = str_replace( "{parse", "{parse", $slice ); $slice = preg_replace( '#(https|http|ftp)://#' , '\1://', $slice ); $slice = str_replace( "\n", "", $slice ); $_newLength = strlen( $slice ); $txt = substr_replace( $txt, $slice, $o, $c ); /* Bump! */ if ( $_newLength != $_origLength ) { foreach( $data['open'] as $_id => $_val ) { $_o = $data['open'][ $_id ]; if ( $_o > $o ) { $data['open'][ $_id ] += ( $_newLength - $_origLength ); $data['close'][ $_id ] += ( $_newLength - $_origLength ); } } } } } return $txt; } /** * Do the actual replacement * * @access protected * @param string $txt Parsed text from database to be edited * @return string BBCode content, ready for editing */ protected function _replaceText( $txt ) { /* Convert old indent tags over */ if ( stristr( $txt, '[code]' ) ) { $txt = str_ireplace( '[code]', '[code=auto:0]', $txt ); } $txt = preg_replace_callback( '#(\[code.*\[/code\])#is', array( $this, '_cleanCodeBoxes' ), $txt ); $this->_bbcode['bbcode_useoption'] = true; $this->_bbcode['bbcode_optional_option'] = true; $_tags = $this->_retrieveTags(); foreach( $_tags as $_tag ) { // ----------------------------------------- // Infinite loop catcher // ----------------------------------------- $_iteration = 0; // ----------------------------------------- // Start building open/close tag // ----------------------------------------- $open_tag = '[' . $_tag; $close_tag = '[/' . $_tag . ']'; // ----------------------------------------- // Doz I can haz opin tag? Loopy loo // ----------------------------------------- while ( ( $this->cur_pos = stripos( $txt, $open_tag, $this->cur_pos ) ) !== false ) { // ----------------------------------------- // Stop infinite loops // ----------------------------------------- if ( $_iteration > $this->settings['max_bbcodes_per_post'] ) { break; } $_iteration ++; $open_length = strlen( $open_tag ); // ----------------------------------------- // Extract the option (like surgery) // ----------------------------------------- $_option = ''; if ( $this->_bbcode['bbcode_useoption'] ) { // ----------------------------------------- // Is option optional? // ----------------------------------------- if ( $this->_bbcode['bbcode_optional_option'] ) { // ----------------------------------------- // Does we haz it? // ----------------------------------------- if ( substr( $txt, $this->cur_pos + strlen( $open_tag ), 1 ) == '=' ) { $open_length += 1; // ----------------------------------------- // This is here to try to capture urls with // [ and ] in them, only works if enclosed in quotes // ----------------------------------------- $cur_content = ''; if ( substr( $txt, $this->cur_pos + $open_length, 6 ) == '"' ) { /* * Skip the bbocde if there is more than 2 * quotes. Bug #21161 */ if ( strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) < 1 or ( substr_count( $txt, '"', $this->cur_pos, strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) ) ) > 2 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } $end_pos = stripos( $txt, '"', $this->cur_pos + $open_length + 1 ) ? stripos( $txt, '"', $this->cur_pos + $open_length + 1 ) : strpos( $txt, ']', $this->cur_pos + $open_length + 1 ); $cur_content = substr( $txt, $this->cur_pos + $open_length + 6, ( $end_pos - ( $this->cur_pos + $open_length + 6 ) ) ); $new_content = str_replace( '[', '%5B', str_replace( ']', '%5D', $cur_content ) ); $txt = substr_replace( $txt, $new_content, $this->cur_pos + $open_length + 6, ( $end_pos - ( $this->cur_pos + $open_length + 6 ) ) ); } else if ( substr( $txt, $this->cur_pos + $open_length, 5 ) == "'" ) { /* * Skip the bbocde if there is mroe than 2 * quotes. Bug #21161 */ if ( strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) < 1 or ( substr_count( $txt, ''', $this->cur_pos, strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) ) ) > 2 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } $end_pos = stripos( $txt, ''', $this->cur_pos + $open_length + 1 ) ? stripos( $txt, ''', $this->cur_pos + $open_length + 1 ) : strpos( $txt, ']', $this->cur_pos + $open_length + 1 ); $cur_content = substr( $txt, $this->cur_pos + $open_length + 5, ( $end_pos - ( $this->cur_pos + $open_length + 5 ) ) ); $new_content = str_replace( '[', '%5B', str_replace( ']', '%5D', $cur_content ) ); $txt = substr_replace( $txt, $new_content, $this->cur_pos + $open_length + 5, ( $end_pos - ( $this->cur_pos + $open_length + 5 ) ) ); } // ----------------------------------------- // Need this because HTML on converts the // entities back to quote/apos // ----------------------------------------- else if ( substr( $txt, $this->cur_pos + $open_length, 1 ) == '"' ) { /* * Skip the bbocde if there is mroe than 2 * quotes. Bug #21161 */ if ( strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) < 1 or ( substr_count( $txt, '"', $this->cur_pos, strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) ) ) > 2 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } $end_pos = stripos( $txt, '"', $this->cur_pos + $open_length + 1 ) ? stripos( $txt, '"', $this->cur_pos + $open_length + 1 ) : strpos( $txt, ']', $this->cur_pos + $open_length + 1 ); $cur_content = substr( $txt, $this->cur_pos + $open_length + 1, ( $end_pos - ( $this->cur_pos + $open_length + 1 ) ) ); $new_content = str_replace( '[', '%5B', str_replace( ']', '%5D', $cur_content ) ); $txt = substr_replace( $txt, $new_content, $this->cur_pos + $open_length + 1, ( $end_pos - ( $this->cur_pos + $open_length + 1 ) ) ); } else if ( substr( $txt, $this->cur_pos + $open_length, 1 ) == "'" ) { /* * Skip the bbocde if there is mroe than 2 * quotes. Bug #21161 */ if ( strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) < 1 or ( substr_count( $txt, "'", $this->cur_pos, strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) ) ) > 2 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } $end_pos = stripos( $txt, "'", $this->cur_pos + $open_length + 1 ) ? stripos( $txt, "'", $this->cur_pos + $open_length + 1 ) : strpos( $txt, ']', $this->cur_pos + $open_length + 1 ); $cur_content = substr( $txt, $this->cur_pos + $open_length + 1, ( $end_pos - ( $this->cur_pos + $open_length + 1 ) ) ); $new_content = str_replace( '[', '%5B', str_replace( ']', '%5D', $cur_content ) ); $txt = substr_replace( $txt, $new_content, $this->cur_pos + $open_length + 1, ( $end_pos - ( $this->cur_pos + $open_length + 1 ) ) ); } $_option = substr( $txt, $this->cur_pos + $open_length, ( strpos( $txt, ']', $this->cur_pos ) - ( $this->cur_pos + $open_length ) ) ); } // ----------------------------------------- // If not, [u] != [url] (for example) // ----------------------------------------- else if ( ( strpos( $txt, ']', $this->cur_pos ) - ( $this->cur_pos + $open_length ) ) !== 0 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } } // ----------------------------------------- // No? Then just grab it // ----------------------------------------- else { $open_length += 1; $_option = substr( $txt, $this->cur_pos + $open_length, ( strpos( $txt, ']', $this->cur_pos ) - ( $this->cur_pos + $open_length ) ) ); } } // ----------------------------------------- // [img] != [i] (for example) // ----------------------------------------- else if ( ( strpos( $txt, ']', $this->cur_pos ) - ( $this->cur_pos + $open_length ) ) !== 0 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } // ----------------------------------------- // Grab the new position to jump to // ----------------------------------------- $new_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; // ----------------------------------------- // No closing tag // ----------------------------------------- if ( stripos( $txt, $close_tag, $new_pos ) === false ) { break; } // ----------------------------------------- // Grab the content // ----------------------------------------- $_content = substr( $txt, ( $this->cur_pos + $open_length + strlen( $_option ) + 1 ), ( ( stripos( $txt, $close_tag, $this->cur_pos + $open_length + strlen( $_option ) + 1 ) ) - ( $this->cur_pos + $open_length + strlen( $_option ) + 1 ) ) ); // ----------------------------------------- // If this is a single tag, that's it // @link: // http://forums.invisionpower.com/index.php?autocom=tracker&showissue=11909 // ----------------------------------------- if ( $_content or $_content === '0' ) { if ( $this->_buildOutput( $_content, $_option ? $_option : $_content ) ) { $txt = substr_replace( $txt, $this->_buildOutput( $_content, $_option ? $_option : $_content ), $this->cur_pos, ( stripos( $txt, $close_tag, $this->cur_pos ) + strlen( $close_tag ) - $this->cur_pos ) ); } } else { $txt = substr_replace( $txt, '', $this->cur_pos, ( stripos( $txt, $close_tag, $this->cur_pos ) + strlen( $close_tag ) - $this->cur_pos ) ); } // ----------------------------------------- // And reset current position to end of open tag // ----------------------------------------- $this->cur_pos = stripos( $txt, $open_tag ) ? stripos( $txt, $open_tag ) : $this->cur_pos + 1; // $new_pos; if ( $this->cur_pos > strlen( $txt ) ) { // ----------------------------------------- // Need to reset for next "tag" // ----------------------------------------- $this->cur_pos = 0; break; } } } return $txt; } /** * Build the actual output to show * * @access protected * @param string $content Text * @param string $option Text * @return string Content to replace bbcode with */ protected function _buildOutput( $content, $option ) { //----------------------------------------- // Strip the optional quote delimiters //----------------------------------------- // This shouldn't be intval'd - it is a string exploded on ':' below //$option = intval( $option ); $content = trim( $content ); $content = preg_replace( '#({$content}";
}
}
//----------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------
class bbcode_plugin_quote extends bbcode_parent_main_class
{
/**#@+
* Quote tracking
*
* @access protected
* @var int
*/
protected $quote_open = 0;
protected $quote_closed = 0;
protected $quote_error = 0;
/**#@-*/
/**
* Constructor
*
* @access public
* @param object Registry object
* @param object Parent bbcode class
* @return @e void
*/
public function __construct( ipsRegistry $registry, $_parent='' )
{
$this->currentBbcode = 'quote';
$this->_parent = $_parent;
parent::__construct( $registry, $_parent );
}
/**
* Do the actual replacement
*
* @access protected
* @param string $txt Parsed text from database to be edited
* @return string BBCode content, ready for editing
*/
protected function _replaceText( $txt )
{
/* Clean up some dickery */
$txt = preg_replace( '#\[quote([^\]]+?)?\]\](\s+?)?\[/url\](\s+?)?\[/quote\]#si', '', $txt );
$orig = $txt;
$txt = preg_replace_callback( '#(\[quote([^\]]+?)?\].*\[/quote\])#is' , array( $this, '_parseQuote' ), $txt );
if( $this->error )
{
return $orig;
}
if( stripos( $txt, '[quote' ) !== false OR stripos( $txt, '[/quote]' ) !== false )
{
$this->error = 'quote_mismatch';
return $orig;
}
return $txt;
}
/**
* Build the actual output to show
*
* @access protected
* @param array $options [Optional] Quote options
* @return string Content to replace bbcode with
*/
protected function _buildOutput( $options=array() )
{
//-----------------------------------------
// Build output and return it
//-----------------------------------------
if ( $options['collapse'] )
{
//if ( $options['collapse'] == '1' )
//{
// $options['collapse'] = $this->lang->words['bbc_quote'];
//}
//$eqid = rand( 1, 1000000 );
//$output = <<