Verzeichnisstruktur phpBB-3.2.0
- Veröffentlicht
- 06.01.2017
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
message_parser.php
0001 <?php
0002 /**
0003 *
0004 * This file is part of the phpBB Forum Software package.
0005 *
0006 * @copyright (c) phpBB Limited <https://www.phpbb.com>
0007 * @license GNU General Public License, version 2 (GPL-2.0)
0008 *
0009 * For full copyright and license information, please see
0010 * the docs/CREDITS.txt file.
0011 *
0012 */
0013
0014 /**
0015 * @ignore
0016 */
0017 if (!defined('IN_PHPBB'))
0018 {
0019 exit;
0020 }
0021
0022 if (!class_exists('bbcode'))
0023 {
0024 // The following lines are for extensions which include message_parser.php
0025 // while $phpbb_root_path and $phpEx are out of the script scope
0026 // which may lead to the 'Undefined variable' and 'failed to open stream' errors
0027 if (!isset($phpbb_root_path))
0028 {
0029 global $phpbb_root_path;
0030 }
0031
0032 if (!isset($phpEx))
0033 {
0034 global $phpEx;
0035 }
0036
0037 include($phpbb_root_path . 'includes/bbcode.' . $phpEx);
0038 }
0039
0040 /**
0041 * BBCODE FIRSTPASS
0042 * BBCODE first pass class (functions for parsing messages for db storage)
0043 */
0044 class bbcode_firstpass extends bbcode
0045 {
0046 var $message = '';
0047 var $warn_msg = array();
0048 var $parsed_items = array();
0049
0050 /**
0051 * Parse BBCode
0052 */
0053 function parse_bbcode()
0054 {
0055 if (!$this->bbcodes)
0056 {
0057 $this->bbcode_init();
0058 }
0059
0060 global $user;
0061
0062 $this->bbcode_bitfield = '';
0063 $bitfield = new bitfield();
0064
0065 foreach ($this->bbcodes as $bbcode_name => $bbcode_data)
0066 {
0067 if (isset($bbcode_data['disabled']) && $bbcode_data['disabled'])
0068 {
0069 foreach ($bbcode_data['regexp'] as $regexp => $replacement)
0070 {
0071 if (preg_match($regexp, $this->message))
0072 {
0073 $this->warn_msg[] = sprintf($user->lang['UNAUTHORISED_BBCODE'] , '[' . $bbcode_name . ']');
0074 continue;
0075 }
0076 }
0077 }
0078 else
0079 {
0080 foreach ($bbcode_data['regexp'] as $regexp => $replacement)
0081 {
0082 // The pattern gets compiled and cached by the PCRE extension,
0083 // it should not demand recompilation
0084 if (preg_match($regexp, $this->message))
0085 {
0086 if (is_callable($replacement))
0087 {
0088 $this->message = preg_replace_callback($regexp, $replacement, $this->message);
0089 }
0090 else
0091 {
0092 $this->message = preg_replace($regexp, $replacement, $this->message);
0093 }
0094 $bitfield->set($bbcode_data['bbcode_id']);
0095 }
0096 }
0097 }
0098 }
0099
0100 $this->bbcode_bitfield = $bitfield->get_base64();
0101 }
0102
0103 /**
0104 * Prepare some bbcodes for better parsing
0105 */
0106 function prepare_bbcodes()
0107 {
0108 // Ok, seems like users instead want the no-parsing of urls, smilies, etc. after and before and within quote tags being tagged as "not a bug".
0109 // Fine by me ;) Will ease our live... but do not come back and cry at us, we won't hear you.
0110
0111 /* Add newline at the end and in front of each quote block to prevent parsing errors (urls, smilies, etc.)
0112 if (strpos($this->message, '[quote') !== false && strpos($this->message, '[/quote]') !== false)
0113 {
0114 $this->message = str_replace("\r\n", "\n", $this->message);
0115
0116 // We strip newlines and spaces after and before quotes in quotes (trimming) and then add exactly one newline
0117 $this->message = preg_replace('#\[quote(=".*?")?\]\s*(.*?)\s*\[/quote\]#siu', '[quote\1]' . "\n" . '\2' ."\n[/quote]", $this->message);
0118 }
0119 */
0120
0121 // Add other checks which needs to be placed before actually parsing anything (be it bbcodes, smilies, urls...)
0122 }
0123
0124 /**
0125 * Init bbcode data for later parsing
0126 */
0127 function bbcode_init($allow_custom_bbcode = true)
0128 {
0129 global $phpbb_dispatcher;
0130
0131 static $rowset;
0132
0133 $bbcode_class = $this;
0134
0135 // This array holds all bbcode data. BBCodes will be processed in this
0136 // order, so it is important to keep [code] in first position and
0137 // [quote] in second position.
0138 // To parse multiline URL we enable dotall option setting only for URL text
0139 // but not for link itself, thus [url][/url] is not affected.
0140 //
0141 // To perform custom validation in extension, use $this->validate_bbcode_by_extension()
0142 // method which accepts variable number of parameters
0143 $this->bbcodes = array(
0144 'code' => array('bbcode_id' => BBCODE_ID_CODE, 'regexp' => array('#\[code(?:=([a-z]+))?\](.+\[/code\])#uis' => function ($match) use($bbcode_class)
0145 {
0146 return $bbcode_class->bbcode_code($match[1], $match[2]);
0147 }
0148 )),
0149 'quote' => array('bbcode_id' => BBCODE_ID_QUOTE, 'regexp' => array('#\[quote(?:="(.*?)")?\](.+)\[/quote\]#uis' => function ($match) use($bbcode_class)
0150 {
0151 return $bbcode_class->bbcode_quote($match[0]);
0152 }
0153 )),
0154 'attachment' => array('bbcode_id' => BBCODE_ID_ATTACH, 'regexp' => array('#\[attachment=([0-9]+)\](.*?)\[/attachment\]#uis' => function ($match) use($bbcode_class)
0155 {
0156 return $bbcode_class->bbcode_attachment($match[1], $match[2]);
0157 }
0158 )),
0159 'b' => array('bbcode_id' => BBCODE_ID_B, 'regexp' => array('#\[b\](.*?)\[/b\]#uis' => function ($match) use($bbcode_class)
0160 {
0161 return $bbcode_class->bbcode_strong($match[1]);
0162 }
0163 )),
0164 'i' => array('bbcode_id' => BBCODE_ID_I, 'regexp' => array('#\[i\](.*?)\[/i\]#uis' => function ($match) use($bbcode_class)
0165 {
0166 return $bbcode_class->bbcode_italic($match[1]);
0167 }
0168 )),
0169 'url' => array('bbcode_id' => BBCODE_ID_URL, 'regexp' => array('#\[url(=(.*))?\](?(1)((?s).*(?-s))|(.*))\[/url\]#uiU' => function ($match) use($bbcode_class)
0170 {
0171 return $bbcode_class->validate_url($match[2], ($match[3]) ? $match[3] : $match[4]);
0172 }
0173 )),
0174 'img' => array('bbcode_id' => BBCODE_ID_IMG, 'regexp' => array('#\[img\](.*)\[/img\]#uiU' => function ($match) use($bbcode_class)
0175 {
0176 return $bbcode_class->bbcode_img($match[1]);
0177 }
0178 )),
0179 'size' => array('bbcode_id' => BBCODE_ID_SIZE, 'regexp' => array('#\[size=([\-\+]?\d+)\](.*?)\[/size\]#uis' => function ($match) use($bbcode_class)
0180 {
0181 return $bbcode_class->bbcode_size($match[1], $match[2]);
0182 }
0183 )),
0184 'color' => array('bbcode_id' => BBCODE_ID_COLOR, 'regexp' => array('!\[color=(#[0-9a-f]{3}|#[0-9a-f]{6}|[a-z\-]+)\](.*?)\[/color\]!uis' => function ($match) use($bbcode_class)
0185 {
0186 return $bbcode_class->bbcode_color($match[1], $match[2]);
0187 }
0188 )),
0189 'u' => array('bbcode_id' => BBCODE_ID_U, 'regexp' => array('#\[u\](.*?)\[/u\]#uis' => function ($match) use($bbcode_class)
0190 {
0191 return $bbcode_class->bbcode_underline($match[1]);
0192 }
0193 )),
0194 'list' => array('bbcode_id' => BBCODE_ID_LIST, 'regexp' => array('#\[list(?:=(?:[a-z0-9]|disc|circle|square))?].*\[/list]#uis' => function ($match) use($bbcode_class)
0195 {
0196 return $bbcode_class->bbcode_parse_list($match[0]);
0197 }
0198 )),
0199 'email' => array('bbcode_id' => BBCODE_ID_EMAIL, 'regexp' => array('#\[email=?(.*?)?\](.*?)\[/email\]#uis' => function ($match) use($bbcode_class)
0200 {
0201 return $bbcode_class->validate_email($match[1], $match[2]);
0202 }
0203 )),
0204 'flash' => array('bbcode_id' => BBCODE_ID_FLASH, 'regexp' => array('#\[flash=([0-9]+),([0-9]+)\](.*?)\[/flash\]#ui' => function ($match) use($bbcode_class)
0205 {
0206 return $bbcode_class->bbcode_flash($match[1], $match[2], $match[3]);
0207 }
0208 ))
0209 );
0210
0211 // Zero the parsed items array
0212 $this->parsed_items = array();
0213
0214 foreach ($this->bbcodes as $tag => $bbcode_data)
0215 {
0216 $this->parsed_items[$tag] = 0;
0217 }
0218
0219 if (!$allow_custom_bbcode)
0220 {
0221 return;
0222 }
0223
0224 if (!is_array($rowset))
0225 {
0226 global $db;
0227 $rowset = array();
0228
0229 $sql = 'SELECT *
0230 FROM ' . BBCODES_TABLE;
0231 $result = $db->sql_query($sql);
0232
0233 while ($row = $db->sql_fetchrow($result))
0234 {
0235 $rowset[] = $row;
0236 }
0237 $db->sql_freeresult($result);
0238 }
0239
0240 foreach ($rowset as $row)
0241 {
0242 $this->bbcodes[$row['bbcode_tag']] = array(
0243 'bbcode_id' => (int) $row['bbcode_id'],
0244 'regexp' => array($row['first_pass_match'] => str_replace('$uid', $this->bbcode_uid, $row['first_pass_replace']))
0245 );
0246 }
0247
0248 $bbcodes = $this->bbcodes;
0249
0250 /**
0251 * Event to modify the bbcode data for later parsing
0252 *
0253 * @event core.modify_bbcode_init
0254 * @var array bbcodes Array of bbcode data for use in parsing
0255 * @var array rowset Array of bbcode data from the database
0256 * @since 3.1.0-a3
0257 */
0258 $vars = array('bbcodes', 'rowset');
0259 extract($phpbb_dispatcher->trigger_event('core.modify_bbcode_init', compact($vars)));
0260
0261 $this->bbcodes = $bbcodes;
0262 }
0263
0264 /**
0265 * Making some pre-checks for bbcodes as well as increasing the number of parsed items
0266 */
0267 function check_bbcode($bbcode, &$in)
0268 {
0269 // when using the /e modifier, preg_replace slashes double-quotes but does not
0270 // seem to slash anything else
0271 $in = str_replace("\r\n", "\n", str_replace('\"', '"', $in));
0272
0273 // Trimming here to make sure no empty bbcodes are parsed accidently
0274 if (trim($in) == '')
0275 {
0276 return false;
0277 }
0278
0279 $this->parsed_items[$bbcode]++;
0280
0281 return true;
0282 }
0283
0284 /**
0285 * Transform some characters in valid bbcodes
0286 */
0287 function bbcode_specialchars($text)
0288 {
0289 $str_from = array('<', '>', '[', ']', '.', ':');
0290 $str_to = array('<', '>', '[', ']', '.', ':');
0291
0292 return str_replace($str_from, $str_to, $text);
0293 }
0294
0295 /**
0296 * Parse size tag
0297 */
0298 function bbcode_size($stx, $in)
0299 {
0300 global $user, $config;
0301
0302 if (!$this->check_bbcode('size', $in))
0303 {
0304 return $in;
0305 }
0306
0307 if ($config['max_' . $this->mode . '_font_size'] && $config['max_' . $this->mode . '_font_size'] < $stx)
0308 {
0309 $this->warn_msg[] = $user->lang('MAX_FONT_SIZE_EXCEEDED', (int) $config['max_' . $this->mode . '_font_size']);
0310
0311 return '[size=' . $stx . ']' . $in . '[/size]';
0312 }
0313
0314 // Do not allow size=0
0315 if ($stx <= 0)
0316 {
0317 return '[size=' . $stx . ']' . $in . '[/size]';
0318 }
0319
0320 return '[size=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/size:' . $this->bbcode_uid . ']';
0321 }
0322
0323 /**
0324 * Parse color tag
0325 */
0326 function bbcode_color($stx, $in)
0327 {
0328 if (!$this->check_bbcode('color', $in))
0329 {
0330 return $in;
0331 }
0332
0333 return '[color=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/color:' . $this->bbcode_uid . ']';
0334 }
0335
0336 /**
0337 * Parse u tag
0338 */
0339 function bbcode_underline($in)
0340 {
0341 if (!$this->check_bbcode('u', $in))
0342 {
0343 return $in;
0344 }
0345
0346 return '[u:' . $this->bbcode_uid . ']' . $in . '[/u:' . $this->bbcode_uid . ']';
0347 }
0348
0349 /**
0350 * Parse b tag
0351 */
0352 function bbcode_strong($in)
0353 {
0354 if (!$this->check_bbcode('b', $in))
0355 {
0356 return $in;
0357 }
0358
0359 return '[b:' . $this->bbcode_uid . ']' . $in . '[/b:' . $this->bbcode_uid . ']';
0360 }
0361
0362 /**
0363 * Parse i tag
0364 */
0365 function bbcode_italic($in)
0366 {
0367 if (!$this->check_bbcode('i', $in))
0368 {
0369 return $in;
0370 }
0371
0372 return '[i:' . $this->bbcode_uid . ']' . $in . '[/i:' . $this->bbcode_uid . ']';
0373 }
0374
0375 /**
0376 * Parse img tag
0377 */
0378 function bbcode_img($in)
0379 {
0380 global $user, $config;
0381
0382 if (!$this->check_bbcode('img', $in))
0383 {
0384 return $in;
0385 }
0386
0387 $in = trim($in);
0388 $error = false;
0389
0390 $in = str_replace(' ', '%20', $in);
0391
0392 // Checking urls
0393 if (!preg_match('#^' . get_preg_expression('url') . '$#iu', $in) && !preg_match('#^' . get_preg_expression('www_url') . '$#iu', $in))
0394 {
0395 return '[img]' . $in . '[/img]';
0396 }
0397
0398 // Try to cope with a common user error... not specifying a protocol but only a subdomain
0399 if (!preg_match('#^[a-z0-9]+://#i', $in))
0400 {
0401 $in = 'http://' . $in;
0402 }
0403
0404 if ($config['max_' . $this->mode . '_img_height'] || $config['max_' . $this->mode . '_img_width'])
0405 {
0406 $imagesize = new \FastImageSize\FastImageSize();
0407 $size_info = $imagesize->getImageSize(htmlspecialchars_decode($in));
0408
0409 if ($size_info === false)
0410 {
0411 $error = true;
0412 $this->warn_msg[] = $user->lang['UNABLE_GET_IMAGE_SIZE'];
0413 }
0414 else
0415 {
0416 if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $size_info['height'])
0417 {
0418 $error = true;
0419 $this->warn_msg[] = $user->lang('MAX_IMG_HEIGHT_EXCEEDED', (int) $config['max_' . $this->mode . '_img_height']);
0420 }
0421
0422 if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $size_info['width'])
0423 {
0424 $error = true;
0425 $this->warn_msg[] = $user->lang('MAX_IMG_WIDTH_EXCEEDED', (int) $config['max_' . $this->mode . '_img_width']);
0426 }
0427 }
0428 }
0429
0430 if ($error || $this->path_in_domain($in))
0431 {
0432 return '[img]' . $in . '[/img]';
0433 }
0434
0435 return '[img:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($in) . '[/img:' . $this->bbcode_uid . ']';
0436 }
0437
0438 /**
0439 * Parse flash tag
0440 */
0441 function bbcode_flash($width, $height, $in)
0442 {
0443 global $user, $config;
0444
0445 if (!$this->check_bbcode('flash', $in))
0446 {
0447 return $in;
0448 }
0449
0450 $in = trim($in);
0451 $error = false;
0452
0453 // Do not allow 0-sizes generally being entered
0454 if ($width <= 0 || $height <= 0)
0455 {
0456 return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]';
0457 }
0458
0459 $in = str_replace(' ', '%20', $in);
0460
0461 // Make sure $in is a URL.
0462 if (!preg_match('#^' . get_preg_expression('url') . '$#iu', $in) &&
0463 !preg_match('#^' . get_preg_expression('www_url') . '$#iu', $in))
0464 {
0465 return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]';
0466 }
0467
0468 // Apply the same size checks on flash files as on images
0469 if ($config['max_' . $this->mode . '_img_height'] || $config['max_' . $this->mode . '_img_width'])
0470 {
0471 if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $height)
0472 {
0473 $error = true;
0474 $this->warn_msg[] = $user->lang('MAX_FLASH_HEIGHT_EXCEEDED', (int) $config['max_' . $this->mode . '_img_height']);
0475 }
0476
0477 if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $width)
0478 {
0479 $error = true;
0480 $this->warn_msg[] = $user->lang('MAX_FLASH_WIDTH_EXCEEDED', (int) $config['max_' . $this->mode . '_img_width']);
0481 }
0482 }
0483
0484 if ($error || $this->path_in_domain($in))
0485 {
0486 return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]';
0487 }
0488
0489 return '[flash=' . $width . ',' . $height . ':' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($in) . '[/flash:' . $this->bbcode_uid . ']';
0490 }
0491
0492 /**
0493 * Parse inline attachments [ia]
0494 */
0495 function bbcode_attachment($stx, $in)
0496 {
0497 if (!$this->check_bbcode('attachment', $in))
0498 {
0499 return $in;
0500 }
0501
0502 return '[attachment=' . $stx . ':' . $this->bbcode_uid . ']<!-- ia' . $stx . ' -->' . trim($in) . '<!-- ia' . $stx . ' -->[/attachment:' . $this->bbcode_uid . ']';
0503 }
0504
0505 /**
0506 * Parse code text from code tag
0507 * @access private
0508 */
0509 function bbcode_parse_code($stx, &$code)
0510 {
0511 switch (strtolower($stx))
0512 {
0513 case 'php':
0514
0515 $remove_tags = false;
0516
0517 $str_from = array('<', '>', '[', ']', '.', ':', ':');
0518 $str_to = array('<', '>', '[', ']', '.', ':', ':');
0519 $code = str_replace($str_from, $str_to, $code);
0520
0521 if (!preg_match('/\<\?.*?\?\>/is', $code))
0522 {
0523 $remove_tags = true;
0524 $code = "<?php $code ?>";
0525 }
0526
0527 $conf = array('highlight.bg', 'highlight.comment', 'highlight.default', 'highlight.html', 'highlight.keyword', 'highlight.string');
0528 foreach ($conf as $ini_var)
0529 {
0530 @ini_set($ini_var, str_replace('highlight.', 'syntax', $ini_var));
0531 }
0532
0533 // Because highlight_string is specialcharing the text (but we already did this before), we have to reverse this in order to get correct results
0534 $code = htmlspecialchars_decode($code);
0535 $code = highlight_string($code, true);
0536
0537 $str_from = array('<span style="color: ', '<font color="syntax', '</font>', '<code>', '</code>','[', ']', '.', ':');
0538 $str_to = array('<span class="', '<span class="syntax', '</span>', '', '', '[', ']', '.', ':');
0539
0540 if ($remove_tags)
0541 {
0542 $str_from[] = '<span class="syntaxdefault"><?php </span>';
0543 $str_to[] = '';
0544 $str_from[] = '<span class="syntaxdefault"><?php ';
0545 $str_to[] = '<span class="syntaxdefault">';
0546 }
0547
0548 $code = str_replace($str_from, $str_to, $code);
0549 $code = preg_replace('#^(<span class="[a-z_]+">)\n?(.*?)\n?(</span>)$#is', '$1$2$3', $code);
0550
0551 if ($remove_tags)
0552 {
0553 $code = preg_replace('#(<span class="[a-z]+">)?\?>(</span>)#', '$1 $2', $code);
0554 }
0555
0556 $code = preg_replace('#^<span class="[a-z]+"><span class="([a-z]+)">(.*)</span></span>#s', '<span class="$1">$2</span>', $code);
0557 $code = preg_replace('#(?:\s++| )*+</span>$#u', '</span>', $code);
0558
0559 // remove newline at the end
0560 if (!empty($code) && substr($code, -1) == "\n")
0561 {
0562 $code = substr($code, 0, -1);
0563 }
0564
0565 return "[code=$stx:" . $this->bbcode_uid . ']' . $code . '[/code:' . $this->bbcode_uid . ']';
0566 break;
0567
0568 default:
0569 return '[code:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($code) . '[/code:' . $this->bbcode_uid . ']';
0570 break;
0571 }
0572 }
0573
0574 /**
0575 * Parse code tag
0576 * Expects the argument to start right after the opening [code] tag and to end with [/code]
0577 */
0578 function bbcode_code($stx, $in)
0579 {
0580 if (!$this->check_bbcode('code', $in))
0581 {
0582 return $in;
0583 }
0584
0585 // We remove the hardcoded elements from the code block here because it is not used in code blocks
0586 // Having it here saves us one preg_replace per message containing [code] blocks
0587 // Additionally, magic url parsing should go after parsing bbcodes, but for safety those are stripped out too...
0588 $htm_match = get_preg_expression('bbcode_htm');
0589 unset($htm_match[4], $htm_match[5]);
0590 $htm_replace = array('\1', '\1', '\2', '\1');
0591
0592 $out = $code_block = '';
0593 $open = 1;
0594
0595 while ($in)
0596 {
0597 // Determine position and tag length of next code block
0598 preg_match('#(.*?)(\[code(?:=([a-z]+))?\])(.+)#is', $in, $buffer);
0599 $pos = (isset($buffer[1])) ? strlen($buffer[1]) : false;
0600 $tag_length = (isset($buffer[2])) ? strlen($buffer[2]) : false;
0601
0602 // Determine position of ending code tag
0603 $pos2 = stripos($in, '[/code]');
0604
0605 // Which is the next block, ending code or code block
0606 if ($pos !== false && $pos < $pos2)
0607 {
0608 // Open new block
0609 if (!$open)
0610 {
0611 $out .= substr($in, 0, $pos);
0612 $in = substr($in, $pos);
0613 $stx = (isset($buffer[3])) ? $buffer[3] : '';
0614 $code_block = '';
0615 }
0616 else
0617 {
0618 // Already opened block, just append to the current block
0619 $code_block .= substr($in, 0, $pos) . ((isset($buffer[2])) ? $buffer[2] : '');
0620 $in = substr($in, $pos);
0621 }
0622
0623 $in = substr($in, $tag_length);
0624 $open++;
0625 }
0626 else
0627 {
0628 // Close the block
0629 if ($open == 1)
0630 {
0631 $code_block .= substr($in, 0, $pos2);
0632 $code_block = preg_replace($htm_match, $htm_replace, $code_block);
0633
0634 // Parse this code block
0635 $out .= $this->bbcode_parse_code($stx, $code_block);
0636 $code_block = '';
0637 $open--;
0638 }
0639 else if ($open)
0640 {
0641 // Close one open tag... add to the current code block
0642 $code_block .= substr($in, 0, $pos2 + 7);
0643 $open--;
0644 }
0645 else
0646 {
0647 // end code without opening code... will be always outside code block
0648 $out .= substr($in, 0, $pos2 + 7);
0649 }
0650
0651 $in = substr($in, $pos2 + 7);
0652 }
0653 }
0654
0655 // if now $code_block has contents we need to parse the remaining code while removing the last closing tag to match up.
0656 if ($code_block)
0657 {
0658 $code_block = substr($code_block, 0, -7);
0659 $code_block = preg_replace($htm_match, $htm_replace, $code_block);
0660
0661 $out .= $this->bbcode_parse_code($stx, $code_block);
0662 }
0663
0664 return $out;
0665 }
0666
0667 /**
0668 * Parse list bbcode
0669 * Expects the argument to start with a tag
0670 */
0671 function bbcode_parse_list($in)
0672 {
0673 if (!$this->check_bbcode('list', $in))
0674 {
0675 return $in;
0676 }
0677
0678 // $tok holds characters to stop at. Since the string starts with a '[' we'll get everything up to the first ']' which should be the opening [list] tag
0679 $tok = ']';
0680 $out = '[';
0681
0682 // First character is [
0683 $in = substr($in, 1);
0684 $list_end_tags = $item_end_tags = array();
0685
0686 do
0687 {
0688 $pos = strlen($in);
0689
0690 for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i)
0691 {
0692 $tmp_pos = strpos($in, $tok[$i]);
0693
0694 if ($tmp_pos !== false && $tmp_pos < $pos)
0695 {
0696 $pos = $tmp_pos;
0697 }
0698 }
0699
0700 $buffer = substr($in, 0, $pos);
0701 $tok = $in[$pos];
0702
0703 $in = substr($in, $pos + 1);
0704
0705 if ($tok == ']')
0706 {
0707 // if $tok is ']' the buffer holds a tag
0708 if (strtolower($buffer) == '/list' && sizeof($list_end_tags))
0709 {
0710 // valid [/list] tag, check nesting so that we don't hit false positives
0711 if (sizeof($item_end_tags) && sizeof($item_end_tags) >= sizeof($list_end_tags))
0712 {
0713 // current li tag has not been closed
0714 $out = preg_replace('/\n?\[$/', '[', $out) . array_pop($item_end_tags) . '][';
0715 }
0716
0717 $out .= array_pop($list_end_tags) . ']';
0718 $tok = '[';
0719 }
0720 else if (preg_match('#^list(=[0-9a-z]+)?$#i', $buffer, $m))
0721 {
0722 // sub-list, add a closing tag
0723 if (empty($m[1]) || preg_match('/^=(?:disc|square|circle)$/i', $m[1]))
0724 {
0725 array_push($list_end_tags, '/list:u:' . $this->bbcode_uid);
0726 }
0727 else
0728 {
0729 array_push($list_end_tags, '/list:o:' . $this->bbcode_uid);
0730 }
0731 $out .= 'list' . substr($buffer, 4) . ':' . $this->bbcode_uid . ']';
0732 $tok = '[';
0733 }
0734 else
0735 {
0736 if (($buffer == '*' || substr($buffer, -2) == '[*') && sizeof($list_end_tags))
0737 {
0738 // the buffer holds a bullet tag and we have a [list] tag open
0739 if (sizeof($item_end_tags) >= sizeof($list_end_tags))
0740 {
0741 if (substr($buffer, -2) == '[*')
0742 {
0743 $out .= substr($buffer, 0, -2) . '[';
0744 }
0745 // current li tag has not been closed
0746 if (preg_match('/\n\[$/', $out, $m))
0747 {
0748 $out = preg_replace('/\n\[$/', '[', $out);
0749 $buffer = array_pop($item_end_tags) . "]\n[*:" . $this->bbcode_uid;
0750 }
0751 else
0752 {
0753 $buffer = array_pop($item_end_tags) . '][*:' . $this->bbcode_uid;
0754 }
0755 }
0756 else
0757 {
0758 $buffer = '*:' . $this->bbcode_uid;
0759 }
0760
0761 $item_end_tags[] = '/*:m:' . $this->bbcode_uid;
0762 }
0763 else if ($buffer == '/*')
0764 {
0765 array_pop($item_end_tags);
0766 $buffer = '/*:' . $this->bbcode_uid;
0767 }
0768
0769 $out .= $buffer . $tok;
0770 $tok = '[]';
0771 }
0772 }
0773 else
0774 {
0775 // Not within a tag, just add buffer to the return string
0776 $out .= $buffer . $tok;
0777 $tok = ($tok == '[') ? ']' : '[]';
0778 }
0779 }
0780 while ($in);
0781
0782 // do we have some tags open? close them now
0783 if (sizeof($item_end_tags))
0784 {
0785 $out .= '[' . implode('][', $item_end_tags) . ']';
0786 }
0787 if (sizeof($list_end_tags))
0788 {
0789 $out .= '[' . implode('][', $list_end_tags) . ']';
0790 }
0791
0792 return $out;
0793 }
0794
0795 /**
0796 * Parse quote bbcode
0797 * Expects the argument to start with a tag
0798 */
0799 function bbcode_quote($in)
0800 {
0801 $in = str_replace("\r\n", "\n", str_replace('\"', '"', trim($in)));
0802
0803 if (!$in)
0804 {
0805 return '';
0806 }
0807
0808 // To let the parser not catch tokens within quote_username quotes we encode them before we start this...
0809 $in = preg_replace_callback('#quote="(.*?)"\]#i', function ($match) {
0810 return 'quote="' . str_replace(array('[', ']', '\\\"'), array('[', ']', '\"'), $match[1]) . '"]';
0811 }, $in);
0812
0813 $tok = ']';
0814 $out = '[';
0815
0816 $in = substr($in, 1);
0817 $close_tags = $error_ary = array();
0818 $buffer = '';
0819
0820 do
0821 {
0822 $pos = strlen($in);
0823 for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i)
0824 {
0825 $tmp_pos = strpos($in, $tok[$i]);
0826 if ($tmp_pos !== false && $tmp_pos < $pos)
0827 {
0828 $pos = $tmp_pos;
0829 }
0830 }
0831
0832 $buffer .= substr($in, 0, $pos);
0833 $tok = $in[$pos];
0834 $in = substr($in, $pos + 1);
0835
0836 if ($tok == ']')
0837 {
0838 if (strtolower($buffer) == '/quote' && sizeof($close_tags) && substr($out, -1, 1) == '[')
0839 {
0840 // we have found a closing tag
0841 $out .= array_pop($close_tags) . ']';
0842 $tok = '[';
0843 $buffer = '';
0844
0845 /* Add space at the end of the closing tag if not happened before to allow following urls/smilies to be parsed correctly
0846 * Do not try to think for the user. :/ Do not parse urls/smilies if there is no space - is the same as with other bbcodes too.
0847 * Also, we won't have any spaces within $in anyway, only adding up spaces -> #10982
0848 if (!$in || $in[0] !== ' ')
0849 {
0850 $out .= ' ';
0851 }*/
0852 }
0853 else if (preg_match('#^quote(?:="(.*?)")?$#is', $buffer, $m) && substr($out, -1, 1) == '[')
0854 {
0855 $this->parsed_items['quote']++;
0856 array_push($close_tags, '/quote:' . $this->bbcode_uid);
0857
0858 if (isset($m[1]) && $m[1])
0859 {
0860 $username = str_replace(array('[', ']'), array('[', ']'), $m[1]);
0861 $username = preg_replace('#\[(?!b|i|u|color|url|email|/b|/i|/u|/color|/url|/email)#iU', '[$1', $username);
0862
0863 $end_tags = array();
0864 $error = false;
0865
0866 preg_match_all('#\[((?:/)?(?:[a-z]+))#i', $username, $tags);
0867 foreach ($tags[1] as $tag)
0868 {
0869 if ($tag[0] != '/')
0870 {
0871 $end_tags[] = '/' . $tag;
0872 }
0873 else
0874 {
0875 $end_tag = array_pop($end_tags);
0876 $error = ($end_tag != $tag) ? true : false;
0877 }
0878 }
0879
0880 if ($error)
0881 {
0882 $username = $m[1];
0883 }
0884
0885 $out .= 'quote="' . $username . '":' . $this->bbcode_uid . ']';
0886 }
0887 else
0888 {
0889 $out .= 'quote:' . $this->bbcode_uid . ']';
0890 }
0891
0892 $tok = '[';
0893 $buffer = '';
0894 }
0895 else if (preg_match('#^quote="(.*?)#is', $buffer, $m))
0896 {
0897 // the buffer holds an invalid opening tag
0898 $buffer .= ']';
0899 }
0900 else
0901 {
0902 $out .= $buffer . $tok;
0903 $tok = '[]';
0904 $buffer = '';
0905 }
0906 }
0907 else
0908 {
0909 /**
0910 * Old quote code working fine, but having errors listed in bug #3572
0911 *
0912 * $out .= $buffer . $tok;
0913 * $tok = ($tok == '[') ? ']' : '[]';
0914 * $buffer = '';
0915 */
0916
0917 $out .= $buffer . $tok;
0918
0919 if ($tok == '[')
0920 {
0921 // Search the text for the next tok... if an ending quote comes first, then change tok to []
0922 $pos1 = stripos($in, '[/quote');
0923 // If the token ] comes first, we change it to ]
0924 $pos2 = strpos($in, ']');
0925 // If the token [ comes first, we change it to [
0926 $pos3 = strpos($in, '[');
0927
0928 if ($pos1 !== false && ($pos2 === false || $pos1 < $pos2) && ($pos3 === false || $pos1 < $pos3))
0929 {
0930 $tok = '[]';
0931 }
0932 else if ($pos3 !== false && ($pos2 === false || $pos3 < $pos2))
0933 {
0934 $tok = '[';
0935 }
0936 else
0937 {
0938 $tok = ']';
0939 }
0940 }
0941 else
0942 {
0943 $tok = '[]';
0944 }
0945 $buffer = '';
0946 }
0947 }
0948 while ($in);
0949
0950 $out .= $buffer;
0951
0952 if (sizeof($close_tags))
0953 {
0954 $out .= '[' . implode('][', $close_tags) . ']';
0955 }
0956
0957 foreach ($error_ary as $error_msg)
0958 {
0959 $this->warn_msg[] = $error_msg;
0960 }
0961
0962 return $out;
0963 }
0964
0965 /**
0966 * Validate email
0967 */
0968 function validate_email($var1, $var2)
0969 {
0970 $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1)));
0971 $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2)));
0972
0973 $txt = $var2;
0974 $email = ($var1) ? $var1 : $var2;
0975
0976 $validated = true;
0977
0978 if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email))
0979 {
0980 $validated = false;
0981 }
0982
0983 if (!$validated)
0984 {
0985 return '[email' . (($var1) ? "=$var1" : '') . ']' . $var2 . '[/email]';
0986 }
0987
0988 $this->parsed_items['email']++;
0989
0990 if ($var1)
0991 {
0992 $retval = '[email=' . $this->bbcode_specialchars($email) . ':' . $this->bbcode_uid . ']' . $txt . '[/email:' . $this->bbcode_uid . ']';
0993 }
0994 else
0995 {
0996 $retval = '[email:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($email) . '[/email:' . $this->bbcode_uid . ']';
0997 }
0998
0999 return $retval;
1000 }
1001
1002 /**
1003 * Validate url
1004 *
1005 * @param string $var1 optional url parameter for url bbcode: [url(=$var1)]$var2[/url]
1006 * @param string $var2 url bbcode content: [url(=$var1)]$var2[/url]
1007 */
1008 function validate_url($var1, $var2)
1009 {
1010 $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1)));
1011 $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2)));
1012
1013 $url = ($var1) ? $var1 : $var2;
1014
1015 if ($var1 && !$var2)
1016 {
1017 $var2 = $var1;
1018 }
1019
1020 if (!$url)
1021 {
1022 return '[url' . (($var1) ? '=' . $var1 : '') . ']' . $var2 . '[/url]';
1023 }
1024
1025 $valid = false;
1026
1027 $url = str_replace(' ', '%20', $url);
1028
1029 // Checking urls
1030 if (preg_match('#^' . get_preg_expression('url') . '$#iu', $url) ||
1031 preg_match('#^' . get_preg_expression('www_url') . '$#iu', $url) ||
1032 preg_match('#^' . preg_quote(generate_board_url(), '#') . get_preg_expression('relative_url') . '$#iu', $url))
1033 {
1034 $valid = true;
1035 }
1036
1037 if ($valid)
1038 {
1039 $this->parsed_items['url']++;
1040
1041 // if there is no scheme, then add http schema
1042 if (!preg_match('#^[a-z][a-z\d+\-.]*:/{2}#i', $url))
1043 {
1044 $url = 'http://' . $url;
1045 }
1046
1047 // Is this a link to somewhere inside this board? If so then remove the session id from the url
1048 if (strpos($url, generate_board_url()) !== false && strpos($url, 'sid=') !== false)
1049 {
1050 $url = preg_replace('/(&|\?)sid=[0-9a-f]{32}&/', '\1', $url);
1051 $url = preg_replace('/(&|\?)sid=[0-9a-f]{32}$/', '', $url);
1052 $url = append_sid($url);
1053 }
1054
1055 return ($var1) ? '[url=' . $this->bbcode_specialchars($url) . ':' . $this->bbcode_uid . ']' . $var2 . '[/url:' . $this->bbcode_uid . ']' : '[url:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($url) . '[/url:' . $this->bbcode_uid . ']';
1056 }
1057
1058 return '[url' . (($var1) ? '=' . $var1 : '') . ']' . $var2 . '[/url]';
1059 }
1060
1061 /**
1062 * Check if url is pointing to this domain/script_path/php-file
1063 *
1064 * @param string $url the url to check
1065 * @return true if the url is pointing to this domain/script_path/php-file, false if not
1066 *
1067 * @access private
1068 */
1069 function path_in_domain($url)
1070 {
1071 global $config, $phpEx, $user;
1072
1073 if ($config['force_server_vars'])
1074 {
1075 $check_path = $config['script_path'];
1076 }
1077 else
1078 {
1079 $check_path = ($user->page['root_script_path'] != '/') ? substr($user->page['root_script_path'], 0, -1) : '/';
1080 }
1081
1082 // Is the user trying to link to a php file in this domain and script path?
1083 if (strpos($url, ".{$phpEx}") !== false && strpos($url, $check_path) !== false)
1084 {
1085 $server_name = $user->host;
1086
1087 // Forcing server vars is the only way to specify/override the protocol
1088 if ($config['force_server_vars'] || !$server_name)
1089 {
1090 $server_name = $config['server_name'];
1091 }
1092
1093 // Check again in correct order...
1094 $pos_ext = strpos($url, ".{$phpEx}");
1095 $pos_path = strpos($url, $check_path);
1096 $pos_domain = strpos($url, $server_name);
1097
1098 if ($pos_domain !== false && $pos_path >= $pos_domain && $pos_ext >= $pos_path)
1099 {
1100 // Ok, actually we allow linking to some files (this may be able to be extended in some way later...)
1101 if (strpos($url, '/' . $check_path . '/download/file.' . $phpEx) !== 0)
1102 {
1103 return false;
1104 }
1105
1106 return true;
1107 }
1108 }
1109
1110 return false;
1111 }
1112 }
1113
1114 /**
1115 * Main message parser for posting, pm, etc. takes raw message
1116 * and parses it for attachments, bbcode and smilies
1117 */
1118 class parse_message extends bbcode_firstpass
1119 {
1120 var $attachment_data = array();
1121 var $filename_data = array();
1122
1123 // Helps ironing out user error
1124 var $message_status = '';
1125
1126 var $allow_img_bbcode = true;
1127 var $allow_flash_bbcode = true;
1128 var $allow_quote_bbcode = true;
1129 var $allow_url_bbcode = true;
1130
1131 var $mode;
1132
1133 /**
1134 * The plupload object used for dealing with attachments
1135 * @var \phpbb\plupload\plupload
1136 */
1137 protected $plupload;
1138
1139 /**
1140 * Init - give message here or manually
1141 */
1142 function parse_message($message = '')
1143 {
1144 // Init BBCode UID
1145 $this->bbcode_uid = substr(base_convert(unique_id(), 16, 36), 0, BBCODE_UID_LEN);
1146 $this->message = $message;
1147 }
1148
1149 /**
1150 * Parse Message
1151 */
1152 function parse($allow_bbcode, $allow_magic_url, $allow_smilies, $allow_img_bbcode = true, $allow_flash_bbcode = true, $allow_quote_bbcode = true, $allow_url_bbcode = true, $update_this_message = true, $mode = 'post')
1153 {
1154 global $config, $user, $phpbb_dispatcher, $phpbb_container;
1155
1156 $this->mode = $mode;
1157
1158 foreach (array('chars', 'smilies', 'urls', 'font_size', 'img_height', 'img_width') as $key)
1159 {
1160 if (!isset($config['max_' . $mode . '_' . $key]))
1161 {
1162 $config['max_' . $mode . '_' . $key] = 0;
1163 }
1164 }
1165
1166 $this->allow_img_bbcode = $allow_img_bbcode;
1167 $this->allow_flash_bbcode = $allow_flash_bbcode;
1168 $this->allow_quote_bbcode = $allow_quote_bbcode;
1169 $this->allow_url_bbcode = $allow_url_bbcode;
1170
1171 // If false, then $this->message won't be altered, the text will be returned instead.
1172 if (!$update_this_message)
1173 {
1174 $tmp_message = $this->message;
1175 $return_message = &$this->message;
1176 }
1177
1178 if ($this->message_status == 'display')
1179 {
1180 $this->decode_message();
1181 }
1182
1183 // Store message length...
1184 $message_length = ($mode == 'post') ? utf8_strlen($this->message) : utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message));
1185
1186 // Maximum message length check. 0 disables this check completely.
1187 if ((int) $config['max_' . $mode . '_chars'] > 0 && $message_length > (int) $config['max_' . $mode . '_chars'])
1188 {
1189 $this->warn_msg[] = $user->lang('CHARS_' . strtoupper($mode) . '_CONTAINS', $message_length) . '<br />' . $user->lang('TOO_MANY_CHARS_LIMIT', (int) $config['max_' . $mode . '_chars']);
1190 return (!$update_this_message) ? $return_message : $this->warn_msg;
1191 }
1192
1193 // Minimum message length check for post only
1194 if ($mode === 'post')
1195 {
1196 if (!$message_length || $message_length < (int) $config['min_post_chars'])
1197 {
1198 $this->warn_msg[] = (!$message_length) ? $user->lang['TOO_FEW_CHARS'] : ($user->lang('CHARS_POST_CONTAINS', $message_length) . '<br />' . $user->lang('TOO_FEW_CHARS_LIMIT', (int) $config['min_post_chars']));
1199 return (!$update_this_message) ? $return_message : $this->warn_msg;
1200 }
1201 }
1202
1203 /**
1204 * This event can be used for additional message checks/cleanup before parsing
1205 *
1206 * @event core.message_parser_check_message
1207 * @var bool allow_bbcode Do we allow BBCodes
1208 * @var bool allow_magic_url Do we allow magic urls
1209 * @var bool allow_smilies Do we allow smilies
1210 * @var bool allow_img_bbcode Do we allow image BBCode
1211 * @var bool allow_flash_bbcode Do we allow flash BBCode
1212 * @var bool allow_quote_bbcode Do we allow quote BBCode
1213 * @var bool allow_url_bbcode Do we allow url BBCode
1214 * @var bool update_this_message Do we alter the parsed message
1215 * @var string mode Posting mode
1216 * @var string message The message text to parse
1217 * @var string bbcode_bitfield The bbcode_bitfield before parsing
1218 * @var string bbcode_uid The bbcode_uid before parsing
1219 * @var bool return Do we return after the event is triggered if $warn_msg is not empty
1220 * @var array warn_msg Array of the warning messages
1221 * @since 3.1.2-RC1
1222 * @change 3.1.3-RC1 Added vars $bbcode_bitfield and $bbcode_uid
1223 */
1224 $message = $this->message;
1225 $warn_msg = $this->warn_msg;
1226 $return = false;
1227 $bbcode_bitfield = $this->bbcode_bitfield;
1228 $bbcode_uid = $this->bbcode_uid;
1229 $vars = array(
1230 'allow_bbcode',
1231 'allow_magic_url',
1232 'allow_smilies',
1233 'allow_img_bbcode',
1234 'allow_flash_bbcode',
1235 'allow_quote_bbcode',
1236 'allow_url_bbcode',
1237 'update_this_message',
1238 'mode',
1239 'message',
1240 'bbcode_bitfield',
1241 'bbcode_uid',
1242 'return',
1243 'warn_msg',
1244 );
1245 extract($phpbb_dispatcher->trigger_event('core.message_parser_check_message', compact($vars)));
1246 $this->message = $message;
1247 $this->warn_msg = $warn_msg;
1248 $this->bbcode_bitfield = $bbcode_bitfield;
1249 $this->bbcode_uid = $bbcode_uid;
1250 if ($return && !empty($this->warn_msg))
1251 {
1252 return (!$update_this_message) ? $return_message : $this->warn_msg;
1253 }
1254
1255 // Get the parser
1256 $parser = $phpbb_container->get('text_formatter.parser');
1257
1258 // Set the parser's options
1259 ($allow_bbcode) ? $parser->enable_bbcodes() : $parser->disable_bbcodes();
1260 ($allow_magic_url) ? $parser->enable_magic_url() : $parser->disable_magic_url();
1261 ($allow_smilies) ? $parser->enable_smilies() : $parser->disable_smilies();
1262 ($allow_img_bbcode) ? $parser->enable_bbcode('img') : $parser->disable_bbcode('img');
1263 ($allow_flash_bbcode) ? $parser->enable_bbcode('flash') : $parser->disable_bbcode('flash');
1264 ($allow_quote_bbcode) ? $parser->enable_bbcode('quote') : $parser->disable_bbcode('quote');
1265 ($allow_url_bbcode) ? $parser->enable_bbcode('url') : $parser->disable_bbcode('url');
1266
1267 // Set some config values
1268 $parser->set_vars(array(
1269 'max_font_size' => $config['max_' . $this->mode . '_font_size'],
1270 'max_img_height' => $config['max_' . $this->mode . '_img_height'],
1271 'max_img_width' => $config['max_' . $this->mode . '_img_width'],
1272 'max_smilies' => $config['max_' . $this->mode . '_smilies'],
1273 'max_urls' => $config['max_' . $this->mode . '_urls']
1274 ));
1275
1276 // Parse this message
1277 $this->message = $parser->parse(htmlspecialchars_decode($this->message, ENT_QUOTES));
1278
1279 // Remove quotes that are nested too deep
1280 if ($config['max_quote_depth'] > 0)
1281 {
1282 $this->remove_nested_quotes($config['max_quote_depth']);
1283 }
1284
1285 // Check for "empty" message. We do not check here for maximum length, because bbcode, smilies, etc. can add to the length.
1286 // The maximum length check happened before any parsings.
1287 if ($mode === 'post' && utf8_clean_string($this->message) === '')
1288 {
1289 $this->warn_msg[] = $user->lang['TOO_FEW_CHARS'];
1290 return (!$update_this_message) ? $return_message : $this->warn_msg;
1291 }
1292
1293 // Remove quotes that are nested too deep
1294 if ($config['max_quote_depth'] > 0)
1295 {
1296 $this->message = $phpbb_container->get('text_formatter.utils')->remove_bbcode(
1297 $this->message,
1298 'quote',
1299 $config['max_quote_depth']
1300 );
1301 }
1302
1303 // Check for errors
1304 $errors = $parser->get_errors();
1305 if ($errors)
1306 {
1307 foreach ($errors as $i => $args)
1308 {
1309 // Translate each error with $user->lang()
1310 $errors[$i] = call_user_func_array(array($user, 'lang'), $args);
1311 }
1312 $this->warn_msg = array_merge($this->warn_msg, $errors);
1313
1314 return (!$update_this_message) ? $return_message : $this->warn_msg;
1315 }
1316
1317 if (!$update_this_message)
1318 {
1319 unset($this->message);
1320 $this->message = $tmp_message;
1321 return $return_message;
1322 }
1323
1324 $this->message_status = 'parsed';
1325 return false;
1326 }
1327
1328 /**
1329 * Formatting text for display
1330 */
1331 function format_display($allow_bbcode, $allow_magic_url, $allow_smilies, $update_this_message = true)
1332 {
1333 global $phpbb_container, $phpbb_dispatcher;
1334
1335 // If false, then the parsed message get returned but internal message not processed.
1336 if (!$update_this_message)
1337 {
1338 $tmp_message = $this->message;
1339 $return_message = &$this->message;
1340 }
1341
1342 $text = $this->message;
1343 $uid = $this->bbcode_uid;
1344
1345 /**
1346 * Event to modify the text before it is parsed
1347 *
1348 * @event core.modify_format_display_text_before
1349 * @var string text The message text to parse
1350 * @var string uid The bbcode uid
1351 * @var bool allow_bbcode Do we allow bbcodes
1352 * @var bool allow_magic_url Do we allow magic urls
1353 * @var bool allow_smilies Do we allow smilies
1354 * @var bool update_this_message Do we update the internal message
1355 * with the parsed result
1356 * @since 3.1.6-RC1
1357 */
1358 $vars = array('text', 'uid', 'allow_bbcode', 'allow_magic_url', 'allow_smilies', 'update_this_message');
1359 extract($phpbb_dispatcher->trigger_event('core.modify_format_display_text_before', compact($vars)));
1360
1361 $this->message = $text;
1362 $this->bbcode_uid = $uid;
1363 unset($text, $uid);
1364
1365 // NOTE: message_status is unreliable for detecting unparsed text because some callers
1366 // change $this->message without resetting $this->message_status to 'plain' so we
1367 // inspect the message instead
1368 //if ($this->message_status == 'plain')
1369 if (!preg_match('/^<[rt][ >]/', $this->message))
1370 {
1371 // Force updating message - of course.
1372 $this->parse($allow_bbcode, $allow_magic_url, $allow_smilies, $this->allow_img_bbcode, $this->allow_flash_bbcode, $this->allow_quote_bbcode, $this->allow_url_bbcode, true);
1373 }
1374
1375 // There's a bug when previewing a topic with no poll, because the empty title of the poll
1376 // gets parsed but $this->message still ends up empty. This fixes it, until a proper fix is
1377 // devised
1378 if ($this->message === '')
1379 {
1380 $this->message = $phpbb_container->get('text_formatter.parser')->parse($this->message);
1381 }
1382
1383 $this->message = $phpbb_container->get('text_formatter.renderer')->render($this->message);
1384
1385 $text = $this->message;
1386 $uid = $this->bbcode_uid;
1387
1388 /**
1389 * Event to modify the text after it is parsed
1390 *
1391 * @event core.modify_format_display_text_after
1392 * @var string text The message text to parse
1393 * @var string uid The bbcode uid
1394 * @var bool allow_bbcode Do we allow bbcodes
1395 * @var bool allow_magic_url Do we allow magic urls
1396 * @var bool allow_smilies Do we allow smilies
1397 * @var bool update_this_message Do we update the internal message
1398 * with the parsed result
1399 * @since 3.1.0-a3
1400 */
1401 $vars = array('text', 'uid', 'allow_bbcode', 'allow_magic_url', 'allow_smilies', 'update_this_message');
1402 extract($phpbb_dispatcher->trigger_event('core.modify_format_display_text_after', compact($vars)));
1403
1404 $this->message = $text;
1405 $this->bbcode_uid = $uid;
1406
1407 if (!$update_this_message)
1408 {
1409 unset($this->message);
1410 $this->message = $tmp_message;
1411 return $return_message;
1412 }
1413
1414 $this->message_status = 'display';
1415 return false;
1416 }
1417
1418 /**
1419 * Decode message to be placed back into form box
1420 */
1421 function decode_message($custom_bbcode_uid = '', $update_this_message = true)
1422 {
1423 // If false, then the parsed message get returned but internal message not processed.
1424 if (!$update_this_message)
1425 {
1426 $tmp_message = $this->message;
1427 $return_message = &$this->message;
1428 }
1429
1430 ($custom_bbcode_uid) ? decode_message($this->message, $custom_bbcode_uid) : decode_message($this->message, $this->bbcode_uid);
1431
1432 if (!$update_this_message)
1433 {
1434 unset($this->message);
1435 $this->message = $tmp_message;
1436 return $return_message;
1437 }
1438
1439 $this->message_status = 'plain';
1440 return false;
1441 }
1442
1443 /**
1444 * Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx.
1445 * Cuts down displayed size of link if over 50 chars, turns absolute links
1446 * into relative versions when the server/script path matches the link
1447 */
1448 function magic_url($server_url)
1449 {
1450 // We use the global make_clickable function
1451 $this->message = make_clickable($this->message, $server_url);
1452 }
1453
1454 /**
1455 * Parse Smilies
1456 */
1457 function smilies($max_smilies = 0)
1458 {
1459 global $db, $user;
1460 static $match;
1461 static $replace;
1462
1463 // See if the static arrays have already been filled on an earlier invocation
1464 if (!is_array($match))
1465 {
1466 $match = $replace = array();
1467
1468 // NOTE: obtain_* function? chaching the table contents?
1469
1470 // For now setting the ttl to 10 minutes
1471 switch ($db->get_sql_layer())
1472 {
1473 case 'mssql_odbc':
1474 case 'mssqlnative':
1475 $sql = 'SELECT *
1476 FROM ' . SMILIES_TABLE . '
1477 ORDER BY LEN(code) DESC';
1478 break;
1479
1480 // LENGTH supported by MySQL, IBM DB2, Oracle and Access for sure...
1481 default:
1482 $sql = 'SELECT *
1483 FROM ' . SMILIES_TABLE . '
1484 ORDER BY LENGTH(code) DESC';
1485 break;
1486 }
1487 $result = $db->sql_query($sql, 600);
1488
1489 while ($row = $db->sql_fetchrow($result))
1490 {
1491 if (empty($row['code']))
1492 {
1493 continue;
1494 }
1495
1496 // (assertion)
1497 $match[] = preg_quote($row['code'], '#');
1498 $replace[] = '<!-- s' . $row['code'] . ' --><img src="{SMILIES_PATH}/' . $row['smiley_url'] . '" alt="' . $row['code'] . '" title="' . $row['emotion'] . '" /><!-- s' . $row['code'] . ' -->';
1499 }
1500 $db->sql_freeresult($result);
1501 }
1502
1503 if (sizeof($match))
1504 {
1505 if ($max_smilies)
1506 {
1507 // 'u' modifier has been added to correctly parse smilies within unicode strings
1508 // For details: http://tracker.phpbb.com/browse/PHPBB3-10117
1509 $num_matches = preg_match_all('#(?<=^|[\n .])(?:' . implode('|', $match) . ')(?![^<>]*>)#u', $this->message, $matches);
1510 unset($matches);
1511
1512 if ($num_matches !== false && $num_matches > $max_smilies)
1513 {
1514 $this->warn_msg[] = sprintf($user->lang['TOO_MANY_SMILIES'], $max_smilies);
1515 return;
1516 }
1517 }
1518
1519 // Make sure the delimiter # is added in front and at the end of every element within $match
1520 // 'u' modifier has been added to correctly parse smilies within unicode strings
1521 // For details: http://tracker.phpbb.com/browse/PHPBB3-10117
1522
1523 $this->message = trim(preg_replace(explode(chr(0), '#(?<=^|[\n .])' . implode('(?![^<>]*>)#u' . chr(0) . '#(?<=^|[\n .])', $match) . '(?![^<>]*>)#u'), $replace, $this->message));
1524 }
1525 }
1526
1527 /**
1528 * Parse Attachments
1529 */
1530 function parse_attachments($form_name, $mode, $forum_id, $submit, $preview, $refresh, $is_message = false)
1531 {
1532 global $config, $auth, $user, $phpbb_root_path, $phpEx, $db, $request;
1533 global $phpbb_container;
1534
1535 $error = array();
1536
1537 $num_attachments = sizeof($this->attachment_data);
1538 $this->filename_data['filecomment'] = $request->variable('filecomment', '', true);
1539 $upload = $request->file($form_name);
1540 $upload_file = (!empty($upload) && $upload['name'] !== 'none' && trim($upload['name']));
1541
1542 $add_file = (isset($_POST['add_file'])) ? true : false;
1543 $delete_file = (isset($_POST['delete_file'])) ? true : false;
1544
1545 // First of all adjust comments if changed
1546 $actual_comment_list = $request->variable('comment_list', array(''), true);
1547
1548 foreach ($actual_comment_list as $comment_key => $comment)
1549 {
1550 if (!isset($this->attachment_data[$comment_key]))
1551 {
1552 continue;
1553 }
1554
1555 if ($this->attachment_data[$comment_key]['attach_comment'] != $actual_comment_list[$comment_key])
1556 {
1557 $this->attachment_data[$comment_key]['attach_comment'] = $actual_comment_list[$comment_key];
1558 }
1559 }
1560
1561 $cfg = array();
1562 $cfg['max_attachments'] = ($is_message) ? $config['max_attachments_pm'] : $config['max_attachments'];
1563 $forum_id = ($is_message) ? 0 : $forum_id;
1564
1565 if ($submit && in_array($mode, array('post', 'reply', 'quote', 'edit')) && $upload_file)
1566 {
1567 if ($num_attachments < $cfg['max_attachments'] || $auth->acl_get('a_') || $auth->acl_get('m_', $forum_id))
1568 {
1569 /** @var \phpbb\attachment\manager $attachment_manager */
1570 $attachment_manager = $phpbb_container->get('attachment.manager');
1571 $filedata = $attachment_manager->upload($form_name, $forum_id, false, '', $is_message);
1572 $error = $filedata['error'];
1573
1574 if ($filedata['post_attach'] && !sizeof($error))
1575 {
1576 $sql_ary = array(
1577 'physical_filename' => $filedata['physical_filename'],
1578 'attach_comment' => $this->filename_data['filecomment'],
1579 'real_filename' => $filedata['real_filename'],
1580 'extension' => $filedata['extension'],
1581 'mimetype' => $filedata['mimetype'],
1582 'filesize' => $filedata['filesize'],
1583 'filetime' => $filedata['filetime'],
1584 'thumbnail' => $filedata['thumbnail'],
1585 'is_orphan' => 1,
1586 'in_message' => ($is_message) ? 1 : 0,
1587 'poster_id' => $user->data['user_id'],
1588 );
1589
1590 $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1591
1592 $new_entry = array(
1593 'attach_id' => $db->sql_nextid(),
1594 'is_orphan' => 1,
1595 'real_filename' => $filedata['real_filename'],
1596 'attach_comment'=> $this->filename_data['filecomment'],
1597 'filesize' => $filedata['filesize'],
1598 );
1599
1600 $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data);
1601 $this->message = preg_replace_callback('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#', function ($match) {
1602 return '[attachment='.($match[1] + 1).']' . $match[2] . '[/attachment]';
1603 }, $this->message);
1604
1605 $this->filename_data['filecomment'] = '';
1606
1607 // This Variable is set to false here, because Attachments are entered into the
1608 // Database in two modes, one if the id_list is 0 and the second one if post_attach is true
1609 // Since post_attach is automatically switched to true if an Attachment got added to the filesystem,
1610 // but we are assigning an id of 0 here, we have to reset the post_attach variable to false.
1611 //
1612 // This is very relevant, because it could happen that the post got not submitted, but we do not
1613 // know this circumstance here. We could be at the posting page or we could be redirected to the entered
1614 // post. :)
1615 $filedata['post_attach'] = false;
1616 }
1617 }
1618 else
1619 {
1620 $error[] = $user->lang('TOO_MANY_ATTACHMENTS', (int) $cfg['max_attachments']);
1621 }
1622 }
1623
1624 if ($preview || $refresh || sizeof($error))
1625 {
1626 if (isset($this->plupload) && $this->plupload->is_active())
1627 {
1628 $json_response = new \phpbb\json_response();
1629 }
1630
1631 // Perform actions on temporary attachments
1632 if ($delete_file)
1633 {
1634 include_once($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
1635
1636 $index = array_keys($request->variable('delete_file', array(0 => 0)));
1637 $index = (!empty($index)) ? $index[0] : false;
1638
1639 if ($index !== false && !empty($this->attachment_data[$index]))
1640 {
1641 /** @var \phpbb\attachment\manager $attachment_manager */
1642 $attachment_manager = $phpbb_container->get('attachment.manager');
1643
1644 // delete selected attachment
1645 if ($this->attachment_data[$index]['is_orphan'])
1646 {
1647 $sql = 'SELECT attach_id, physical_filename, thumbnail
1648 FROM ' . ATTACHMENTS_TABLE . '
1649 WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id'] . '
1650 AND is_orphan = 1
1651 AND poster_id = ' . $user->data['user_id'];
1652 $result = $db->sql_query($sql);
1653 $row = $db->sql_fetchrow($result);
1654 $db->sql_freeresult($result);
1655
1656 if ($row)
1657 {
1658 $attachment_manager->unlink($row['physical_filename'], 'file');
1659
1660 if ($row['thumbnail'])
1661 {
1662 $attachment_manager->unlink($row['physical_filename'], 'thumbnail');
1663 }
1664
1665 $db->sql_query('DELETE FROM ' . ATTACHMENTS_TABLE . ' WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id']);
1666 }
1667 }
1668 else
1669 {
1670 $attachment_manager->delete('attach', $this->attachment_data[$index]['attach_id']);
1671 }
1672
1673 unset($this->attachment_data[$index]);
1674 $this->message = preg_replace_callback('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#', function ($match) use($index) {
1675 return ($match[1] == $index) ? '' : (($match[1] > $index) ? '[attachment=' . ($match[1] - 1) . ']' . $match[2] . '[/attachment]' : $match[0]);
1676 }, $this->message);
1677
1678 // Reindex Array
1679 $this->attachment_data = array_values($this->attachment_data);
1680 if (isset($this->plupload) && $this->plupload->is_active())
1681 {
1682 $json_response->send($this->attachment_data);
1683 }
1684 }
1685 }
1686 else if (($add_file || $preview) && $upload_file)
1687 {
1688 if ($num_attachments < $cfg['max_attachments'] || $auth->acl_gets('m_', 'a_', $forum_id))
1689 {
1690 /** @var \phpbb\attachment\manager $attachment_manager */
1691 $attachment_manager = $phpbb_container->get('attachment.manager');
1692 $filedata = $attachment_manager->upload($form_name, $forum_id, false, '', $is_message);
1693 $error = array_merge($error, $filedata['error']);
1694
1695 if (!sizeof($error))
1696 {
1697 $sql_ary = array(
1698 'physical_filename' => $filedata['physical_filename'],
1699 'attach_comment' => $this->filename_data['filecomment'],
1700 'real_filename' => $filedata['real_filename'],
1701 'extension' => $filedata['extension'],
1702 'mimetype' => $filedata['mimetype'],
1703 'filesize' => $filedata['filesize'],
1704 'filetime' => $filedata['filetime'],
1705 'thumbnail' => $filedata['thumbnail'],
1706 'is_orphan' => 1,
1707 'in_message' => ($is_message) ? 1 : 0,
1708 'poster_id' => $user->data['user_id'],
1709 );
1710
1711 $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1712
1713 $new_entry = array(
1714 'attach_id' => $db->sql_nextid(),
1715 'is_orphan' => 1,
1716 'real_filename' => $filedata['real_filename'],
1717 'attach_comment'=> $this->filename_data['filecomment'],
1718 'filesize' => $filedata['filesize'],
1719 );
1720
1721 $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data);
1722 $this->message = preg_replace_callback('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#', function ($match) {
1723 return '[attachment=' . ($match[1] + 1) . ']' . $match[2] . '[/attachment]';
1724 }, $this->message);
1725 $this->filename_data['filecomment'] = '';
1726
1727 if (isset($this->plupload) && $this->plupload->is_active())
1728 {
1729 $download_url = append_sid("{$phpbb_root_path}download/file.{$phpEx}", 'mode=view&id=' . $new_entry['attach_id']);
1730
1731 // Send the client the attachment data to maintain state
1732 $json_response->send(array('data' => $this->attachment_data, 'download_url' => $download_url));
1733 }
1734 }
1735 }
1736 else
1737 {
1738 $error[] = $user->lang('TOO_MANY_ATTACHMENTS', (int) $cfg['max_attachments']);
1739 }
1740
1741 if (!empty($error) && isset($this->plupload) && $this->plupload->is_active())
1742 {
1743 // If this is a plupload (and thus ajax) request, give the
1744 // client the first error we have
1745 $json_response->send(array(
1746 'jsonrpc' => '2.0',
1747 'id' => 'id',
1748 'error' => array(
1749 'code' => 105,
1750 'message' => current($error),
1751 ),
1752 ));
1753 }
1754 }
1755 }
1756
1757 foreach ($error as $error_msg)
1758 {
1759 $this->warn_msg[] = $error_msg;
1760 }
1761 }
1762
1763 /**
1764 * Get Attachment Data
1765 */
1766 function get_submitted_attachment_data($check_user_id = false)
1767 {
1768 global $user, $db;
1769 global $request;
1770
1771 $this->filename_data['filecomment'] = $request->variable('filecomment', '', true);
1772 $attachment_data = $request->variable('attachment_data', array(0 => array('' => '')), true, \phpbb\request\request_interface::POST);
1773 $this->attachment_data = array();
1774
1775 $check_user_id = ($check_user_id === false) ? $user->data['user_id'] : $check_user_id;
1776
1777 if (!sizeof($attachment_data))
1778 {
1779 return;
1780 }
1781
1782 $not_orphan = $orphan = array();
1783
1784 foreach ($attachment_data as $pos => $var_ary)
1785 {
1786 if ($var_ary['is_orphan'])
1787 {
1788 $orphan[(int) $var_ary['attach_id']] = $pos;
1789 }
1790 else
1791 {
1792 $not_orphan[(int) $var_ary['attach_id']] = $pos;
1793 }
1794 }
1795
1796 // Regenerate already posted attachments
1797 if (sizeof($not_orphan))
1798 {
1799 // Get the attachment data, based on the poster id...
1800 $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment, filesize
1801 FROM ' . ATTACHMENTS_TABLE . '
1802 WHERE ' . $db->sql_in_set('attach_id', array_keys($not_orphan)) . '
1803 AND poster_id = ' . $check_user_id;
1804 $result = $db->sql_query($sql);
1805
1806 while ($row = $db->sql_fetchrow($result))
1807 {
1808 $pos = $not_orphan[$row['attach_id']];
1809 $this->attachment_data[$pos] = $row;
1810 $this->attachment_data[$pos]['attach_comment'] = $attachment_data[$pos]['attach_comment'];
1811
1812 unset($not_orphan[$row['attach_id']]);
1813 }
1814 $db->sql_freeresult($result);
1815 }
1816
1817 if (sizeof($not_orphan))
1818 {
1819 trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR);
1820 }
1821
1822 // Regenerate newly uploaded attachments
1823 if (sizeof($orphan))
1824 {
1825 $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment, filesize
1826 FROM ' . ATTACHMENTS_TABLE . '
1827 WHERE ' . $db->sql_in_set('attach_id', array_keys($orphan)) . '
1828 AND poster_id = ' . $user->data['user_id'] . '
1829 AND is_orphan = 1';
1830 $result = $db->sql_query($sql);
1831
1832 while ($row = $db->sql_fetchrow($result))
1833 {
1834 $pos = $orphan[$row['attach_id']];
1835 $this->attachment_data[$pos] = $row;
1836 $this->attachment_data[$pos]['attach_comment'] = $attachment_data[$pos]['attach_comment'];
1837
1838 unset($orphan[$row['attach_id']]);
1839 }
1840 $db->sql_freeresult($result);
1841 }
1842
1843 if (sizeof($orphan))
1844 {
1845 trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR);
1846 }
1847
1848 ksort($this->attachment_data);
1849 }
1850
1851 /**
1852 * Parse Poll
1853 */
1854 function parse_poll(&$poll)
1855 {
1856 global $user, $config;
1857
1858 $poll_max_options = $poll['poll_max_options'];
1859
1860 // Parse Poll Option text
1861 $tmp_message = $this->message;
1862
1863 $poll['poll_options'] = preg_split('/\s*?\n\s*/', trim($poll['poll_option_text']));
1864 $poll['poll_options_size'] = sizeof($poll['poll_options']);
1865
1866 foreach ($poll['poll_options'] as &$poll_option)
1867 {
1868 $this->message = $poll_option;
1869 $poll_option = $this->parse($poll['enable_bbcode'], ($config['allow_post_links']) ? $poll['enable_urls'] : false, $poll['enable_smilies'], $poll['img_status'], false, false, $config['allow_post_links'], false, 'poll');
1870 }
1871 unset($poll_option);
1872 $poll['poll_option_text'] = implode("\n", $poll['poll_options']);
1873
1874 // Parse Poll Title
1875 $this->message = $poll['poll_title'];
1876 if (!$poll['poll_title'] && $poll['poll_options_size'])
1877 {
1878 $this->warn_msg[] = $user->lang['NO_POLL_TITLE'];
1879 }
1880 else
1881 {
1882 if (utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message)) > 100)
1883 {
1884 $this->warn_msg[] = $user->lang['POLL_TITLE_TOO_LONG'];
1885 }
1886 $poll['poll_title'] = $this->parse($poll['enable_bbcode'], ($config['allow_post_links']) ? $poll['enable_urls'] : false, $poll['enable_smilies'], $poll['img_status'], false, false, $config['allow_post_links'], false, 'poll');
1887 if (strlen($poll['poll_title']) > 255)
1888 {
1889 $this->warn_msg[] = $user->lang['POLL_TITLE_COMP_TOO_LONG'];
1890 }
1891 }
1892
1893 if (sizeof($poll['poll_options']) == 1)
1894 {
1895 $this->warn_msg[] = $user->lang['TOO_FEW_POLL_OPTIONS'];
1896 }
1897 else if ($poll['poll_options_size'] > (int) $config['max_poll_options'])
1898 {
1899 $this->warn_msg[] = $user->lang['TOO_MANY_POLL_OPTIONS'];
1900 }
1901 else if ($poll_max_options > $poll['poll_options_size'])
1902 {
1903 $this->warn_msg[] = $user->lang['TOO_MANY_USER_OPTIONS'];
1904 }
1905
1906 $poll['poll_max_options'] = ($poll['poll_max_options'] < 1) ? 1 : (($poll['poll_max_options'] > $config['max_poll_options']) ? $config['max_poll_options'] : $poll['poll_max_options']);
1907
1908 $this->message = $tmp_message;
1909 }
1910
1911 /**
1912 * Remove nested quotes at given depth in current parsed message
1913 *
1914 * @param integer $max_depth Depth limit
1915 * @return null
1916 */
1917 public function remove_nested_quotes($max_depth)
1918 {
1919 global $phpbb_container;
1920
1921 if (preg_match('#^<[rt][ >]#', $this->message))
1922 {
1923 $this->message = $phpbb_container->get('text_formatter.utils')->remove_bbcode(
1924 $this->message,
1925 'quote',
1926 $max_depth
1927 );
1928
1929 return;
1930 }
1931
1932 // Capture all [quote] and [/quote] tags
1933 preg_match_all('(\\[/?quote(?:="(.*?)")?:' . $this->bbcode_uid . '\\])', $this->message, $matches, PREG_OFFSET_CAPTURE);
1934
1935 // Iterate over the quote tags to mark the ranges that must be removed
1936 $depth = 0;
1937 $ranges = array();
1938 $start_pos = 0;
1939 foreach ($matches[0] as $match)
1940 {
1941 if ($match[0][1] === '/')
1942 {
1943 --$depth;
1944 if ($depth == $max_depth)
1945 {
1946 $end_pos = $match[1] + strlen($match[0]);
1947 $length = $end_pos - $start_pos;
1948 $ranges[] = array($start_pos, $length);
1949 }
1950 }
1951 else
1952 {
1953 ++$depth;
1954 if ($depth == $max_depth + 1)
1955 {
1956 $start_pos = $match[1];
1957 }
1958 }
1959 }
1960
1961 foreach (array_reverse($ranges) as $range)
1962 {
1963 list($start_pos, $length) = $range;
1964 $this->message = substr_replace($this->message, '', $start_pos, $length);
1965 }
1966 }
1967
1968 /**
1969 * Setter function for passing the plupload object
1970 *
1971 * @param \phpbb\plupload\plupload $plupload The plupload object
1972 *
1973 * @return null
1974 */
1975 public function set_plupload(\phpbb\plupload\plupload $plupload)
1976 {
1977 $this->plupload = $plupload;
1978 }
1979
1980 /**
1981 * Function to perform custom bbcode validation by extensions
1982 * can be used in bbcode_init() to assign regexp replacement
1983 * Example: 'regexp' => array('#\[b\](.*?)\[/b\]#uise' => "\$this->validate_bbcode_by_extension('\$1')")
1984 *
1985 * Accepts variable number of parameters
1986 *
1987 * @return mixed Validation result
1988 */
1989 public function validate_bbcode_by_extension()
1990 {
1991 global $phpbb_dispatcher;
1992
1993 $return = false;
1994 $params_array = func_get_args();
1995
1996 /**
1997 * Event to validate bbcode with the custom validating methods
1998 * provided by extensions
1999 *
2000 * @event core.validate_bbcode_by_extension
2001 * @var array params_array Array with the function parameters
2002 * @var mixed return Validation result to return
2003 *
2004 * @since 3.1.5-RC1
2005 */
2006 $vars = array('params_array', 'return');
2007 extract($phpbb_dispatcher->trigger_event('core.validate_bbcode_by_extension', compact($vars)));
2008
2009 return $return;
2010 }
2011 }
2012