[ Index ]

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


Generated: Thu Mar 24 21:31:15 2022 Cross-referenced by PHPXref 0.7.1