[ Index ] |
PHP Cross Reference of phpBB-3.1.12-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * This file is part of the phpBB Forum Software package. 5 * 6 * @copyright (c) phpBB Limited <https://www.phpbb.com> 7 * @license GNU General Public License, version 2 (GPL-2.0) 8 * 9 * For full copyright and license information, please see 10 * the docs/CREDITS.txt file. 11 * 12 */ 13 14 /** 15 * @ignore 16 */ 17 if (!defined('IN_PHPBB')) 18 { 19 exit; 20 } 21 22 if (!class_exists('bbcode')) 23 { 24 // The following lines are for extensions which include message_parser.php 25 // while $phpbb_root_path and $phpEx are out of the script scope 26 // which may lead to the 'Undefined variable' and 'failed to open stream' errors 27 if (!isset($phpbb_root_path)) 28 { 29 global $phpbb_root_path; 30 } 31 32 if (!isset($phpEx)) 33 { 34 global $phpEx; 35 } 36 37 include($phpbb_root_path . 'includes/bbcode.' . $phpEx); 38 } 39 40 /** 41 * BBCODE FIRSTPASS 42 * BBCODE first pass class (functions for parsing messages for db storage) 43 */ 44 class bbcode_firstpass extends bbcode 45 { 46 var $message = ''; 47 var $warn_msg = array(); 48 var $parsed_items = array(); 49 50 /** 51 * Parse BBCode 52 */ 53 function parse_bbcode() 54 { 55 if (!$this->bbcodes) 56 { 57 $this->bbcode_init(); 58 } 59 60 global $user; 61 62 $this->bbcode_bitfield = ''; 63 $bitfield = new bitfield(); 64 65 foreach ($this->bbcodes as $bbcode_name => $bbcode_data) 66 { 67 if (isset($bbcode_data['disabled']) && $bbcode_data['disabled']) 68 { 69 foreach ($bbcode_data['regexp'] as $regexp => $replacement) 70 { 71 if (preg_match($regexp, $this->message)) 72 { 73 $this->warn_msg[] = sprintf($user->lang['UNAUTHORISED_BBCODE'] , '[' . $bbcode_name . ']'); 74 continue; 75 } 76 } 77 } 78 else 79 { 80 foreach ($bbcode_data['regexp'] as $regexp => $replacement) 81 { 82 // The pattern gets compiled and cached by the PCRE extension, 83 // it should not demand recompilation 84 if (preg_match($regexp, $this->message)) 85 { 86 $this->message = preg_replace($regexp, $replacement, $this->message); 87 $bitfield->set($bbcode_data['bbcode_id']); 88 } 89 } 90 } 91 } 92 93 $this->bbcode_bitfield = $bitfield->get_base64(); 94 } 95 96 /** 97 * Prepare some bbcodes for better parsing 98 */ 99 function prepare_bbcodes() 100 { 101 // 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". 102 // Fine by me ;) Will ease our live... but do not come back and cry at us, we won't hear you. 103 104 /* Add newline at the end and in front of each quote block to prevent parsing errors (urls, smilies, etc.) 105 if (strpos($this->message, '[quote') !== false && strpos($this->message, '[/quote]') !== false) 106 { 107 $this->message = str_replace("\r\n", "\n", $this->message); 108 109 // We strip newlines and spaces after and before quotes in quotes (trimming) and then add exactly one newline 110 $this->message = preg_replace('#\[quote(=".*?")?\]\s*(.*?)\s*\[/quote\]#siu', '[quote\1]' . "\n" . '\2' ."\n[/quote]", $this->message); 111 } 112 */ 113 114 // Add other checks which needs to be placed before actually parsing anything (be it bbcodes, smilies, urls...) 115 } 116 117 /** 118 * Init bbcode data for later parsing 119 */ 120 function bbcode_init($allow_custom_bbcode = true) 121 { 122 global $phpbb_dispatcher; 123 124 static $rowset; 125 126 // This array holds all bbcode data. BBCodes will be processed in this 127 // order, so it is important to keep [code] in first position and 128 // [quote] in second position. 129 // To parse multiline URL we enable dotall option setting only for URL text 130 // but not for link itself, thus [url][/url] is not affected. 131 // 132 // To perform custom validation in extension, use $this->validate_bbcode_by_extension() 133 // method which accepts variable number of parameters 134 $this->bbcodes = array( 135 'code' => array('bbcode_id' => 8, 'regexp' => array('#\[code(?:=([a-z]+))?\](.+\[/code\])#uise' => "\$this->bbcode_code('\$1', '\$2')")), 136 'quote' => array('bbcode_id' => 0, 'regexp' => array('#\[quote(?:="(.*?)")?\](.+)\[/quote\]#uise' => "\$this->bbcode_quote('\$0')")), 137 'attachment' => array('bbcode_id' => 12, 'regexp' => array('#\[attachment=([0-9]+)\](.*?)\[/attachment\]#uise' => "\$this->bbcode_attachment('\$1', '\$2')")), 138 'b' => array('bbcode_id' => 1, 'regexp' => array('#\[b\](.*?)\[/b\]#uise' => "\$this->bbcode_strong('\$1')")), 139 'i' => array('bbcode_id' => 2, 'regexp' => array('#\[i\](.*?)\[/i\]#uise' => "\$this->bbcode_italic('\$1')")), 140 'url' => array('bbcode_id' => 3, 'regexp' => array('#\[url(=(.*))?\](?(1)((?s).*(?-s))|(.*))\[/url\]#uiUe' => "\$this->validate_url('\$2', ('\$3') ? '\$3' : '\$4')")), 141 'img' => array('bbcode_id' => 4, 'regexp' => array('#\[img\](.*)\[/img\]#uiUe' => "\$this->bbcode_img('\$1')")), 142 'size' => array('bbcode_id' => 5, 'regexp' => array('#\[size=([\-\+]?\d+)\](.*?)\[/size\]#uise' => "\$this->bbcode_size('\$1', '\$2')")), 143 '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')")), 144 'u' => array('bbcode_id' => 7, 'regexp' => array('#\[u\](.*?)\[/u\]#uise' => "\$this->bbcode_underline('\$1')")), 145 'list' => array('bbcode_id' => 9, 'regexp' => array('#\[list(?:=(?:[a-z0-9]|disc|circle|square))?].*\[/list]#uise' => "\$this->bbcode_parse_list('\$0')")), 146 'email' => array('bbcode_id' => 10, 'regexp' => array('#\[email=?(.*?)?\](.*?)\[/email\]#uise' => "\$this->validate_email('\$1', '\$2')")), 147 'flash' => array('bbcode_id' => 11, 'regexp' => array('#\[flash=([0-9]+),([0-9]+)\](.*?)\[/flash\]#uie' => "\$this->bbcode_flash('\$1', '\$2', '\$3')")) 148 ); 149 150 // Zero the parsed items array 151 $this->parsed_items = array(); 152 153 foreach ($this->bbcodes as $tag => $bbcode_data) 154 { 155 $this->parsed_items[$tag] = 0; 156 } 157 158 if (!$allow_custom_bbcode) 159 { 160 return; 161 } 162 163 if (!is_array($rowset)) 164 { 165 global $db; 166 $rowset = array(); 167 168 $sql = 'SELECT * 169 FROM ' . BBCODES_TABLE; 170 $result = $db->sql_query($sql); 171 172 while ($row = $db->sql_fetchrow($result)) 173 { 174 $rowset[] = $row; 175 } 176 $db->sql_freeresult($result); 177 } 178 179 foreach ($rowset as $row) 180 { 181 $this->bbcodes[$row['bbcode_tag']] = array( 182 'bbcode_id' => (int) $row['bbcode_id'], 183 'regexp' => array($row['first_pass_match'] => str_replace('$uid', $this->bbcode_uid, $row['first_pass_replace'])) 184 ); 185 } 186 187 $bbcodes = $this->bbcodes; 188 189 /** 190 * Event to modify the bbcode data for later parsing 191 * 192 * @event core.modify_bbcode_init 193 * @var array bbcodes Array of bbcode data for use in parsing 194 * @var array rowset Array of bbcode data from the database 195 * @since 3.1.0-a3 196 */ 197 $vars = array('bbcodes', 'rowset'); 198 extract($phpbb_dispatcher->trigger_event('core.modify_bbcode_init', compact($vars))); 199 200 $this->bbcodes = $bbcodes; 201 } 202 203 /** 204 * Making some pre-checks for bbcodes as well as increasing the number of parsed items 205 */ 206 function check_bbcode($bbcode, &$in) 207 { 208 // when using the /e modifier, preg_replace slashes double-quotes but does not 209 // seem to slash anything else 210 $in = str_replace("\r\n", "\n", str_replace('\"', '"', $in)); 211 212 // Trimming here to make sure no empty bbcodes are parsed accidently 213 if (trim($in) == '') 214 { 215 return false; 216 } 217 218 $this->parsed_items[$bbcode]++; 219 220 return true; 221 } 222 223 /** 224 * Transform some characters in valid bbcodes 225 */ 226 function bbcode_specialchars($text) 227 { 228 $str_from = array('<', '>', '[', ']', '.', ':'); 229 $str_to = array('<', '>', '[', ']', '.', ':'); 230 231 return str_replace($str_from, $str_to, $text); 232 } 233 234 /** 235 * Parse size tag 236 */ 237 function bbcode_size($stx, $in) 238 { 239 global $user, $config; 240 241 if (!$this->check_bbcode('size', $in)) 242 { 243 return $in; 244 } 245 246 if ($config['max_' . $this->mode . '_font_size'] && $config['max_' . $this->mode . '_font_size'] < $stx) 247 { 248 $this->warn_msg[] = $user->lang('MAX_FONT_SIZE_EXCEEDED', (int) $config['max_' . $this->mode . '_font_size']); 249 250 return '[size=' . $stx . ']' . $in . '[/size]'; 251 } 252 253 // Do not allow size=0 254 if ($stx <= 0) 255 { 256 return '[size=' . $stx . ']' . $in . '[/size]'; 257 } 258 259 return '[size=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/size:' . $this->bbcode_uid . ']'; 260 } 261 262 /** 263 * Parse color tag 264 */ 265 function bbcode_color($stx, $in) 266 { 267 if (!$this->check_bbcode('color', $in)) 268 { 269 return $in; 270 } 271 272 return '[color=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/color:' . $this->bbcode_uid . ']'; 273 } 274 275 /** 276 * Parse u tag 277 */ 278 function bbcode_underline($in) 279 { 280 if (!$this->check_bbcode('u', $in)) 281 { 282 return $in; 283 } 284 285 return '[u:' . $this->bbcode_uid . ']' . $in . '[/u:' . $this->bbcode_uid . ']'; 286 } 287 288 /** 289 * Parse b tag 290 */ 291 function bbcode_strong($in) 292 { 293 if (!$this->check_bbcode('b', $in)) 294 { 295 return $in; 296 } 297 298 return '[b:' . $this->bbcode_uid . ']' . $in . '[/b:' . $this->bbcode_uid . ']'; 299 } 300 301 /** 302 * Parse i tag 303 */ 304 function bbcode_italic($in) 305 { 306 if (!$this->check_bbcode('i', $in)) 307 { 308 return $in; 309 } 310 311 return '[i:' . $this->bbcode_uid . ']' . $in . '[/i:' . $this->bbcode_uid . ']'; 312 } 313 314 /** 315 * Parse img tag 316 */ 317 function bbcode_img($in) 318 { 319 global $user, $config; 320 321 if (!$this->check_bbcode('img', $in)) 322 { 323 return $in; 324 } 325 326 $in = trim($in); 327 $error = false; 328 329 $in = str_replace(' ', '%20', $in); 330 331 // Checking urls 332 if (!preg_match('#^' . get_preg_expression('url') . '$#iu', $in) && !preg_match('#^' . get_preg_expression('www_url') . '$#iu', $in)) 333 { 334 return '[img]' . $in . '[/img]'; 335 } 336 337 // Try to cope with a common user error... not specifying a protocol but only a subdomain 338 if (!preg_match('#^[a-z0-9]+://#i', $in)) 339 { 340 $in = 'http://' . $in; 341 } 342 343 if ($config['max_' . $this->mode . '_img_height'] || $config['max_' . $this->mode . '_img_width']) 344 { 345 $stats = @getimagesize(htmlspecialchars_decode($in)); 346 347 if ($stats === false) 348 { 349 $error = true; 350 $this->warn_msg[] = $user->lang['UNABLE_GET_IMAGE_SIZE']; 351 } 352 else 353 { 354 if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $stats[1]) 355 { 356 $error = true; 357 $this->warn_msg[] = $user->lang('MAX_IMG_HEIGHT_EXCEEDED', (int) $config['max_' . $this->mode . '_img_height']); 358 } 359 360 if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $stats[0]) 361 { 362 $error = true; 363 $this->warn_msg[] = $user->lang('MAX_IMG_WIDTH_EXCEEDED', (int) $config['max_' . $this->mode . '_img_width']); 364 } 365 } 366 } 367 368 if ($error || $this->path_in_domain($in)) 369 { 370 return '[img]' . $in . '[/img]'; 371 } 372 373 return '[img:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($in) . '[/img:' . $this->bbcode_uid . ']'; 374 } 375 376 /** 377 * Parse flash tag 378 */ 379 function bbcode_flash($width, $height, $in) 380 { 381 global $user, $config; 382 383 if (!$this->check_bbcode('flash', $in)) 384 { 385 return $in; 386 } 387 388 $in = trim($in); 389 $error = false; 390 391 // Do not allow 0-sizes generally being entered 392 if ($width <= 0 || $height <= 0) 393 { 394 return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]'; 395 } 396 397 $in = str_replace(' ', '%20', $in); 398 399 // Make sure $in is a URL. 400 if (!preg_match('#^' . get_preg_expression('url') . '$#iu', $in) && 401 !preg_match('#^' . get_preg_expression('www_url') . '$#iu', $in)) 402 { 403 return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]'; 404 } 405 406 // Apply the same size checks on flash files as on images 407 if ($config['max_' . $this->mode . '_img_height'] || $config['max_' . $this->mode . '_img_width']) 408 { 409 if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $height) 410 { 411 $error = true; 412 $this->warn_msg[] = $user->lang('MAX_FLASH_HEIGHT_EXCEEDED', (int) $config['max_' . $this->mode . '_img_height']); 413 } 414 415 if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $width) 416 { 417 $error = true; 418 $this->warn_msg[] = $user->lang('MAX_FLASH_WIDTH_EXCEEDED', (int) $config['max_' . $this->mode . '_img_width']); 419 } 420 } 421 422 if ($error || $this->path_in_domain($in)) 423 { 424 return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]'; 425 } 426 427 return '[flash=' . $width . ',' . $height . ':' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($in) . '[/flash:' . $this->bbcode_uid . ']'; 428 } 429 430 /** 431 * Parse inline attachments [ia] 432 */ 433 function bbcode_attachment($stx, $in) 434 { 435 if (!$this->check_bbcode('attachment', $in)) 436 { 437 return $in; 438 } 439 440 return '[attachment=' . $stx . ':' . $this->bbcode_uid . ']<!-- ia' . $stx . ' -->' . trim($in) . '<!-- ia' . $stx . ' -->[/attachment:' . $this->bbcode_uid . ']'; 441 } 442 443 /** 444 * Parse code text from code tag 445 * @access private 446 */ 447 function bbcode_parse_code($stx, &$code) 448 { 449 switch (strtolower($stx)) 450 { 451 case 'php': 452 453 $remove_tags = false; 454 455 $str_from = array('<', '>', '[', ']', '.', ':', ':'); 456 $str_to = array('<', '>', '[', ']', '.', ':', ':'); 457 $code = str_replace($str_from, $str_to, $code); 458 459 if (!preg_match('/\<\?.*?\?\>/is', $code)) 460 { 461 $remove_tags = true; 462 $code = "<?php $code ?>"; 463 } 464 465 $conf = array('highlight.bg', 'highlight.comment', 'highlight.default', 'highlight.html', 'highlight.keyword', 'highlight.string'); 466 foreach ($conf as $ini_var) 467 { 468 @ini_set($ini_var, str_replace('highlight.', 'syntax', $ini_var)); 469 } 470 471 // Because highlight_string is specialcharing the text (but we already did this before), we have to reverse this in order to get correct results 472 $code = htmlspecialchars_decode($code); 473 $code = highlight_string($code, true); 474 475 $str_from = array('<span style="color: ', '<font color="syntax', '</font>', '<code>', '</code>','[', ']', '.', ':'); 476 $str_to = array('<span class="', '<span class="syntax', '</span>', '', '', '[', ']', '.', ':'); 477 478 if ($remove_tags) 479 { 480 $str_from[] = '<span class="syntaxdefault"><?php </span>'; 481 $str_to[] = ''; 482 $str_from[] = '<span class="syntaxdefault"><?php '; 483 $str_to[] = '<span class="syntaxdefault">'; 484 } 485 486 $code = str_replace($str_from, $str_to, $code); 487 $code = preg_replace('#^(<span class="[a-z_]+">)\n?(.*?)\n?(</span>)$#is', '$1$2$3', $code); 488 489 if ($remove_tags) 490 { 491 $code = preg_replace('#(<span class="[a-z]+">)?\?>(</span>)#', '$1 $2', $code); 492 } 493 494 $code = preg_replace('#^<span class="[a-z]+"><span class="([a-z]+)">(.*)</span></span>#s', '<span class="$1">$2</span>', $code); 495 $code = preg_replace('#(?:\s++| )*+</span>$#u', '</span>', $code); 496 497 // remove newline at the end 498 if (!empty($code) && substr($code, -1) == "\n") 499 { 500 $code = substr($code, 0, -1); 501 } 502 503 return "[code=$stx:" . $this->bbcode_uid . ']' . $code . '[/code:' . $this->bbcode_uid . ']'; 504 break; 505 506 default: 507 return '[code:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($code) . '[/code:' . $this->bbcode_uid . ']'; 508 break; 509 } 510 } 511 512 /** 513 * Parse code tag 514 * Expects the argument to start right after the opening [code] tag and to end with [/code] 515 */ 516 function bbcode_code($stx, $in) 517 { 518 if (!$this->check_bbcode('code', $in)) 519 { 520 return $in; 521 } 522 523 // We remove the hardcoded elements from the code block here because it is not used in code blocks 524 // Having it here saves us one preg_replace per message containing [code] blocks 525 // Additionally, magic url parsing should go after parsing bbcodes, but for safety those are stripped out too... 526 $htm_match = get_preg_expression('bbcode_htm'); 527 unset($htm_match[4], $htm_match[5]); 528 $htm_replace = array('\1', '\1', '\2', '\1'); 529 530 $out = $code_block = ''; 531 $open = 1; 532 533 while ($in) 534 { 535 // Determine position and tag length of next code block 536 preg_match('#(.*?)(\[code(?:=([a-z]+))?\])(.+)#is', $in, $buffer); 537 $pos = (isset($buffer[1])) ? strlen($buffer[1]) : false; 538 $tag_length = (isset($buffer[2])) ? strlen($buffer[2]) : false; 539 540 // Determine position of ending code tag 541 $pos2 = stripos($in, '[/code]'); 542 543 // Which is the next block, ending code or code block 544 if ($pos !== false && $pos < $pos2) 545 { 546 // Open new block 547 if (!$open) 548 { 549 $out .= substr($in, 0, $pos); 550 $in = substr($in, $pos); 551 $stx = (isset($buffer[3])) ? $buffer[3] : ''; 552 $code_block = ''; 553 } 554 else 555 { 556 // Already opened block, just append to the current block 557 $code_block .= substr($in, 0, $pos) . ((isset($buffer[2])) ? $buffer[2] : ''); 558 $in = substr($in, $pos); 559 } 560 561 $in = substr($in, $tag_length); 562 $open++; 563 } 564 else 565 { 566 // Close the block 567 if ($open == 1) 568 { 569 $code_block .= substr($in, 0, $pos2); 570 $code_block = preg_replace($htm_match, $htm_replace, $code_block); 571 572 // Parse this code block 573 $out .= $this->bbcode_parse_code($stx, $code_block); 574 $code_block = ''; 575 $open--; 576 } 577 else if ($open) 578 { 579 // Close one open tag... add to the current code block 580 $code_block .= substr($in, 0, $pos2 + 7); 581 $open--; 582 } 583 else 584 { 585 // end code without opening code... will be always outside code block 586 $out .= substr($in, 0, $pos2 + 7); 587 } 588 589 $in = substr($in, $pos2 + 7); 590 } 591 } 592 593 // if now $code_block has contents we need to parse the remaining code while removing the last closing tag to match up. 594 if ($code_block) 595 { 596 $code_block = substr($code_block, 0, -7); 597 $code_block = preg_replace($htm_match, $htm_replace, $code_block); 598 599 $out .= $this->bbcode_parse_code($stx, $code_block); 600 } 601 602 return $out; 603 } 604 605 /** 606 * Parse list bbcode 607 * Expects the argument to start with a tag 608 */ 609 function bbcode_parse_list($in) 610 { 611 if (!$this->check_bbcode('list', $in)) 612 { 613 return $in; 614 } 615 616 // $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 617 $tok = ']'; 618 $out = '['; 619 620 // First character is [ 621 $in = substr($in, 1); 622 $list_end_tags = $item_end_tags = array(); 623 624 do 625 { 626 $pos = strlen($in); 627 628 for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i) 629 { 630 $tmp_pos = strpos($in, $tok[$i]); 631 632 if ($tmp_pos !== false && $tmp_pos < $pos) 633 { 634 $pos = $tmp_pos; 635 } 636 } 637 638 $buffer = substr($in, 0, $pos); 639 $tok = $in[$pos]; 640 641 $in = substr($in, $pos + 1); 642 643 if ($tok == ']') 644 { 645 // if $tok is ']' the buffer holds a tag 646 if (strtolower($buffer) == '/list' && sizeof($list_end_tags)) 647 { 648 // valid [/list] tag, check nesting so that we don't hit false positives 649 if (sizeof($item_end_tags) && sizeof($item_end_tags) >= sizeof($list_end_tags)) 650 { 651 // current li tag has not been closed 652 $out = preg_replace('/\n?\[$/', '[', $out) . array_pop($item_end_tags) . ']['; 653 } 654 655 $out .= array_pop($list_end_tags) . ']'; 656 $tok = '['; 657 } 658 else if (preg_match('#^list(=[0-9a-z]+)?$#i', $buffer, $m)) 659 { 660 // sub-list, add a closing tag 661 if (empty($m[1]) || preg_match('/^=(?:disc|square|circle)$/i', $m[1])) 662 { 663 array_push($list_end_tags, '/list:u:' . $this->bbcode_uid); 664 } 665 else 666 { 667 array_push($list_end_tags, '/list:o:' . $this->bbcode_uid); 668 } 669 $out .= 'list' . substr($buffer, 4) . ':' . $this->bbcode_uid . ']'; 670 $tok = '['; 671 } 672 else 673 { 674 if (($buffer == '*' || substr($buffer, -2) == '[*') && sizeof($list_end_tags)) 675 { 676 // the buffer holds a bullet tag and we have a [list] tag open 677 if (sizeof($item_end_tags) >= sizeof($list_end_tags)) 678 { 679 if (substr($buffer, -2) == '[*') 680 { 681 $out .= substr($buffer, 0, -2) . '['; 682 } 683 // current li tag has not been closed 684 if (preg_match('/\n\[$/', $out, $m)) 685 { 686 $out = preg_replace('/\n\[$/', '[', $out); 687 $buffer = array_pop($item_end_tags) . "]\n[*:" . $this->bbcode_uid; 688 } 689 else 690 { 691 $buffer = array_pop($item_end_tags) . '][*:' . $this->bbcode_uid; 692 } 693 } 694 else 695 { 696 $buffer = '*:' . $this->bbcode_uid; 697 } 698 699 $item_end_tags[] = '/*:m:' . $this->bbcode_uid; 700 } 701 else if ($buffer == '/*') 702 { 703 array_pop($item_end_tags); 704 $buffer = '/*:' . $this->bbcode_uid; 705 } 706 707 $out .= $buffer . $tok; 708 $tok = '[]'; 709 } 710 } 711 else 712 { 713 // Not within a tag, just add buffer to the return string 714 $out .= $buffer . $tok; 715 $tok = ($tok == '[') ? ']' : '[]'; 716 } 717 } 718 while ($in); 719 720 // do we have some tags open? close them now 721 if (sizeof($item_end_tags)) 722 { 723 $out .= '[' . implode('][', $item_end_tags) . ']'; 724 } 725 if (sizeof($list_end_tags)) 726 { 727 $out .= '[' . implode('][', $list_end_tags) . ']'; 728 } 729 730 return $out; 731 } 732 733 /** 734 * Parse quote bbcode 735 * Expects the argument to start with a tag 736 */ 737 function bbcode_quote($in) 738 { 739 global $config, $user; 740 741 $in = str_replace("\r\n", "\n", str_replace('\"', '"', trim($in))); 742 743 if (!$in) 744 { 745 return ''; 746 } 747 748 // To let the parser not catch tokens within quote_username quotes we encode them before we start this... 749 $in = preg_replace('#quote="(.*?)"\]#ie', "'quote="' . str_replace(array('[', ']', '\\\"'), array('[', ']', '\"'), '\$1') . '"]'", $in); 750 751 $tok = ']'; 752 $out = '['; 753 754 $in = substr($in, 1); 755 $close_tags = $error_ary = array(); 756 $buffer = ''; 757 758 do 759 { 760 $pos = strlen($in); 761 for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i) 762 { 763 $tmp_pos = strpos($in, $tok[$i]); 764 if ($tmp_pos !== false && $tmp_pos < $pos) 765 { 766 $pos = $tmp_pos; 767 } 768 } 769 770 $buffer .= substr($in, 0, $pos); 771 $tok = $in[$pos]; 772 $in = substr($in, $pos + 1); 773 774 if ($tok == ']') 775 { 776 if (strtolower($buffer) == '/quote' && sizeof($close_tags) && substr($out, -1, 1) == '[') 777 { 778 // we have found a closing tag 779 $out .= array_pop($close_tags) . ']'; 780 $tok = '['; 781 $buffer = ''; 782 783 /* Add space at the end of the closing tag if not happened before to allow following urls/smilies to be parsed correctly 784 * 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. 785 * Also, we won't have any spaces within $in anyway, only adding up spaces -> #10982 786 if (!$in || $in[0] !== ' ') 787 { 788 $out .= ' '; 789 }*/ 790 } 791 else if (preg_match('#^quote(?:="(.*?)")?$#is', $buffer, $m) && substr($out, -1, 1) == '[') 792 { 793 $this->parsed_items['quote']++; 794 array_push($close_tags, '/quote:' . $this->bbcode_uid); 795 796 if (isset($m[1]) && $m[1]) 797 { 798 $username = str_replace(array('[', ']'), array('[', ']'), $m[1]); 799 $username = preg_replace('#\[(?!b|i|u|color|url|email|/b|/i|/u|/color|/url|/email)#iU', '[$1', $username); 800 801 $end_tags = array(); 802 $error = false; 803 804 preg_match_all('#\[((?:/)?(?:[a-z]+))#i', $username, $tags); 805 foreach ($tags[1] as $tag) 806 { 807 if ($tag[0] != '/') 808 { 809 $end_tags[] = '/' . $tag; 810 } 811 else 812 { 813 $end_tag = array_pop($end_tags); 814 $error = ($end_tag != $tag) ? true : false; 815 } 816 } 817 818 if ($error) 819 { 820 $username = $m[1]; 821 } 822 823 $out .= 'quote="' . $username . '":' . $this->bbcode_uid . ']'; 824 } 825 else 826 { 827 $out .= 'quote:' . $this->bbcode_uid . ']'; 828 } 829 830 $tok = '['; 831 $buffer = ''; 832 } 833 else if (preg_match('#^quote="(.*?)#is', $buffer, $m)) 834 { 835 // the buffer holds an invalid opening tag 836 $buffer .= ']'; 837 } 838 else 839 { 840 $out .= $buffer . $tok; 841 $tok = '[]'; 842 $buffer = ''; 843 } 844 } 845 else 846 { 847 /** 848 * Old quote code working fine, but having errors listed in bug #3572 849 * 850 * $out .= $buffer . $tok; 851 * $tok = ($tok == '[') ? ']' : '[]'; 852 * $buffer = ''; 853 */ 854 855 $out .= $buffer . $tok; 856 857 if ($tok == '[') 858 { 859 // Search the text for the next tok... if an ending quote comes first, then change tok to [] 860 $pos1 = stripos($in, '[/quote'); 861 // If the token ] comes first, we change it to ] 862 $pos2 = strpos($in, ']'); 863 // If the token [ comes first, we change it to [ 864 $pos3 = strpos($in, '['); 865 866 if ($pos1 !== false && ($pos2 === false || $pos1 < $pos2) && ($pos3 === false || $pos1 < $pos3)) 867 { 868 $tok = '[]'; 869 } 870 else if ($pos3 !== false && ($pos2 === false || $pos3 < $pos2)) 871 { 872 $tok = '['; 873 } 874 else 875 { 876 $tok = ']'; 877 } 878 } 879 else 880 { 881 $tok = '[]'; 882 } 883 $buffer = ''; 884 } 885 } 886 while ($in); 887 888 $out .= $buffer; 889 890 if (sizeof($close_tags)) 891 { 892 $out .= '[' . implode('][', $close_tags) . ']'; 893 } 894 895 foreach ($error_ary as $error_msg) 896 { 897 $this->warn_msg[] = $error_msg; 898 } 899 900 return $out; 901 } 902 903 /** 904 * Validate email 905 */ 906 function validate_email($var1, $var2) 907 { 908 $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1))); 909 $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2))); 910 911 $txt = $var2; 912 $email = ($var1) ? $var1 : $var2; 913 914 $validated = true; 915 916 if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email)) 917 { 918 $validated = false; 919 } 920 921 if (!$validated) 922 { 923 return '[email' . (($var1) ? "=$var1" : '') . ']' . $var2 . '[/email]'; 924 } 925 926 $this->parsed_items['email']++; 927 928 if ($var1) 929 { 930 $retval = '[email=' . $this->bbcode_specialchars($email) . ':' . $this->bbcode_uid . ']' . $txt . '[/email:' . $this->bbcode_uid . ']'; 931 } 932 else 933 { 934 $retval = '[email:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($email) . '[/email:' . $this->bbcode_uid . ']'; 935 } 936 937 return $retval; 938 } 939 940 /** 941 * Validate url 942 * 943 * @param string $var1 optional url parameter for url bbcode: [url(=$var1)]$var2[/url] 944 * @param string $var2 url bbcode content: [url(=$var1)]$var2[/url] 945 */ 946 function validate_url($var1, $var2) 947 { 948 global $config; 949 950 $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1))); 951 $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2))); 952 953 $url = ($var1) ? $var1 : $var2; 954 955 if ($var1 && !$var2) 956 { 957 $var2 = $var1; 958 } 959 960 if (!$url) 961 { 962 return '[url' . (($var1) ? '=' . $var1 : '') . ']' . $var2 . '[/url]'; 963 } 964 965 $valid = false; 966 967 $url = str_replace(' ', '%20', $url); 968 969 // Checking urls 970 if (preg_match('#^' . get_preg_expression('url') . '$#iu', $url) || 971 preg_match('#^' . get_preg_expression('www_url') . '$#iu', $url) || 972 preg_match('#^' . preg_quote(generate_board_url(), '#') . get_preg_expression('relative_url') . '$#iu', $url)) 973 { 974 $valid = true; 975 } 976 977 if ($valid) 978 { 979 $this->parsed_items['url']++; 980 981 // if there is no scheme, then add http schema 982 if (!preg_match('#^[a-z][a-z\d+\-.]*:/{2}#i', $url)) 983 { 984 $url = 'http://' . $url; 985 } 986 987 // Is this a link to somewhere inside this board? If so then remove the session id from the url 988 if (strpos($url, generate_board_url()) !== false && strpos($url, 'sid=') !== false) 989 { 990 $url = preg_replace('/(&|\?)sid=[0-9a-f]{32}&/', '\1', $url); 991 $url = preg_replace('/(&|\?)sid=[0-9a-f]{32}$/', '', $url); 992 $url = append_sid($url); 993 } 994 995 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 . ']'; 996 } 997 998 return '[url' . (($var1) ? '=' . $var1 : '') . ']' . $var2 . '[/url]'; 999 } 1000 1001 /** 1002 * Check if url is pointing to this domain/script_path/php-file 1003 * 1004 * @param string $url the url to check 1005 * @return true if the url is pointing to this domain/script_path/php-file, false if not 1006 * 1007 * @access private 1008 */ 1009 function path_in_domain($url) 1010 { 1011 global $config, $phpEx, $user; 1012 1013 if ($config['force_server_vars']) 1014 { 1015 $check_path = $config['script_path']; 1016 } 1017 else 1018 { 1019 $check_path = ($user->page['root_script_path'] != '/') ? substr($user->page['root_script_path'], 0, -1) : '/'; 1020 } 1021 1022 // Is the user trying to link to a php file in this domain and script path? 1023 if (strpos($url, ".{$phpEx}") !== false && strpos($url, $check_path) !== false) 1024 { 1025 $server_name = $user->host; 1026 1027 // Forcing server vars is the only way to specify/override the protocol 1028 if ($config['force_server_vars'] || !$server_name) 1029 { 1030 $server_name = $config['server_name']; 1031 } 1032 1033 // Check again in correct order... 1034 $pos_ext = strpos($url, ".{$phpEx}"); 1035 $pos_path = strpos($url, $check_path); 1036 $pos_domain = strpos($url, $server_name); 1037 1038 if ($pos_domain !== false && $pos_path >= $pos_domain && $pos_ext >= $pos_path) 1039 { 1040 // Ok, actually we allow linking to some files (this may be able to be extended in some way later...) 1041 if (strpos($url, '/' . $check_path . '/download/file.' . $phpEx) !== 0) 1042 { 1043 return false; 1044 } 1045 1046 return true; 1047 } 1048 } 1049 1050 return false; 1051 } 1052 } 1053 1054 /** 1055 * Main message parser for posting, pm, etc. takes raw message 1056 * and parses it for attachments, bbcode and smilies 1057 */ 1058 class parse_message extends bbcode_firstpass 1059 { 1060 var $attachment_data = array(); 1061 var $filename_data = array(); 1062 1063 // Helps ironing out user error 1064 var $message_status = ''; 1065 1066 var $allow_img_bbcode = true; 1067 var $allow_flash_bbcode = true; 1068 var $allow_quote_bbcode = true; 1069 var $allow_url_bbcode = true; 1070 1071 var $mode; 1072 1073 /** 1074 * The plupload object used for dealing with attachments 1075 * @var \phpbb\plupload\plupload 1076 */ 1077 protected $plupload; 1078 1079 /** 1080 * The mimetype guesser object used for attachment mimetypes 1081 * @var \phpbb\mimetype\guesser 1082 */ 1083 protected $mimetype_guesser; 1084 1085 /** 1086 * Init - give message here or manually 1087 */ 1088 function parse_message($message = '') 1089 { 1090 // Init BBCode UID 1091 $this->bbcode_uid = substr(base_convert(unique_id(), 16, 36), 0, BBCODE_UID_LEN); 1092 $this->message = $message; 1093 } 1094 1095 /** 1096 * Parse Message 1097 */ 1098 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') 1099 { 1100 global $config, $db, $user, $phpbb_dispatcher; 1101 1102 $this->mode = $mode; 1103 1104 foreach (array('chars', 'smilies', 'urls', 'font_size', 'img_height', 'img_width') as $key) 1105 { 1106 if (!isset($config['max_' . $mode . '_' . $key])) 1107 { 1108 $config['max_' . $mode . '_' . $key] = 0; 1109 } 1110 } 1111 1112 $this->allow_img_bbcode = $allow_img_bbcode; 1113 $this->allow_flash_bbcode = $allow_flash_bbcode; 1114 $this->allow_quote_bbcode = $allow_quote_bbcode; 1115 $this->allow_url_bbcode = $allow_url_bbcode; 1116 1117 // If false, then $this->message won't be altered, the text will be returned instead. 1118 if (!$update_this_message) 1119 { 1120 $tmp_message = $this->message; 1121 $return_message = &$this->message; 1122 } 1123 1124 if ($this->message_status == 'display') 1125 { 1126 $this->decode_message(); 1127 } 1128 1129 // Do some general 'cleanup' first before processing message, 1130 // e.g. remove excessive newlines(?), smilies(?) 1131 $match = array('#(script|about|applet|activex|chrome):#i'); 1132 $replace = array("\\1:"); 1133 $this->message = preg_replace($match, $replace, trim($this->message)); 1134 1135 // Store message length... 1136 $message_length = ($mode == 'post') ? utf8_strlen($this->message) : utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message)); 1137 1138 // Maximum message length check. 0 disables this check completely. 1139 if ((int) $config['max_' . $mode . '_chars'] > 0 && $message_length > (int) $config['max_' . $mode . '_chars']) 1140 { 1141 $this->warn_msg[] = $user->lang('CHARS_' . strtoupper($mode) . '_CONTAINS', $message_length) . '<br />' . $user->lang('TOO_MANY_CHARS_LIMIT', (int) $config['max_' . $mode . '_chars']); 1142 return (!$update_this_message) ? $return_message : $this->warn_msg; 1143 } 1144 1145 // Minimum message length check for post only 1146 if ($mode === 'post') 1147 { 1148 if (!$message_length || $message_length < (int) $config['min_post_chars']) 1149 { 1150 $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'])); 1151 return (!$update_this_message) ? $return_message : $this->warn_msg; 1152 } 1153 } 1154 1155 /** 1156 * This event can be used for additional message checks/cleanup before parsing 1157 * 1158 * @event core.message_parser_check_message 1159 * @var bool allow_bbcode Do we allow BBCodes 1160 * @var bool allow_magic_url Do we allow magic urls 1161 * @var bool allow_smilies Do we allow smilies 1162 * @var bool allow_img_bbcode Do we allow image BBCode 1163 * @var bool allow_flash_bbcode Do we allow flash BBCode 1164 * @var bool allow_quote_bbcode Do we allow quote BBCode 1165 * @var bool allow_url_bbcode Do we allow url BBCode 1166 * @var bool update_this_message Do we alter the parsed message 1167 * @var string mode Posting mode 1168 * @var string message The message text to parse 1169 * @var string bbcode_bitfield The bbcode_bitfield before parsing 1170 * @var string bbcode_uid The bbcode_uid before parsing 1171 * @var bool return Do we return after the event is triggered if $warn_msg is not empty 1172 * @var array warn_msg Array of the warning messages 1173 * @since 3.1.2-RC1 1174 * @changed 3.1.3-RC1 Added vars $bbcode_bitfield and $bbcode_uid 1175 */ 1176 $message = $this->message; 1177 $warn_msg = $this->warn_msg; 1178 $return = false; 1179 $bbcode_bitfield = $this->bbcode_bitfield; 1180 $bbcode_uid = $this->bbcode_uid; 1181 $vars = array( 1182 'allow_bbcode', 1183 'allow_magic_url', 1184 'allow_smilies', 1185 'allow_img_bbcode', 1186 'allow_flash_bbcode', 1187 'allow_quote_bbcode', 1188 'allow_url_bbcode', 1189 'update_this_message', 1190 'mode', 1191 'message', 1192 'bbcode_bitfield', 1193 'bbcode_uid', 1194 'return', 1195 'warn_msg', 1196 ); 1197 extract($phpbb_dispatcher->trigger_event('core.message_parser_check_message', compact($vars))); 1198 $this->message = $message; 1199 $this->warn_msg = $warn_msg; 1200 $this->bbcode_bitfield = $bbcode_bitfield; 1201 $this->bbcode_uid = $bbcode_uid; 1202 if ($return && !empty($this->warn_msg)) 1203 { 1204 return (!$update_this_message) ? $return_message : $this->warn_msg; 1205 } 1206 1207 // Prepare BBcode (just prepares some tags for better parsing) 1208 if ($allow_bbcode && strpos($this->message, '[') !== false) 1209 { 1210 $this->bbcode_init(); 1211 $disallow = array('img', 'flash', 'quote', 'url'); 1212 foreach ($disallow as $bool) 1213 { 1214 if (!${'allow_' . $bool . '_bbcode'}) 1215 { 1216 $this->bbcodes[$bool]['disabled'] = true; 1217 } 1218 } 1219 1220 $this->prepare_bbcodes(); 1221 } 1222 1223 // Parse smilies 1224 if ($allow_smilies) 1225 { 1226 $this->smilies($config['max_' . $mode . '_smilies']); 1227 } 1228 1229 $num_urls = 0; 1230 1231 // Parse BBCode 1232 if ($allow_bbcode && strpos($this->message, '[') !== false) 1233 { 1234 $this->parse_bbcode(); 1235 $num_urls += $this->parsed_items['url']; 1236 } 1237 1238 // Parse URL's 1239 if ($allow_magic_url) 1240 { 1241 $this->magic_url(generate_board_url()); 1242 1243 if ($config['max_' . $mode . '_urls']) 1244 { 1245 $num_urls += preg_match_all('#\<!-- ([lmwe]) --\>.*?\<!-- \1 --\>#', $this->message, $matches); 1246 } 1247 } 1248 1249 // Check for out-of-bounds characters that are currently 1250 // not supported by utf8_bin in MySQL 1251 if (preg_match_all('/[\x{10000}-\x{10FFFF}]/u', $this->message, $matches)) 1252 { 1253 $character_list = implode('<br />', $matches[0]); 1254 $this->warn_msg[] = $user->lang('UNSUPPORTED_CHARACTERS_MESSAGE', $character_list); 1255 return $update_this_message ? $this->warn_msg : $return_message; 1256 } 1257 1258 // Remove quotes that are nested too deep 1259 if ($config['max_quote_depth'] > 0) 1260 { 1261 $this->remove_nested_quotes($config['max_quote_depth']); 1262 } 1263 1264 // Check for "empty" message. We do not check here for maximum length, because bbcode, smilies, etc. can add to the length. 1265 // The maximum length check happened before any parsings. 1266 if ($mode === 'post' && utf8_clean_string($this->message) === '') 1267 { 1268 $this->warn_msg[] = $user->lang['TOO_FEW_CHARS']; 1269 return (!$update_this_message) ? $return_message : $this->warn_msg; 1270 } 1271 1272 // Check number of links 1273 if ($config['max_' . $mode . '_urls'] && $num_urls > $config['max_' . $mode . '_urls']) 1274 { 1275 $this->warn_msg[] = sprintf($user->lang['TOO_MANY_URLS'], $config['max_' . $mode . '_urls']); 1276 return (!$update_this_message) ? $return_message : $this->warn_msg; 1277 } 1278 1279 if (!$update_this_message) 1280 { 1281 unset($this->message); 1282 $this->message = $tmp_message; 1283 return $return_message; 1284 } 1285 1286 $this->message_status = 'parsed'; 1287 return false; 1288 } 1289 1290 /** 1291 * Formatting text for display 1292 */ 1293 function format_display($allow_bbcode, $allow_magic_url, $allow_smilies, $update_this_message = true) 1294 { 1295 global $phpbb_dispatcher; 1296 1297 // If false, then the parsed message get returned but internal message not processed. 1298 if (!$update_this_message) 1299 { 1300 $tmp_message = $this->message; 1301 $return_message = &$this->message; 1302 } 1303 1304 $text = $this->message; 1305 $uid = $this->bbcode_uid; 1306 1307 /** 1308 * Event to modify the text before it is parsed 1309 * 1310 * @event core.modify_format_display_text_before 1311 * @var string text The message text to parse 1312 * @var string uid The bbcode uid 1313 * @var bool allow_bbcode Do we allow bbcodes 1314 * @var bool allow_magic_url Do we allow magic urls 1315 * @var bool allow_smilies Do we allow smilies 1316 * @var bool update_this_message Do we update the internal message 1317 * with the parsed result 1318 * @since 3.1.6-RC1 1319 */ 1320 $vars = array('text', 'uid', 'allow_bbcode', 'allow_magic_url', 'allow_smilies', 'update_this_message'); 1321 extract($phpbb_dispatcher->trigger_event('core.modify_format_display_text_before', compact($vars))); 1322 1323 $this->message = $text; 1324 $this->bbcode_uid = $uid; 1325 unset($text, $uid); 1326 1327 if ($this->message_status == 'plain') 1328 { 1329 // Force updating message - of course. 1330 $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); 1331 } 1332 1333 // Replace naughty words such as farty pants 1334 $this->message = censor_text($this->message); 1335 1336 // Parse BBcode 1337 if ($allow_bbcode) 1338 { 1339 $this->bbcode_cache_init(); 1340 1341 // We are giving those parameters to be able to use the bbcode class on its own 1342 $this->bbcode_second_pass($this->message, $this->bbcode_uid); 1343 } 1344 1345 $this->message = bbcode_nl2br($this->message); 1346 $this->message = smiley_text($this->message, !$allow_smilies); 1347 1348 $text = $this->message; 1349 $uid = $this->bbcode_uid; 1350 1351 /** 1352 * Event to modify the text after it is parsed 1353 * 1354 * @event core.modify_format_display_text_after 1355 * @var string text The message text to parse 1356 * @var string uid The bbcode uid 1357 * @var bool allow_bbcode Do we allow bbcodes 1358 * @var bool allow_magic_url Do we allow magic urls 1359 * @var bool allow_smilies Do we allow smilies 1360 * @var bool update_this_message Do we update the internal message 1361 * with the parsed result 1362 * @since 3.1.0-a3 1363 */ 1364 $vars = array('text', 'uid', 'allow_bbcode', 'allow_magic_url', 'allow_smilies', 'update_this_message'); 1365 extract($phpbb_dispatcher->trigger_event('core.modify_format_display_text_after', compact($vars))); 1366 1367 $this->message = $text; 1368 $this->bbcode_uid = $uid; 1369 1370 if (!$update_this_message) 1371 { 1372 unset($this->message); 1373 $this->message = $tmp_message; 1374 return $return_message; 1375 } 1376 1377 $this->message_status = 'display'; 1378 return false; 1379 } 1380 1381 /** 1382 * Decode message to be placed back into form box 1383 */ 1384 function decode_message($custom_bbcode_uid = '', $update_this_message = true) 1385 { 1386 // If false, then the parsed message get returned but internal message not processed. 1387 if (!$update_this_message) 1388 { 1389 $tmp_message = $this->message; 1390 $return_message = &$this->message; 1391 } 1392 1393 ($custom_bbcode_uid) ? decode_message($this->message, $custom_bbcode_uid) : decode_message($this->message, $this->bbcode_uid); 1394 1395 if (!$update_this_message) 1396 { 1397 unset($this->message); 1398 $this->message = $tmp_message; 1399 return $return_message; 1400 } 1401 1402 $this->message_status = 'plain'; 1403 return false; 1404 } 1405 1406 /** 1407 * Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx. 1408 * Cuts down displayed size of link if over 50 chars, turns absolute links 1409 * into relative versions when the server/script path matches the link 1410 */ 1411 function magic_url($server_url) 1412 { 1413 // We use the global make_clickable function 1414 $this->message = make_clickable($this->message, $server_url); 1415 } 1416 1417 /** 1418 * Parse Smilies 1419 */ 1420 function smilies($max_smilies = 0) 1421 { 1422 global $db, $user; 1423 static $match; 1424 static $replace; 1425 1426 // See if the static arrays have already been filled on an earlier invocation 1427 if (!is_array($match)) 1428 { 1429 $match = $replace = array(); 1430 1431 // NOTE: obtain_* function? chaching the table contents? 1432 1433 // For now setting the ttl to 10 minutes 1434 switch ($db->get_sql_layer()) 1435 { 1436 case 'mssql': 1437 case 'mssql_odbc': 1438 case 'mssqlnative': 1439 $sql = 'SELECT * 1440 FROM ' . SMILIES_TABLE . ' 1441 ORDER BY LEN(code) DESC'; 1442 break; 1443 1444 // LENGTH supported by MySQL, IBM DB2, Oracle and Access for sure... 1445 default: 1446 $sql = 'SELECT * 1447 FROM ' . SMILIES_TABLE . ' 1448 ORDER BY LENGTH(code) DESC'; 1449 break; 1450 } 1451 $result = $db->sql_query($sql, 600); 1452 1453 while ($row = $db->sql_fetchrow($result)) 1454 { 1455 if (empty($row['code'])) 1456 { 1457 continue; 1458 } 1459 1460 // (assertion) 1461 $match[] = preg_quote($row['code'], '#'); 1462 $replace[] = '<!-- s' . $row['code'] . ' --><img src="{SMILIES_PATH}/' . $row['smiley_url'] . '" alt="' . $row['code'] . '" title="' . $row['emotion'] . '" /><!-- s' . $row['code'] . ' -->'; 1463 } 1464 $db->sql_freeresult($result); 1465 } 1466 1467 if (sizeof($match)) 1468 { 1469 if ($max_smilies) 1470 { 1471 // 'u' modifier has been added to correctly parse smilies within unicode strings 1472 // For details: http://tracker.phpbb.com/browse/PHPBB3-10117 1473 $num_matches = preg_match_all('#(?<=^|[\n .])(?:' . implode('|', $match) . ')(?![^<>]*>)#u', $this->message, $matches); 1474 unset($matches); 1475 1476 if ($num_matches !== false && $num_matches > $max_smilies) 1477 { 1478 $this->warn_msg[] = sprintf($user->lang['TOO_MANY_SMILIES'], $max_smilies); 1479 return; 1480 } 1481 } 1482 1483 // Make sure the delimiter # is added in front and at the end of every element within $match 1484 // 'u' modifier has been added to correctly parse smilies within unicode strings 1485 // For details: http://tracker.phpbb.com/browse/PHPBB3-10117 1486 1487 $this->message = trim(preg_replace(explode(chr(0), '#(?<=^|[\n .])' . implode('(?![^<>]*>)#u' . chr(0) . '#(?<=^|[\n .])', $match) . '(?![^<>]*>)#u'), $replace, $this->message)); 1488 } 1489 } 1490 1491 /** 1492 * Parse Attachments 1493 */ 1494 function parse_attachments($form_name, $mode, $forum_id, $submit, $preview, $refresh, $is_message = false) 1495 { 1496 global $config, $auth, $user, $phpbb_root_path, $phpEx, $db, $request; 1497 1498 $error = array(); 1499 1500 $num_attachments = sizeof($this->attachment_data); 1501 $this->filename_data['filecomment'] = utf8_normalize_nfc(request_var('filecomment', '', true)); 1502 $upload = $request->file($form_name); 1503 $upload_file = (!empty($upload) && $upload['name'] !== 'none' && trim($upload['name'])); 1504 1505 $add_file = (isset($_POST['add_file'])) ? true : false; 1506 $delete_file = (isset($_POST['delete_file'])) ? true : false; 1507 1508 // First of all adjust comments if changed 1509 $actual_comment_list = utf8_normalize_nfc(request_var('comment_list', array(''), true)); 1510 1511 foreach ($actual_comment_list as $comment_key => $comment) 1512 { 1513 if (!isset($this->attachment_data[$comment_key])) 1514 { 1515 continue; 1516 } 1517 1518 if ($this->attachment_data[$comment_key]['attach_comment'] != $actual_comment_list[$comment_key]) 1519 { 1520 $this->attachment_data[$comment_key]['attach_comment'] = $actual_comment_list[$comment_key]; 1521 } 1522 } 1523 1524 $cfg = array(); 1525 $cfg['max_attachments'] = ($is_message) ? $config['max_attachments_pm'] : $config['max_attachments']; 1526 $forum_id = ($is_message) ? 0 : $forum_id; 1527 1528 if ($submit && in_array($mode, array('post', 'reply', 'quote', 'edit')) && $upload_file) 1529 { 1530 if ($num_attachments < $cfg['max_attachments'] || $auth->acl_get('a_') || $auth->acl_get('m_', $forum_id)) 1531 { 1532 $filedata = upload_attachment($form_name, $forum_id, false, '', $is_message); 1533 $error = $filedata['error']; 1534 1535 if ($filedata['post_attach'] && !sizeof($error)) 1536 { 1537 $sql_ary = array( 1538 'physical_filename' => $filedata['physical_filename'], 1539 'attach_comment' => $this->filename_data['filecomment'], 1540 'real_filename' => $filedata['real_filename'], 1541 'extension' => $filedata['extension'], 1542 'mimetype' => $filedata['mimetype'], 1543 'filesize' => $filedata['filesize'], 1544 'filetime' => $filedata['filetime'], 1545 'thumbnail' => $filedata['thumbnail'], 1546 'is_orphan' => 1, 1547 'in_message' => ($is_message) ? 1 : 0, 1548 'poster_id' => $user->data['user_id'], 1549 ); 1550 1551 $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); 1552 1553 $new_entry = array( 1554 'attach_id' => $db->sql_nextid(), 1555 'is_orphan' => 1, 1556 'real_filename' => $filedata['real_filename'], 1557 'attach_comment'=> $this->filename_data['filecomment'], 1558 'filesize' => $filedata['filesize'], 1559 ); 1560 1561 $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data); 1562 $this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "'[attachment='.(\\1 + 1).']\\2[/attachment]'", $this->message); 1563 1564 $this->filename_data['filecomment'] = ''; 1565 1566 // This Variable is set to false here, because Attachments are entered into the 1567 // Database in two modes, one if the id_list is 0 and the second one if post_attach is true 1568 // Since post_attach is automatically switched to true if an Attachment got added to the filesystem, 1569 // but we are assigning an id of 0 here, we have to reset the post_attach variable to false. 1570 // 1571 // This is very relevant, because it could happen that the post got not submitted, but we do not 1572 // know this circumstance here. We could be at the posting page or we could be redirected to the entered 1573 // post. :) 1574 $filedata['post_attach'] = false; 1575 } 1576 } 1577 else 1578 { 1579 $error[] = $user->lang('TOO_MANY_ATTACHMENTS', (int) $cfg['max_attachments']); 1580 } 1581 } 1582 1583 if ($preview || $refresh || sizeof($error)) 1584 { 1585 if (isset($this->plupload) && $this->plupload->is_active()) 1586 { 1587 $json_response = new \phpbb\json_response(); 1588 } 1589 1590 // Perform actions on temporary attachments 1591 if ($delete_file) 1592 { 1593 include_once($phpbb_root_path . 'includes/functions_admin.' . $phpEx); 1594 1595 $index = array_keys(request_var('delete_file', array(0 => 0))); 1596 $index = (!empty($index)) ? $index[0] : false; 1597 1598 if ($index !== false && !empty($this->attachment_data[$index])) 1599 { 1600 // delete selected attachment 1601 if ($this->attachment_data[$index]['is_orphan']) 1602 { 1603 $sql = 'SELECT attach_id, physical_filename, thumbnail 1604 FROM ' . ATTACHMENTS_TABLE . ' 1605 WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id'] . ' 1606 AND is_orphan = 1 1607 AND poster_id = ' . $user->data['user_id']; 1608 $result = $db->sql_query($sql); 1609 $row = $db->sql_fetchrow($result); 1610 $db->sql_freeresult($result); 1611 1612 if ($row) 1613 { 1614 phpbb_unlink($row['physical_filename'], 'file'); 1615 1616 if ($row['thumbnail']) 1617 { 1618 phpbb_unlink($row['physical_filename'], 'thumbnail'); 1619 } 1620 1621 $db->sql_query('DELETE FROM ' . ATTACHMENTS_TABLE . ' WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id']); 1622 } 1623 } 1624 else 1625 { 1626 delete_attachments('attach', array(intval($this->attachment_data[$index]['attach_id']))); 1627 } 1628 1629 unset($this->attachment_data[$index]); 1630 $this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "(\\1 == \$index) ? '' : ((\\1 > \$index) ? '[attachment=' . (\\1 - 1) . ']\\2[/attachment]' : '\\0')", $this->message); 1631 1632 // Reindex Array 1633 $this->attachment_data = array_values($this->attachment_data); 1634 if (isset($this->plupload) && $this->plupload->is_active()) 1635 { 1636 $json_response->send($this->attachment_data); 1637 } 1638 } 1639 } 1640 else if (($add_file || $preview) && $upload_file) 1641 { 1642 if ($num_attachments < $cfg['max_attachments'] || $auth->acl_gets('m_', 'a_', $forum_id)) 1643 { 1644 $filedata = upload_attachment($form_name, $forum_id, false, '', $is_message, false, $this->mimetype_guesser, $this->plupload); 1645 $error = array_merge($error, $filedata['error']); 1646 1647 if (!sizeof($error)) 1648 { 1649 $sql_ary = array( 1650 'physical_filename' => $filedata['physical_filename'], 1651 'attach_comment' => $this->filename_data['filecomment'], 1652 'real_filename' => $filedata['real_filename'], 1653 'extension' => $filedata['extension'], 1654 'mimetype' => $filedata['mimetype'], 1655 'filesize' => $filedata['filesize'], 1656 'filetime' => $filedata['filetime'], 1657 'thumbnail' => $filedata['thumbnail'], 1658 'is_orphan' => 1, 1659 'in_message' => ($is_message) ? 1 : 0, 1660 'poster_id' => $user->data['user_id'], 1661 ); 1662 1663 $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); 1664 1665 $new_entry = array( 1666 'attach_id' => $db->sql_nextid(), 1667 'is_orphan' => 1, 1668 'real_filename' => $filedata['real_filename'], 1669 'attach_comment'=> $this->filename_data['filecomment'], 1670 'filesize' => $filedata['filesize'], 1671 ); 1672 1673 $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data); 1674 $this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "'[attachment='.(\\1 + 1).']\\2[/attachment]'", $this->message); 1675 $this->filename_data['filecomment'] = ''; 1676 1677 if (isset($this->plupload) && $this->plupload->is_active()) 1678 { 1679 $download_url = append_sid("{$phpbb_root_path}download/file.{$phpEx}", 'mode=view&id=' . $new_entry['attach_id']); 1680 1681 // Send the client the attachment data to maintain state 1682 $json_response->send(array('data' => $this->attachment_data, 'download_url' => $download_url)); 1683 } 1684 } 1685 } 1686 else 1687 { 1688 $error[] = $user->lang('TOO_MANY_ATTACHMENTS', (int) $cfg['max_attachments']); 1689 } 1690 1691 if (!empty($error) && isset($this->plupload) && $this->plupload->is_active()) 1692 { 1693 // If this is a plupload (and thus ajax) request, give the 1694 // client the first error we have 1695 $json_response->send(array( 1696 'jsonrpc' => '2.0', 1697 'id' => 'id', 1698 'error' => array( 1699 'code' => 105, 1700 'message' => current($error), 1701 ), 1702 )); 1703 } 1704 } 1705 } 1706 1707 foreach ($error as $error_msg) 1708 { 1709 $this->warn_msg[] = $error_msg; 1710 } 1711 } 1712 1713 /** 1714 * Get Attachment Data 1715 */ 1716 function get_submitted_attachment_data($check_user_id = false) 1717 { 1718 global $user, $db, $phpbb_root_path, $phpEx, $config; 1719 global $request; 1720 1721 $this->filename_data['filecomment'] = utf8_normalize_nfc(request_var('filecomment', '', true)); 1722 $attachment_data = $request->variable('attachment_data', array(0 => array('' => '')), true, \phpbb\request\request_interface::POST); 1723 $this->attachment_data = array(); 1724 1725 $check_user_id = ($check_user_id === false) ? $user->data['user_id'] : $check_user_id; 1726 1727 if (!sizeof($attachment_data)) 1728 { 1729 return; 1730 } 1731 1732 $not_orphan = $orphan = array(); 1733 1734 foreach ($attachment_data as $pos => $var_ary) 1735 { 1736 if ($var_ary['is_orphan']) 1737 { 1738 $orphan[(int) $var_ary['attach_id']] = $pos; 1739 } 1740 else 1741 { 1742 $not_orphan[(int) $var_ary['attach_id']] = $pos; 1743 } 1744 } 1745 1746 // Regenerate already posted attachments 1747 if (sizeof($not_orphan)) 1748 { 1749 // Get the attachment data, based on the poster id... 1750 $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment, filesize 1751 FROM ' . ATTACHMENTS_TABLE . ' 1752 WHERE ' . $db->sql_in_set('attach_id', array_keys($not_orphan)) . ' 1753 AND poster_id = ' . $check_user_id; 1754 $result = $db->sql_query($sql); 1755 1756 while ($row = $db->sql_fetchrow($result)) 1757 { 1758 $pos = $not_orphan[$row['attach_id']]; 1759 $this->attachment_data[$pos] = $row; 1760 $this->attachment_data[$pos]['attach_comment'] = $attachment_data[$pos]['attach_comment']; 1761 1762 unset($not_orphan[$row['attach_id']]); 1763 } 1764 $db->sql_freeresult($result); 1765 } 1766 1767 if (sizeof($not_orphan)) 1768 { 1769 trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR); 1770 } 1771 1772 // Regenerate newly uploaded attachments 1773 if (sizeof($orphan)) 1774 { 1775 $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment, filesize 1776 FROM ' . ATTACHMENTS_TABLE . ' 1777 WHERE ' . $db->sql_in_set('attach_id', array_keys($orphan)) . ' 1778 AND poster_id = ' . $user->data['user_id'] . ' 1779 AND is_orphan = 1'; 1780 $result = $db->sql_query($sql); 1781 1782 while ($row = $db->sql_fetchrow($result)) 1783 { 1784 $pos = $orphan[$row['attach_id']]; 1785 $this->attachment_data[$pos] = $row; 1786 $this->attachment_data[$pos]['attach_comment'] = $attachment_data[$pos]['attach_comment']; 1787 1788 unset($orphan[$row['attach_id']]); 1789 } 1790 $db->sql_freeresult($result); 1791 } 1792 1793 if (sizeof($orphan)) 1794 { 1795 trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR); 1796 } 1797 1798 ksort($this->attachment_data); 1799 } 1800 1801 /** 1802 * Parse Poll 1803 */ 1804 function parse_poll(&$poll) 1805 { 1806 global $auth, $user, $config; 1807 1808 $poll_max_options = $poll['poll_max_options']; 1809 1810 // Parse Poll Option text ;) 1811 $tmp_message = $this->message; 1812 $this->message = $poll['poll_option_text']; 1813 $bbcode_bitfield = $this->bbcode_bitfield; 1814 1815 $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'); 1816 1817 $bbcode_bitfield = base64_encode(base64_decode($bbcode_bitfield) | base64_decode($this->bbcode_bitfield)); 1818 $this->message = $tmp_message; 1819 1820 // Parse Poll Title 1821 $tmp_message = $this->message; 1822 $this->message = $poll['poll_title']; 1823 $this->bbcode_bitfield = $bbcode_bitfield; 1824 1825 $poll['poll_options'] = preg_split('/\s*?\n\s*/', trim($poll['poll_option_text'])); 1826 $poll['poll_options_size'] = sizeof($poll['poll_options']); 1827 1828 if (!$poll['poll_title'] && $poll['poll_options_size']) 1829 { 1830 $this->warn_msg[] = $user->lang['NO_POLL_TITLE']; 1831 } 1832 else 1833 { 1834 if (utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message)) > 100) 1835 { 1836 $this->warn_msg[] = $user->lang['POLL_TITLE_TOO_LONG']; 1837 } 1838 $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'); 1839 if (strlen($poll['poll_title']) > 255) 1840 { 1841 $this->warn_msg[] = $user->lang['POLL_TITLE_COMP_TOO_LONG']; 1842 } 1843 } 1844 1845 $this->bbcode_bitfield = base64_encode(base64_decode($bbcode_bitfield) | base64_decode($this->bbcode_bitfield)); 1846 $this->message = $tmp_message; 1847 unset($tmp_message); 1848 1849 if (sizeof($poll['poll_options']) == 1) 1850 { 1851 $this->warn_msg[] = $user->lang['TOO_FEW_POLL_OPTIONS']; 1852 } 1853 else if ($poll['poll_options_size'] > (int) $config['max_poll_options']) 1854 { 1855 $this->warn_msg[] = $user->lang['TOO_MANY_POLL_OPTIONS']; 1856 } 1857 else if ($poll_max_options > $poll['poll_options_size']) 1858 { 1859 $this->warn_msg[] = $user->lang['TOO_MANY_USER_OPTIONS']; 1860 } 1861 1862 $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']); 1863 } 1864 1865 /** 1866 * Remove nested quotes at given depth in current parsed message 1867 * 1868 * @param integer $max_depth Depth limit 1869 * @return null 1870 */ 1871 public function remove_nested_quotes($max_depth) 1872 { 1873 // Capture all [quote] and [/quote] tags 1874 preg_match_all('(\\[/?quote(?:="(.*?)")?:' . $this->bbcode_uid . '\\])', $this->message, $matches, PREG_OFFSET_CAPTURE); 1875 1876 // Iterate over the quote tags to mark the ranges that must be removed 1877 $depth = 0; 1878 $ranges = array(); 1879 $start_pos = 0; 1880 foreach ($matches[0] as $match) 1881 { 1882 if ($match[0][1] === '/') 1883 { 1884 --$depth; 1885 if ($depth == $max_depth) 1886 { 1887 $end_pos = $match[1] + strlen($match[0]); 1888 $length = $end_pos - $start_pos; 1889 $ranges[] = array($start_pos, $length); 1890 } 1891 } 1892 else 1893 { 1894 ++$depth; 1895 if ($depth == $max_depth + 1) 1896 { 1897 $start_pos = $match[1]; 1898 } 1899 } 1900 } 1901 1902 foreach (array_reverse($ranges) as $range) 1903 { 1904 list($start_pos, $length) = $range; 1905 $this->message = substr_replace($this->message, '', $start_pos, $length); 1906 } 1907 } 1908 1909 /** 1910 * Setter function for passing the plupload object 1911 * 1912 * @param \phpbb\plupload\plupload $plupload The plupload object 1913 * 1914 * @return null 1915 */ 1916 public function set_plupload(\phpbb\plupload\plupload $plupload) 1917 { 1918 $this->plupload = $plupload; 1919 } 1920 1921 /** 1922 * Setter function for passing the mimetype_guesser object 1923 * 1924 * @param \phpbb\mimetype\guesser $mimetype_guesser The mimetype_guesser object 1925 * 1926 * @return null 1927 */ 1928 public function set_mimetype_guesser(\phpbb\mimetype\guesser $mimetype_guesser) 1929 { 1930 $this->mimetype_guesser = $mimetype_guesser; 1931 } 1932 1933 /** 1934 * Function to perform custom bbcode validation by extensions 1935 * can be used in bbcode_init() to assign regexp replacement 1936 * Example: 'regexp' => array('#\[b\](.*?)\[/b\]#uise' => "\$this->validate_bbcode_by_extension('\$1')") 1937 * 1938 * Accepts variable number of parameters 1939 * 1940 * @return mixed Validation result 1941 */ 1942 public function validate_bbcode_by_extension() 1943 { 1944 global $phpbb_dispatcher; 1945 1946 $return = false; 1947 $params_array = func_get_args(); 1948 1949 /** 1950 * Event to validate bbcode with the custom validating methods 1951 * provided by extensions 1952 * 1953 * @event core.validate_bbcode_by_extension 1954 * @var array params_array Array with the function parameters 1955 * @var mixed return Validation result to return 1956 * 1957 * @since 3.1.5-RC1 1958 */ 1959 $vars = array('params_array', 'return'); 1960 extract($phpbb_dispatcher->trigger_event('core.validate_bbcode_by_extension', compact($vars))); 1961 1962 return $return; 1963 } 1964 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jan 11 00:25:41 2018 | Cross-referenced by PHPXref 0.7.1 |