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();
foreach( $this->cache->getCache('bbcode') as $bbcode )
{
if( $bbcode['bbcode_tag'] == $this->currentBbcode )
{
$this->_bbcode = $bbcode;
break;
}
}
}
/**
* 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 )
{
$this->cur_pos = 0;
$this->end_pos = 0;
$this->error = '';
if( $this->_bbcode['bbcode_parse'] == 1 )
{
return $this->_replaceText( $txt );
}
else
{
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 = '';
if( $this->_bbcode['bbcode_parse'] == 2 )
{
return $this->_replaceText( $txt );
}
else
{
return $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_img extends bbcode_parent_class implements bbcodePlugin
{
/**
* Constructor
*
* @access public
* @param object Registry object
* @return void
*/
public function __construct( ipsRegistry $registry )
{
$this->currentBbcode = 'img';
parent::__construct( $registry );
}
/**
* 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 parent::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 )
{
return parent::preDisplayParse( $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 )
{
if ( !$this->settings['allow_images'] )
{
return $txt;
}
$_tags = $this->_retrieveTags();
foreach( $_tags as $_tag )
{
//-----------------------------------------
// Start building open/close tag
//-----------------------------------------
$open_tag = '[' . $_tag . ']';
$close_tag = '[/' . $_tag . ']';
//-----------------------------------------
// Member not viewing images?
//-----------------------------------------
if( !$this->memberData[ 'view_img' ] )
{
$txt = str_replace( $open_tag, '', $txt );
$txt = str_replace( $close_tag, '', $txt );
return $txt;
}
//-----------------------------------------
// 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 > 2000 )
{
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 )
{
$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 private
* @param array $content Image URL to link to
* @return string Content to replace bbcode with
*/
private 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;
}
}
}
//-----------------------------------------
// Dynamic image?
// We removed this setting - no longer used
//-----------------------------------------
/*if( !$this->settings['allow_dynamic_img'] )
{
$dynamic = array( '?', '&' );
foreach( $dynamic as $indicator )
{
if( strpos( $content, $indicator ) !== false )
{
$this->error = 'no_dynamic';
return $content;
}
}
}*/
//-----------------------------------------
// Allowed type?
// We removed this setting - no longer used
//-----------------------------------------
if ( $this->settings['img_ext'] )
{
$path = @parse_url( $content, PHP_URL_PATH );
$pieces = explode( '.', $path );
$ext = array_pop( $pieces );
$ext = strtolower( $ext );
if( !in_array( $ext, explode( ',', str_replace( '.', '', 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 "";
}
}
//----------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------
class bbcode_quote extends bbcode_parent_class implements bbcodePlugin
{
/**
* Constructor
*
* @access public
* @param object Registry object
* @return void
*/
public function __construct( ipsRegistry $registry )
{
$this->currentBbcode = 'quote';
parent::__construct( $registry );
}
/**
* 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 )
{
$this->cache->updateCacheWithoutSaving( '_tmp_bbcode_quotes', 0 );
return parent::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 )
{
$this->cache->updateCacheWithoutSaving( '_tmp_bbcode_quotes', 0 );
return parent::preDisplayParse( $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 )
{
$_tags = $this->_retrieveTags();
foreach( $_tags as $_tag )
{
//-----------------------------------------
// Determine open and close tags
//-----------------------------------------
$open_tag = '[' . $_tag;
$close_tag = '[/' . $_tag . ']';
//-----------------------------------------
// Ok, quotes suck A LOT
// We have to first match the CLOSE tag, unlike other bbcode
// Then we back-track to find the matching opening tag
// Do the replacement, and repeat....
//-----------------------------------------
while( ( $this->end_pos = stripos( $txt, $close_tag, $this->end_pos ) ) !== false )
{
//-----------------------------------------
// Ok at this point, we have a closing quote tag
// Set start position to end position point
//-----------------------------------------
$this->cur_pos = $this->end_pos;
//-----------------------------------------
// We are at 0 or above still...
//-----------------------------------------
while( $this->cur_pos > 0 )
{
//-----------------------------------------
// Loop over characters moving backwards 1 by 1
//-----------------------------------------
$this->cur_pos = $this->cur_pos - 1;
//-----------------------------------------
// Shouldn't hit this, but if we do we're done
//-----------------------------------------
if( $this->cur_pos < 0 )
{
break;
}
//-----------------------------------------
// Have we finally hit the first preceeding open tag?
//-----------------------------------------
if( stripos( $txt, $open_tag, $this->cur_pos ) === $this->cur_pos )
{
//-----------------------------------------
// Start figuring out open tag length
//-----------------------------------------
$open_length = strlen($open_tag);
//-----------------------------------------
// Extract the options (like surgery)
//-----------------------------------------
$_option = '';
$quoteOptions = array();
//-----------------------------------------
// 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)) );
$quoteOptions = $this->_extractSurgicallyTehOptions( $_option );
}
//-----------------------------------------
// If not, [u] != [url] (for example)
//-----------------------------------------
else if( (strpos( $txt, ']', $this->cur_pos ) - ( $this->cur_pos + $open_length )) !== 0 )
{
continue;
}
//-----------------------------------------
// Otherwise replace out the content too
//-----------------------------------------
$_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)) );
//-----------------------------------------
// Turn attachments into links
// Prevents em from breaking on other pages
//-----------------------------------------
preg_match_all( "#\[attachment=(.+?):(.+?)\]#", $_content, $matches );
if( is_array( $matches[1] ) && count( $matches[1] ) )
{
foreach( $matches[1] as $idx => $attach_id )
{
$_content = str_replace( "[attachment={$attach_id}:{$matches[2][$idx]}]", $this->registry->getClass('output')->getReplacement('post_attach_link') . " {$matches[2][$idx]}", $_content );
}
}
//-----------------------------------------
// Trim off leading newlines / brs, etc
//-----------------------------------------
$_content = IPSText::br2nl( $_content );
$_content = trim( $_content );
$_content = nl2br( $_content );
//-----------------------------------------
// And replace...
//-----------------------------------------
$txt = substr_replace( $txt, $this->_buildOutput( $_content, $quoteOptions ? $quoteOptions : '' ), $this->cur_pos, (stripos( $txt, $close_tag, $this->cur_pos ) + strlen($close_tag) - $this->cur_pos) );
//-----------------------------------------
// Now we break since we've done the inner replacement
//-----------------------------------------
break;
}
}
//-----------------------------------------
// And reset end position to one char further
//-----------------------------------------
$this->end_pos += 1;
//-----------------------------------------
// If end_pos is greater than length of text we're done
//-----------------------------------------
if( $this->end_pos > strlen($txt) )
{
//-----------------------------------------
// Need to reset for next "tag"
//-----------------------------------------
$this->end_pos = 0;
break;
}
}
}
return $txt;
}
/**
* Raw options string
*
* @access private
* @param string $string The options submitted with the post as a string
* @return array Key => value Option pairs
*/
private function _extractSurgicallyTehOptions( $options='' )
{
if( !$options )
{
return array();
}
/**
* Strip tags removes any added tags
* @see http://community.invisionpower.com/tracker/issue-19960-problem-when-tags-is-put-inside-quote-starter-tag/
*/
$options = str_replace( ''', "'", strip_tags($options) );
$options = str_replace( '"', '"', $options );
$finalOpts = array();
// Need to push through string, pulling out keys/options
$pos = 0;
$options = trim($options);
$key = '';
$value = '';
$inKey = true;
$inValue = false;
while( $pos < strlen($options) )
{
if( $options{$pos} == ' ' AND !$inKey AND !$inValue )
{
$key = '';
$value = '';
$inKey = true;
}
else if( $options{$pos} == "'" OR $options{$pos} == '"' )
{
if( $inKey )
{
$inKey = false;
$inValue = true;
}
else
{
$inValue = false;
$finalOpts[ trim($key) ] = $value;
}
}
else
{
if( $inKey )
{
if( $options{$pos} != '=' )
{
$key .= $options{$pos};
}
}
else if( $inValue )
{
$value .= $options{$pos};
}
}
$pos++;
}
return $finalOpts;
}
/**
* Build the actual output to show
*
* @access private
* @param string $content This is the contents of the quote
* @param array $options [Optional] Quote options
* @return string Content to replace bbcode with
*/
private function _buildOutput( $content, $options=array() )
{
//-----------------------------------------
// Too many quotes?
//-----------------------------------------
$existing = $this->cache->getCache('_tmp_bbcode_quotes');
$existing = intval($existing) + 1;
if ( $this->settings['max_quotes_per_post'] )
{
if ($existing > $this->settings['max_quotes_per_post'])
{
$this->error = 'too_many_quotes';
return $content;
}
}
$this->cache->updateCacheWithoutSaving( '_tmp_bbcode_quotes', $existing );
//-----------------------------------------
// Build output and return it
//-----------------------------------------
$output = "