[ Index ] |
PHP Cross Reference of MyBB |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * MyBB 1.6 4 * Copyright 2010 MyBB Group, All Rights Reserved 5 * 6 * Website: http://mybb.com 7 * License: http://mybb.com/about/license 8 * 9 * $Id$ 10 */ 11 12 /* 13 options = array( 14 allow_html 15 allow_smilies 16 allow_mycode 17 nl2br 18 filter_badwords 19 me_username 20 shorten_urls 21 highlight 22 ) 23 */ 24 25 class postParser 26 { 27 /** 28 * Internal cache of MyCode. 29 * 30 * @access public 31 * @var mixed 32 */ 33 public $mycode_cache = 0; 34 35 /** 36 * Internal cache of smilies 37 * 38 * @access public 39 * @var mixed 40 */ 41 public $smilies_cache = 0; 42 43 /** 44 * Internal cache of badwords filters 45 * 46 * @access public 47 * @var mixed 48 */ 49 public $badwords_cache = 0; 50 51 /** 52 * Base URL for smilies 53 * 54 * @access public 55 * @var string 56 */ 57 public $base_url; 58 59 /** 60 * Parsed Highlights cache 61 * 62 * @access public 63 * @var array 64 */ 65 public $highlight_cache = array(); 66 67 /** 68 * Options for this parsed message (Private - set by parse_message argument) 69 * 70 * @access public 71 * @var array 72 */ 73 public $options; 74 75 /** 76 * Parses a message with the specified options. 77 * 78 * @param string The message to be parsed. 79 * @param array Array of yes/no options - allow_html,filter_badwords,allow_mycode,allow_smilies,nl2br,me_username. 80 * @return string The parsed message. 81 */ 82 function parse_message($message, $options=array()) 83 { 84 global $plugins, $mybb; 85 86 // Set base URL for parsing smilies 87 $this->base_url = $mybb->settings['bburl']; 88 89 if($this->base_url != "") 90 { 91 if(my_substr($this->base_url, my_strlen($this->base_url) -1) != "/") 92 { 93 $this->base_url = $this->base_url."/"; 94 } 95 } 96 97 // Set the options 98 $this->options = $options; 99 100 $message = $plugins->run_hooks("parse_message_start", $message); 101 102 // Get rid of cartridge returns for they are the workings of the devil 103 $message = str_replace("\r", "", $message); 104 105 // Filter bad words if requested. 106 if($this->options['filter_badwords']) 107 { 108 $message = $this->parse_badwords($message); 109 } 110 111 if($this->options['allow_html'] != 1) 112 { 113 $message = $this->parse_html($message); 114 } 115 else 116 { 117 while(preg_match("#<s(cript|tyle)(.*)>(.*)</s(cript|tyle)(.*)>#is", $message)) 118 { 119 $message = preg_replace("#<s(cript|tyle)(.*)>(.*)</s(cript|tyle)(.*)>#is", "<s$1$2>$3</s$4$5>", $message); 120 } 121 122 $find = array('<?php', '<!--', '-->', '?>', "<br />\n", "<br>\n"); 123 $replace = array('<?php', '<!--', '-->', '?>', "\n", "\n"); 124 $message = str_replace($find, $replace, $message); 125 } 126 127 // If MyCode needs to be replaced, first filter out [code] and [php] tags. 128 if($this->options['allow_mycode']) 129 { 130 preg_match_all("#\[(code|php)\](.*?)\[/\\1\](\r\n?|\n?)#si", $message, $code_matches, PREG_SET_ORDER); 131 $message = preg_replace("#\[(code|php)\](.*?)\[/\\1\](\r\n?|\n?)#si", "<mybb-code>\n", $message); 132 } 133 134 // Always fix bad Javascript in the message. 135 $message = $this->fix_javascript($message); 136 137 // Replace "me" code and slaps if we have a username 138 if($this->options['me_username']) 139 { 140 global $lang; 141 142 $message = preg_replace('#(>|^|\r|\n)/me ([^\r\n<]*)#i', "\\1<span style=\"color: red;\">* {$this->options['me_username']} \\2</span>", $message); 143 $message = preg_replace('#(>|^|\r|\n)/slap ([^\r\n<]*)#i', "\\1<span style=\"color: red;\">* {$this->options['me_username']} {$lang->slaps} \\2 {$lang->with_trout}</span>", $message); 144 } 145 146 // If we can, parse smilies 147 if($this->options['allow_smilies']) 148 { 149 $message = $this->parse_smilies($message, $this->options['allow_html']); 150 } 151 152 // Replace MyCode if requested. 153 if($this->options['allow_mycode']) 154 { 155 $message = $this->parse_mycode($message, $this->options); 156 } 157 158 // Parse Highlights 159 if(!empty($this->options['highlight'])) 160 { 161 $message = $this->highlight_message($message, $this->options['highlight']); 162 } 163 164 // Run plugin hooks 165 $message = $plugins->run_hooks("parse_message", $message); 166 167 if($this->options['allow_mycode']) 168 { 169 // Now that we're done, if we split up any code tags, parse them and glue it all back together 170 if(count($code_matches) > 0) 171 { 172 foreach($code_matches as $text) 173 { 174 // Fix up HTML inside the code tags so it is clean 175 if($options['allow_html'] != 0) 176 { 177 $text[2] = $this->parse_html($text[2]); 178 } 179 180 if(my_strtolower($text[1]) == "code") 181 { 182 $code = $this->mycode_parse_code($text[2]); 183 } 184 elseif(my_strtolower($text[1]) == "php") 185 { 186 $code = $this->mycode_parse_php($text[2]); 187 } 188 $message = preg_replace("#\<mybb-code>\n?#", $code, $message, 1); 189 } 190 } 191 } 192 193 // Replace meta and base tags in our post - these are > dangerous < 194 if($this->options['allow_html']) 195 { 196 $message = preg_replace_callback("#<((m[^a])|(b[^diloru>])|(s[^aemptu>]))(\s*[^>]*)>#si", create_function( 197 '$matches', 198 'return htmlspecialchars_uni($matches[0]);' 199 ), $message); 200 } 201 202 if(!isset($options['nl2br']) || $options['nl2br'] != 0) 203 { 204 $message = nl2br($message); 205 // Fix up new lines and block level elements 206 $message = preg_replace("#(</?(?:html|head|body|div|p|form|table|thead|tbody|tfoot|tr|td|th|ul|ol|li|div|p|blockquote|cite|hr)[^>]*>)\s*<br />#i", "$1", $message); 207 $message = preg_replace("#( )+(</?(?:html|head|body|div|p|form|table|thead|tbody|tfoot|tr|td|th|ul|ol|li|div|p|blockquote|cite|hr)[^>]*>)#i", "$2", $message); 208 } 209 210 $message = my_wordwrap($message); 211 212 $message = $plugins->run_hooks("parse_message_end", $message); 213 214 return $message; 215 } 216 217 /** 218 * Converts HTML in a message to their specific entities whilst allowing unicode characters. 219 * 220 * @param string The message to be parsed. 221 * @return string The formatted message. 222 */ 223 function parse_html($message) 224 { 225 $message = preg_replace("#&(?!\#[0-9]+;)#si", "&", $message); // fix & but allow unicode 226 $message = str_replace("<","<",$message); 227 $message = str_replace(">",">",$message); 228 return $message; 229 } 230 231 /** 232 * Generates a cache of MyCode, both standard and custom. 233 * 234 * @access private 235 */ 236 private function cache_mycode() 237 { 238 global $cache, $lang; 239 $this->mycode_cache = array(); 240 241 $standard_mycode = $callback_mycode = array(); 242 243 $standard_mycode['b']['regex'] = "#\[b\](.*?)\[/b\]#si"; 244 $standard_mycode['b']['replacement'] = "<span style=\"font-weight: bold;\">$1</span>"; 245 246 $standard_mycode['u']['regex'] = "#\[u\](.*?)\[/u\]#si"; 247 $standard_mycode['u']['replacement'] = "<span style=\"text-decoration: underline;\">$1</span>"; 248 249 $standard_mycode['i']['regex'] = "#\[i\](.*?)\[/i\]#si"; 250 $standard_mycode['i']['replacement'] = "<span style=\"font-style: italic;\">$1</span>"; 251 252 $standard_mycode['s']['regex'] = "#\[s\](.*?)\[/s\]#si"; 253 $standard_mycode['s']['replacement'] = "<del>$1</del>"; 254 255 $standard_mycode['copy']['regex'] = "#\(c\)#i"; 256 $standard_mycode['copy']['replacement'] = "©"; 257 258 $standard_mycode['tm']['regex'] = "#\(tm\)#i"; 259 $standard_mycode['tm']['replacement'] = "™"; 260 261 $standard_mycode['reg']['regex'] = "#\(r\)#i"; 262 $standard_mycode['reg']['replacement'] = "®"; 263 264 $callback_mycode['url_simple']['regex'] = "#\[url\]([a-z]+?://)([^\r\n\"<]+?)\[/url\]#si"; 265 $callback_mycode['url_simple']['replacement'] = array($this, 'mycode_parse_url_callback1'); 266 267 $callback_mycode['url_simple2']['regex'] = "#\[url\]([^\r\n\"<]+?)\[/url\]#i"; 268 $callback_mycode['url_simple2']['replacement'] = array($this, 'mycode_parse_url_callback2'); 269 270 $callback_mycode['url_complex']['regex'] = "#\[url=([a-z]+?://)([^\r\n\"<]+?)\](.+?)\[/url\]#si"; 271 $callback_mycode['url_complex']['replacement'] = array($this, 'mycode_parse_url_callback1'); 272 273 $callback_mycode['url_complex2']['regex'] = "#\[url=([^\r\n\"<&\(\)]+?)\](.+?)\[/url\]#si"; 274 $callback_mycode['url_complex2']['replacement'] = array($this, 'mycode_parse_url_callback2'); 275 276 $callback_mycode['email_simple']['regex'] = "#\[email\](.*?)\[/email\]#i"; 277 $callback_mycode['email_simple']['replacement'] = array($this, 'mycode_parse_email_callback'); 278 279 $callback_mycode['email_complex']['regex'] = "#\[email=(.*?)\](.*?)\[/email\]#i"; 280 $callback_mycode['email_complex']['replacement'] = array($this, 'mycode_parse_email_callback'); 281 282 $standard_mycode['hr']['regex'] = "#\[hr\]#si"; 283 $standard_mycode['hr']['replacement'] = "<hr />"; 284 285 $nestable_mycode['color']['regex'] = "#\[color=([a-zA-Z]*|\#?[0-9a-fA-F]{6})](.*?)\[/color\]#si"; 286 $nestable_mycode['color']['replacement'] = "<span style=\"color: $1;\">$2</span>"; 287 288 $nestable_mycode['size']['regex'] = "#\[size=(xx-small|x-small|small|medium|large|x-large|xx-large)\](.*?)\[/size\]#si"; 289 $nestable_mycode['size']['replacement'] = "<span style=\"font-size: $1;\">$2</span>"; 290 291 $callback_mycode['size_int']['regex'] = "#\[size=([0-9\+\-]+?)\](.*?)\[/size\]#si"; 292 $callback_mycode['size_int']['replacement'] = array($this, 'mycode_handle_size_callback'); 293 294 $nestable_mycode['font']['regex'] = "#\[font=([a-z ]+?)\](.+?)\[/font\]#si"; 295 $nestable_mycode['font']['replacement'] = "<span style=\"font-family: $1;\">$2</span>"; 296 297 $nestable_mycode['align']['regex'] = "#\[align=(left|center|right|justify)\](.*?)\[/align\]#si"; 298 $nestable_mycode['align']['replacement'] = "<div style=\"text-align: $1;\">$2</div>"; 299 300 $custom_mycode = $cache->read("mycode"); 301 302 // If there is custom MyCode, load it. 303 if(is_array($custom_mycode)) 304 { 305 foreach($custom_mycode as $key => $mycode) 306 { 307 $mycode['regex'] = str_replace("\x0", "", $mycode['regex']); 308 $custom_mycode[$key]['regex'] = "#".$mycode['regex']."#si"; 309 } 310 $mycode = array_merge($standard_mycode, $custom_mycode); 311 } 312 else 313 { 314 $mycode = $standard_mycode; 315 } 316 317 // Assign the MyCode to the cache. 318 foreach($mycode as $code) 319 { 320 $this->mycode_cache['standard']['find'][] = $code['regex']; 321 $this->mycode_cache['standard']['replacement'][] = $code['replacement']; 322 } 323 324 // Assign the nestable MyCode to the cache. 325 foreach($nestable_mycode as $code) 326 { 327 $this->mycode_cache['nestable'][] = array('find' => $code['regex'], 'replacement' => $code['replacement']); 328 } 329 330 // Assign the nestable MyCode to the cache. 331 foreach($callback_mycode as $code) 332 { 333 $this->mycode_cache['callback'][] = array('find' => $code['regex'], 'replacement' => $code['replacement']); 334 } 335 } 336 337 /** 338 * Parses MyCode tags in a specific message with the specified options. 339 * 340 * @param string The message to be parsed. 341 * @param array Array of options in yes/no format. Options are allow_imgcode. 342 * @return string The parsed message. 343 */ 344 function parse_mycode($message, $options=array()) 345 { 346 global $lang; 347 348 // Cache the MyCode globally if needed. 349 if($this->mycode_cache == 0) 350 { 351 $this->cache_mycode(); 352 } 353 354 // Parse quotes first 355 $message = $this->mycode_parse_quotes($message); 356 357 $message = $this->mycode_auto_url($message); 358 359 $message = str_replace('$', '$', $message); 360 361 // Replace the rest 362 $message = preg_replace($this->mycode_cache['standard']['find'], $this->mycode_cache['standard']['replacement'], $message); 363 foreach($this->mycode_cache['callback'] as $replace) 364 { 365 $message = preg_replace_callback($replace['find'], $replace['replacement'], $message); 366 } 367 368 // Replace the nestable mycode's 369 foreach($this->mycode_cache['nestable'] as $mycode) 370 { 371 while(preg_match($mycode['find'], $message)) 372 { 373 $message = preg_replace($mycode['find'], $mycode['replacement'], $message); 374 } 375 } 376 377 // Special code requiring special attention 378 while(preg_match("#\[list\](.*?)\[/list\]#si", $message)) 379 { 380 $message = preg_replace_callback("#\s?\[list\](.*?)\[/list\](\r\n?|\n?)#si", array($this, 'mycode_parse_list_callback'), $message); 381 } 382 383 // Replace lists. 384 while(preg_match("#\[list=(a|A|i|I|1)\](.*?)\[/list\](\r\n?|\n?)#si", $message)) 385 { 386 $message = preg_replace_callback("#\s?\[list=(a|A|i|I|1)\](.*?)\[/list\]#si", array($this, 'mycode_parse_list_callback_type'), $message); 387 } 388 389 // Convert images when allowed. 390 if($options['allow_imgcode'] != 0) 391 { 392 $message = preg_replace_callback("#\[img\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is", array($this, 'mycode_parse_img_callback1'), $message); 393 $message = preg_replace_callback("#\[img=([0-9]{1,3})x([0-9]{1,3})\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is", array($this, 'mycode_parse_img_callback2'), $message); 394 $message = preg_replace_callback("#\[img align=([a-z]+)\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is", array($this, 'mycode_parse_img_callback3'), $message); 395 $message = preg_replace_callback("#\[img=([0-9]{1,3})x([0-9]{1,3}) align=([a-z]+)\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is", array($this, 'mycode_parse_img_callback4'), $message); 396 } 397 398 // Convert videos when allow. 399 if($options['allow_videocode'] != 0) 400 { 401 $message = preg_replace_callback("#\[video=(.*?)\](.*?)\[/video\]#i", array($this, 'mycode_parse_video_callback'), $message); 402 } 403 404 return $message; 405 } 406 407 /** 408 * Generates a cache of smilies 409 * 410 * @access private 411 */ 412 private function cache_smilies() 413 { 414 global $cache, $mybb; 415 $this->smilies_cache = array(); 416 417 $smilies = $cache->read("smilies"); 418 if(is_array($smilies)) 419 { 420 foreach($smilies as $sid => $smilie) 421 { 422 if(defined("IN_ARCHIVE") && substr($smilie['image'], 0, 4) != "http") 423 { 424 // We're in the archive and not using an outside image, add in our address 425 $smilie['image'] = $mybb->settings['bburl']."/".$smilie['image']; 426 } 427 428 $this->smilies_cache[$smilie['find']] = "<img src=\"{$smilie['image']}\" style=\"vertical-align: middle;\" border=\"0\" alt=\"{$smilie['name']}\" title=\"{$smilie['name']}\" />"; 429 } 430 } 431 } 432 433 /** 434 * Parses smilie code in the specified message. 435 * 436 * @param string The message being parsed. 437 * @param string Base URL for the image tags created by smilies. 438 * @param string Yes/No if HTML is allowed in the post 439 * @return string The parsed message. 440 */ 441 function parse_smilies($message, $allow_html=0) 442 { 443 if($this->smilies_cache == 0) 444 { 445 $this->cache_smilies(); 446 } 447 448 $message = ' ' . $message . ' '; 449 450 // First we take out any of the tags we don't want parsed between (url= etc) 451 preg_match_all("#\[(url(=[^\]]*)?\]|quote=([^\]]*)?\])#i", $message, $bad_matches, PREG_PATTERN_ORDER); 452 $message = preg_replace("#\[(url(=[^\]]*)?\]|quote=([^\]]*)?\])#si", "<mybb-bad-sm>", $message); 453 454 // Impose a hard limit of 500 smilies per message as to not overload the parser 455 $remaining = 500; 456 457 if(is_array($this->smilies_cache)) 458 { 459 foreach($this->smilies_cache as $find => $replace) 460 { 461 $orig_message = $message; 462 $find = $this->parse_html($find); 463 $find = preg_quote($find, "#"); 464 465 $replace = strip_tags($replace, "<img>"); 466 467 // Fix issues for smileys starting with a ";" 468 $orig_find = $find; 469 if(substr($find, 0, 1) == ";") 470 { 471 $find = "(?<!>|<|&)".$find; 472 } 473 474 $message = @preg_replace("#(?<=[^\"])".$find."(?=.\W|\"|\W.|\W$)#si", $replace, $message, $remaining, $replacements); 475 476 if($message == null) 477 { 478 $message = preg_replace("#(?<=[^&;\"])".$orig_find."(?=.\W|\"|\W.|\W$)#si", $replace, $orig_message, $remaining); 479 } 480 481 $remaining -= $replacements; 482 if($remaining <= 0) 483 { 484 break; // Reached the limit 485 } 486 } 487 unset($orig_message, $orig_find); 488 } 489 490 // If we matched any tags previously, swap them back in 491 if(count($bad_matches[0]) > 0) 492 { 493 foreach($bad_matches[0] as $match) 494 { 495 $match = str_replace('$', '\$', $match); 496 $message = preg_replace("#<mybb-bad-sm>#", $match, $message, 1); 497 } 498 } 499 500 return trim($message); 501 } 502 503 /** 504 * Generates a cache of badwords filters. 505 * 506 * @access private 507 */ 508 private function cache_badwords() 509 { 510 global $cache; 511 $this->badwords_cache = array(); 512 $this->badwords_cache = $cache->read("badwords"); 513 } 514 515 /** 516 * Parses a list of filtered/badwords in the specified message. 517 * 518 * @param string The message to be parsed. 519 * @param array Array of parser options in yes/no format. 520 * @return string The parsed message. 521 */ 522 function parse_badwords($message, $options=array()) 523 { 524 if($this->badwords_cache == 0) 525 { 526 $this->cache_badwords(); 527 } 528 if(is_array($this->badwords_cache)) 529 { 530 reset($this->badwords_cache); 531 foreach($this->badwords_cache as $bid => $badword) 532 { 533 if(!$badword['replacement']) 534 { 535 $badword['replacement'] = "*****"; 536 } 537 538 // Take into account the position offset for our last replacement. 539 $index = substr_count($badword['badword'], '*')+2; 540 $badword['badword'] = str_replace('\*', '([a-zA-Z0-9_]{1})', preg_quote($badword['badword'], "#")); 541 542 // Ensure we run the replacement enough times but not recursively (i.e. not while(preg_match..)) 543 $count = preg_match_all("#(^|\W)".$badword['badword']."(\W|$)#i", $message, $matches); 544 for($i=0; $i < $count; ++$i) 545 { 546 $message = preg_replace("#(^|\W)".$badword['badword']."(\W|$)#i", "\\1".$badword['replacement'].'\\'.$index, $message); 547 } 548 } 549 } 550 if(isset($options['strip_tags']) && $options['strip_tags'] == 1) 551 { 552 $message = strip_tags($message); 553 } 554 return $message; 555 } 556 557 /** 558 * Attempts to move any javascript references in the specified message. 559 * 560 * @param string The message to be parsed. 561 * @return string The parsed message. 562 */ 563 function fix_javascript($message) 564 { 565 $js_array = array( 566 "#(&\#(0*)106;?|&\#(0*)74;?|&\#x(0*)4a;?|&\#x(0*)6a;?|j)((&\#(0*)97;?|&\#(0*)65;?|a)(&\#(0*)118;?|&\#(0*)86;?|v)(&\#(0*)97;?|&\#(0*)65;?|a)(\s)?(&\#(0*)115;?|&\#(0*)83;?|s)(&\#(0*)99;?|&\#(0*)67;?|c)(&\#(0*)114;?|&\#(0*)82;?|r)(&\#(0*)105;?|&\#(0*)73;?|i)(&\#112;?|&\#(0*)80;?|p)(&\#(0*)116;?|&\#(0*)84;?|t)(&\#(0*)58;?|\:))#i", 567 "#(o)(nmouseover\s?=)#i", 568 "#(o)(nmouseout\s?=)#i", 569 "#(o)(nmousedown\s?=)#i", 570 "#(o)(nmousemove\s?=)#i", 571 "#(o)(nmouseup\s?=)#i", 572 "#(o)(nclick\s?=)#i", 573 "#(o)(ndblclick\s?=)#i", 574 "#(o)(nload\s?=)#i", 575 "#(o)(nsubmit\s?=)#i", 576 "#(o)(nblur\s?=)#i", 577 "#(o)(nchange\s?=)#i", 578 "#(o)(nfocus\s?=)#i", 579 "#(o)(nselect\s?=)#i", 580 "#(o)(nunload\s?=)#i", 581 "#(o)(nkeypress\s?=)#i", 582 "#(o)(nerror\s?=)#i", 583 "#(o)(nreset\s?=)#i", 584 "#(o)(nabort\s?=)#i" 585 ); 586 587 $message = preg_replace($js_array, "$1<strong></strong>$2$4", $message); 588 589 return $message; 590 } 591 592 /** 593 * Handles fontsize. 594 * 595 * @param string The original size. 596 * @param string The text within a size tag. 597 * @return string The parsed text. 598 */ 599 function mycode_handle_size($size, $text) 600 { 601 $size = intval($size)+10; 602 603 if($size > 50) 604 { 605 $size = 50; 606 } 607 608 $text = "<span style=\"font-size: {$size}pt;\">".str_replace("\'", "'", $text)."</span>"; 609 610 return $text; 611 } 612 613 /** 614 * Handles fontsize. 615 * 616 * @param array Matches. 617 * @return string The parsed text. 618 */ 619 function mycode_handle_size_callback($matches) 620 { 621 return $this->mycode_handle_size($matches[1], $matches[2]); 622 } 623 624 /** 625 * Parses quote MyCode. 626 * 627 * @param string The message to be parsed 628 * @param boolean Are we formatting as text? 629 * @return string The parsed message. 630 */ 631 function mycode_parse_quotes($message, $text_only=false) 632 { 633 global $lang, $templates, $theme, $mybb; 634 635 // Assign pattern and replace values. 636 $pattern = "#\[quote\](.*?)\[\/quote\](\r\n?|\n?)#si"; 637 $pattern_callback = "#\[quote=([\"']|"|)(.*?)(?:\\1)(.*?)(?:[\"']|")?\](.*?)\[/quote\](\r\n?|\n?)#si"; 638 639 if($text_only == false) 640 { 641 $replace = "<blockquote><cite>$lang->quote</cite>$1</blockquote>\n"; 642 $replace_callback = array($this, 'mycode_parse_post_quotes_callback1'); 643 } 644 else 645 { 646 $replace = "\n{$lang->quote}\n--\n$1\n--\n"; 647 $replace_callback = array($this, 'mycode_parse_post_quotes_callback2'); 648 } 649 650 do 651 { 652 // preg_replace has erased the message? Restore it... 653 $previous_message = $message; 654 $message = preg_replace($pattern, $replace, $message, -1, $count); 655 $message = preg_replace_callback($pattern_callback, $replace_callback, $message, -1, $count_callback); 656 if(!$message) 657 { 658 $message = $previous_message; 659 break; 660 } 661 } while($count || $count_callback); 662 663 if($text_only == false) 664 { 665 $find = array( 666 "#(\r\n*|\n*)<\/cite>(\r\n*|\n*)#", 667 "#(\r\n*|\n*)<\/blockquote>#" 668 ); 669 670 $replace = array( 671 "</cite><br />", 672 "</blockquote>" 673 ); 674 $message = preg_replace($find, $replace, $message); 675 } 676 return $message; 677 } 678 679 /** 680 * Parses quotes with post id and/or dateline. 681 * 682 * @param string The message to be parsed 683 * @param string The username to be parsed 684 * @param boolean Are we formatting as text? 685 * @return string The parsed message. 686 */ 687 function mycode_parse_post_quotes($message, $username, $text_only=false) 688 { 689 global $lang, $templates, $theme, $mybb; 690 691 $linkback = $date = ""; 692 693 $message = trim($message); 694 $message = preg_replace("#(^<br(\s?)(\/?)>|<br(\s?)(\/?)>$)#i", "", $message); 695 696 if(!$message) return ''; 697 698 $message = str_replace('\"', '"', $message); 699 $username = str_replace('\"', '"', $username)."'"; 700 $delete_quote = true; 701 702 preg_match("#pid=(?:"|\"|')?([0-9]+)[\"']?(?:"|\"|')?#i", $username, $match); 703 if(intval($match[1])) 704 { 705 $pid = intval($match[1]); 706 $url = $mybb->settings['bburl']."/".get_post_link($pid)."#pid$pid"; 707 if(defined("IN_ARCHIVE")) 708 { 709 $linkback = " <a href=\"{$url}\">[ -> ]</a>"; 710 } 711 else 712 { 713 eval("\$linkback = \" ".$templates->get("postbit_gotopost", 1, 0)."\";"); 714 } 715 716 $username = preg_replace("#(?:"|\"|')? pid=(?:"|\"|')?[0-9]+[\"']?(?:"|\"|')?#i", '', $username); 717 $delete_quote = false; 718 } 719 720 unset($match); 721 preg_match("#dateline=(?:"|\"|')?([0-9]+)(?:"|\"|')?#i", $username, $match); 722 if(intval($match[1])) 723 { 724 if($match[1] < TIME_NOW) 725 { 726 $postdate = my_date($mybb->settings['dateformat'], intval($match[1])); 727 $posttime = my_date($mybb->settings['timeformat'], intval($match[1])); 728 $date = " ({$postdate} {$posttime})"; 729 } 730 $username = preg_replace("#(?:"|\"|')? dateline=(?:"|\"|')?[0-9]+(?:"|\"|')?#i", '', $username); 731 $delete_quote = false; 732 } 733 734 if($delete_quote) 735 { 736 $username = my_substr($username, 0, my_strlen($username)-1); 737 } 738 739 if($text_only) 740 { 741 return "\n".htmlspecialchars_uni($username)." $lang->wrote{$date}\n--\n{$message}\n--\n"; 742 } 743 else 744 { 745 $span = ""; 746 if(!$delete_quote) 747 { 748 $span = "<span>{$date}</span>"; 749 } 750 751 return "<blockquote><cite>{$span}".htmlspecialchars_uni($username)." $lang->wrote{$linkback}</cite>{$message}</blockquote>\n"; 752 } 753 } 754 755 /** 756 * Parses quotes with post id and/or dateline. 757 * 758 * @param array Matches. 759 * @return string The parsed message. 760 */ 761 function mycode_parse_post_quotes_callback1($matches) 762 { 763 return $this->mycode_parse_post_quotes($matches[4],$matches[2].$matches[3]); 764 } 765 766 /** 767 * Parses quotes with post id and/or dateline. 768 * 769 * @param array Matches. 770 * @return string The parsed message. 771 */ 772 function mycode_parse_post_quotes_callback2($matches) 773 { 774 return $this->mycode_parse_post_quotes($matches[4],$matches[2].$matches[3], true); 775 } 776 777 /** 778 * Parses code MyCode. 779 * 780 * @param string The message to be parsed 781 * @param boolean Are we formatting as text? 782 * @return string The parsed message. 783 */ 784 function mycode_parse_code($code, $text_only=false) 785 { 786 global $lang; 787 788 if($text_only == true) 789 { 790 return "\n{$lang->code}\n--\n{$code}\n--\n"; 791 } 792 793 // Clean the string before parsing. 794 $code = preg_replace('#^(\t*)(\n|\r|\0|\x0B| )*#', '\\1', $code); 795 $code = rtrim($code); 796 $original = preg_replace('#^\t*#', '', $code); 797 798 if(empty($original)) 799 { 800 return; 801 } 802 803 $code = str_replace('$', '$', $code); 804 $code = preg_replace('#\$([0-9])#', '\\\$\\1', $code); 805 $code = str_replace('\\', '\', $code); 806 $code = str_replace("\t", ' ', $code); 807 $code = str_replace(" ", ' ', $code); 808 809 return "<div class=\"codeblock\">\n<div class=\"title\">".$lang->code."\n</div><div class=\"body\" dir=\"ltr\"><code>".$code."</code></div></div>\n"; 810 } 811 812 /** 813 * Parses code MyCode. 814 * 815 * @param array Matches. 816 * @return string The parsed message. 817 */ 818 function mycode_parse_code_callback($matches) 819 { 820 return $this->mycode_parse_code($matches[1], true); 821 } 822 823 /** 824 * Parses PHP code MyCode. 825 * 826 * @param string The message to be parsed 827 * @param boolean Whether or not it should return it as pre-wrapped in a div or not. 828 * @param boolean Are we formatting as text? 829 * @return string The parsed message. 830 */ 831 function mycode_parse_php($str, $bare_return = false, $text_only = false) 832 { 833 global $lang; 834 835 if($text_only == true) 836 { 837 return "\n{$lang->php_code}\n--\n$str\n--\n"; 838 } 839 840 // Clean the string before parsing except tab spaces. 841 $str = preg_replace('#^(\t*)(\n|\r|\0|\x0B| )*#', '\\1', $str); 842 $str = rtrim($str); 843 844 $original = preg_replace('#^\t*#', '', $str); 845 846 if(empty($original)) 847 { 848 return; 849 } 850 851 $str = str_replace('&', '&', $str); 852 $str = str_replace('<', '<', $str); 853 $str = str_replace('>', '>', $str); 854 855 // See if open and close tags are provided. 856 $added_open_tag = false; 857 if(!preg_match("#^\s*<\?#si", $str)) 858 { 859 $added_open_tag = true; 860 $str = "<?php \n".$str; 861 } 862 863 $added_end_tag = false; 864 if(!preg_match("#\?>\s*$#si", $str)) 865 { 866 $added_end_tag = true; 867 $str = $str." \n?>"; 868 } 869 870 $code = @highlight_string($str, true); 871 872 // Do the actual replacing. 873 $code = preg_replace('#<code>\s*<span style="color: \#000000">\s*#i', "<code>", $code); 874 $code = preg_replace("#</span>\s*</code>#", "</code>", $code); 875 $code = preg_replace("#</span>(\r\n?|\n?)</code>#", "</span></code>", $code); 876 $code = str_replace("\\", '\', $code); 877 $code = str_replace('$', '$', $code); 878 $code = preg_replace("#&\#([0-9]+);#si", "&#$1;", $code); 879 880 if($added_open_tag) 881 { 882 $code = preg_replace("#<code><span style=\"color: \#([A-Z0-9]{6})\"><\?php( | )(<br />?)#", "<code><span style=\"color: #$1\">", $code); 883 } 884 885 if($added_end_tag) 886 { 887 $code = str_replace("?></span></code>", "</span></code>", $code); 888 // Wait a minute. It fails highlighting? Stupid highlighter. 889 $code = str_replace("?></code>", "</code>", $code); 890 } 891 892 $code = preg_replace("#<span style=\"color: \#([A-Z0-9]{6})\"></span>#", "", $code); 893 $code = str_replace("<code>", "<div dir=\"ltr\"><code>", $code); 894 $code = str_replace("</code>", "</code></div>", $code); 895 $code = preg_replace("# *$#", "", $code); 896 897 if($bare_return) 898 { 899 return $code; 900 } 901 902 // Send back the code all nice and pretty 903 return "<div class=\"codeblock phpcodeblock\"><div class=\"title\">$lang->php_code\n</div><div class=\"body\">".$code."</div></div>\n"; 904 } 905 906 /** 907 * Parses PHP code MyCode. 908 * 909 * @param array Matches. 910 * @return string The parsed message. 911 */ 912 function mycode_parse_php_callback($matches) 913 { 914 return $this->mycode_parse_php($matches[1], false, true); 915 } 916 917 /** 918 * Parses URL MyCode. 919 * 920 * @param string The URL to link to. 921 * @param string The name of the link. 922 * @return string The built-up link. 923 */ 924 function mycode_parse_url($url, $name="") 925 { 926 if(!preg_match("#^[a-z0-9]+://#i", $url)) 927 { 928 $url = "http://".$url; 929 } 930 $fullurl = $url; 931 932 $url = str_replace('&', '&', $url); 933 $name = str_replace('&', '&', $name); 934 935 if(!$name) 936 { 937 $name = $url; 938 } 939 940 $name = str_replace("\'", "'", $name); 941 $url = str_replace("\'", "'", $url); 942 $fullurl = str_replace("\'", "'", $fullurl); 943 944 if($name == $url && (!isset($this->options['shorten_urls']) || $this->options['shorten_urls'] != 0)) 945 { 946 if(my_strlen($url) > 55) 947 { 948 $name = my_substr($url, 0, 40)."...".my_substr($url, -10); 949 } 950 } 951 952 $nofollow = ''; 953 if(isset($this->options['nofollow_on'])) 954 { 955 $nofollow = " rel=\"nofollow\""; 956 } 957 958 // Fix some entities in URLs 959 $entities = array('$' => '%24', '$' => '%24', '^' => '%5E', '`' => '%60', '[' => '%5B', ']' => '%5D', '{' => '%7B', '}' => '%7D', '"' => '%22', '<' => '%3C', '>' => '%3E', ' ' => '%20'); 960 $fullurl = str_replace(array_keys($entities), array_values($entities), $fullurl); 961 962 $name = preg_replace("#&\#([0-9]+);#si", "&#$1;", $name); // Fix & but allow unicode 963 $link = "<a href=\"$fullurl\" target=\"_blank\"{$nofollow}>$name</a>"; 964 return $link; 965 } 966 967 /** 968 * Parses URL MyCode. 969 * 970 * @param array Matches. 971 * @return string The built-up link. 972 */ 973 function mycode_parse_url_callback1($matches) 974 { 975 if(!isset($matches[3])) 976 { 977 $matches[3] = ''; 978 } 979 return $this->mycode_parse_url($matches[1].$matches[2], $matches[3]); 980 } 981 982 /** 983 * Parses URL MyCode. 984 * 985 * @param array Matches. 986 * @return string The built-up link. 987 */ 988 function mycode_parse_url_callback2($matches) 989 { 990 if(!isset($matches[2])) 991 { 992 $matches[2] = ''; 993 } 994 return $this->mycode_parse_url($matches[1], $matches[2]); 995 } 996 997 /** 998 * Parses IMG MyCode. 999 * 1000 * @param string The URL to the image 1001 * @param array Optional array of dimensions 1002 */ 1003 function mycode_parse_img($url, $dimensions=array(), $align='') 1004 { 1005 global $lang; 1006 $url = trim($url); 1007 $url = str_replace("\n", "", $url); 1008 $url = str_replace("\r", "", $url); 1009 if($align == "right") 1010 { 1011 $css_align = " style=\"float: right;\""; 1012 } 1013 else if($align == "left") 1014 { 1015 $css_align = " style=\"float: left;\""; 1016 } 1017 $alt = htmlspecialchars_uni(basename($url)); 1018 if(my_strlen($alt) > 55) 1019 { 1020 $alt = my_substr($alt, 0, 40)."...".my_substr($alt, -10); 1021 } 1022 $alt = $lang->sprintf($lang->posted_image, $alt); 1023 if($dimensions[0] > 0 && $dimensions[1] > 0) 1024 { 1025 return "<img src=\"{$url}\" width=\"{$dimensions[0]}\" height=\"{$dimensions[1]}\" border=\"0\" alt=\"{$alt}\"{$css_align} />"; 1026 } 1027 else 1028 { 1029 return "<img src=\"{$url}\" border=\"0\" alt=\"{$alt}\"{$css_align} />"; 1030 } 1031 } 1032 1033 /** 1034 * Parses IMG MyCode. 1035 * 1036 * @param array Matches. 1037 * @return string Image code. 1038 */ 1039 function mycode_parse_img_callback1($matches) 1040 { 1041 return $this->mycode_parse_img($matches[2])."\n"; 1042 } 1043 1044 /** 1045 * Parses IMG MyCode. 1046 * 1047 * @param array Matches. 1048 * @return string Image code. 1049 */ 1050 function mycode_parse_img_callback2($matches) 1051 { 1052 return $this->mycode_parse_img($matches[4], array($matches[1], $matches[2])); 1053 } 1054 1055 /** 1056 * Parses IMG MyCode. 1057 * 1058 * @param array Matches. 1059 * @return string Image code. 1060 */ 1061 function mycode_parse_img_callback3($matches) 1062 { 1063 return $this->mycode_parse_img($matches[3], array(), $matches[1]); 1064 } 1065 1066 /** 1067 * Parses IMG MyCode. 1068 * 1069 * @param array Matches. 1070 * @return string Image code. 1071 */ 1072 function mycode_parse_img_callback4($matches) 1073 { 1074 return $this->mycode_parse_img($matches[5], array($matches[1], $matches[2]), $matches[3]); 1075 } 1076 1077 /** 1078 * Parses email MyCode. 1079 * 1080 * @param string The email address to link to. 1081 * @param string The name for the link. 1082 * @return string The built-up email link. 1083 */ 1084 function mycode_parse_email($email, $name="") 1085 { 1086 $name = str_replace("\\'", "'", $name); 1087 $email = str_replace("\\'", "'", $email); 1088 if(!$name) 1089 { 1090 $name = $email; 1091 } 1092 if(preg_match("/^([a-zA-Z0-9-_\+\.]+?)@[a-zA-Z0-9-]+\.[a-zA-Z0-9\.-]+$/si", $email)) 1093 { 1094 return "<a href=\"mailto:$email\">".$name."</a>"; 1095 } 1096 else 1097 { 1098 return $email; 1099 } 1100 } 1101 1102 /** 1103 * Parses email MyCode. 1104 * 1105 * @param array Matches 1106 * @return string The built-up email link. 1107 */ 1108 function mycode_parse_email_callback($matches) 1109 { 1110 if(!isset($matches[2])) 1111 { 1112 $matches[2] = ''; 1113 } 1114 return $this->mycode_parse_email($matches[1], $matches[2]); 1115 } 1116 1117 /** 1118 * Parses video MyCode. 1119 * 1120 * @param string The video provider. 1121 * @param string The video to link to. 1122 * @return string The built-up video code. 1123 */ 1124 function mycode_parse_video($video, $url) 1125 { 1126 global $templates; 1127 1128 if(empty($video) || empty($url)) 1129 { 1130 return "[video={$video}]{$url}[/video]"; 1131 } 1132 1133 $parsed_url = @parse_url(urldecode($url)); 1134 if($parsed_url == false) 1135 { 1136 return "[video={$video}]{$url}[/video]"; 1137 } 1138 1139 $fragments = array(); 1140 if($parsed_url['fragment']) 1141 { 1142 $fragments = explode("&", $parsed_url['fragment']); 1143 } 1144 1145 $queries = explode("&", $parsed_url['query']); 1146 1147 $input = array(); 1148 foreach($queries as $query) 1149 { 1150 list($key, $value) = explode("=", $query); 1151 $key = str_replace("amp;", "", $key); 1152 $input[$key] = $value; 1153 } 1154 1155 $path = explode('/', $parsed_url['path']); 1156 1157 switch($video) 1158 { 1159 case "dailymotion": 1160 list($id, ) = split("_", $path[2], 1); // http://www.dailymotion.com/video/fds123_title-goes-here 1161 break; 1162 case "metacafe": 1163 $id = $path[2]; // http://www.metacafe.com/watch/fds123/title_goes_here/ 1164 $title = htmlspecialchars_uni($path[3]); 1165 break; 1166 case "myspacetv": 1167 $id = $path[4]; // http://www.myspace.com/video/fds/fds/123 1168 break; 1169 case "yahoo": 1170 $id = $path[1]; // http://xy.screen.yahoo.com/fds-123.html 1171 // Support for localized portals 1172 $domain = explode('.', $parsed_url['host']); 1173 if($domain[0] != 'screen') 1174 { 1175 $local = $domain[0].'.'; 1176 } 1177 else 1178 { 1179 $local = ''; 1180 } 1181 break; 1182 case "vimeo": 1183 $id = $path[1]; // http://vimeo.com/fds123 1184 break; 1185 case "youtube": 1186 if($fragments[0]) 1187 { 1188 $id = str_replace('!v=', '', $fragments[0]); // http://www.youtube.com/watch#!v=fds123 1189 } 1190 elseif($input['v']) 1191 { 1192 $id = $input['v']; // http://www.youtube.com/watch?v=fds123 1193 } 1194 else 1195 { 1196 $id = $path[1]; // http://www.youtu.be/fds123 1197 } 1198 break; 1199 default: 1200 return "[video={$video}]{$url}[/video]"; 1201 } 1202 1203 if(empty($id)) 1204 { 1205 return "[video={$video}]{$url}[/video]"; 1206 } 1207 1208 $id = htmlspecialchars_uni($id); 1209 1210 eval("\$video_code = \"".$templates->get("video_{$video}_embed")."\";"); 1211 1212 return $video_code; 1213 } 1214 1215 /** 1216 * Parses video MyCode. 1217 * 1218 * @param array Matches. 1219 * @return string The built-up video code. 1220 */ 1221 function mycode_parse_video_callback($matches) 1222 { 1223 return $this->mycode_parse_video($matches[1], $matches[2]); 1224 } 1225 1226 /** 1227 * Parses URLs automatically. 1228 * 1229 * @param string The message to be parsed 1230 * @return string The parsed message. 1231 */ 1232 function mycode_auto_url($message) 1233 { 1234 $message = " ".$message; 1235 $message = preg_replace("#([\>\s\(\)])(http|https|ftp|news){1}://([^\/\"\s\<\[\.]+\.([^\/\"\s\<\[\.]+\.)*[\w]+(:[0-9]+)?(/[^\"\s<\[]*)?)#i", "$1[url]$2://$3[/url]", $message); 1236 $message = preg_replace("#([\>\s\(\)])(www|ftp)\.(([^\/\"\s\<\[\.]+\.)*[\w]+(:[0-9]+)?(/[^\"\s<\[]*)?)#i", "$1[url]$2.$3[/url]", $message); 1237 $message = my_substr($message, 1); 1238 1239 return $message; 1240 } 1241 1242 /** 1243 * Parses list MyCode. 1244 * 1245 * @param string The message to be parsed 1246 * @param string The list type 1247 * @return string The parsed message. 1248 */ 1249 function mycode_parse_list($message, $type="") 1250 { 1251 $message = str_replace('\"', '"', $message); 1252 $message = preg_replace("#\s*\[\*\]\s*#", "</li>\n<li>", $message); 1253 $message .= "</li>"; 1254 1255 if($type) 1256 { 1257 $list = "\n<ol type=\"$type\">$message</ol>\n"; 1258 } 1259 else 1260 { 1261 $list = "<ul>$message</ul>\n"; 1262 } 1263 $list = preg_replace("#<(ol type=\"$type\"|ul)>\s*</li>#", "<$1>", $list); 1264 return $list; 1265 } 1266 1267 /** 1268 * Parses list MyCode. 1269 * 1270 * @param array Matches 1271 * @return string The parsed message. 1272 */ 1273 function mycode_parse_list_callback($matches) 1274 { 1275 return $this->mycode_parse_list($matches[1])."\n"; 1276 } 1277 1278 /** 1279 * Parses list MyCode. 1280 * 1281 * @param array Matches 1282 * @return string The parsed message. 1283 */ 1284 function mycode_parse_list_callback_type($matches) 1285 { 1286 return $this->mycode_parse_list($matches[2], $matches[1])."\n"; 1287 } 1288 1289 /** 1290 * Strips smilies from a string 1291 * 1292 * @param string The message for smilies to be stripped from 1293 * @return string The message with smilies stripped 1294 */ 1295 function strip_smilies($message) 1296 { 1297 if($this->smilies_cache == 0) 1298 { 1299 $this->cache_smilies(); 1300 } 1301 if(is_array($this->smilies_cache)) 1302 { 1303 $message = str_replace($this->smilies_cache, array_keys($this->smilies_cache), $message); 1304 } 1305 return $message; 1306 } 1307 1308 /** 1309 * Highlights a string 1310 * 1311 * @param string The message to be highligted 1312 * @param string The highlight keywords 1313 * @return string The message with highlight bbcodes 1314 */ 1315 function highlight_message($message, $highlight) 1316 { 1317 if(empty($this->highlight_cache)) 1318 { 1319 $this->highlight_cache = build_highlight_array($highlight); 1320 } 1321 1322 if(is_array($this->highlight_cache) && !empty($this->highlight_cache)) 1323 { 1324 $message = preg_replace(array_keys($this->highlight_cache), $this->highlight_cache, $message); 1325 } 1326 1327 return $message; 1328 } 1329 1330 /** 1331 * Parses message to plain text equivalents of MyCode. 1332 * 1333 * @param string The message to be parsed 1334 * @return string The parsed message. 1335 */ 1336 function text_parse_message($message, $options=array()) 1337 { 1338 global $plugins; 1339 1340 // Filter bad words if requested. 1341 if($options['filter_badwords'] != 0) 1342 { 1343 $message = $this->parse_badwords($message); 1344 } 1345 1346 // Parse quotes first 1347 $message = $this->mycode_parse_quotes($message, true); 1348 1349 $message = preg_replace_callback("#\[php\](.*?)\[/php\](\r\n?|\n?)#is", array($this, 'mycode_parse_php_callback'), $message); 1350 $message = preg_replace_callback("#\[code\](.*?)\[/code\](\r\n?|\n?)#is", array($this, 'mycode_parse_code_callback'), $message); 1351 1352 $find = array( 1353 "#\[(b|u|i|s|url|email|color|img)\](.*?)\[/\\1\]#is", 1354 "#\[img=([0-9]{1,3})x([0-9]{1,3})\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is", 1355 "#\[url=([a-z]+?://)([^\r\n\"<]+?)\](.+?)\[/url\]#si", 1356 "#\[url=([^\r\n\"<&\(\)]+?)\](.+?)\[/url\]#si", 1357 ); 1358 1359 $replace = array( 1360 "$2", 1361 "$4", 1362 "$3 ($1$2)", 1363 "$2 ($1)", 1364 ); 1365 $message = preg_replace($find, $replace, $message); 1366 1367 // Replace "me" code and slaps if we have a username 1368 if($options['me_username']) 1369 { 1370 global $lang; 1371 1372 $message = preg_replace('#(>|^|\r|\n)/me ([^\r\n<]*)#i', "\\1* {$options['me_username']} \\2", $message); 1373 $message = preg_replace('#(>|^|\r|\n)/slap ([^\r\n<]*)#i', "\\1* {$options['me_username']} {$lang->slaps} \\2 {$lang->with_trout}", $message); 1374 } 1375 1376 // Special code requiring special attention 1377 while(preg_match("#\[list\](.*?)\[/list\]#si", $message)) 1378 { 1379 $message = preg_replace_callback("#\s?\[list\](.*?)\[/list\](\r\n?|\n?)#si", array($this, 'mycode_parse_list_callback'), $message); 1380 } 1381 1382 // Replace lists. 1383 while(preg_match("#\[list=(a|A|i|I|1)\](.*?)\[/list\](\r\n?|\n?)#si", $message)) 1384 { 1385 $message = preg_replace_callback("#\s?\[list=(a|A|i|I|1)\](.*?)\[/list\]#si", array($this, 'mycode_parse_list_callback_type'), $message); 1386 } 1387 1388 // Run plugin hooks 1389 $message = $plugins->run_hooks("text_parse_message", $message); 1390 1391 return $message; 1392 } 1393 } 1394 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Oct 8 19:19:50 2013 | Cross-referenced by PHPXref 0.7.1 |