[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/includes/ -> message_parser.php (source)

   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(=&quot;.*?&quot;)?\]\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(?:=&quot;(.*?)&quot;)?\](.+)\[/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('&lt;', '&gt;', '&#91;', '&#93;', '&#46;', '&#58;');
 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('&lt;', '&gt;', '&#91;', '&#93;', '&#46;', '&#58;', '&#058;');
 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>', '', '', '&#91;', '&#93;', '&#46;', '&#58;');
 442  
 443                  if ($remove_tags)
 444                  {
 445                      $str_from[] = '<span class="syntaxdefault">&lt;?php </span>';
 446                      $str_to[] = '';
 447                      $str_from[] = '<span class="syntaxdefault">&lt;?php&nbsp;';
 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]+">)?\?&gt;(</span>)#', '$1&nbsp;$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++|&nbsp;)*+</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=&quot;(.*?)&quot;\]#ie', "'quote=&quot;' . str_replace(array('[', ']', '\\\"'), array('&#91;', '&#93;', '\"'), '\$1') . '&quot;]'", $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(?:=&quot;(.*?)&quot;)?$#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('&#91;', '&#93;'), array('[', ']'), $m[1]);
 789                          $username = preg_replace('#\[(?!b|i|u|color|url|email|/b|/i|/u|/color|/url|/email)#iU', '&#91;$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=&quot;' . $username . '&quot;:' . $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=&quot;(.*?)#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('/(&amp;|\?)sid=[0-9a-f]{32}&amp;/', '\1', $url);
 981                  $url = preg_replace('/(&amp;|\?)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&#058;");
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  ?>


Generated: Wed Oct 2 15:03:47 2013 Cross-referenced by PHPXref 0.7.1