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