* $parser = new parseBbcode( $registry );
*
* # If you wish convert posted text to store in the database
* $parser->parse_smilies = 1;
* $parser->parse_bbcode = 1;
* $parser->parsing_section = 'your section key';
* $bbcode_text = $parser->preDbParse( $_POST['text'] );
*
* # If you wish to display this content
* $parser->parse_html = 0;
* $parser->parse_nl2br = 1;
* $parser->parsing_section = 'your section key';
* $parser->parsing_mgroup = 'member group id of poster';
* $parser->parsing_mgroup_others = 'member other group ids of poster';
* $ready_to_print = $parser->preDisplayParse( $bbcode_text );
*
* # If you wish to convert already converted BBCode back into the raw format
* # (for use in an editing screen, for example) use this:
* $raw_post = $parser->preEditParse( $parsed_text );
*
* # Of course, if you're using the rich text editor (WYSIWYG) then you don't want to uncovert the HTML
* # otherwise the rich text editor will show unparsed BBCode tags, and not formatted HTML. In this case use this:
* $raw_post = $parser->convert_ipb_html_to_html( $parsed_text );
*
*
*/
if ( ! defined( 'IN_IPB' ) )
{
print "
Incorrect access
You cannot access this file directly. If you have recently upgraded, make sure you upgraded all the relevant files.";
exit();
}
class class_bbcode_core
{
/**
* Parse emoticons?
*
* @access public
* @var boolean
*/
public $parse_smilies = true;
/**
* Parse HTML?
* HIGHLY NOT RECOMMENDED IN MOST CASES
*
* @access public
* @var boolean
*/
public $parse_html = false;
/**
* Parse bbcode?
*
* @access public
* @var boolean
*/
public $parse_bbcode = true;
/**
* Strip quotes?
* Strips quotes from the resulting text
*
* @access public
* @var boolean
*/
public $strip_quotes = false;
/**
* Wordwrap cutoff
* Attempts to cut strings as close as possible at these points
*
* @access public
* @var integer
*/
public $parse_wordwrap = 0;
/**
* Auto convert newlines to html line breaks
*
* @access public
* @var boolean
*/
public $parse_nl2br = true;
/**
* Bypass badwords?
*
* @access public
* @var boolean
*/
public $bypass_badwords = false;
/**
* Section keyword for parsing area
*
* @access public
* @var string
*/
public $parsing_section = 'post';
/**
* Group id of poster
*
* @access public
* @var int
*/
public $parsing_mgroup = 0;
/**
* Value of mgroup_others for poster
*
* @access public
* @var string
*/
public $parsing_mgroup_others = '';
/**
* Allow unicode (parses escaped entities to actual entities)
*
* @access public
* @var boolean
*/
public $allow_unicode = false;
/**
* Maximum number of embeded quotes
*
* @access public
* @var integer
*/
public $max_embed_quotes = 15;
/**
* Error code stored
*
* @access public
* @var string
*/
public $error = '';
/**
* Warning code stored (warning is like an error but will not stop parsing execution)
*
* @access public
* @var string
*/
public $warning = '';
/**
* Number of images we've parsed so far
*
* @access protected
* @var integer
*/
protected $image_count = 0;
/**
* Number of emoticons we've parsed so far
*
* @access protected
* @var integer
*/
protected $emoticon_count = 0;
/**
* 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;
/**
* Cache object
*
* @access protected
* @var object
*/
protected $cache;
/**
* BBCodes we should parse.
* Takes into account what section we are in, and our group.
*
* @access protected
* @var array
*/
protected $_bbcodes = array();
/**
* Plugin objects
*
* @access private
* @var array Holds plugin objects
*/
private $plugins = array();
/**
* Current position in the text document
*
* @access private
* @var integer
*/
private $cur_pos = 0;
/**
* Multi-dimensional array of bbcodes not being parsed inside
*
* @access protected
* @var array
*/
protected $noParseStorage = array();
/**
* Identifier for replacement
*
* @access protected
* @var integer
*/
protected $_storedNoParsing = 0;
/**
* Emoticon code
*
* @access private
* @var string
*/
private $_emoCode = '';
/**
* Emoticon image
*
* @access private
* @var string
*/
private $_emoImage = '';
/**
* Constructor
*
* @access public
* @param object Registry object
* @return void
*/
public function __construct( ipsRegistry $registry )
{
/* 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();
//require_once( IPS_ROOT_PATH . 'sources/interfaces/interface_bbcode.php' );
$_NOW = IPSDebug::getMemoryDebugFlag();
$this->initOurBbcodes();
IPSDebug::setMemoryDebugFlag( "BBCodes initialized", $_NOW );
/* Check for emoticons */
if ( ! is_array( $this->caches['emoticons'] ) OR ! count( $this->caches['emoticons'] ) )
{
$this->cache->rebuildCache( 'emoticons', 'global' );
}
}
/**
* Initialize our bbcodes
*
* @access public
* @return void
*/
public function initOurBbcodes()
{
$this->_bbcodes = array();
foreach( $this->cache->getCache('bbcode') as $bbcode )
{
//-----------------------------------------
// BBcode allowed in this section?
//-----------------------------------------
if( $bbcode['bbcode_sections'] != 'all' )
{
$pass = false;
$sections = explode( ',', $bbcode['bbcode_sections'] );
foreach( $sections as $section )
{
if( $section == $this->parsing_section OR $this->parsing_section == 'global' )
{
$pass = true;
break;
}
}
if( !$pass )
{
continue;
}
}
//-----------------------------------------
// Store into the array
//-----------------------------------------
$this->_bbcodes[ $bbcode['bbcode_parse'] == 1 ? 'db' : 'display' ][ $bbcode['bbcode_tag'] ] = $bbcode;
}
}
/**
* Reset bbcode internal pointers
*
* @access protected
* @return void
*/
protected function _resetPointers()
{
$this->error = '';
$this->image_count = 0;
$this->emoticon_count = 0;
}
/**
* Remove session keys from URLs
*
* @access protected
* @param array Array of matches
* @return string Converted text
*/
protected function _bashSession( $matches=array() )
{
$start_tok = str_replace( '&', '&', $matches[1] );
$end_tok = str_replace( '&', '&', $matches[3] );
if ( ( $start_tok == '?' OR $start_tok == '&' ) and $end_tok == '')
{
return '';
}
else if ( $start_tok == '?' and $end_tok == '&' )
{
return '?';
}
else if ( $start_tok == '&' and $end_tok == '&' )
{
return "&";
}
else
{
return $start_tok . $end_tok;
}
}
/**
* Check against XSS
*
* @access public
* @param string Original string
* @param boolean Fix script HTML tags
* @return string "Cleaned" text
*/
public function checkXss( $txt='', $fixScript=false )
{
//-----------------------------------------
// Opening script tags...
// Check for spaces and new lines...
//-----------------------------------------
if ( $fixScript )
{
$txt = preg_replace( "#<(\s+?)?s(\s+?)?c(\s+?)?r(\s+?)?i(\s+?)?p(\s+?)?t#is" , "<script" , $txt );
$txt = preg_replace( "#<(\s+?)?/(\s+?)?s(\s+?)?c(\s+?)?r(\s+?)?i(\s+?)?p(\s+?)?t#is", "</script", $txt );
}
//-----------------------------------------
// Here we can do some generic checking for XSS
// This should not be considered fool proof, though can provide
// a centralized point for maintenance and checking
//-----------------------------------------
$txt = preg_replace( "/(j)avascript/i" , "\\1avascript", $txt );
//$txt = str_ireplace( "alert" , "alert" , $txt );
$txt = str_ireplace( "behavior" , "behavior" , $txt );
$txt = preg_replace( "/(e)((\/\*.*?\*\/)*)x((\/\*.*?\*\/)*)p((\/\*.*?\*\/)*)r((\/\*.*?\*\/)*)e((\/\*.*?\*\/)*)s((\/\*.*?\*\/)*)s((\/\*.*?\*\/)*)i((\/\*.*?\*\/)*)o((\/\*.*?\*\/)*)n/is" , "\\1xpression" , $txt );
$txt = preg_replace( "/(e)((\\\|\)*)x((\\\|\)*)p((\\\|\)*)r((\\\|\)*)e((\\\|\)*)s((\\\|\)*)s((\\\|\)*)i((\\\|\)*)o((\\\|\)*)n/is" , "\\1xpression" , $txt );
$txt = preg_replace( "/m((\\\|\)*)o((\\\|\)*)z((\\\|\)*)\-((\\\|\)*)b((\\\|\)*)i((\\\|\)*)n((\\\|\)*)d((\\\|\)*)i((\\\|\)*)n((\\\|\)*)g/is" , "moz-binding" , $txt );
$txt = str_ireplace( "about:" , "about:" , $txt );
$txt = str_ireplace( "bypass_badwords == 1 )
{
return $text;
}
$temp_text = $text;
//-----------------------------------------
// Convert back entities
//-----------------------------------------
for( $i = 65; $i <= 90; $i++ )
{
$text = str_replace( "" . $i . ";", chr($i), $text );
}
for( $i = 97; $i <= 122; $i++ )
{
$text = str_replace( "" . $i . ";", chr($i), $text );
}
//-----------------------------------------
// Go all loopy
//-----------------------------------------
if ( is_array( $this->cache->getCache('badwords') ) )
{
if ( count($this->cache->getCache('badwords')) > 0 )
{
foreach( $this->cache->getCache('badwords') as $r )
{
$replace = $r['swop'] ? $r['swop'] : '######';
$r['type'] = preg_quote( $r['type'], "/" );
if ( $r['m_exact'] )
{
$text = preg_replace( "/(^|\b|\s)" . $r['type'] . "(\b|!|\?|\.|,|$)/i", "\\1{$replace}\\2", $text );
}
else
{
//----------------------------
// 'ass' in 'class' kills css
//----------------------------
if( $r['type'] == 'ass' )
{
$text = preg_replace( "/(?' );
//-----------------------------------------
// Characters that will reset the wordwrap
//-----------------------------------------
$resetters = array( ' ', "\n", "\t" );
//-----------------------------------------
// Other init...
//-----------------------------------------
$inTag = false;
$totalLength = strlen( $txt );
$curPos = 0;
$charsSince = 0;
//-----------------------------------------
// Loop over each char
//-----------------------------------------
while( $curPos < $totalLength )
{
$curPos++;
//-----------------------------------------
// We within a tag?
//-----------------------------------------
if( isset( $txt{$curPos} ) && $txt{$curPos} == '<' )
{
$inTag = true;
$charsSince++;
continue;
}
//-----------------------------------------
// We out of a tag?
//-----------------------------------------
if( isset( $txt{$curPos} ) && $txt{$curPos} == '>' )
{
$inTag = false;
}
//-----------------------------------------
// Within a tag...
//-----------------------------------------
if( $inTag )
{
//-----------------------------------------
// This a tag we're skipping?
//-----------------------------------------
if( in_array( substr( $txt, $curPos, (strpos( $txt, ' ', $curPos ) - $curPos) ), $noBreak ) )
{
$curTag = substr( $txt, $curPos, (strpos( $txt, ' ', $curPos ) - $curPos) );
$charsSince = 0;
$curPos = stripos( $txt, '' . $curTag . '>', $curPos );
continue;
}
else if( in_array( substr( $txt, $curPos, (strpos( $txt, '>', $curPos ) - $curPos) ), $noBreak ) )
{
$curTag = substr( $txt, $curPos, (strpos( $txt, '>', $curPos ) - $curPos) );
$charsSince = 0;
$curPos = stripos( $txt, '' . $curTag . '>', $curPos );
continue;
}
//-----------------------------------------
// No, skip tag, but increment chars by 1
//-----------------------------------------
else
{
$charsSince++;
$curPos = strpos( $txt, '>', $curPos ) - 1;
continue;
}
}
//-----------------------------------------
// Reset the chars since if necessary
//-----------------------------------------
if( isset( $txt{$curPos} ) && in_array( $txt{$curPos}, $resetters ) )
{
$charsSince = 0;
continue;
}
//-----------------------------------------
// Time to break?
//-----------------------------------------
if( $charsSince >= $chrs )
{
if( in_array( $txt{$curPos}, $breakAt ) )
{
$txt = substr_replace( $txt, $replace, $curPos + 1, 0 );
$curPos = $curPos + 1 + strlen($replace);
$charsSince = 0;
continue;
}
}
//-----------------------------------------
// One more char since last break
//-----------------------------------------
$charsSince++;
}
return $txt;
}
/**
* Makes data for quote strings "safe"
*
* @access public
* @param string Raw text
* @return string Converted text
*/
public function makeQuoteSafe( $txt='' )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$begin = '';
$end = '';
//-----------------------------------------
// Come via preg_replace_callback?
//-----------------------------------------
if ( is_array( $txt ) )
{
$begin = $txt[1];
$end = $txt[3];
$txt = $txt[2];
}
//-----------------------------------------
// Sort name
//-----------------------------------------
$txt = str_replace( "+", "+" , $txt );
$txt = str_replace( "-", "-" , $txt );
$txt = str_replace( ":", ":" , $txt );
$txt = str_replace( "[", "[" , $txt );
$txt = str_replace( "]", "]" , $txt );
$txt = str_replace( ")", ")" , $txt );
$txt = str_replace( "(", "(" , $txt );
$txt = str_replace( "'", "'" , $txt );
return $begin . $txt . $end;
}
/**
* Removes bbcode tag + contents within the tag
*
* @access public
* @param string Tag to strip
* @param string Raw text
* @return string Converted text
*/
public function stripBbcode( $tag, $txt )
{
//-----------------------------------------
// Protect against endless loops
//-----------------------------------------
static $iteration = array();
if( array_key_exists( $tag, $iteration ) AND $iteration[ $tag ] > 2000 )
{
return $txt;
}
$iteration[ $tag ]++;
// Got Quotes (tm)? or any tag really
if( stripos( $txt, '[' . $tag ) !== false )
{
//-----------------------------------------
// First grab start and end positions
//-----------------------------------------
$start_position = stripos( $txt, '[' . $tag );
$end_position = stripos( $txt, '[/' . $tag . ']', $start_position );
//-----------------------------------------
// If no end position or start position,
// we have a mismatched bbcode...return
//-----------------------------------------
if( $start_position === false OR $end_position === false )
{
return $txt;
}
//-----------------------------------------
// Then extract the content inside the bbcode
//-----------------------------------------
$inner_content = substr( $txt, stripos( $txt, ']', $start_position ) + 1, $end_position - (stripos( $txt, ']', $start_position ) + 1) );
//-----------------------------------------
// Is this bbcode nested in the inner content
//-----------------------------------------
$extra_closers = substr_count( $inner_content, '[' . $tag );
//-----------------------------------------
// If so we need to move to the last ending tag
//-----------------------------------------
if( $extra_closers > 0 )
{
for( $done=0; $done < $extra_closers; $done++ )
{
$end_position = stripos( $txt, '[/' . $tag . ']', $end_position + 1 );
}
}
//-----------------------------------------
// Get rid of the bbcode opening + content + closing
//-----------------------------------------
$txt = substr_replace( $txt, '', $start_position, $end_position - $start_position + strlen('[/' . $tag . ']') );
//-----------------------------------------
// And parse recursively
//-----------------------------------------
return $this->stripBbcode( $tag, trim($txt) );
}
else
{
return $txt;
}
}
/**
* Remove ALL tags
*
* @access public
* @param string Raw text
* @param boolean Whether or not to run through pre-edit-parse first
* @param boolean Check "strip from search" option
* @return string Converted text
* @todo [Future] Can this (and should this) be done without regex?
*/
public function stripAllTags( $txt, $pre_edit_parse=true, $only_search=true )
{
if( $pre_edit_parse )
{
$txt = $this->preEditParse( $txt );
}
$txt = $this->stripBbcode( 'quote', $txt );
foreach( $this->cache->getCache('bbcode') as $bbcode )
{
if( $only_search && isset( $bbcode['bbcode_strip_search'] ) && $bbcode['bbcode_strip_search'] )
{
$txt = $this->stripBbcode( $bbcode['bbcode_tag'], $txt );
}
else
{
//$txt = preg_replace( "#\[{$bbcode['bbcode_tag']}\](.+?)\[/{$bbcode['bbcode_tag']}\]#is", "\\1 ", $txt );
$txt = preg_replace( "#\[{$bbcode['bbcode_tag']}=(.+?)\](.+?)\[/{$bbcode['bbcode_tag']}\]#is", "\\2 ", $txt );
$txt = str_replace( "[{$bbcode['bbcode_tag']}]", '', $txt );
$txt = str_replace( "[/{$bbcode['bbcode_tag']}]", '', $txt );
}
//-----------------------------------------
// Strip single bbcodes properly
//-----------------------------------------
if( $bbcode['bbcode_single_tag'] )
{
$regex = $bbcode['bbcode_single_tag'];
//-----------------------------------------
// If this has option, adjust regex
//-----------------------------------------
if( $bbcode['bbcode_useoption'] )
{
$regex .= '=([^\]]+)';
}
$txt = preg_replace( "#\[{$regex}\]#is", " ", $txt );
}
}
$txt = preg_replace( "#\[(.+?)\]#is", " ", $txt );
$txt = preg_replace( "#\[(.+?)=(.+?)\]#is", " ", $txt );
$txt = preg_replace( "#\[/(.+?)\]#is", " ", $txt );
$txt = preg_replace( "#\[attachment=(.+?)\]#is", " ", $txt );
$txt = str_replace( '[*]', '', $txt );
return $txt;
}
/**
* Convert special IPB HTML to normal HTML
*
* @access public
* @param string Raw text
* @return string Converted text
*/
public function convertForRTE( $t="" )
{
$this->cache->updateCacheWithoutSaving( '_tmp_bbcode_isForRte', true );
//-----------------------------------------
// IE handles RTE BR tags differently
//-----------------------------------------
$t = str_ireplace( " ", " ", $t );
//-----------------------------------------
// Get out codeboxes first...
//-----------------------------------------
$_codeboxes = array();
$_increment = 0;
while( preg_match( "/\[code\](.+?)\[\/code\]/is", $t, $matches ) )
{
$_codeboxes[ $_increment ] = $matches[0];
$t = str_replace( $matches[0], "__CODEBOX_{$_increment}__", $t );
$_increment++;
}
//-----------------------------------------
// Prevent from parsing inside code
//-----------------------------------------
$t = $this->_storeNonParsed( $t, 'display' );
//-----------------------------------------
// Now we'll "fix" the replacement html...
//-----------------------------------------
$_tags = array( 'url', 'left', 'right', 'center', 'indent', 'b', 'i', 'u', 'strike', 'sub', 'sup', 'url', 'img', 'background', 'color', 'size', 'font', 'list' );
foreach( $this->_bbcodes['display'] as $_tag => $_bbcode )
{
if( !in_array( $_tag, $_tags ) )
{
unset( $this->_bbcodes['display'][ $_tag ] );
}
}
//-----------------------------------------
// Parse bbcodes for RTE
//-----------------------------------------
$this->_bbcodes['display']['left']['bbcode_replace'] = '