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