[ Index ]

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


Generated: Wed Nov 11 20:33:01 2020 Cross-referenced by PHPXref 0.7.1