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