* Invision Power Services * IP.Board v3.2.3 * Custom bbcode plugin interfaces * Last Updated: $Date: 2011-09-23 09:03:29 -0400 (Fri, 23 Sep 2011) $ * * * @author $author$ * @copyright (c) 2001 - 2009 Invision Power Services, Inc. * @license This is NULLED! * @package IP.Board * @link http://hatynka.in * @version $Rev: 9543 $ */ interface bbcodePlugin { /** * Method that is run before the content is stored in the database * You are responsible for ensuring you mark the replaced text appropriately so that you * are able to unparse it, if you wish to have bbcode parsed on save * * @access public * @param string $txt BBCode text from submission to be stored in database * @return string Formatted content, ready for display */ public function preDbParse( $txt ); /** * Method that is run before the content is displayed to the user * This is the safest method of parsing, as the original submitted text is left in tact. * No markers are necessary if you use parse on display. * * @access public * @param string $txt BBCode/parsed text from database to be displayed * @return string Formatted content, ready for display */ public function preDisplayParse( $txt ); /** * Method that is run before the content is placed into an editor for editing * If you use "parse on display" you may simply return $txt * * @access public * @param string $txt Parsed text from database to be edited * @return string BBCode content, ready for editing */ public function preEditParse( $txt ); } /** *
* Invision Power Services * IP.Board v3.2.3 * BBCode parser: default custom bbcodes: img, quote, list, size, member, media, url, snapback * Last Updated: $Date: 2011-09-23 09:03:29 -0400 (Fri, 23 Sep 2011) $ ** * @author $author$ * @copyright (c) 2001 - 2009 Invision Power Services, Inc. * @license This is NULLED! * @package IP.Board * @link http://hatynka.in * @version $Rev: 9543 $ * */ class bbcode_parent_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; /** * Core bbcode class * * @access protected * @var object */ protected $_parentBBcode; /** * Current bbcode * * @access protected * @var string */ protected $currentBbcode; /** * 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 ]; } /** * Method that is run before the content is stored in the database * You are responsible for ensuring you mark the replaced text appropriately so that you * are able to unparse it, if you wish to have bbcode parsed on save * * @access public * @param string $txt BBCode text from submission to be stored in database * @return string Formatted content, ready for display */ public function preDbParse( $txt ) { return $txt; } /** * Method that is run before the content is displayed to the user * This is the safest method of parsing, as the original submitted text is left in tact. * No markers are necessary if you use parse on display. * * @access public * @param string $txt BBCode/parsed text from database to be displayed * @return string Formatted content, ready for display */ public function preDisplayParse( $txt ) { $this->cur_pos = 0; $this->end_pos = 0; $this->error = ''; return $this->_replaceText( $txt ); } /** * Method that is run before the content is placed into an editor for editing * If you use "parse on display" you may simply return $txt * * @access public * @param string $txt Parsed text from database to be edited * @return string BBCode content, ready for editing */ public function preEditParse( $txt ) { $this->error = ''; return $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_sharedmedia extends bbcode_parent_class implements bbcodePlugin { /** * 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=null ) { $this->currentBbcode = 'sharedmedia'; parent::__construct( $registry, $_parent ); } /** * We use preDbParse 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 preDbParse( $txt ) { $_ignored = preg_replace_callback( '#(\[sharedmedia=(.+?):(.+?):(.+?)\])#is' , array( $this, '_checkPostingPermissions' ), $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 !$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; } /** * Do the actual replacement * * @param string $txt Parsed text from database to be edited * @return @e string */ protected function _replaceText( $txt ) { $txt = preg_replace_callback( '#(\[sharedmedia=(.+?):(.+?):(.+?)\])#is' , array( $this, '_parseMedia' ), $txt ); 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 !$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_img extends bbcode_parent_class implements bbcodePlugin { /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent=null ) { $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(); 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'); $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 //----------------------------------------- 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? //----------------------------------------- if ( $this->settings['img_ext'] ) { $path = @parse_url( html_entity_decode( $content ), PHP_URL_PATH ); $pieces = explode( '.', $path ); $ext = array_pop( $pieces ); $ext = mb_strtolower( $ext ); if( !in_array( $ext, explode( ',', str_replace( '.', '', mb_strtolower($this->settings['img_ext']) ) ) ) ) { $this->error = 'invalid_ext'; return $content; } } //----------------------------------------- // URL filtering? //----------------------------------------- if ( $this->settings['ipb_use_url_filter'] ) { $list_type = $this->settings['ipb_url_filter_option'] == "black" ? "blacklist" : "whitelist"; if( $this->settings['ipb_url_' . $list_type ] ) { $list_values = array(); $list_values = explode( "\n", str_replace( "\r", "", $this->settings['ipb_url_' . $list_type ] ) ); if( $list_type == 'whitelist' ) { $list_values[] = "http://{$_SERVER['HTTP_HOST']}/*"; } if ( count( $list_values ) ) { $good_url = 0; foreach( $list_values as $my_url ) { if( !trim($my_url) ) { continue; } $my_url = preg_quote( $my_url, '/' ); $my_url = str_replace( '\*', "(.*?)", $my_url ); if ( $list_type == "blacklist" ) { if( preg_match( '/' . $my_url . '/i', $content ) ) { $this->error = 'domain_not_allowed'; return $content; } } else { if ( preg_match( '/' . $my_url . '/i', $content ) ) { $good_url = 1; } } } if ( ! $good_url AND $list_type == "whitelist" ) { $this->error = 'domain_not_allowed'; return $content; } } } } return "
"; $snapback = ''; if( $options['post'] ) { $snapback = "" . $this->registry->output->getReplacement( 'snapback' ) . ""; } if( $options['name'] OR $options['date'] OR $options['timestamp'] ) { // sort timestamp if ( $options['timestamp'] AND strlen( $options['timestamp'] ) == 10 AND ( intval($options['timestamp']) == $options['timestamp'] ) ) { if ( $this->settings['cc_on'] ) { /* Add for parsing */ $options['date'] = ''; } else { $options['date'] = $this->registry->getClass('class_localization')->getDate( $options['timestamp'], 'LONG' ); } } if( $options['name'] AND $options['date'] ) { $output .= $snapback . sprintf( $this->lang->words['bbc_full_cite'], $options['name'], $options['date'] ) ; } else if( $options['name'] ) { $output .= $snapback . sprintf( $this->lang->words['bbc_name_cite'], $options['name'] ) ; } else if( $options['date'] ) { $output .= $snapback . sprintf( $this->lang->words['bbc_date_cite'], $options['date'] ) ; } } else { $output .= $snapback . $this->lang->words['bbc_quote']; } $output .= "
"; $output .= 'quote_open = 0; $this->quote_closed = 0; $this->quote_error = 0; //----------------------------------------- // Make sure we don't have too many embedded //----------------------------------------- if ( $this->settings['max_quotes_per_post'] ) { if ( substr_count( mb_strtolower($txt), '[quote' ) > $this->settings['max_quotes_per_post'] ) { $this->error = 'too_many_quotes'; return $txt; } } //----------------------------------------- // Fix char 173 //----------------------------------------- // This breaks Chinese and probably other MB languages // http://community.invisionpower.com/tracker/issue-26289-multibyte-issue-with-quotes //$txt = str_replace( chr(173).']', ']', $txt ); //----------------------------------------- // Trim the quote content //----------------------------------------- $txt = preg_replace_callback( '#\[quote([^\]]+?)?\](.+?)\[/quote\]#si', array( $this, '_trimQuote' ), $txt ); //----------------------------------------- // Clean usernames with brackets and quotes //----------------------------------------- $txt = preg_replace_callback( '#(name=(?:&\#39;|"|\'|\"))(.+?)(&\#39;|"|\'|\")#si', array( $this, '_makeQuoteSafe' ), $txt ); $txt = preg_replace_callback( '#name=(&\#39;|"|\'|\")(.+?)\]#si', array( $this, '_makeNameSafe' ), $txt ); //----------------------------------------- // Replace out end tag //----------------------------------------- $txt = str_ireplace( "[/quote]", "
{$content}
"; } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_size extends bbcode_parent_class implements bbcodePlugin { /** * 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=null ) { $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_snapback extends bbcode_parent_class implements bbcodePlugin { /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent=null ) { $this->currentBbcode = 'snapback'; 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; //----------------------------------------- // Grab content //----------------------------------------- $close_tag = '[/' . $_tag . ']'; $_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 ) { $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 string $content Snapback ID * @return string Content to replace bbcode with */ protected function _buildOutput( $content ) { //----------------------------------------- // Prevent XSS in URL //----------------------------------------- $content = intval($content); if( !$content ) { return ''; } return "" . $this->registry->output->getReplacement( 'snapback' ) . ""; } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_member extends bbcode_parent_class implements bbcodePlugin { /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent=null ) { $this->currentBbcode = 'member'; 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))) ); $_length = strlen( $_option ); /* @link http://community.invisionpower.com/tracker/issue-24470-bbcodemember-not-working-when-in-the-name/ */ if( substr( $_option, 0, 1 ) == "'" OR substr( $_option, 0, 1 ) == '"' ) { $_option = substr( $_option, 1 ); } if( substr( $_option, 0, 5 ) == "'" ) { $_option = substr( $_option, 5 ); } if( substr( $_option, 0, 6 ) == """ ) { $_option = substr( $_option, 6 ); } if( substr( $_option, -1 ) == "'" OR substr( $_option, -1 ) == '"' ) { $_option = substr( $_option, 0, -1 ); } if( substr( $_option, -5, 5 ) == "'" ) { $_option = substr( $_option, 0, -5 ); } if( substr( $_option, -6, 6 ) == """ ) { $_option = substr( $_option, 0, -6 ); } $existing = $this->cache->getCache( '_tmp_bbcode_members', false ); $existing = is_array($existing) ? $existing : array(); if( isset($existing[ $_option ]) ) { $_content = $this->_buildOutput( $existing[ $_option ] ); } else { $member = IPSMember::load( $_option, 'core', 'displayname' ); $existing[ $_option ] = array( 'member_id' => $member['member_id'], 'members_display_name' => $member['members_display_name'], 'members_seo_name' => $member['members_seo_name'] ); $this->cache->updateCacheWithoutSaving( '_tmp_bbcode_members', $existing ); if( $member['members_display_name'] ) { $_content = $this->_buildOutput( $existing[ $_option ] ); } } //----------------------------------------- // If this is a single tag, that's it //----------------------------------------- if( $_content ) { $txt = substr_replace( $txt, $_content, $this->cur_pos, (strlen($open_tag) + $_length + 1) ); } else { $txt = substr_replace( $txt, '', $this->cur_pos, (strlen($open_tag) + $_length + 1) ); } //----------------------------------------- // 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 $member Member id and display name * @return string Content to replace bbcode with */ protected function _buildOutput( $member ) { return "{$member['members_display_name']}"; } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_media extends bbcode_parent_class implements bbcodePlugin { /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent=null ) { $this->currentBbcode = 'media'; 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/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; $_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 ); 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 ) - ($this->cur_pos + $open_length + strlen($_option) + 1)) ); if ( strpos( $_content, "(.+?)\<\/a\>/i', "\\1", $_content ); } /* Make sure we've not embedded [media] */ if ( stristr( $_content, '[media]' ) ) { return $txt; } //----------------------------------------- // If this is a single tag, that's it //----------------------------------------- if( $_content ) { $txt = substr_replace( $txt, $this->_buildOutput( $_content, $_option ? $_option : '' ), $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 * @param string $option [Optional] Dimension options (width,height) * @return string Content to replace bbcode with */ protected function _buildOutput( $content, $option='' ) { //----------------------------------------- // Too many media files? //----------------------------------------- $existing = $this->cache->getCache('_tmp_bbcode_media'); $existing = intval($existing) + 1; if ( $this->settings['max_media_files'] ) { if ( $existing > $this->settings['max_media_files'] ) { $this->error = 'too_many_media'; return $content; } } $this->cache->updateCacheWithoutSaving( '_tmp_bbcode_media', $existing ); //----------------------------------------- // XSS check //----------------------------------------- if ( ! IPSText::xssCheckUrl( $content ) ) { return $content; } //----------------------------------------- // Loop through media tags and extract //----------------------------------------- $media = $this->cache->getCache( 'mediatag' ); $original = $content; if( is_array($media) AND count($media) ) { foreach( $media as $type => $r ) { if( preg_match( "#^" . $r['match'] . "$#is", $content, $matches ) ) { $content = preg_replace( "#^" . $r['match'] . "$#is", $r['replace'], $content ); if( $option ) { list( $width, $height ) = explode( ',', str_replace( array( '"', "'", ''', '"' ), '', $option ) ); if( $width AND $height ) { if ( $width > $this->settings['max_w_flash'] ) { $this->error = 'flash_too_big'; return $original; } if ( $height > $this->settings['max_h_flash'] ) { $this->error = 'flash_too_big'; return $original; } $content = str_replace( '{width}', "width='{$width}'", $content ); $content = str_replace( '{height}', "height='{$height}'", $content ); } } else { $content = str_replace( '{width}', "", $content ); $content = str_replace( '{height}', "", $content ); } $content = str_replace( '{base_url}', $this->settings['board_url'] . '/index.php?', $content ); $content = str_replace( '{board_url}', $this->settings['board_url'], $content ); $content = str_replace( '{image_url}', $this->settings['img_url'], $content ); preg_match( '/\{text\.(.+?)\}/i', $content, $matches ); if( is_array($matches) AND count($matches) ) { $content = str_replace( $matches[0], $this->lang->words[ $matches[1] ], $content ); } } } } return $content; } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_url extends bbcode_parent_class implements bbcodePlugin { /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent=null ) { $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(); 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, '[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; } } } //----------------------------------------- // URL filtering? //----------------------------------------- if ( $this->settings['ipb_use_url_filter'] ) { $list_type = $this->settings['ipb_url_filter_option'] == "black" ? "blacklist" : "whitelist"; if( $this->settings['ipb_url_' . $list_type ] ) { $list_values = array(); $list_values = explode( "\n", str_replace( "\r", "", $this->settings['ipb_url_' . $list_type ] ) ); if ( $list_type == "whitelist" ) { $list_values[] = "http://{$_SERVER['HTTP_HOST']}/*"; } if ( count( $list_values ) ) { $good_url = 0; foreach( $list_values as $my_url ) { if( !trim($my_url) ) { continue; } $my_url = preg_quote( $my_url, '/' ); $my_url = str_replace( '\*', "(.*?)", $my_url ); if ( $list_type == "blacklist" ) { if( preg_match( '/' . $my_url . '/i', $option ) ) { $this->warning = 'domain_not_allowed'; return $content; } } else { if ( preg_match( '/' . $my_url . '/i', $option ) ) { $good_url = 1; } } } if ( ! $good_url AND $list_type == "whitelist" ) { $this->warning = 'domain_not_allowed'; return $content; } } } } //----------------------------------------- // Let's remove any nested links.. //----------------------------------------- $content = preg_replace( '/(.+?)<\/a>/is', "\\3", $content ); //----------------------------------------- // Need to "truncate" the "content" to ~35 // EDIT: but only if it's the same as content //----------------------------------------- if( ( empty( $this->settings['__noTruncateUrl'] ) ) AND $content == $option AND IPSText::mbstrlen($content) > 38 ) { $content = htmlspecialchars( IPSText::mbsubstr( html_entity_decode( $content ), 0, 20 ) ) . '...' . htmlspecialchars( IPSText::mbsubstr( html_entity_decode( $content ), -15 ) ); } //----------------------------------------- // 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 ) . "'"; } return "{$content}"; } }