AnonSec Shell
Server IP : 213.186.33.4  /  Your IP : 216.73.216.193
Web Server : Apache
System : Linux webm006.cluster103.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64
User : awebpaca ( 35430)
PHP Version : 8.5.0
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /home/awebpaca/blog/libraries/kunena/external/nbbc/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME ]     

Current File : /home/awebpaca/blog/libraries/kunena/external/nbbc/nbbc.php
<?php
/*
This is a compressed copy of NBBC. Do not edit!

Copyright (c) 2008-9, the Phantom Inker.  All rights reserved.
Portions Copyright (c) 2004-2008 AddedBytes.com

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in
  the documentation and/or other materials provided with the
  distribution.

THIS SOFTWARE IS PROVIDED BY THE PHANTOM INKER "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

define("BBCODE_VERSION", "1.4.5");
define("BBCODE_RELEASE", "2010-09-17");
define("BBCODE_VERBATIM", 2);
define("BBCODE_REQUIRED", 1);
define("BBCODE_OPTIONAL", 0);
define("BBCODE_PROHIBIT", -1);
define("BBCODE_CHECK", 1);
define("BBCODE_OUTPUT", 2);
define("BBCODE_ENDTAG", 5);
define("BBCODE_TAG", 4);
define("BBCODE_TEXT", 3);
define("BBCODE_NL", 2);
define("BBCODE_WS", 1);
define("BBCODE_EOI", 0);
define("BBCODE_LEXSTATE_TEXT", 0);
define("BBCODE_LEXSTATE_TAG", 1);
define("BBCODE_MODE_SIMPLE", 0);
define("BBCODE_MODE_CALLBACK", 1);
define("BBCODE_MODE_INTERNAL", 2);
define("BBCODE_MODE_LIBRARY", 3);
define("BBCODE_MODE_ENHANCED", 4);
define("BBCODE_STACK_TOKEN", 0);
define("BBCODE_STACK_TEXT", 1);
define("BBCODE_STACK_TAG", 2);
define("BBCODE_STACK_CLASS", 3);
if (!function_exists('str_split'))
{
	function str_split($string, $split_length = 1)
	{
		$array = explode("\r\n", chunk_split($string, $split_length));
		array_pop($array);

		return $array;
	}
}
$BBCode_SourceDir = dirname(__FILE__);

class BBCodeLexer
{
	var $token;
	var $text;
	var $tag;
	var $state;
	var $input;
	var $ptr;
	var $unget;
	var $verbatim;
	var $debug;
	var $tagmarker;
	var $end_tagmarker;
	var $pat_main;
	var $pat_comment;
	var $pat_comment2;
	var $pat_wiki;

	function __construct($string, $tagmarker = '[')
	{
		$regex_beginmarkers = Array('[' => '\[', '<' => '<', '{' => '\{', '(' => '\(');
		$regex_endmarkers   = Array('[' => '\]', '<' => '>', '{' => '\}', '(' => '\)');
		$endmarkers         = Array('[' => ']', '<' => '>', '{' => '}', '(' => ')');
		if (!isset($regex_endmarkers[$tagmarker]))
		{
			$tagmarker = '[';
		}
		$e                   = $regex_endmarkers[$tagmarker];
		$b                   = $regex_beginmarkers[$tagmarker];
		$this->tagmarker     = $tagmarker;
		$this->end_tagmarker = $endmarkers[$tagmarker];
		$this->pat_main      = "/( "
			. "{$b}"
			. "(?! -- | ' | !-- | {$b}{$b} )"
			. "(?: [^\\n\\r{$b}{$e}] | \\\" [^\\\"\\n\\r]* \\\" | \\' [^\\'\\n\\r]* \\' )*"
			. "{$e}"
			. "| {$b}{$b} (?: [^{$e}\\r\\n] | {$e}[^{$e}\\r\\n] )* {$e}{$e}"
			. "| {$b} (?: -- | ' ) (?: [^{$e}\\n\\r]* ) {$e}"
			. "| {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e}"
			. "| -----+"
			. "| \\x0D\\x0A | \\x0A\\x0D | \\x0D | \\x0A"
			. "| [\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+(?=[\\x0D\\x0A{$b}]|-----|$)"
			. "| (?<=[\\x0D\\x0A{$e}]|-----|^)[\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+"
			. " )/Dx";
		$this->input         = preg_split($this->pat_main, $string, -1, PREG_SPLIT_DELIM_CAPTURE);
		$this->pat_comment   = "/^ {$b} (?: -- | ' ) /Dx";
		$this->pat_comment2  = "/^ {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e} $/Dx";
		$this->pat_wiki      = "/^ {$b}{$b} ([^\\|]*) (?:\\|(.*))? {$e}{$e} $/Dx";
		$this->ptr           = 0;
		$this->unget         = false;
		$this->state         = BBCODE_LEXSTATE_TEXT;
		$this->verbatim      = false;
		$this->token         = BBCODE_EOI;
		$this->tag           = false;
		$this->text          = "";
	}

	function GuessTextLength()
	{
		$length = 0;
		$ptr    = 0;
		$state  = BBCODE_LEXSTATE_TEXT;
		while ($ptr < count($this->input))
		{
			$text = $this->input[$ptr++];
			if ($state == BBCODE_LEXSTATE_TEXT)
			{
				$state = BBCODE_LEXSTATE_TAG;
				$length += strlen($text);
			}
			else
			{
				switch (ord(substr($this->text, 0, 1)))
				{
					case 10:
					case 13:
						$state = BBCODE_LEXSTATE_TEXT;
						$length++;
						break;
					default:
						$state = BBCODE_LEXSTATE_TEXT;
						$length += strlen($text);
						break;
					case 40:
					case 60:
					case 91:
					case 123:
						$state = BBCODE_LEXSTATE_TEXT;
						break;
				}
			}
		}

		return $length;
	}

	function NextToken()
	{
		if ($this->unget)
		{
			$this->unget = false;

			return $this->token;
		}
		while (true)
		{
			if ($this->ptr >= count($this->input))
			{
				$this->text = "";
				$this->tag  = false;

				return $this->token = BBCODE_EOI;
			}
			$this->text = preg_replace("/[\\x00-\\x08\\x0B-\\x0C\\x0E-\\x1F]/", "",
				$this->input[$this->ptr++]);
			if ($this->verbatim)
			{
				$this->tag = false;
				if ($this->state == BBCODE_LEXSTATE_TEXT)
				{
					$this->state = BBCODE_LEXSTATE_TAG;
					$token_type  = BBCODE_TEXT;
				}
				else
				{
					$this->state = BBCODE_LEXSTATE_TEXT;
					switch (ord(substr($this->text, 0, 1)))
					{
						case 10:
						case 13:
							$token_type = BBCODE_NL;
							break;
						default:
							$token_type = BBCODE_WS;
							break;
						case 45:
						case 40:
						case 60:
						case 91:
						case 123:
							$token_type = BBCODE_TEXT;
							break;
					}
				}
				if (strlen($this->text) > 0)
				{
					return $this->token = $token_type;
				}
			}
			else
			{
				if ($this->state == BBCODE_LEXSTATE_TEXT)
				{
					$this->state = BBCODE_LEXSTATE_TAG;
					$this->tag   = false;
					if (strlen($this->text) > 0)
					{
						return $this->token = BBCODE_TEXT;
					}
				}
				else
				{
					switch (ord(substr($this->text, 0, 1)))
					{
						case 10:
						case 13:
							$this->tag   = false;
							$this->state = BBCODE_LEXSTATE_TEXT;

							return $this->token = BBCODE_NL;
						case 45:
							if (preg_match("/^-----/", $this->text))
							{
								$this->tag   = Array('_name' => 'rule', '_endtag' => false, '_default' => '');
								$this->state = BBCODE_LEXSTATE_TEXT;

								return $this->token = BBCODE_TAG;
							}
							else
							{
								$this->tag   = false;
								$this->state = BBCODE_LEXSTATE_TEXT;
								if (strlen($this->text) > 0)
								{
									return $this->token = BBCODE_TEXT;
								}
								continue;
							}
						default:
							$this->tag   = false;
							$this->state = BBCODE_LEXSTATE_TEXT;

							return $this->token = BBCODE_WS;
						case 40:
						case 60:
						case 91:
						case 123:
							if (preg_match($this->pat_comment, $this->text))
							{
								$this->state = BBCODE_LEXSTATE_TEXT;
								continue;
							}
							if (preg_match($this->pat_comment2, $this->text))
							{
								$this->state = BBCODE_LEXSTATE_TEXT;
								continue;
							}
							if (preg_match($this->pat_wiki, $this->text, $matches))
							{
								$this->tag   = Array('_name'    => 'wiki', '_endtag' => false,
								                     '_default' => @$matches[1], 'title' => @$matches[2]);
								$this->state = BBCODE_LEXSTATE_TEXT;

								return $this->token = BBCODE_TAG;
							}
							$this->tag   = $this->Internal_DecodeTag($this->text);
							$this->state = BBCODE_LEXSTATE_TEXT;

							return $this->token = ($this->tag['_end'] ? BBCODE_ENDTAG : BBCODE_TAG);
					}
				}
			}
		}
	}

	function UngetToken()
	{
		if ($this->token !== BBCODE_EOI)
		{
			$this->unget = true;
		}
	}

	function PeekToken()
	{
		$result = $this->NextToken();
		if ($this->token !== BBCODE_EOI)
		{
			$this->unget = true;
		}

		return $result;
	}

	function SaveState()
	{
		return Array(
			'token'    => $this->token,
			'text'     => $this->text,
			'tag'      => $this->tag,
			'state'    => $this->state,
			'input'    => $this->input,
			'ptr'      => $this->ptr,
			'unget'    => $this->unget,
			'verbatim' => $this->verbatim
		);
	}

	function RestoreState($state)
	{
		if (!is_array($state))
		{
			return;
		}
		$this->token    = @$state['token'];
		$this->text     = @$state['text'];
		$this->tag      = @$state['tag'];
		$this->state    = @$state['state'];
		$this->input    = @$state['input'];
		$this->ptr      = @$state['ptr'];
		$this->unget    = @$state['unget'];
		$this->verbatim = @$state['verbatim'];
	}

	function Internal_StripQuotes($string)
	{
		if (preg_match("/^\\\"(.*)\\\"$/", $string, $matches))
		{
			return $matches[1];
		}
		else
		{
			if (preg_match("/^\\'(.*)\\'$/", $string, $matches))
			{
				return $matches[1];
			}
			else
			{
				return $string;
			}
		}
	}

	function Internal_ClassifyPiece($ptr, $pieces)
	{
		if ($ptr >= count($pieces))
		{
			return -1;
		}
		$piece = $pieces[$ptr];
		if ($piece == '=')
		{
			return '=';
		}
		else
		{
			if (preg_match("/^[\\'\\\"]/", $piece))
			{
				return '"';
			}
			else
			{
				if (preg_match("/^[\\x00-\\x20]+$/", $piece))
				{
					return ' ';
				}
				else
				{
					return 'A';
				}
			}
		}
	}

	function Internal_DecodeTag($tag)
	{
		$result = Array('_tag'    => $tag, '_endtag' => '', '_name' => '',
		                '_hasend' => false, '_end' => false, '_default' => false);
		$tag    = substr($tag, 1, strlen($tag) - 2);
		$ch     = ord(substr($tag, 0, 1));
		if ($ch >= 0 && $ch <= 32)
		{
			return $result;
		}
		$pieces = preg_split("/(\\\"[^\\\"]+\\\"|\\'[^\\']+\\'|=|[\\x00-\\x20]+)/",
			$tag, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
		$ptr    = 0;
		if (count($pieces) < 1)
		{
			return $result;
		}
		if (@substr($pieces[$ptr], 0, 1) == '/')
		{
			$result['_name'] = strtolower(substr($pieces[$ptr++], 1));
			$result['_end']  = true;
		}
		else
		{
			$result['_name'] = strtolower($pieces[$ptr++]);
			$result['_end']  = false;
		}
		while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ')
			$ptr++;
		$params = Array();
		if ($type != '=')
		{
			$result['_default'] = false;
			$params[]           = Array('key' => '', 'value' => '');
		}
		else
		{
			$ptr++;
			while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ')
				$ptr++;
			if ($type == "\"")
			{
				$value = $this->Internal_StripQuotes($pieces[$ptr++]);
			}
			else
			{
				$after_space = false;
				$start       = $ptr;
				while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1)
				{
					if ($type == ' ')
					{
						$after_space = true;
					}
					if ($type == '=' && $after_space)
					{
						break;
					}
					$ptr++;
				}
				if ($type == -1)
				{
					$ptr--;
				}
				if ($type == '=')
				{
					$ptr--;
					while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) == ' ')
						$ptr--;
					while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) != ' ')
						$ptr--;
				}
				$value = "";
				for (; $start <= $ptr; $start++)
				{
					if ($this->Internal_ClassifyPiece($start, $pieces) == ' ')
					{
						$value .= " ";
					}
					else
					{
						$value .= $this->Internal_StripQuotes($pieces[$start]);
					}
				}
				$value = trim($value);
				$ptr++;
			}
			$result['_default'] = $value;
			$params[]           = Array('key' => '', 'value' => $value);
		}
		while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1)
		{
			while ($type == ' ')
			{
				$ptr++;
				$type = $this->Internal_ClassifyPiece($ptr, $pieces);
			}
			if ($type == 'A' || $type == '"')
			{
				$key = strtolower($this->Internal_StripQuotes(@$pieces[$ptr++]));
			}
			else
			{
				if ($type == '=')
				{
					$ptr++;
					continue;
				}
				else
				{
					if ($type == -1)
					{
						break;
					}
				}
			}
			while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ')
				$ptr++;
			if ($type != '=')
			{
				$value = $this->Internal_StripQuotes($key);
			}
			else
			{
				$ptr++;
				while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ')
					$ptr++;
				if ($type == '"')
				{
					$value = $this->Internal_StripQuotes($pieces[$ptr++]);
				}
				else
				{
					if ($type != -1)
					{
						$value = $pieces[$ptr++];
						while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1
							&& $type != ' ')
							$value .= $pieces[$ptr++];
					}
					else
					{
						$value = "";
					}
				}
			}
			if (substr($key, 0, 1) != '_')
			{
				$result[$key] = $value;
			}
			$params[] = Array('key' => $key, 'value' => $value);
		}
		$result['_params'] = $params;

		return $result;
	}
}

class BBCodeLibrary
{
	var $default_smileys = Array(
		':)'     => 'smile.gif', ':-)' => 'smile.gif',
		'=)'     => 'smile.gif', '=-)' => 'smile.gif',
		':('     => 'frown.gif', ':-(' => 'frown.gif',
		'=('     => 'frown.gif', '=-(' => 'frown.gif',
		':D'     => 'bigsmile.gif', ':-D' => 'bigsmile.gif',
		'=D'     => 'bigsmile.gif', '=-D' => 'bigsmile.gif',
		'>:('    => 'angry.gif', '>:-(' => 'angry.gif',
		'>=('    => 'angry.gif', '>=-(' => 'angry.gif',
		'D:'     => 'angry.gif', 'D-:' => 'angry.gif',
		'D='     => 'angry.gif', 'D-=' => 'angry.gif',
		'>:)'    => 'evil.gif', '>:-)' => 'evil.gif',
		'>=)'    => 'evil.gif', '>=-)' => 'evil.gif',
		'>:D'    => 'evil.gif', '>:-D' => 'evil.gif',
		'>=D'    => 'evil.gif', '>=-D' => 'evil.gif',
		'>;)'    => 'sneaky.gif', '>;-)' => 'sneaky.gif',
		'>;D'    => 'sneaky.gif', '>;-D' => 'sneaky.gif',
		'O:)'    => 'saint.gif', 'O:-)' => 'saint.gif',
		'O=)'    => 'saint.gif', 'O=-)' => 'saint.gif',
		':O'     => 'surprise.gif', ':-O' => 'surprise.gif',
		'=O'     => 'surprise.gif', '=-O' => 'surprise.gif',
		':?'     => 'confuse.gif', ':-?' => 'confuse.gif',
		'=?'     => 'confuse.gif', '=-?' => 'confuse.gif',
		':s'     => 'worry.gif', ':-S' => 'worry.gif',
		'=s'     => 'worry.gif', '=-S' => 'worry.gif',
		':|'     => 'neutral.gif', ':-|' => 'neutral.gif',
		'=|'     => 'neutral.gif', '=-|' => 'neutral.gif',
		':I'     => 'neutral.gif', ':-I' => 'neutral.gif',
		'=I'     => 'neutral.gif', '=-I' => 'neutral.gif',
		':/'     => 'irritated.gif', ':-/' => 'irritated.gif',
		'=/'     => 'irritated.gif', '=-/' => 'irritated.gif',
		':\\'    => 'irritated.gif', ':-\\' => 'irritated.gif',
		'=\\'    => 'irritated.gif', '=-\\' => 'irritated.gif',
		':P'     => 'tongue.gif', ':-P' => 'tongue.gif',
		'=P'     => 'tongue.gif', '=-P' => 'tongue.gif',
		'X-P'    => 'tongue.gif',
		'8)'     => 'bigeyes.gif', '8-)' => 'bigeyes.gif',
		'B)'     => 'cool.gif', 'B-)' => 'cool.gif',
		';)'     => 'wink.gif', ';-)' => 'wink.gif',
		';D'     => 'bigwink.gif', ';-D' => 'bigwink.gif',
		'^_^'    => 'anime.gif', '^^;' => 'sweatdrop.gif',
		'>_>'    => 'lookright.gif', '>.>' => 'lookright.gif',
		'<_<'    => 'lookleft.gif', '<.<' => 'lookleft.gif',
		'XD'     => 'laugh.gif', 'X-D' => 'laugh.gif',
		':3'     => 'smile3.gif', ':-3' => 'smile3.gif',
		'=3'     => 'smile3.gif', '=-3' => 'smile3.gif',
		';3'     => 'wink3.gif', ';-3' => 'wink3.gif',
		'<g>'    => 'teeth.gif', '<G>' => 'teeth.gif',
		'o.O'    => 'boggle.gif', 'O.o' => 'boggle.gif',
		':blue:' => 'blue.gif',
		':zzz:'  => 'sleepy.gif',
		'<3'     => 'heart.gif',
		':star:' => 'star.gif',
	);
	var $default_tag_rules = Array(
		'b'       => Array(
			'simple_start' => "<b>",
			'simple_end'   => "</b>",
			'class'        => 'inline',
			'allow_in'     => Array('listitem', 'block', 'columns', 'inline', 'link'),
			'plain_start'  => "<b>",
			'plain_end'    => "</b>",
		),
		'i'       => Array(
			'simple_start' => "<i>",
			'simple_end'   => "</i>",
			'class'        => 'inline',
			'allow_in'     => Array('listitem', 'block', 'columns', 'inline', 'link'),
			'plain_start'  => "<i>",
			'plain_end'    => "</i>",
		),
		'u'       => Array(
			'simple_start' => "<u>",
			'simple_end'   => "</u>",
			'class'        => 'inline',
			'allow_in'     => Array('listitem', 'block', 'columns', 'inline', 'link'),
			'plain_start'  => "<u>",
			'plain_end'    => "</u>",
		),
		's'       => Array(
			'simple_start' => "<strike>",
			'simple_end'   => "</strike>",
			'class'        => 'inline',
			'allow_in'     => Array('listitem', 'block', 'columns', 'inline', 'link'),
			'plain_start'  => "<i>",
			'plain_end'    => "</i>",
		),
		'font'    => Array(
			'mode'     => BBCODE_MODE_LIBRARY,
			'allow'    => Array('_default' => '/^[a-zA-Z0-9._ -]+$/'),
			'method'   => 'DoFont',
			'class'    => 'inline',
			'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
		),
		'color'   => Array(
			'mode'     => BBCODE_MODE_ENHANCED,
			'allow'    => Array('_default' => '/^#?[a-zA-Z0-9._ -]+$/'),
			'template' => '<span style="color:{$_default/tw}">{$_content/v}</span>',
			'class'    => 'inline',
			'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
		),
		'size'    => Array(
			'mode'     => BBCODE_MODE_LIBRARY,
			'allow'    => Array('_default' => '/^[0-9.]+$/D'),
			'method'   => 'DoSize',
			'class'    => 'inline',
			'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
		),
		'sup'     => Array(
			'simple_start' => "<sup>",
			'simple_end'   => "</sup>",
			'class'        => 'inline',
			'allow_in'     => Array('listitem', 'block', 'columns', 'inline', 'link'),
		),
		'sub'     => Array(
			'simple_start' => "<sub>",
			'simple_end'   => "</sub>",
			'class'        => 'inline',
			'allow_in'     => Array('listitem', 'block', 'columns', 'inline', 'link'),
		),
		'spoiler' => Array(
			'simple_start' => "<span class=\"bbcode_spoiler\">",
			'simple_end'   => "</span>",
			'class'        => 'inline',
			'allow_in'     => Array('listitem', 'block', 'columns', 'inline', 'link'),
		),
		'acronym' => Array(
			'mode'     => BBCODE_MODE_ENHANCED,
			'template' => '<span class="bbcode_acronym" title="{$_default/e}">{$_content/v}</span>',
			'class'    => 'inline',
			'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'),
		),
		'url'     => Array(
			'mode'          => BBCODE_MODE_LIBRARY,
			'method'        => 'DoURL',
			'class'         => 'link',
			'allow_in'      => Array('listitem', 'block', 'columns', 'inline'),
			'content'       => BBCODE_REQUIRED,
			'plain_start'   => "<a href=\"{\$link}\">",
			'plain_end'     => "</a>",
			'plain_content' => Array('_content', '_default'),
			'plain_link'    => Array('_default', '_content'),
		),
		'email'   => Array(
			'mode'          => BBCODE_MODE_LIBRARY,
			'method'        => 'DoEmail',
			'class'         => 'link',
			'allow_in'      => Array('listitem', 'block', 'columns', 'inline'),
			'content'       => BBCODE_REQUIRED,
			'plain_start'   => "<a href=\"mailto:{\$link}\">",
			'plain_end'     => "</a>",
			'plain_content' => Array('_content', '_default'),
			'plain_link'    => Array('_default', '_content'),
		),
		'wiki'    => Array(
			'mode'          => BBCODE_MODE_LIBRARY,
			'method'        => "DoWiki",
			'class'         => 'link',
			'allow_in'      => Array('listitem', 'block', 'columns', 'inline'),
			'end_tag'       => BBCODE_PROHIBIT,
			'content'       => BBCODE_PROHIBIT,
			'plain_start'   => "<b>[",
			'plain_end'     => "]</b>",
			'plain_content' => Array('title', '_default'),
			'plain_link'    => Array('_default', '_content'),
		),
		'img'     => Array(
			'mode'          => BBCODE_MODE_LIBRARY,
			'method'        => "DoImage",
			'class'         => 'image',
			'allow_in'      => Array('listitem', 'block', 'columns', 'inline', 'link'),
			'end_tag'       => BBCODE_REQUIRED,
			'content'       => BBCODE_REQUIRED,
			'plain_start'   => "[image]",
			'plain_content' => Array(),
		),
		'rule'    => Array(
			'mode'          => BBCODE_MODE_LIBRARY,
			'method'        => "DoRule",
			'class'         => 'block',
			'allow_in'      => Array('listitem', 'block', 'columns'),
			'end_tag'       => BBCODE_PROHIBIT,
			'content'       => BBCODE_PROHIBIT,
			'before_tag'    => "sns",
			'after_tag'     => "sns",
			'plain_start'   => "\n-----\n",
			'plain_end'     => "",
			'plain_content' => Array(),
		),
		'br'      => Array(
			'mode'          => BBCODE_MODE_SIMPLE,
			'simple_start'  => "<br />\n",
			'simple_end'    => "",
			'class'         => 'inline',
			'allow_in'      => Array('listitem', 'block', 'columns', 'inline', 'link'),
			'end_tag'       => BBCODE_PROHIBIT,
			'content'       => BBCODE_PROHIBIT,
			'before_tag'    => "s",
			'after_tag'     => "s",
			'plain_start'   => "\n",
			'plain_end'     => "",
			'plain_content' => Array(),
		),
		'left'    => Array(
			'simple_start'  => "\n<div class=\"bbcode_left\" style=\"text-align:left\">\n",
			'simple_end'    => "\n</div>\n",
			'allow_in'      => Array('listitem', 'block', 'columns'),
			'before_tag'    => "sns",
			'after_tag'     => "sns",
			'before_endtag' => "sns",
			'after_endtag'  => "sns",
			'plain_start'   => "\n",
			'plain_end'     => "\n",
		),
		'right'   => Array(
			'simple_start'  => "\n<div class=\"bbcode_right\" style=\"text-align:right\">\n",
			'simple_end'    => "\n</div>\n",
			'allow_in'      => Array('listitem', 'block', 'columns'),
			'before_tag'    => "sns",
			'after_tag'     => "sns",
			'before_endtag' => "sns",
			'after_endtag'  => "sns",
			'plain_start'   => "\n",
			'plain_end'     => "\n",
		),
		'center'  => Array(
			'simple_start'  => "\n<div class=\"bbcode_center\" style=\"text-align:center\">\n",
			'simple_end'    => "\n</div>\n",
			'allow_in'      => Array('listitem', 'block', 'columns'),
			'before_tag'    => "sns",
			'after_tag'     => "sns",
			'before_endtag' => "sns",
			'after_endtag'  => "sns",
			'plain_start'   => "\n",
			'plain_end'     => "\n",
		),
		'indent'  => Array(
			'simple_start'  => "\n<div class=\"bbcode_indent\" style=\"margin-left:4em\">\n",
			'simple_end'    => "\n</div>\n",
			'allow_in'      => Array('listitem', 'block', 'columns'),
			'before_tag'    => "sns",
			'after_tag'     => "sns",
			'before_endtag' => "sns",
			'after_endtag'  => "sns",
			'plain_start'   => "\n",
			'plain_end'     => "\n",
		),
		'columns' => Array(
			'simple_start'  => "\n<table class=\"bbcode_columns\"><tbody><tr><td class=\"bbcode_column bbcode_firstcolumn\">\n",
			'simple_end'    => "\n</td></tr></tbody></table>\n",
			'class'         => 'columns',
			'allow_in'      => Array('listitem', 'block', 'columns'),
			'end_tag'       => BBCODE_REQUIRED,
			'content'       => BBCODE_REQUIRED,
			'before_tag'    => "sns",
			'after_tag'     => "sns",
			'before_endtag' => "sns",
			'after_endtag'  => "sns",
			'plain_start'   => "\n",
			'plain_end'     => "\n",
		),
		'nextcol' => Array(
			'simple_start'  => "\n</td><td class=\"bbcode_column\">\n",
			'class'         => 'nextcol',
			'allow_in'      => Array('columns'),
			'end_tag'       => BBCODE_PROHIBIT,
			'content'       => BBCODE_PROHIBIT,
			'before_tag'    => "sns",
			'after_tag'     => "sns",
			'before_endtag' => "sns",
			'after_endtag'  => "sns",
			'plain_start'   => "\n",
			'plain_end'     => "",
		),
		'code'    => Array(
			'mode'          => BBCODE_MODE_ENHANCED,
			'template'      => "\n<div class=\"bbcode_code\">\n<div class=\"bbcode_code_head\">Code:</div>\n<div class=\"bbcode_code_body\" style=\"white-space:pre\">{\$_content/v}</div>\n</div>\n",
			'class'         => 'code',
			'allow_in'      => Array('listitem', 'block', 'columns'),
			'content'       => BBCODE_VERBATIM,
			'before_tag'    => "sns",
			'after_tag'     => "sn",
			'before_endtag' => "sn",
			'after_endtag'  => "sns",
			'plain_start'   => "\n<b>Code:</b>\n",
			'plain_end'     => "\n",
		),
		'quote'   => Array(
			'mode'          => BBCODE_MODE_LIBRARY,
			'method'        => "DoQuote",
			'allow_in'      => Array('listitem', 'block', 'columns'),
			'before_tag'    => "sns",
			'after_tag'     => "sns",
			'before_endtag' => "sns",
			'after_endtag'  => "sns",
			'plain_start'   => "\n<b>Quote:</b>\n",
			'plain_end'     => "\n",
		),
		'list'    => Array(
			'mode'          => BBCODE_MODE_LIBRARY,
			'method'        => 'DoList',
			'class'         => 'list',
			'allow_in'      => Array('listitem', 'block', 'columns'),
			'before_tag'    => "sns",
			'after_tag'     => "sns",
			'before_endtag' => "sns",
			'after_endtag'  => "sns",
			'plain_start'   => "\n",
			'plain_end'     => "\n",
		),
		'*'       => Array(
			'simple_start'  => "<li>",
			'simple_end'    => "</li>\n",
			'class'         => 'listitem',
			'allow_in'      => Array('list'),
			'end_tag'       => BBCODE_OPTIONAL,
			'before_tag'    => "s",
			'after_tag'     => "s",
			'before_endtag' => "sns",
			'after_endtag'  => "sns",
			'plain_start'   => "\n * ",
			'plain_end'     => "\n",
		),
	);

	function DoURL($bbcode, $action, $name, $default, $params, $content)
	{
		if ($action == BBCODE_CHECK)
		{
			return true;
		}
		$url = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content));
		if ($bbcode->IsValidURL($url))
		{
			if ($bbcode->debug)
			{
				print "ISVALIDURL<br />";
			}
			if ($bbcode->url_targetable !== false && isset($params['target']))
			{
				$target = " target=\"" . htmlspecialchars($params['target']) . "\"";
			}
			else
			{
				$target = "";
			}
			if ($bbcode->url_target !== false)
			{
				if (!($bbcode->url_targetable == 'override' && isset($params['target'])))
				{
					$target = " target=\"" . htmlspecialchars($bbcode->url_target) . "\"";
				}
			}

			return '<a href="' . htmlspecialchars($url) . '" class="bbcode_url"' . $target . '>' . $content . '</a>';
		}
		else
		{
			return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']);
		}
	}

	function DoEmail($bbcode, $action, $name, $default, $params, $content)
	{
		if ($action == BBCODE_CHECK)
		{
			return true;
		}
		$email = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content));
		if ($bbcode->IsValidEmail($email))
		{
			return '<a href="mailto:' . htmlspecialchars($email) . '" class="bbcode_email">' . $content . '</a>';
		}
		else
		{
			return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']);
		}
	}

	function DoSize($bbcode, $action, $name, $default, $params, $content)
	{
		switch ($default)
		{
			case '0':
				$size = '.5em';
				break;
			case '1':
				$size = '.67em';
				break;
			case '2':
				$size = '.83em';
				break;
			default:
			case '3':
				$size = '1.0em';
				break;
			case '4':
				$size = '1.17em';
				break;
			case '5':
				$size = '1.5em';
				break;
			case '6':
				$size = '2.0em';
				break;
			case '7':
				$size = '2.5em';
				break;
		}

		return "<span style=\"font-size:$size\">$content</span>";
	}

	function DoFont($bbcode, $action, $name, $default, $params, $content)
	{
		$fonts         = explode(",", $default);
		$result        = "";
		$special_fonts = Array(
			'serif'      => 'serif',
			'sans-serif' => 'sans-serif',
			'sans serif' => 'sans-serif',
			'sansserif'  => 'sans-serif',
			'sans'       => 'sans-serif',
			'cursive'    => 'cursive',
			'fantasy'    => 'fantasy',
			'monospace'  => 'monospace',
			'mono'       => 'monospace',
		);
		foreach ($fonts as $font)
		{
			$font = trim($font);
			if (isset($special_fonts[$font]))
			{
				if (strlen($result) > 0)
				{
					$result .= ",";
				}
				$result .= $special_fonts[$font];
			}
			else
			{
				if (strlen($font) > 0)
				{
					if (strlen($result) > 0)
					{
						$result .= ",";
					}
					$result .= "'$font'";
				}
			}
		}

		return "<span style=\"font-family:$result\">$content</span>";
	}

	function DoWiki($bbcode, $action, $name, $default, $params, $content)
	{
		$name = $bbcode->Wikify($default);
		if ($action == BBCODE_CHECK)
		{
			return strlen($name) > 0;
		}
		$title = trim(@$params['title']);
		if (strlen($title) <= 0)
		{
			$title = trim($default);
		}

		return "<a href=\"{$bbcode->wiki_url}$name\" class=\"bbcode_wiki\">"
		. htmlspecialchars($title) . "</a>";
	}

	function DoImage($bbcode, $action, $name, $default, $params, $content)
	{
		if ($action == BBCODE_CHECK)
		{
			return true;
		}
		$content = trim($bbcode->UnHTMLEncode(strip_tags($content)));
		if (preg_match("/\\.(?:gif|jpeg|jpg|jpe|png)$/", $content))
		{
			if (preg_match("/^[a-zA-Z0-9_][^:]+$/", $content))
			{
				if (!preg_match("/(?:\\/\\.\\.\\/)|(?:^\\.\\.\\/)|(?:^\\/)/", $content))
				{
					$info = @getimagesize("{$bbcode->local_img_dir}/{$content}");
					if ($info[2] == IMAGETYPE_GIF || $info[2] == IMAGETYPE_JPEG || $info[2] == IMAGETYPE_PNG)
					{
						return "<img src=\""
						. htmlspecialchars("{$bbcode->local_img_url}/{$content}") . "\" alt=\""
						. htmlspecialchars(basename($content)) . "\" width=\""
						. htmlspecialchars($info[0]) . "\" height=\""
						. htmlspecialchars($info[1]) . "\" class=\"bbcode_img\" />";
					}
				}
			}
			else
			{
				if ($bbcode->IsValidURL($content, false))
				{
					return "<img src=\"" . htmlspecialchars($content) . "\" alt=\""
					. htmlspecialchars(basename($content)) . "\" class=\"bbcode_img\" />";
				}
			}
		}

		return htmlspecialchars($params['_tag']) . htmlspecialchars($content) . htmlspecialchars($params['_endtag']);
	}

	function DoRule($bbcode, $action, $name, $default, $params, $content)
	{
		if ($action == BBCODE_CHECK)
		{
			return true;
		}
		else
		{
			return $bbcode->rule_html;
		}
	}

	function DoQuote($bbcode, $action, $name, $default, $params, $content)
	{
		if ($action == BBCODE_CHECK)
		{
			return true;
		}
		if (isset($params['name']))
		{
			$title = htmlspecialchars(trim($params['name'])) . " wrote";
			if (isset($params['date']))
			{
				$title .= " on " . htmlspecialchars(trim($params['date']));
			}
			$title .= ":";
			if (isset($params['url']))
			{
				$url = trim($params['url']);
				if ($bbcode->IsValidURL($url))
				{
					$title = "<a href=\"" . htmlspecialchars($params['url']) . "\">" . $title . "</a>";
				}
			}
		}
		else
		{
			if (!is_string($default))
			{
				$title = "Quote:";
			}
			else
			{
				$title = htmlspecialchars(trim($default)) . " wrote:";
			}
		}

		return "\n<div class=\"bbcode_quote\">\n<div class=\"bbcode_quote_head\">"
		. $title . "</div>\n<div class=\"bbcode_quote_body\">"
		. $content . "</div>\n</div>\n";
	}

	function DoList($bbcode, $action, $name, $default, $params, $content)
	{
		$list_styles    = Array(
			'1'  => 'decimal',
			'01' => 'decimal-leading-zero',
			'i'  => 'lower-roman',
			'I'  => 'upper-roman',
			'a'  => 'lower-alpha',
			'A'  => 'upper-alpha',
		);
		$ci_list_styles = Array(
			'circle'   => 'circle',
			'disc'     => 'disc',
			'square'   => 'square',
			'greek'    => 'lower-greek',
			'armenian' => 'armenian',
			'georgian' => 'georgian',
		);
		$ul_types       = Array(
			'circle' => 'circle',
			'disc'   => 'disc',
			'square' => 'square',
		);
		$default        = trim($default);
		if ($action == BBCODE_CHECK)
		{
			if (!is_string($default) || strlen($default) == "")
			{
				return true;
			}
			else
			{
				if (isset($list_styles[$default]))
				{
					return true;
				}
				else
				{
					if (isset($ci_list_styles[strtolower($default)]))
					{
						return true;
					}
					else
					{
						return false;
					}
				}
			}
		}
		if (!is_string($default) || strlen($default) == "")
		{
			$elem = 'ul';
			$type = '';
		}
		else
		{
			if ($default == '1')
			{
				$elem = 'ol';
				$type = '';
			}
			else
			{
				if (isset($list_styles[$default]))
				{
					$elem = 'ol';
					$type = $list_styles[$default];
				}
				else
				{
					$default = strtolower($default);
					if (isset($ul_types[$default]))
					{
						$elem = 'ul';
						$type = $ul_types[$default];
					}
					else
					{
						if (isset($ci_list_styles[$default]))
						{
							$elem = 'ol';
							$type = $ci_list_styles[$default];
						}
					}
				}
			}
		}
		if (strlen($type))
		{
			return "\n<$elem class=\"bbcode_list\" style=\"list-style-type:$type\">\n$content</$elem>\n";
		}
		else
		{
			return "\n<$elem class=\"bbcode_list\">\n$content</$elem>\n";
		}
	}
}

class BBCodeEmailAddressValidator
{
	function check_email_address($strEmailAddress)
	{
		if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $strEmailAddress))
		{
			return false;
		}
		$intAtSymbol = strrpos($strEmailAddress, '@');
		if ($intAtSymbol === false)
		{
			return false;
		}
		$arrEmailAddress[0] = substr($strEmailAddress, 0, $intAtSymbol);
		$arrEmailAddress[1] = substr($strEmailAddress, $intAtSymbol + 1);
		$arrTempAddress[0]  = preg_replace('/"[^"]+"/'
			, ''
			, $arrEmailAddress[0]);
		$arrTempAddress[1]  = $arrEmailAddress[1];
		$strTempAddress     = $arrTempAddress[0] . $arrTempAddress[1];
		if (strrpos($strTempAddress, '@') !== false)
		{
			return false;
		}
		if (!$this->check_local_portion($arrEmailAddress[0]))
		{
			return false;
		}
		if (!$this->check_domain_portion($arrEmailAddress[1]))
		{
			return false;
		}

		return true;
	}

	function check_local_portion($strLocalPortion)
	{
		if (!$this->check_text_length($strLocalPortion, 1, 64))
		{
			return false;
		}
		$arrLocalPortion = explode('.', $strLocalPortion);
		for ($i = 0, $max = sizeof($arrLocalPortion); $i < $max; $i++)
		{
			if (!preg_match('.^('
				. '([A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]'
				. '[A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]{0,63})'
				. '|'
				. '("[^\\\"]{0,62}")'
				. ')$.'
				, $arrLocalPortion[$i])
			)
			{
				return false;
			}
		}

		return true;
	}

	function check_domain_portion($strDomainPortion)
	{
		if (!$this->check_text_length($strDomainPortion, 1, 255))
		{
			return false;
		}
		if (preg_match('/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])'
				. '(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}$/'
				, $strDomainPortion) ||
			preg_match('/^\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])'
				. '(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\]$/'
				, $strDomainPortion)
		)
		{
			return true;
		}
		else
		{
			$arrDomainPortion = explode('.', $strDomainPortion);
			if (sizeof($arrDomainPortion) < 2)
			{
				return false;
			}
			for ($i = 0, $max = sizeof($arrDomainPortion); $i < $max; $i++)
			{
				if (!$this->check_text_length($arrDomainPortion[$i], 1, 63))
				{
					return false;
				}
				if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|'
					. '([A-Za-z0-9]+))$/', $arrDomainPortion[$i])
				)
				{
					return false;
				}
			}
		}

		return true;
	}

	function check_text_length($strText, $intMinimum, $intMaximum)
	{
		$intTextLength = strlen($strText);
		if (($intTextLength < $intMinimum) || ($intTextLength > $intMaximum))
		{
			return false;
		}
		else
		{
			return true;
		}
	}
}

class NBBC_BBCode
{
	var $tag_rules;
	var $defaults;
	var $current_class;
	var $root_class;
	var $lost_start_tags;
	var $start_tags;
	var $allow_ampersand;
	var $tag_marker;
	var $ignore_newlines;
	var $plain_mode;
	var $detect_urls;
	var $url_pattern;
	var $output_limit;
	var $text_length;
	var $was_limited;
	var $limit_tail;
	var $limit_precision;
	var $smiley_dir;
	var $smiley_url;
	var $smileys;
	var $smiley_regex;
	var $enable_smileys;
	var $wiki_url;
	var $local_img_dir;
	var $local_img_url;
	var $url_targetable;
	var $url_target;
	var $rule_html;
	var $pre_trim;
	var $post_trim;
	var $debug;

	function __construct()
	{
		$this->defaults         = new BBCodeLibrary;
		$this->tag_rules        = $this->defaults->default_tag_rules;
		$this->smileys          = $this->defaults->default_smileys;
		$this->enable_smileys   = true;
		$this->smiley_regex     = false;
		$this->smiley_dir       = $this->GetDefaultSmileyDir();
		$this->smiley_url       = $this->GetDefaultSmileyURL();
		$this->wiki_url         = $this->GetDefaultWikiURL();
		$this->local_img_dir    = $this->GetDefaultLocalImgDir();
		$this->local_img_url    = $this->GetDefaultLocalImgURL();
		$this->rule_html        = $this->GetDefaultRuleHTML();
		$this->pre_trim         = "";
		$this->post_trim        = "";
		$this->root_class       = 'block';
		$this->lost_start_tags  = Array();
		$this->start_tags       = Array();
		$this->tag_marker       = '[';
		$this->allow_ampsersand = false;
		$this->current_class    = $this->root_class;
		$this->debug            = false;
		$this->ignore_newlines  = false;
		$this->output_limit     = 0;
		$this->plain_mode       = false;
		$this->was_limited      = false;
		$this->limit_tail       = "...";
		$this->limit_precision  = 0.15;
		$this->detect_urls      = false;
		$this->url_pattern      = '<a href="{$url/h}">{$text/h}</a>';
		$this->url_targetable   = false;
		$this->url_target       = false;
	}

	function SetPreTrim($trim = "a")
	{
		$this->pre_trim = $trim;
	}

	function GetPreTrim()
	{
		return $this->pre_trim;
	}

	function SetPostTrim($trim = "a")
	{
		$this->post_trim = $trim;
	}

	function GetPostTrim()
	{
		return $this->post_trim;
	}

	function SetRoot($class = 'block')
	{
		$this->root_class = $class;
	}

	function SetRootInline()
	{
		$this->root_class = 'inline';
	}

	function SetRootBlock()
	{
		$this->root_class = 'block';
	}

	function GetRoot()
	{
		return $this->root_class;
	}

	function SetDebug($enable = true)
	{
		$this->debug = $enable;
	}

	function GetDebug()
	{
		return $this->debug;
	}

	function SetAllowAmpersand($enable = true)
	{
		$this->allow_ampersand = $enable;
	}

	function GetAllowAmpersand()
	{
		return $this->allow_ampersand;
	}

	function SetTagMarker($marker = '[')
	{
		$this->tag_marker = $marker;
	}

	function GetTagMarker()
	{
		return $this->tag_marker;
	}

	function SetIgnoreNewlines($ignore = true)
	{
		$this->ignore_newlines = $ignore;
	}

	function GetIgnoreNewlines()
	{
		return $this->ignore_newlines;
	}

	function SetLimit($limit = 0)
	{
		$this->output_limit = $limit;
	}

	function GetLimit()
	{
		return $this->output_limit;
	}

	function SetLimitTail($tail = "...")
	{
		$this->limit_tail = $tail;
	}

	function GetLimitTail()
	{
		return $this->limit_tail;
	}

	function SetLimitPrecision($prec = 0.15)
	{
		$this->limit_precision = $prec;
	}

	function GetLimitPrecision()
	{
		return $this->limit_precision;
	}

	function WasLimited()
	{
		return $this->was_limited;
	}

	function SetPlainMode($enable = true)
	{
		$this->plain_mode = $enable;
	}

	function GetPlainMode()
	{
		return $this->plain_mode;
	}

	function SetDetectURLs($enable = true)
	{
		$this->detect_urls = $enable;
	}

	function GetDetectURLs()
	{
		return $this->detect_urls;
	}

	function SetURLPattern($pattern)
	{
		$this->url_pattern = $pattern;
	}

	function GetURLPattern()
	{
		return $this->url_pattern;
	}

	function SetURLTargetable($enable)
	{
		$this->url_targetable = $enable;
	}

	function GetURLTargetable()
	{
		return $this->url_targetable;
	}

	function SetURLTarget($target)
	{
		$this->url_target = $target;
	}

	function GetURLTarget()
	{
		return $this->url_target;
	}

	function AddRule($name, $rule)
	{
		$this->tag_rules[$name] = $rule;
	}

	function RemoveRule($name)
	{
		unset($this->tag_rules[$name]);
	}

	function GetRule($name)
	{
		return isset($this->tag_rules[$name])
			? $this->tag_rules[$name] : false;
	}

	function ClearRules()
	{
		$this->tag_rules = Array();
	}

	function GetDefaultRule($name)
	{
		return isset($this->defaults->default_tag_rules[$name])
			? $this->defaults->default_tag_rules[$name] : false;
	}

	function SetDefaultRule($name)
	{
		if (isset($this->defaults->default_tag_rules[$name]))
		{
			$this->AddRule($name, $this->defaults->default_tag_rules[$name]);
		}
		else
		{
			$this->RemoveRule($name);
		}
	}

	function GetDefaultRules()
	{
		return $this->defaults->default_tag_rules;
	}

	function SetDefaultRules()
	{
		$this->tag_rules = $this->defaults->default_tag_rules;
	}

	function SetWikiURL($url)
	{
		$this->wiki_url = $url;
	}

	function GetWikiURL($url)
	{
		return $this->wiki_url;
	}

	function GetDefaultWikiURL()
	{
		return '/?page=';
	}

	function SetLocalImgDir($path)
	{
		$this->local_img_dir = $path;
	}

	function GetLocalImgDir()
	{
		return $this->local_img_dir;
	}

	function GetDefaultLocalImgDir()
	{
		return "img";
	}

	function SetLocalImgURL($path)
	{
		$this->local_img_url = $path;
	}

	function GetLocalImgURL()
	{
		return $this->local_img_url;
	}

	function GetDefaultLocalImgURL()
	{
		return "img";
	}

	function SetRuleHTML($html)
	{
		$this->rule_html = $html;
	}

	function GetRuleHTML()
	{
		return $this->rule_html;
	}

	function GetDefaultRuleHTML()
	{
		return "\n<hr class=\"bbcode_rule\" />\n";
	}

	function AddSmiley($code, $image)
	{
		$this->smileys[$code] = $image;
		$this->smiley_regex   = false;
	}

	function RemoveSmiley($code)
	{
		unset($this->smileys[$code]);
		$this->smiley_regex = false;
	}

	function GetSmiley($code)
	{
		return isset($this->smileys[$code])
			? $this->smileys[$code] : false;
	}

	function ClearSmileys()
	{
		$this->smileys      = Array();
		$this->smiley_regex = false;
	}

	function GetDefaultSmiley($code)
	{
		return isset($this->defaults->default_smileys[$code])
			? $this->defaults->default_smileys[$code] : false;
	}

	function SetDefaultSmiley($code)
	{
		$this->smileys[$code] = @$this->defaults->default_smileys[$code];
		$this->smiley_regex   = false;
	}

	function GetDefaultSmileys()
	{
		return $this->defaults->default_smileys;
	}

	function SetDefaultSmileys()
	{
		$this->smileys      = $this->defaults->default_smileys;
		$this->smiley_regex = false;
	}

	function SetSmileyDir($path)
	{
		$this->smiley_dir = $path;
	}

	function GetSmileyDir()
	{
		return $this->smiley_dir;
	}

	function GetDefaultSmileyDir()
	{
		return "smileys";
	}

	function SetSmileyURL($path)
	{
		$this->smiley_url = $path;
	}

	function GetSmileyURL()
	{
		return $this->smiley_url;
	}

	function GetDefaultSmileyURL()
	{
		return "smileys";
	}

	function SetEnableSmileys($enable = true)
	{
		$this->enable_smileys = $enable;
	}

	function GetEnableSmileys()
	{
		return $this->enable_smileys;
	}

	function nl2br($string)
	{
		return preg_replace("/\\x0A|\\x0D|\\x0A\\x0D|\\x0D\\x0A/", "<br />\n", $string);
	}

	function UnHTMLEncode($string)
	{
		if (function_exists("html_entity_decode"))
		{
			return html_entity_decode($string);
		}
		$string    = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $string);
		$string    = preg_replace('~&#([0-9]+);~e', 'chr("\\1")', $string);
		$trans_tbl = get_html_translation_table(HTML_ENTITIES);
		$trans_tbl = array_flip($trans_tbl);

		return strtr($string, $trans_tbl);
	}

	function Wikify($string)
	{
		return rawurlencode(str_replace(" ", "_",
			trim(preg_replace("/[!?;@#\$%\\^&*<>=+`~\\x00-\\x20_-]+/", " ", $string))));
	}

	function IsValidURL($string, $email_too = true)
	{
		if (preg_match("/^
(?:https?|ftp):\\/\\/
(?:
(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+
[a-zA-Z0-9]
(?:[a-zA-Z0-9-]*[a-zA-Z0-9])?
|
\\[
(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}
(?:
25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]:
(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F]
|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+
)
\\]
)
(?::[0-9]{1,5})?
(?:[\\/\\?\\#][^\\n\\r]*)?
$/Dx", $string))
		{
			return true;
		}
		if (preg_match("/^[^:]+([\\/\\\\?#][^\\r\\n]*)?$/D", $string))
		{
			return true;
		}
		if ($email_too)
		{
			if (substr($string, 0, 7) == "mailto:")
			{
				return $this->IsValidEmail(substr($string, 7));
			}
		}

		return false;
	}

	function IsValidEmail($string)
	{
		$validator = new BBCodeEmailAddressValidator;

		return $validator->check_email_address($string);
		/*
return preg_match("/^
(?:
[a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+
(?:\.[a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+)*
|
\"(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]
|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])*\"
)
@
(?:
(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+
[a-z0-9]
(?:[a-z0-9-]*[a-z0-9])?
|
\\[
(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}
(?:
25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:
(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F]
|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+
)
\\]
)
$/Dx", $string);
*/
	}

	function HTMLEncode($string)
	{
		if (!$this->allow_ampersand)
		{
			return htmlspecialchars($string);
		}
		else
		{
			return str_replace(Array('<', '>', '"'),
				Array('&lt;', '&gt;', '&quot;'), $string);
		}
	}

	function FixupOutput($string)
	{
		/*HACK >*/
		if (!$this->detect_urls || $this->autolink_disable)
		{
			/*< HACK*/
			$output = $this->Internal_ProcessSmileys($string);
		}
		else
		{
			$chunks = $this->Internal_AutoDetectURLs($string);
			$output = Array();
			if (count($chunks))
			{
				$is_a_url = false;
				foreach ($chunks as $index => $chunk)
				{
					if (!$is_a_url)
					{
						$chunk = $this->Internal_ProcessSmileys($chunk);
					}
					$output[] = $chunk;
					$is_a_url = !$is_a_url;
				}
			}
			$output = implode("", $output);
		}

		return $output;
	}

	function Internal_ProcessSmileys($string)
	{
		if (!$this->enable_smileys || $this->plain_mode)
		{
			$output = $this->HTMLEncode($string);
		}
		else
		{
			if ($this->smiley_regex === false)
			{
				$this->Internal_RebuildSmileys();
			}
			$tokens = preg_split($this->smiley_regex, $string, -1, PREG_SPLIT_DELIM_CAPTURE);
			if (count($tokens) <= 1)
			{
				$output = $this->HTMLEncode($string);
			}
			else
			{
				$output      = "";
				$is_a_smiley = false;
				foreach ($tokens as $token)
				{
					if (!$is_a_smiley)
					{
						$output .= $this->HTMLEncode($token);
					}
					else
					{
						if (isset($this->smiley_info[$token]))
						{
							$info = $this->smiley_info[$token];
						}
						else
						{
							$info                      = @getimagesize($this->smiley_dir . '/' . $this->smileys[$token]);
							$this->smiley_info[$token] = $info;
						}
						$alt = htmlspecialchars($token);
						$output .= "<img src=\"" . htmlspecialchars($this->smiley_url . '/' . $this->smileys[$token])
							. "\" width=\"{$info[0]}\" height=\"{$info[1]}\""
							. " alt=\"$alt\" title=\"$alt\" class=\"bbcode_smiley\" />";
					}
					$is_a_smiley = !$is_a_smiley;
				}
			}
		}

		return $output;
	}

	function Internal_RebuildSmileys()
	{
		$regex = Array("/(?<![\\w])(");
		$first = true;
		foreach ($this->smileys as $code => $filename)
		{
			if (!$first)
			{
				$regex[] = "|";
			}
			$regex[] = preg_quote("$code", '/');
			$first   = false;
		}
		$regex[]            = ")(?![\\w])/";
		$this->smiley_regex = implode("", $regex);
	}

	function Internal_AutoDetectURLs($string)
	{
		$output = preg_split("/( (?:
(?:https?|ftp) : \\/*
(?:
(?: (?: [a-zA-Z0-9-]{2,} \\. )+
(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2}
| aero | biz | coop | info | museum | name | pro
| example | invalid | localhost | test | local | onion | swift ) )
| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} )
| (?: [0-9A-Fa-f:]+ : [0-9A-Fa-f]{1,4} )
)
(?: : [0-9]+ )?
(?! [a-zA-Z0-9.:-] )
(?:
\\/
[^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]*
)?
(?:
[?#]
[^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+
)?
) | (?:
(?:
(?: (?: [a-zA-Z0-9-]{2,} \\. )+
(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2}
| aero | biz | coop | info | museum | name | pro
| example | invalid | localhost | test | local | onion | swift ) )
| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} )
)
(?: : [0-9]+ )?
(?! [a-zA-Z0-9.:-] )
(?:
\\/
[^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]*
)?
(?:
[?#]
[^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+
)?
) | (?:
[a-zA-Z0-9._-]{2,} @
(?:
(?: (?: [a-zA-Z0-9-]{2,} \\. )+
(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2}
| aero | biz | coop | info | museum | name | pro
| example | invalid | localhost | test | local | onion | swift ) )
| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} )
)
) )/Dx", $string, -1, PREG_SPLIT_DELIM_CAPTURE);
		if (count($output) > 1)
		{
			$is_a_url = false;
			foreach ($output as $index => $token)
			{
				if ($is_a_url)
				{
					if (preg_match("/^[a-zA-Z0-9._-]{2,}@/", $token))
					{
						$url = "mailto:" . $token;
					}
					else
					{
						if (preg_match("/^(https?:|ftp:)\\/*([^\\/&?#]+)\\/*(.*)\$/", $token, $matches))
						{
							$url = $matches[1] . '/' . '/' . $matches[2] . "/" . $matches[3];
						}
						else
						{
							preg_match("/^([^\\/&?#]+)\\/*(.*)\$/", $token, $matches);
							$url = "http:/" . "/" . $matches[1] . "/" . $matches[2];
						}
					}
					$params = @parse_url($url);
					/*HACK >*/
					if (!is_array($params))
					{
						$output[$index] = $token;
						continue;
					}
					/*< HACK*/
					$params['url']   = $url;
					$params['isurl'] = $this->IsValidURL($url);
					$params['link']  = $url;
					$params['text']  = $token;
					/*HACK >*/
					$output[$index] = $this->FillTemplate($this->url_pattern, $params);
					/*< HACK*/
				}
				$is_a_url = !$is_a_url;
			}
		}

		return $output;
	}

	function FillTemplate($template, $insert_array, $default_array = Array())
	{
		/*HACK >*/
		if (is_array($template))
		{
			return call_user_func($template, $insert_array);
		}
		/*< HACK*/
		$pieces = preg_split('/(\{\$[a-zA-Z0-9_.:\/-]+\})/', $template,
			-1, PREG_SPLIT_DELIM_CAPTURE);
		if (count($pieces) <= 1)
		{
			return $template;
		}
		$result       = Array();
		$is_an_insert = false;
		foreach ($pieces as $piece)
		{
			if (!$is_an_insert)
			{
				$result[] = $piece;
			}
			else
			{
				if (!preg_match('/\{\$([a-zA-Z0-9_:-]+)((?:\\.[a-zA-Z0-9_:-]+)*)(?:\/([a-zA-Z0-9_:-]+))?\}/', $piece, $matches))
				{
					$result[] = $piece;
				}
				else
				{
					if (isset($insert_array[$matches[1]]))
					{
						$value = @$insert_array[$matches[1]];
					}
					else
					{
						$value = @$default_array[$matches[1]];
					}
					if (strlen(@$matches[2]))
					{
						foreach (split(".", substr($matches[2], 1)) as $index)
						{
							if (is_array($value))
							{
								$value = @$value[$index];
							}
							else
							{
								if (is_object($value))
								{
									$value = (array) $value;
									$value = @$value[$index];
								}
								else
								{
									$value = "";
								}
							}
						}
					}
					switch (gettype($value))
					{
						case 'boolean':
							$value = $value ? "true" : "false";
							break;
						case 'integer':
							$value = (string) $value;
							break;
						case 'double':
							$value = (string) $value;
							break;
						case 'string':
							break;
						default:
							$value = "";
							break;
					}
					if (strlen(@$matches[3]))
					{
						$flags = array_flip(str_split($matches[3]));
					}
					else
					{
						$flags = Array();
					}
					if (!isset($flags['v']))
					{
						if (isset($flags['w']))
						{
							$value = preg_replace("/[\\x00-\\x09\\x0B-\x0C\x0E-\\x20]+/", " ", $value);
						}
						if (isset($flags['t']))
						{
							$value = trim($value);
						}
						if (isset($flags['b']))
						{
							$value = basename($value);
						}
						if (isset($flags['e']))
						{
							$value = $this->HTMLEncode($value);
						}
						else
						{
							if (isset($flags['k']))
							{
								$value = $this->Wikify($value);
							}
							else
							{
								if (isset($flags['h']))
								{
									$value = htmlspecialchars($value);
								}
								else
								{
									if (isset($flags['u']))
									{
										$value = urlencode($value);
									}
								}
							}
						}
						if (isset($flags['n']))
						{
							$value = $this->nl2br($value);
						}
					}
					$result[] = $value;
				}
			}
			$is_an_insert = !$is_an_insert;
		}

		return implode("", $result);
	}

	function Internal_CollectText($array, $start = 0)
	{
		ob_start();
		for ($start = intval($start), $end = count($array); $start < $end; $start++)
			print $array[$start][BBCODE_STACK_TEXT];
		$output = ob_get_contents();
		ob_end_clean();

		return $output;
	}

	function Internal_CollectTextReverse($array, $start = 0, $end = 0)
	{
		ob_start();
		for ($start = intval($start); $start >= $end; $start--)
			print $array[$start][BBCODE_STACK_TEXT];
		$output = ob_get_contents();
		ob_end_clean();

		return $output;
	}

	function Internal_GenerateOutput($pos)
	{
		$output = Array();
		while (count($this->stack) > $pos)
		{
			$token = array_pop($this->stack);
			if ($token[BBCODE_STACK_TOKEN] != BBCODE_TAG)
			{
				$output[] = $token;
			}
			else
			{
				$name    = @$token[BBCODE_STACK_TAG]['_name'];
				$rule    = @$this->tag_rules[$name];
				$end_tag = @$rule['end_tag'];
				if (!isset($rule['end_tag']))
				{
					$end_tag = BBCODE_REQUIRED;
				}
				else
				{
					$end_tag = $rule['end_tag'];
				}
				array_pop($this->start_tags[$name]);
				if ($end_tag == BBCODE_PROHIBIT)
				{
					$output[] = Array(
						BBCODE_STACK_TOKEN => BBCODE_TEXT,
						BBCODE_STACK_TAG   => false,
						BBCODE_STACK_TEXT  => $token[BBCODE_STACK_TEXT],
						BBCODE_STACK_CLASS => $this->current_class,
					);
				}
				else
				{
					if ($end_tag == BBCODE_REQUIRED)
					{
						@$this->lost_start_tags[$name] + 1;
					}
					$end = $this->Internal_CleanupWSByIteratingPointer(@$rule['before_endtag'], 0, $output);
					$this->Internal_CleanupWSByPoppingStack(@$rule['after_tag'], $output);
					$tag_body = $this->Internal_CollectTextReverse($output, count($output) - 1, $end);
					$this->Internal_CleanupWSByPoppingStack(@$rule['before_tag'], $this->stack);
					$this->Internal_UpdateParamsForMissingEndTag(@$token[BBCODE_STACK_TAG]);
					$tag_output = $this->DoTag(BBCODE_OUTPUT, $name,
						@$token[BBCODE_STACK_TAG]['_default'], @$token[BBCODE_STACK_TAG], $tag_body);
					$output     = Array(Array(
						BBCODE_STACK_TOKEN => BBCODE_TEXT,
						BBCODE_STACK_TAG   => false,
						BBCODE_STACK_TEXT  => $tag_output,
						BBCODE_STACK_CLASS => $this->current_class
					));
				}
			}
		}
		$this->Internal_ComputeCurrentClass();

		return $output;
	}

	function Internal_RewindToClass($class_list)
	{
		$pos = count($this->stack) - 1;
		while ($pos >= 0 && !in_array($this->stack[$pos][BBCODE_STACK_CLASS], $class_list))
			$pos--;
		if ($pos < 0)
		{
			if (!in_array($this->root_class, $class_list))
			{
				return false;
			}
		}
		$output = $this->Internal_GenerateOutput($pos + 1);
		while (count($output))
		{
			$token                     = array_pop($output);
			$token[BBCODE_STACK_CLASS] = $this->current_class;
			$this->stack[]             = $token;
		}

		return true;
	}

	function Internal_FinishTag($tag_name)
	{
		if (strlen($tag_name) <= 0)
		{
			return false;
		}
		if (isset($this->start_tags[$tag_name])
			&& count($this->start_tags[$tag_name])
		)
		{
			$pos = array_pop($this->start_tags[$tag_name]);
		}
		else
		{
			$pos = -1;
		}
		if ($pos < 0)
		{
			return false;
		}
		$newpos = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['after_tag'],
			$pos + 1, $this->stack);
		$delta  = $newpos - ($pos + 1);
		$output = $this->Internal_GenerateOutput($newpos);
		$newend = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['before_endtag'],
			0, $output);
		$output = $this->Internal_CollectTextReverse($output, count($output) - 1, $newend);
		while ($delta-- > 0)
			array_pop($this->stack);
		$this->Internal_ComputeCurrentClass();

		return $output;
	}

	function Internal_ComputeCurrentClass()
	{
		if (count($this->stack) > 0)
		{
			$this->current_class = $this->stack[count($this->stack) - 1][BBCODE_STACK_CLASS];
		}
		else
		{
			$this->current_class = $this->root_class;
		}
	}

	function Internal_DumpStack($array = false, $raw = false)
	{
		if (!$raw)
		{
			$string = "<span style='color: #00C;'>";
		}
		else
		{
			$string = "";
		}
		if ($array === false)
		{
			$array = $this->stack;
		}
		foreach ($array as $item)
		{
			switch (@$item[BBCODE_STACK_TOKEN])
			{
				case BBCODE_TEXT:
					$string .= "\"" . htmlspecialchars(@$item[BBCODE_STACK_TEXT]) . "\" ";
					break;
				case BBCODE_WS:
					$string .= "WS ";
					break;
				case BBCODE_NL:
					$string .= "NL ";
					break;
				case BBCODE_TAG:
					$string .= "[" . htmlspecialchars(@$item[BBCODE_STACK_TAG]['_name']) . "] ";
					break;
				default:
					$string .= "unknown ";
					break;
			}
		}
		if (!$raw)
		{
			$string .= "</span>";
		}

		return $string;
	}

	function Internal_CleanupWSByPoppingStack($pattern, &$array)
	{
		if (strlen($pattern) <= 0)
		{
			return;
		}
		$oldlen = count($array);
		foreach (str_split($pattern) as $char)
		{
			switch ($char)
			{
				case 's':
					while (count($array) > 0 && $array[count($array) - 1][BBCODE_STACK_TOKEN] == BBCODE_WS)
						array_pop($array);
					break;
				case 'n':
					if (count($array) > 0 && $array[count($array) - 1][BBCODE_STACK_TOKEN] == BBCODE_NL)
					{
						array_pop($array);
					}
					break;
				case 'a':
					while (count($array) > 0
						&& (($token = $array[count($array) - 1][BBCODE_STACK_TOKEN]) == BBCODE_WS
							|| $token == BBCODE_NL))
						array_pop($array);
					break;
			}
		}
		if (count($array) != $oldlen)
		{
			$this->Internal_ComputeCurrentClass();
		}
	}

	function Internal_CleanupWSByEatingInput($pattern)
	{
		if (strlen($pattern) <= 0)
		{
			return;
		}
		foreach (str_split($pattern) as $char)
		{
			switch ($char)
			{
				case 's':
					$token_type = $this->lexer->NextToken();
					while ($token_type == BBCODE_WS)
					{
						$token_type = $this->lexer->NextToken();
					}
					$this->lexer->UngetToken();
					break;
				case 'n':
					$token_type = $this->lexer->NextToken();
					if ($token_type != BBCODE_NL)
					{
						$this->lexer->UngetToken();
					}
					break;
				case 'a':
					$token_type = $this->lexer->NextToken();
					while ($token_type == BBCODE_WS || $token_type == BBCODE_NL)
					{
						$token_type = $this->lexer->NextToken();
					}
					$this->lexer->UngetToken();
					break;
			}
		}
	}

	function Internal_CleanupWSByIteratingPointer($pattern, $pos, $array)
	{
		if (strlen($pattern) <= 0)
		{
			return $pos;
		}
		foreach (str_split($pattern) as $char)
		{
			switch ($char)
			{
				case 's':
					while ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_WS)
						$pos++;
					break;
				case 'n':
					if ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_NL)
					{
						$pos++;
					}
					break;
				case 'a':
					while ($pos < count($array)
						&& (($token = $array[$pos][BBCODE_STACK_TOKEN]) == BBCODE_WS || $token == BBCODE_NL))
						$pos++;
					break;
			}
		}

		return $pos;
	}

	function Internal_LimitText($string, $limit)
	{
		$chunks = preg_split("/([\\x00-\\x20]+)/", $string, -1, PREG_SPLIT_DELIM_CAPTURE);
		$output = "";
		foreach ($chunks as $chunk)
		{
			if (strlen($output) + strlen($chunk) > $limit)
			{
				break;
			}
			$output .= $chunk;
		}
		$output = rtrim($output);

		return $output;
	}

	function Internal_DoLimit()
	{
		$this->Internal_CleanupWSByPoppingStack("a", $this->stack);
		if (strlen($this->limit_tail) > 0)
		{
			$this->stack[] = Array(
				BBCODE_STACK_TOKEN => BBCODE_TEXT,
				BBCODE_STACK_TEXT  => $this->limit_tail,
				BBCODE_STACK_TAG   => false,
				BBCODE_STACK_CLASS => $this->current_class,
			);
		}
		$this->was_limited = true;
	}

	function DoTag($action, $tag_name, $default_value, $params, $contents)
	{
		$tag_rule = @$this->tag_rules[$tag_name];
		switch ($action)
		{
			case BBCODE_CHECK:
				if (isset($tag_rule['allow']))
				{
					foreach ($tag_rule['allow'] as $param => $pattern)
					{
						if ($param == '_content')
						{
							$value = $contents;
						}
						else
						{
							if ($param == '_defaultcontent')
							{
								if (strlen($default_value))
								{
									$value = $default_value;
								}
								else
								{
									$value = $contents;
								}
							}
							else
							{
								if (isset($params[$param]))
								{
									$value = $params[$param];
								}
								else
								{
									$value = @$tag_rule['default'][$param];
								}
							}
						}
						if (!preg_match($pattern, $value))
						{
							return false;
						}
					}

					return true;
				}
				switch (@$tag_rule['mode'])
				{
					default:
					case BBCODE_MODE_SIMPLE:
						$result = true;
						break;
					case BBCODE_MODE_ENHANCED:
						$result = true;
						break;
					case BBCODE_MODE_INTERNAL:
						$result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_CHECK,
							$tag_name, $default_value, $params, $contents);
						break;
					case BBCODE_MODE_LIBRARY:
						$result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_CHECK,
							$tag_name, $default_value, $params, $contents);
						break;
					case BBCODE_MODE_CALLBACK:
						$result = @call_user_func(@$tag_rule['method'], $this, BBCODE_CHECK,
							$tag_name, $default_value, $params, $contents);
						break;
				}

				return $result;
			case BBCODE_OUTPUT:
				if ($this->plain_mode)
				{
					if (!isset($tag_rule['plain_content']))
					{
						$plain_content = Array('_content');
					}
					else
					{
						$plain_content = $tag_rule['plain_content'];
					}
					$result = $possible_content = "";
					foreach ($plain_content as $possible_content)
					{
						if ($possible_content == '_content'
							&& strlen($contents) > 0
						)
						{
							$result = $contents;
							break;
						}
						if (isset($params[$possible_content])
							&& strlen($params[$possible_content]) > 0
						)
						{
							$result = htmlspecialchars($params[$possible_content]);
							break;
						}
					}
					$start = @$tag_rule['plain_start'];
					$end   = @$tag_rule['plain_end'];
					if (isset($tag_rule['plain_link']))
					{
						$link = $possible_content = "";
						foreach ($tag_rule['plain_link'] as $possible_content)
						{
							if ($possible_content == '_content'
								&& strlen($contents) > 0
							)
							{
								$link = $this->UnHTMLEncode(strip_tags($contents));
								break;
							}
							if (isset($params[$possible_content])
								&& strlen($params[$possible_content]) > 0
							)
							{
								$link = $params[$possible_content];
								break;
							}
						}
						$params = @parse_url($link);
						if (!is_array($params))
						{
							$params = Array();
						}
						$params['link'] = $link;
						$params['url']  = $link;
						$start          = $this->FillTemplate($start, $params);
						$end            = $this->FillTemplate($end, $params);
					}

					return $start . $result . $end;
				}
				switch (@$tag_rule['mode'])
				{
					default:
					case BBCODE_MODE_SIMPLE:
						$result = @$tag_rule['simple_start'] . $contents . @$tag_rule['simple_end'];
						break;
					case BBCODE_MODE_ENHANCED:
						$result = $this->Internal_DoEnhancedTag($tag_rule, $params, $contents);
						break;
					case BBCODE_MODE_INTERNAL:
						$result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_OUTPUT,
							$tag_name, $default_value, $params, $contents);
						break;
					case BBCODE_MODE_LIBRARY:
						$result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_OUTPUT,
							$tag_name, $default_value, $params, $contents);
						break;
					case BBCODE_MODE_CALLBACK:
						$result = @call_user_func(@$tag_rule['method'], $this, BBCODE_OUTPUT,
							$tag_name, $default_value, $params, $contents);
						break;
				}

				return $result;
			default:
				return false;
		}
	}

	function Internal_DoEnhancedTag($tag_rule, $params, $contents)
	{
		$params['_content']        = $contents;
		$params['_defaultcontent'] = strlen(@$params['_default']) ? $params['_default'] : $contents;

		return $this->FillTemplate(@$tag_rule['template'], $params, @$tag_rule['default']);
	}

	function Internal_UpdateParamsForMissingEndTag($params)
	{
		switch ($this->tag_marker)
		{
			case '[':
				$tail_marker = ']';
				break;
			case '<':
				$tail_marker = '>';
				break;
			case '{':
				$tail_marker = '}';
				break;
			case '(':
				$tail_marker = ')';
				break;
			default:
				$tail_marker = $this->tag_marker;
				break;
		}
		$params['_endtag'] = $this->tag_marker . '/' . $params['_name'] . $tail_marker;
	}

	function Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule)
	{
		if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, ""))
		{
			$this->stack[] = Array(
				BBCODE_STACK_TOKEN => BBCODE_TEXT,
				BBCODE_STACK_TEXT  => $this->FixupOutput($this->lexer->text),
				BBCODE_STACK_TAG   => false,
				BBCODE_STACK_CLASS => $this->current_class,
			);

			return;
		}
		$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack);
		$output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$tag_params['_default'], $tag_params, "");
		$this->Internal_CleanupWSByEatingInput(@$tag_rule['after_tag']);
		$this->stack[] = Array(
			BBCODE_STACK_TOKEN => BBCODE_TEXT,
			BBCODE_STACK_TEXT  => $output,
			BBCODE_STACK_TAG   => false,
			BBCODE_STACK_CLASS => $this->current_class,
		);
	}

	function Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule)
	{
		$state                 = $this->lexer->SaveState();
		$end_tag               = $this->lexer->tagmarker . "/" . $tag_name . $this->lexer->end_tagmarker;
		$start                 = count($this->stack);
		$this->lexer->verbatim = true;
		while (($token_type = $this->lexer->NextToken()) != BBCODE_EOI)
		{
			// START FIX
			if (strtolower($this->lexer->text) == $end_tag)
			{
				// END FIX
				$end_tag_params = $this->lexer->tag;
				break;
			}
			// START FIX
			if ($this->was_limited)
			{
				continue;
			}
			// END FIX
			if ($this->output_limit > 0
				&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit
			)
			{
				$text = $this->Internal_LimitText($this->lexer->text,
					$this->output_limit - $this->text_length);
				if (strlen($text) > 0)
				{
					$this->text_length += strlen($text);
					$this->stack[] = Array(
						BBCODE_STACK_TOKEN => BBCODE_TEXT,
						BBCODE_STACK_TEXT  => $this->FixupOutput($text),
						BBCODE_STACK_TAG   => false,
						BBCODE_STACK_CLASS => $this->current_class,
					);
				}
				$this->Internal_DoLimit();
				// START FIX
				continue;
				// END FIX
			}
			$this->text_length += strlen($this->lexer->text);
			$this->stack[] = Array(
				BBCODE_STACK_TOKEN => $token_type,
				BBCODE_STACK_TEXT  => htmlspecialchars($this->lexer->text),
				BBCODE_STACK_TAG   => $this->lexer->tag,
				BBCODE_STACK_CLASS => $this->current_class,
			);
		}
		$this->lexer->verbatim = false;
		if ($token_type == BBCODE_EOI)
		{
			$this->lexer->RestoreState($state);
			$this->stack[] = Array(
				BBCODE_STACK_TOKEN => BBCODE_TEXT,
				BBCODE_STACK_TEXT  => $this->FixupOutput($this->lexer->text),
				BBCODE_STACK_TAG   => false,
				BBCODE_STACK_CLASS => $this->current_class,
			);

			return;
		}
		$newstart = $this->Internal_CleanupWSByIteratingPointer(@$tag_rule['after_tag'], $start, $this->stack);
		$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_endtag'], $this->stack);
		$this->Internal_CleanupWSByEatingInput(@$tag_rule['after_endtag']);
		$content = $this->Internal_CollectText($this->stack, $newstart);
		array_splice($this->stack, $start);
		$this->Internal_ComputeCurrentClass();
		$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack);
		$tag_params['_endtag'] = $end_tag_params['_tag'];
		$tag_params['_hasend'] = true;
		$output                = $this->DoTag(BBCODE_OUTPUT, $tag_name,
			@$tag_params['_default'], $tag_params, $content);
		$this->stack[]         = Array(
			BBCODE_STACK_TOKEN => BBCODE_TEXT,
			BBCODE_STACK_TEXT  => $output,
			BBCODE_STACK_TAG   => false,
			BBCODE_STACK_CLASS => $this->current_class,
		);
	}

	function Internal_ParseStartTagToken()
	{
		$tag_params = $this->lexer->tag;
		$tag_name   = @$tag_params['_name'];
		if (!isset($this->tag_rules[$tag_name]))
		{
			$this->stack[] = Array(
				BBCODE_STACK_TOKEN => BBCODE_TEXT,
				BBCODE_STACK_TEXT  => $this->FixupOutput($this->lexer->text),
				BBCODE_STACK_TAG   => false,
				BBCODE_STACK_CLASS => $this->current_class,
			);

			return;
		}
		$tag_rule = $this->tag_rules[$tag_name];
		$allow_in = is_array($tag_rule['allow_in'])
			? $tag_rule['allow_in'] : Array($this->root_class);
		if (!in_array($this->current_class, $allow_in))
		{
			if (!$this->Internal_RewindToClass($allow_in))
			{
				$this->stack[] = Array(
					BBCODE_STACK_TOKEN => BBCODE_TEXT,
					BBCODE_STACK_TEXT  => $this->FixupOutput($this->lexer->text),
					BBCODE_STACK_TAG   => false,
					BBCODE_STACK_CLASS => $this->current_class,
				);

				return;
			}
		}
		$end_tag = isset($tag_rule['end_tag']) ? $tag_rule['end_tag'] : BBCODE_REQUIRED;
		if ($end_tag == BBCODE_PROHIBIT)
		{
			$this->Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule);

			return;
		}
		if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, ""))
		{
			$this->stack[] = Array(
				BBCODE_STACK_TOKEN => BBCODE_TEXT,
				BBCODE_STACK_TEXT  => $this->FixupOutput($this->lexer->text),
				BBCODE_STACK_TAG   => false,
				BBCODE_STACK_CLASS => $this->current_class,
			);

			return;
		}
		if (@$tag_rule['content'] == BBCODE_VERBATIM)
		{
			$this->Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule);

			return;
		}
		if (isset($tag_rule['class']))
		{
			$newclass = $tag_rule['class'];
		}
		else
		{
			$newclass = $this->root_class;
		}
		$this->stack[] = Array(
			BBCODE_STACK_TOKEN => $this->lexer->token,
			BBCODE_STACK_TEXT  => $this->FixupOutput($this->lexer->text),
			BBCODE_STACK_TAG   => $this->lexer->tag,
			BBCODE_STACK_CLASS => ($this->current_class = $newclass),
		);
		if (!isset($this->start_tags[$tag_name]))
		{
			$this->start_tags[$tag_name] = Array(count($this->stack) - 1);
		}
		else
		{
			$this->start_tags[$tag_name][] = count($this->stack) - 1;
		}
	}

	function Internal_ParseEndTagToken()
	{
		$tag_params = $this->lexer->tag;
		$tag_name   = @$tag_params['_name'];
		$contents   = $this->Internal_FinishTag($tag_name);
		if ($contents === false)
		{
			if (@$this->lost_start_tags[$tag_name] > 0)
			{
				$this->lost_start_tags[$tag_name]--;
			}
			else
			{
				$this->stack[] = Array(
					BBCODE_STACK_TOKEN => BBCODE_TEXT,
					BBCODE_STACK_TEXT  => $this->FixupOutput($this->lexer->text),
					BBCODE_STACK_TAG   => false,
					BBCODE_STACK_CLASS => $this->current_class,
				);
			}

			return;
		}
		$start_tag_node   = array_pop($this->stack);
		$start_tag_params = $start_tag_node[BBCODE_STACK_TAG];
		$this->Internal_ComputeCurrentClass();
		$this->Internal_CleanupWSByPoppingStack(@$this->tag_rules[$tag_name]['before_tag'], $this->stack);
		$start_tag_params['_endtag'] = $tag_params['_tag'];
		$start_tag_params['_hasend'] = true;
		$output                      = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$start_tag_params['_default'],
			$start_tag_params, $contents);
		$this->Internal_CleanupWSByEatingInput(@$this->tag_rules[$tag_name]['after_endtag']);
		$this->stack[] = Array(
			BBCODE_STACK_TOKEN => BBCODE_TEXT,
			BBCODE_STACK_TEXT  => $output,
			BBCODE_STACK_TAG   => false,
			BBCODE_STACK_CLASS => $this->current_class,
		);
	}

	function Parse($string)
	{
		$this->lexer        = new BBCodeLexer($string, $this->tag_marker);
		$this->lexer->debug = $this->debug;
		$old_output_limit   = $this->output_limit;
		/** HACK >
		 * if ($this->output_limit > 0) {
		 * if (strlen($string) < $this->output_limit) {
		 * $this->output_limit = 0;
		 * }
		 * else if ($this->limit_precision > 0) {
		 * $guess_length = $this->lexer->GuessTextLength();
		 * if ($guess_length < $this->output_limit * ($this->limit_precision + 1.0)) {
		 * $this->output_limit = 0;
		 * }
		 * else {
		 * }
		 * }
		 * }
		 * < HACK */
		$this->stack           = Array();
		$this->start_tags      = Array();
		$this->lost_start_tags = Array();
		$this->text_length     = 0;
		$this->was_limited     = false;
		if (strlen($this->pre_trim) > 0)
		{
			$this->Internal_CleanupWSByEatingInput($this->pre_trim);
		}
		$newline = $this->plain_mode ? "\n" : "<br />\n";
		while (true)
		{
			if (($token_type = $this->lexer->NextToken()) == BBCODE_EOI)
			{
				break;
			}
			switch ($token_type)
			{
				case BBCODE_TEXT:
					if ($this->output_limit > 0
						&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit
					)
					{
						$text = $this->Internal_LimitText($this->lexer->text,
							$this->output_limit - $this->text_length);
						if (strlen($text) > 0)
						{
							$this->text_length += strlen($text);
							$this->stack[] = Array(
								BBCODE_STACK_TOKEN => BBCODE_TEXT,
								BBCODE_STACK_TEXT  => $this->FixupOutput($text),
								BBCODE_STACK_TAG   => false,
								BBCODE_STACK_CLASS => $this->current_class,
							);
						}
						$this->Internal_DoLimit();
						break 2;
					}
					$this->text_length += strlen($this->lexer->text);
					$this->stack[] = Array(
						BBCODE_STACK_TOKEN => BBCODE_TEXT,
						BBCODE_STACK_TEXT  => $this->FixupOutput($this->lexer->text),
						BBCODE_STACK_TAG   => false,
						BBCODE_STACK_CLASS => $this->current_class,
					);
					break;
				case BBCODE_WS:
					if ($this->output_limit > 0
						&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit
					)
					{
						$this->Internal_DoLimit();
						break 2;
					}
					$this->text_length += strlen($this->lexer->text);
					$this->stack[] = Array(
						BBCODE_STACK_TOKEN => BBCODE_WS,
						BBCODE_STACK_TEXT  => $this->lexer->text,
						BBCODE_STACK_TAG   => false,
						BBCODE_STACK_CLASS => $this->current_class,
					);
					break;
				case BBCODE_NL:
					if ($this->ignore_newlines)
					{
						if ($this->output_limit > 0
							&& $this->text_length + 1 >= $this->output_limit
						)
						{
							$this->Internal_DoLimit();
							break 2;
						}
						$this->text_length += 1;
						$this->stack[] = Array(
							BBCODE_STACK_TOKEN => BBCODE_WS,
							BBCODE_STACK_TEXT  => "\n",
							BBCODE_STACK_TAG   => false,
							BBCODE_STACK_CLASS => $this->current_class,
						);
					}
					else
					{
						$this->Internal_CleanupWSByPoppingStack("s", $this->stack);
						if ($this->output_limit > 0
							&& $this->text_length + 1 >= $this->output_limit
						)
						{
							$this->Internal_DoLimit();
							break 2;
						}
						$this->text_length += 1;
						$this->stack[] = Array(
							BBCODE_STACK_TOKEN => BBCODE_NL,
							BBCODE_STACK_TEXT  => $newline,
							BBCODE_STACK_TAG   => false,
							BBCODE_STACK_CLASS => $this->current_class,
						);
						$this->Internal_CleanupWSByEatingInput("s");
					}
					break;
				case BBCODE_TAG:
					$this->Internal_ParseStartTagToken();
					break;
				case BBCODE_ENDTAG:
					$this->Internal_ParseEndTagToken();
					break;
				default:
					break;
			}
		}
		if (strlen($this->post_trim) > 0)
		{
			$this->Internal_CleanupWSByPoppingStack($this->post_trim, $this->stack);
		}
		$result             = $this->Internal_GenerateOutput(0);
		$result             = $this->Internal_CollectTextReverse($result, count($result) - 1);
		$this->output_limit = $old_output_limit;
		if ($this->plain_mode)
		{
			$result = preg_replace("/[\\x00-\\x09\\x0B-\\x20]+/", " ", $result);
			$result = preg_replace("/(?:[\\x20]*\\n){2,}[\\x20]*/", "\n\n", $result);
			$result = trim($result);
		}

		return $result;
	}
}


Anon7 - 2022
AnonSec Team