[ Index ]

PHP Cross Reference of phpBB-3.3.0-deutsch

title

Body

[close]

/includes/ -> message_parser.php (source)

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


Generated: Tue Apr 7 19:44:41 2020 Cross-referenced by PHPXref 0.7.1