Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

message_parser.php

Zuletzt modifiziert: 09.10.2024, 12:51 - Dateigröße: 58.24 KiB


0001  <?php
0002  /**
0003  *
0004  * This file is part of the phpBB Forum Software package.
0005  *
0006  * @copyright (c) phpBB Limited <https://www.phpbb.com>
0007  * @license GNU General Public License, version 2 (GPL-2.0)
0008  *
0009  * For full copyright and license information, please see
0010  * the docs/CREDITS.txt file.
0011  *
0012  */
0013   
0014  /**
0015  * @ignore
0016  */
0017  if (!defined('IN_PHPBB'))
0018  {
0019      exit;
0020  }
0021   
0022  if (!class_exists('bbcode'))
0023  {
0024      // The following lines are for extensions which include message_parser.php
0025      // while $phpbb_root_path and $phpEx are out of the script scope
0026      // which may lead to the 'Undefined variable' and 'failed to open stream' errors
0027      if (!isset($phpbb_root_path))
0028      {
0029          global $phpbb_root_path;
0030      }
0031   
0032      if (!isset($phpEx))
0033      {
0034          global $phpEx;
0035      }
0036   
0037      include($phpbb_root_path . 'includes/bbcode.' . $phpEx);
0038  }
0039   
0040  /**
0041  * BBCODE FIRSTPASS
0042  * BBCODE first pass class (functions for parsing messages for db storage)
0043  */
0044  class bbcode_firstpass extends bbcode
0045  {
0046      var $message = '';
0047      var $warn_msg = array();
0048      var $parsed_items = array();
0049   
0050      /**
0051      * Parse BBCode
0052      */
0053      function parse_bbcode()
0054      {
0055          if (!$this->bbcodes)
0056          {
0057              $this->bbcode_init();
0058          }
0059   
0060          global $user;
0061   
0062          $this->bbcode_bitfield = '';
0063          $bitfield = new bitfield();
0064   
0065          foreach ($this->bbcodes as $bbcode_name => $bbcode_data)
0066          {
0067              if (isset($bbcode_data['disabled']) && $bbcode_data['disabled'])
0068              {
0069                  foreach ($bbcode_data['regexp'] as $regexp => $replacement)
0070                  {
0071                      if (preg_match($regexp, $this->message))
0072                      {
0073                          $this->warn_msg[] = sprintf($user->lang['UNAUTHORISED_BBCODE'] , '[' . $bbcode_name . ']');
0074                          continue;
0075                      }
0076                  }
0077              }
0078              else
0079              {
0080                  foreach ($bbcode_data['regexp'] as $regexp => $replacement)
0081                  {
0082                      // The pattern gets compiled and cached by the PCRE extension,
0083                      // it should not demand recompilation
0084                      if (preg_match($regexp, $this->message))
0085                      {
0086                          if (is_callable($replacement))
0087                          {
0088                              $this->message = preg_replace_callback($regexp, $replacement, $this->message);
0089                          }
0090                          else
0091                          {
0092                              $this->message = preg_replace($regexp, $replacement, $this->message);
0093                          }
0094                          $bitfield->set($bbcode_data['bbcode_id']);
0095                      }
0096                  }
0097              }
0098          }
0099   
0100          $this->bbcode_bitfield = $bitfield->get_base64();
0101      }
0102   
0103      /**
0104      * Prepare some bbcodes for better parsing
0105      */
0106      function prepare_bbcodes()
0107      {
0108          // 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".
0109          // Fine by me ;) Will ease our live... but do not come back and cry at us, we won't hear you.
0110   
0111          /* Add newline at the end and in front of each quote block to prevent parsing errors (urls, smilies, etc.)
0112          if (strpos($this->message, '[quote') !== false && strpos($this->message, '[/quote]') !== false)
0113          {
0114              $this->message = str_replace("\r\n", "\n", $this->message);
0115   
0116              // We strip newlines and spaces after and before quotes in quotes (trimming) and then add exactly one newline
0117              $this->message = preg_replace('#\[quote(=&quot;.*?&quot;)?\]\s*(.*?)\s*\[/quote\]#siu', '[quote\1]' . "\n" . '\2' ."\n[/quote]", $this->message);
0118          }
0119          */
0120   
0121          // Add other checks which needs to be placed before actually parsing anything (be it bbcodes, smilies, urls...)
0122      }
0123   
0124      /**
0125      * Init bbcode data for later parsing
0126      */
0127      function bbcode_init($allow_custom_bbcode = true)
0128      {
0129          global $phpbb_dispatcher;
0130   
0131          static $rowset;
0132   
0133          $bbcode_class = $this;
0134   
0135          // This array holds all bbcode data. BBCodes will be processed in this
0136          // order, so it is important to keep [code] in first position and
0137          // [quote] in second position.
0138          // To parse multiline URL we enable dotall option setting only for URL text
0139          // but not for link itself, thus [url][/url] is not affected.
0140          //
0141          // To perform custom validation in extension, use $this->validate_bbcode_by_extension()
0142          // method which accepts variable number of parameters
0143          $this->bbcodes = array(
0144              'code'            => array('bbcode_id' => BBCODE_ID_CODE,    'regexp' => array('#\[code(?:=([a-z]+))?\](.+\[/code\])#uis' => function ($match) use($bbcode_class)
0145                  {
0146                      return $bbcode_class->bbcode_code($match[1], $match[2]);
0147                  }
0148              )),
0149              'quote'            => array('bbcode_id' => BBCODE_ID_QUOTE,    'regexp' => array('#\[quote(?:=&quot;(.*?)&quot;)?\](.+)\[/quote\]#uis' => function ($match) use($bbcode_class)
0150                  {
0151                      return $bbcode_class->bbcode_quote($match[0]);
0152                  }
0153              )),
0154              'attachment'    => array('bbcode_id' => BBCODE_ID_ATTACH,    'regexp' => array('#\[attachment=([0-9]+)\](.*?)\[/attachment\]#uis' => function ($match) use($bbcode_class)
0155                  {
0156                      return $bbcode_class->bbcode_attachment($match[1], $match[2]);
0157                  }
0158              )),
0159              'b'                => array('bbcode_id' => BBCODE_ID_B,    'regexp' => array('#\[b\](.*?)\[/b\]#uis' => function ($match) use($bbcode_class)
0160                  {
0161                      return $bbcode_class->bbcode_strong($match[1]);
0162                  }
0163              )),
0164              'i'                => array('bbcode_id' => BBCODE_ID_I,    'regexp' => array('#\[i\](.*?)\[/i\]#uis' => function ($match) use($bbcode_class)
0165                  {
0166                      return $bbcode_class->bbcode_italic($match[1]);
0167                  }
0168              )),
0169              'url'            => array('bbcode_id' => BBCODE_ID_URL,    'regexp' => array('#\[url(=(.*))?\](?(1)((?s).*(?-s))|(.*))\[/url\]#uiU' => function ($match) use($bbcode_class)
0170                  {
0171                      return $bbcode_class->validate_url($match[2], ($match[3]) ? $match[3] : $match[4]);
0172                  }
0173              )),
0174              'img'            => array('bbcode_id' => BBCODE_ID_IMG,    'regexp' => array('#\[img\](.*)\[/img\]#uiU' => function ($match) use($bbcode_class)
0175                  {
0176                      return $bbcode_class->bbcode_img($match[1]);
0177                  }
0178              )),
0179              'size'            => array('bbcode_id' => BBCODE_ID_SIZE,    'regexp' => array('#\[size=([\-\+]?\d+)\](.*?)\[/size\]#uis' => function ($match) use($bbcode_class)
0180                  {
0181                      return $bbcode_class->bbcode_size($match[1], $match[2]);
0182                  }
0183              )),
0184              '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)
0185                  {
0186                      return $bbcode_class->bbcode_color($match[1], $match[2]);
0187                  }
0188              )),
0189              'u'                => array('bbcode_id' => BBCODE_ID_U,    'regexp' => array('#\[u\](.*?)\[/u\]#uis' => function ($match) use($bbcode_class)
0190                  {
0191                      return $bbcode_class->bbcode_underline($match[1]);
0192                  }
0193              )),
0194              'list'            => array('bbcode_id' => BBCODE_ID_LIST,    'regexp' => array('#\[list(?:=(?:[a-z0-9]|disc|circle|square))?].*\[/list]#uis' => function ($match) use($bbcode_class)
0195                  {
0196                      return $bbcode_class->bbcode_parse_list($match[0]);
0197                  }
0198              )),
0199              'email'            => array('bbcode_id' => BBCODE_ID_EMAIL,    'regexp' => array('#\[email=?(.*?)?\](.*?)\[/email\]#uis' => function ($match) use($bbcode_class)
0200                  {
0201                      return $bbcode_class->validate_email($match[1], $match[2]);
0202                  }
0203              )),
0204              'flash'            => array('bbcode_id' => BBCODE_ID_FLASH,    'regexp' => array('#\[flash=([0-9]+),([0-9]+)\](.*?)\[/flash\]#ui' => function ($match) use($bbcode_class)
0205                  {
0206                      return $bbcode_class->bbcode_flash($match[1], $match[2], $match[3]);
0207                  }
0208              ))
0209          );
0210   
0211          // Zero the parsed items array
0212          $this->parsed_items = array();
0213   
0214          foreach ($this->bbcodes as $tag => $bbcode_data)
0215          {
0216              $this->parsed_items[$tag] = 0;
0217          }
0218   
0219          if (!$allow_custom_bbcode)
0220          {
0221              return;
0222          }
0223   
0224          if (!is_array($rowset))
0225          {
0226              global $db;
0227              $rowset = array();
0228   
0229              $sql = 'SELECT *
0230                  FROM ' . BBCODES_TABLE;
0231              $result = $db->sql_query($sql);
0232   
0233              while ($row = $db->sql_fetchrow($result))
0234              {
0235                  $rowset[] = $row;
0236              }
0237              $db->sql_freeresult($result);
0238          }
0239   
0240          foreach ($rowset as $row)
0241          {
0242              $this->bbcodes[$row['bbcode_tag']] = array(
0243                  'bbcode_id'    => (int) $row['bbcode_id'],
0244                  'regexp'    => array($row['first_pass_match'] => str_replace('$uid', $this->bbcode_uid, $row['first_pass_replace']))
0245              );
0246          }
0247   
0248          $bbcodes = $this->bbcodes;
0249   
0250          /**
0251          * Event to modify the bbcode data for later parsing
0252          *
0253          * @event core.modify_bbcode_init
0254          * @var array    bbcodes        Array of bbcode data for use in parsing
0255          * @var array    rowset        Array of bbcode data from the database
0256          * @since 3.1.0-a3
0257          */
0258          $vars = array('bbcodes', 'rowset');
0259          extract($phpbb_dispatcher->trigger_event('core.modify_bbcode_init', compact($vars)));
0260   
0261          $this->bbcodes = $bbcodes;
0262      }
0263   
0264      /**
0265      * Making some pre-checks for bbcodes as well as increasing the number of parsed items
0266      */
0267      function check_bbcode($bbcode, &$in)
0268      {
0269          // when using the /e modifier, preg_replace slashes double-quotes but does not
0270          // seem to slash anything else
0271          $in = str_replace("\r\n", "\n", str_replace('\"', '"', $in));
0272   
0273          // Trimming here to make sure no empty bbcodes are parsed accidently
0274          if (trim($in) == '')
0275          {
0276              return false;
0277          }
0278   
0279          $this->parsed_items[$bbcode]++;
0280   
0281          return true;
0282      }
0283   
0284      /**
0285      * Transform some characters in valid bbcodes
0286      */
0287      function bbcode_specialchars($text)
0288      {
0289          $str_from = array('<', '>', '[', ']', '.', ':');
0290          $str_to = array('&lt;', '&gt;', '&#91;', '&#93;', '&#46;', '&#58;');
0291   
0292          return str_replace($str_from, $str_to, $text);
0293      }
0294   
0295      /**
0296      * Parse size tag
0297      */
0298      function bbcode_size($stx, $in)
0299      {
0300          global $user, $config;
0301   
0302          if (!$this->check_bbcode('size', $in))
0303          {
0304              return $in;
0305          }
0306   
0307          if ($config['max_' . $this->mode . '_font_size'] && $config['max_' . $this->mode . '_font_size'] < $stx)
0308          {
0309              $this->warn_msg[] = $user->lang('MAX_FONT_SIZE_EXCEEDED', (int) $config['max_' . $this->mode . '_font_size']);
0310   
0311              return '[size=' . $stx . ']' . $in . '[/size]';
0312          }
0313   
0314          // Do not allow size=0
0315          if ($stx <= 0)
0316          {
0317              return '[size=' . $stx . ']' . $in . '[/size]';
0318          }
0319   
0320          return '[size=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/size:' . $this->bbcode_uid . ']';
0321      }
0322   
0323      /**
0324      * Parse color tag
0325      */
0326      function bbcode_color($stx, $in)
0327      {
0328          if (!$this->check_bbcode('color', $in))
0329          {
0330              return $in;
0331          }
0332   
0333          return '[color=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/color:' . $this->bbcode_uid . ']';
0334      }
0335   
0336      /**
0337      * Parse u tag
0338      */
0339      function bbcode_underline($in)
0340      {
0341          if (!$this->check_bbcode('u', $in))
0342          {
0343              return $in;
0344          }
0345   
0346          return '[u:' . $this->bbcode_uid . ']' . $in . '[/u:' . $this->bbcode_uid . ']';
0347      }
0348   
0349      /**
0350      * Parse b tag
0351      */
0352      function bbcode_strong($in)
0353      {
0354          if (!$this->check_bbcode('b', $in))
0355          {
0356              return $in;
0357          }
0358   
0359          return '[b:' . $this->bbcode_uid . ']' . $in . '[/b:' . $this->bbcode_uid . ']';
0360      }
0361   
0362      /**
0363      * Parse i tag
0364      */
0365      function bbcode_italic($in)
0366      {
0367          if (!$this->check_bbcode('i', $in))
0368          {
0369              return $in;
0370          }
0371   
0372          return '[i:' . $this->bbcode_uid . ']' . $in . '[/i:' . $this->bbcode_uid . ']';
0373      }
0374   
0375      /**
0376      * Parse img tag
0377      */
0378      function bbcode_img($in)
0379      {
0380          global $user, $config;
0381   
0382          if (!$this->check_bbcode('img', $in))
0383          {
0384              return $in;
0385          }
0386   
0387          $in = trim($in);
0388          $error = false;
0389   
0390          $in = str_replace(' ', '%20', $in);
0391   
0392          // Checking urls
0393          if (!preg_match('#^' . get_preg_expression('url') . '$#iu', $in) && !preg_match('#^' . get_preg_expression('www_url') . '$#iu', $in))
0394          {
0395              return '[img]' . $in . '[/img]';
0396          }
0397   
0398          // Try to cope with a common user error... not specifying a protocol but only a subdomain
0399          if (!preg_match('#^[a-z0-9]+://#i', $in))
0400          {
0401              $in = 'http://' . $in;
0402          }
0403   
0404          if ($config['max_' . $this->mode . '_img_height'] || $config['max_' . $this->mode . '_img_width'])
0405          {
0406              $imagesize = new \FastImageSize\FastImageSize();
0407              $size_info = $imagesize->getImageSize(htmlspecialchars_decode($in));
0408   
0409              if ($size_info === false)
0410              {
0411                  $error = true;
0412                  $this->warn_msg[] = $user->lang['UNABLE_GET_IMAGE_SIZE'];
0413              }
0414              else
0415              {
0416                  if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $size_info['height'])
0417                  {
0418                      $error = true;
0419                      $this->warn_msg[] = $user->lang('MAX_IMG_HEIGHT_EXCEEDED', (int) $config['max_' . $this->mode . '_img_height']);
0420                  }
0421   
0422                  if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $size_info['width'])
0423                  {
0424                      $error = true;
0425                      $this->warn_msg[] = $user->lang('MAX_IMG_WIDTH_EXCEEDED', (int) $config['max_' . $this->mode . '_img_width']);
0426                  }
0427              }
0428          }
0429   
0430          if ($error || $this->path_in_domain($in))
0431          {
0432              return '[img]' . $in . '[/img]';
0433          }
0434   
0435          return '[img:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($in) . '[/img:' . $this->bbcode_uid . ']';
0436      }
0437   
0438      /**
0439      * Parse flash tag
0440      */
0441      function bbcode_flash($width, $height, $in)
0442      {
0443          global $user, $config;
0444   
0445          if (!$this->check_bbcode('flash', $in))
0446          {
0447              return $in;
0448          }
0449   
0450          $in = trim($in);
0451          $error = false;
0452   
0453          // Do not allow 0-sizes generally being entered
0454          if ($width <= 0 || $height <= 0)
0455          {
0456              return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]';
0457          }
0458   
0459          $in = str_replace(' ', '%20', $in);
0460   
0461          // Make sure $in is a URL.
0462          if (!preg_match('#^' . get_preg_expression('url') . '$#iu', $in) &&
0463              !preg_match('#^' . get_preg_expression('www_url') . '$#iu', $in))
0464          {
0465              return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]';
0466          }
0467   
0468          // Apply the same size checks on flash files as on images
0469          if ($config['max_' . $this->mode . '_img_height'] || $config['max_' . $this->mode . '_img_width'])
0470          {
0471              if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $height)
0472              {
0473                  $error = true;
0474                  $this->warn_msg[] = $user->lang('MAX_FLASH_HEIGHT_EXCEEDED', (int) $config['max_' . $this->mode . '_img_height']);
0475              }
0476   
0477              if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $width)
0478              {
0479                  $error = true;
0480                  $this->warn_msg[] = $user->lang('MAX_FLASH_WIDTH_EXCEEDED', (int) $config['max_' . $this->mode . '_img_width']);
0481              }
0482          }
0483   
0484          if ($error || $this->path_in_domain($in))
0485          {
0486              return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]';
0487          }
0488   
0489          return '[flash=' . $width . ',' . $height . ':' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($in) . '[/flash:' . $this->bbcode_uid . ']';
0490      }
0491   
0492      /**
0493      * Parse inline attachments [ia]
0494      */
0495      function bbcode_attachment($stx, $in)
0496      {
0497          if (!$this->check_bbcode('attachment', $in))
0498          {
0499              return $in;
0500          }
0501   
0502          return '[attachment=' . $stx . ':' . $this->bbcode_uid . ']<!-- ia' . $stx . ' -->' . trim($in) . '<!-- ia' . $stx . ' -->[/attachment:' . $this->bbcode_uid . ']';
0503      }
0504   
0505      /**
0506      * Parse code text from code tag
0507      * @access private
0508      */
0509      function bbcode_parse_code($stx, &$code)
0510      {
0511          switch (strtolower($stx))
0512          {
0513              case 'php':
0514   
0515                  $remove_tags = false;
0516   
0517                  $str_from = array('&lt;', '&gt;', '&#91;', '&#93;', '&#46;', '&#58;', '&#058;');
0518                  $str_to = array('<', '>', '[', ']', '.', ':', ':');
0519                  $code = str_replace($str_from, $str_to, $code);
0520   
0521                  if (!preg_match('/\<\?.*?\?\>/is', $code))
0522                  {
0523                      $remove_tags = true;
0524                      $code = "<?php $code ?>";
0525                  }
0526   
0527                  $conf = array('highlight.bg', 'highlight.comment', 'highlight.default', 'highlight.html', 'highlight.keyword', 'highlight.string');
0528                  foreach ($conf as $ini_var)
0529                  {
0530                      @ini_set($ini_var, str_replace('highlight.', 'syntax', $ini_var));
0531                  }
0532   
0533                  // Because highlight_string is specialcharing the text (but we already did this before), we have to reverse this in order to get correct results
0534                  $code = htmlspecialchars_decode($code);
0535                  $code = highlight_string($code, true);
0536   
0537                  $str_from = array('<span style="color: ', '<font color="syntax', '</font>', '<code>', '</code>','[', ']', '.', ':');
0538                  $str_to = array('<span class="', '<span class="syntax', '</span>', '', '', '&#91;', '&#93;', '&#46;', '&#58;');
0539   
0540                  if ($remove_tags)
0541                  {
0542                      $str_from[] = '<span class="syntaxdefault">&lt;?php </span>';
0543                      $str_to[] = '';
0544                      $str_from[] = '<span class="syntaxdefault">&lt;?php&nbsp;';
0545                      $str_to[] = '<span class="syntaxdefault">';
0546                  }
0547   
0548                  $code = str_replace($str_from, $str_to, $code);
0549                  $code = preg_replace('#^(<span class="[a-z_]+">)\n?(.*?)\n?(</span>)$#is', '$1$2$3', $code);
0550   
0551                  if ($remove_tags)
0552                  {
0553                      $code = preg_replace('#(<span class="[a-z]+">)?\?&gt;(</span>)#', '$1&nbsp;$2', $code);
0554                  }
0555   
0556                  $code = preg_replace('#^<span class="[a-z]+"><span class="([a-z]+)">(.*)</span></span>#s', '<span class="$1">$2</span>', $code);
0557                  $code = preg_replace('#(?:\s++|&nbsp;)*+</span>$#u', '</span>', $code);
0558   
0559                  // remove newline at the end
0560                  if (!empty($code) && substr($code, -1) == "\n")
0561                  {
0562                      $code = substr($code, 0, -1);
0563                  }
0564   
0565                  return "[code=$stx:" . $this->bbcode_uid . ']' . $code . '[/code:' . $this->bbcode_uid . ']';
0566              break;
0567   
0568              default:
0569                  return '[code:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($code) . '[/code:' . $this->bbcode_uid . ']';
0570              break;
0571          }
0572      }
0573   
0574      /**
0575      * Parse code tag
0576      * Expects the argument to start right after the opening [code] tag and to end with [/code]
0577      */
0578      function bbcode_code($stx, $in)
0579      {
0580          if (!$this->check_bbcode('code', $in))
0581          {
0582              return $in;
0583          }
0584   
0585          // We remove the hardcoded elements from the code block here because it is not used in code blocks
0586          // Having it here saves us one preg_replace per message containing [code] blocks
0587          // Additionally, magic url parsing should go after parsing bbcodes, but for safety those are stripped out too...
0588          $htm_match = get_preg_expression('bbcode_htm');
0589          unset($htm_match[4], $htm_match[5]);
0590          $htm_replace = array('\1', '\1', '\2', '\1');
0591   
0592          $out = $code_block = '';
0593          $open = 1;
0594   
0595          while ($in)
0596          {
0597              // Determine position and tag length of next code block
0598              preg_match('#(.*?)(\[code(?:=([a-z]+))?\])(.+)#is', $in, $buffer);
0599              $pos = (isset($buffer[1])) ? strlen($buffer[1]) : false;
0600              $tag_length = (isset($buffer[2])) ? strlen($buffer[2]) : false;
0601   
0602              // Determine position of ending code tag
0603              $pos2 = stripos($in, '[/code]');
0604   
0605              // Which is the next block, ending code or code block
0606              if ($pos !== false && $pos < $pos2)
0607              {
0608                  // Open new block
0609                  if (!$open)
0610                  {
0611                      $out .= substr($in, 0, $pos);
0612                      $in = substr($in, $pos);
0613                      $stx = (isset($buffer[3])) ? $buffer[3] : '';
0614                      $code_block = '';
0615                  }
0616                  else
0617                  {
0618                      // Already opened block, just append to the current block
0619                      $code_block .= substr($in, 0, $pos) . ((isset($buffer[2])) ? $buffer[2] : '');
0620                      $in = substr($in, $pos);
0621                  }
0622   
0623                  $in = substr($in, $tag_length);
0624                  $open++;
0625              }
0626              else
0627              {
0628                  // Close the block
0629                  if ($open == 1)
0630                  {
0631                      $code_block .= substr($in, 0, $pos2);
0632                      $code_block = preg_replace($htm_match, $htm_replace, $code_block);
0633   
0634                      // Parse this code block
0635                      $out .= $this->bbcode_parse_code($stx, $code_block);
0636                      $code_block = '';
0637                      $open--;
0638                  }
0639                  else if ($open)
0640                  {
0641                      // Close one open tag... add to the current code block
0642                      $code_block .= substr($in, 0, $pos2 + 7);
0643                      $open--;
0644                  }
0645                  else
0646                  {
0647                      // end code without opening code... will be always outside code block
0648                      $out .= substr($in, 0, $pos2 + 7);
0649                  }
0650   
0651                  $in = substr($in, $pos2 + 7);
0652              }
0653          }
0654   
0655          // if now $code_block has contents we need to parse the remaining code while removing the last closing tag to match up.
0656          if ($code_block)
0657          {
0658              $code_block = substr($code_block, 0, -7);
0659              $code_block = preg_replace($htm_match, $htm_replace, $code_block);
0660   
0661              $out .= $this->bbcode_parse_code($stx, $code_block);
0662          }
0663   
0664          return $out;
0665      }
0666   
0667      /**
0668      * Parse list bbcode
0669      * Expects the argument to start with a tag
0670      */
0671      function bbcode_parse_list($in)
0672      {
0673          if (!$this->check_bbcode('list', $in))
0674          {
0675              return $in;
0676          }
0677   
0678          // $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
0679          $tok = ']';
0680          $out = '[';
0681   
0682          // First character is [
0683          $in = substr($in, 1);
0684          $list_end_tags = $item_end_tags = array();
0685   
0686          do
0687          {
0688              $pos = strlen($in);
0689   
0690              for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i)
0691              {
0692                  $tmp_pos = strpos($in, $tok[$i]);
0693   
0694                  if ($tmp_pos !== false && $tmp_pos < $pos)
0695                  {
0696                      $pos = $tmp_pos;
0697                  }
0698              }
0699   
0700              $buffer = substr($in, 0, $pos);
0701              $tok = $in[$pos];
0702   
0703              $in = substr($in, $pos + 1);
0704   
0705              if ($tok == ']')
0706              {
0707                  // if $tok is ']' the buffer holds a tag
0708                  if (strtolower($buffer) == '/list' && sizeof($list_end_tags))
0709                  {
0710                      // valid [/list] tag, check nesting so that we don't hit false positives
0711                      if (sizeof($item_end_tags) && sizeof($item_end_tags) >= sizeof($list_end_tags))
0712                      {
0713                          // current li tag has not been closed
0714                          $out = preg_replace('/\n?\[$/', '[', $out) . array_pop($item_end_tags) . '][';
0715                      }
0716   
0717                      $out .= array_pop($list_end_tags) . ']';
0718                      $tok = '[';
0719                  }
0720                  else if (preg_match('#^list(=[0-9a-z]+)?$#i', $buffer, $m))
0721                  {
0722                      // sub-list, add a closing tag
0723                      if (empty($m[1]) || preg_match('/^=(?:disc|square|circle)$/i', $m[1]))
0724                      {
0725                          array_push($list_end_tags, '/list:u:' . $this->bbcode_uid);
0726                      }
0727                      else
0728                      {
0729                          array_push($list_end_tags, '/list:o:' . $this->bbcode_uid);
0730                      }
0731                      $out .= 'list' . substr($buffer, 4) . ':' . $this->bbcode_uid . ']';
0732                      $tok = '[';
0733                  }
0734                  else
0735                  {
0736                      if (($buffer == '*' || substr($buffer, -2) == '[*') && sizeof($list_end_tags))
0737                      {
0738                          // the buffer holds a bullet tag and we have a [list] tag open
0739                          if (sizeof($item_end_tags) >= sizeof($list_end_tags))
0740                          {
0741                              if (substr($buffer, -2) == '[*')
0742                              {
0743                                  $out .= substr($buffer, 0, -2) . '[';
0744                              }
0745                              // current li tag has not been closed
0746                              if (preg_match('/\n\[$/', $out, $m))
0747                              {
0748                                  $out = preg_replace('/\n\[$/', '[', $out);
0749                                  $buffer = array_pop($item_end_tags) . "]\n[*:" . $this->bbcode_uid;
0750                              }
0751                              else
0752                              {
0753                                  $buffer = array_pop($item_end_tags) . '][*:' . $this->bbcode_uid;
0754                              }
0755                          }
0756                          else
0757                          {
0758                              $buffer = '*:' . $this->bbcode_uid;
0759                          }
0760   
0761                          $item_end_tags[] = '/*:m:' . $this->bbcode_uid;
0762                      }
0763                      else if ($buffer == '/*')
0764                      {
0765                          array_pop($item_end_tags);
0766                          $buffer = '/*:' . $this->bbcode_uid;
0767                      }
0768   
0769                      $out .= $buffer . $tok;
0770                      $tok = '[]';
0771                  }
0772              }
0773              else
0774              {
0775                  // Not within a tag, just add buffer to the return string
0776                  $out .= $buffer . $tok;
0777                  $tok = ($tok == '[') ? ']' : '[]';
0778              }
0779          }
0780          while ($in);
0781   
0782          // do we have some tags open? close them now
0783          if (sizeof($item_end_tags))
0784          {
0785              $out .= '[' . implode('][', $item_end_tags) . ']';
0786          }
0787          if (sizeof($list_end_tags))
0788          {
0789              $out .= '[' . implode('][', $list_end_tags) . ']';
0790          }
0791   
0792          return $out;
0793      }
0794   
0795      /**
0796      * Parse quote bbcode
0797      * Expects the argument to start with a tag
0798      */
0799      function bbcode_quote($in)
0800      {
0801          $in = str_replace("\r\n", "\n", str_replace('\"', '"', trim($in)));
0802   
0803          if (!$in)
0804          {
0805              return '';
0806          }
0807   
0808          // To let the parser not catch tokens within quote_username quotes we encode them before we start this...
0809          $in = preg_replace_callback('#quote=&quot;(.*?)&quot;\]#i', function ($match) {
0810              return 'quote=&quot;' . str_replace(array('[', ']', '\\\"'), array('&#91;', '&#93;', '\"'), $match[1]) . '&quot;]';
0811          }, $in);
0812   
0813          $tok = ']';
0814          $out = '[';
0815   
0816          $in = substr($in, 1);
0817          $close_tags = $error_ary = array();
0818          $buffer = '';
0819   
0820          do
0821          {
0822              $pos = strlen($in);
0823              for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i)
0824              {
0825                  $tmp_pos = strpos($in, $tok[$i]);
0826                  if ($tmp_pos !== false && $tmp_pos < $pos)
0827                  {
0828                      $pos = $tmp_pos;
0829                  }
0830              }
0831   
0832              $buffer .= substr($in, 0, $pos);
0833              $tok = $in[$pos];
0834              $in = substr($in, $pos + 1);
0835   
0836              if ($tok == ']')
0837              {
0838                  if (strtolower($buffer) == '/quote' && sizeof($close_tags) && substr($out, -1, 1) == '[')
0839                  {
0840                      // we have found a closing tag
0841                      $out .= array_pop($close_tags) . ']';
0842                      $tok = '[';
0843                      $buffer = '';
0844   
0845                      /* Add space at the end of the closing tag if not happened before to allow following urls/smilies to be parsed correctly
0846                      * 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.
0847                      * Also, we won't have any spaces within $in anyway, only adding up spaces -> #10982
0848                      if (!$in || $in[0] !== ' ')
0849                      {
0850                          $out .= ' ';
0851                      }*/
0852                  }
0853                  else if (preg_match('#^quote(?:=&quot;(.*?)&quot;)?$#is', $buffer, $m) && substr($out, -1, 1) == '[')
0854                  {
0855                      $this->parsed_items['quote']++;
0856                      array_push($close_tags, '/quote:' . $this->bbcode_uid);
0857   
0858                      if (isset($m[1]) && $m[1])
0859                      {
0860                          $username = str_replace(array('&#91;', '&#93;'), array('[', ']'), $m[1]);
0861                          $username = preg_replace('#\[(?!b|i|u|color|url|email|/b|/i|/u|/color|/url|/email)#iU', '&#91;$1', $username);
0862   
0863                          $end_tags = array();
0864                          $error = false;
0865   
0866                          preg_match_all('#\[((?:/)?(?:[a-z]+))#i', $username, $tags);
0867                          foreach ($tags[1] as $tag)
0868                          {
0869                              if ($tag[0] != '/')
0870                              {
0871                                  $end_tags[] = '/' . $tag;
0872                              }
0873                              else
0874                              {
0875                                  $end_tag = array_pop($end_tags);
0876                                  $error = ($end_tag != $tag) ? true : false;
0877                              }
0878                          }
0879   
0880                          if ($error)
0881                          {
0882                              $username = $m[1];
0883                          }
0884   
0885                          $out .= 'quote=&quot;' . $username . '&quot;:' . $this->bbcode_uid . ']';
0886                      }
0887                      else
0888                      {
0889                          $out .= 'quote:' . $this->bbcode_uid . ']';
0890                      }
0891   
0892                      $tok = '[';
0893                      $buffer = '';
0894                  }
0895                  else if (preg_match('#^quote=&quot;(.*?)#is', $buffer, $m))
0896                  {
0897                      // the buffer holds an invalid opening tag
0898                      $buffer .= ']';
0899                  }
0900                  else
0901                  {
0902                      $out .= $buffer . $tok;
0903                      $tok = '[]';
0904                      $buffer = '';
0905                  }
0906              }
0907              else
0908              {
0909  /**
0910  *                Old quote code working fine, but having errors listed in bug #3572
0911  *
0912  *                $out .= $buffer . $tok;
0913  *                $tok = ($tok == '[') ? ']' : '[]';
0914  *                $buffer = '';
0915  */
0916   
0917                  $out .= $buffer . $tok;
0918   
0919                  if ($tok == '[')
0920                  {
0921                      // Search the text for the next tok... if an ending quote comes first, then change tok to []
0922                      $pos1 = stripos($in, '[/quote');
0923                      // If the token ] comes first, we change it to ]
0924                      $pos2 = strpos($in, ']');
0925                      // If the token [ comes first, we change it to [
0926                      $pos3 = strpos($in, '[');
0927   
0928                      if ($pos1 !== false && ($pos2 === false || $pos1 < $pos2) && ($pos3 === false || $pos1 < $pos3))
0929                      {
0930                          $tok = '[]';
0931                      }
0932                      else if ($pos3 !== false && ($pos2 === false || $pos3 < $pos2))
0933                      {
0934                          $tok = '[';
0935                      }
0936                      else
0937                      {
0938                          $tok = ']';
0939                      }
0940                  }
0941                  else
0942                  {
0943                      $tok = '[]';
0944                  }
0945                  $buffer = '';
0946              }
0947          }
0948          while ($in);
0949   
0950          $out .= $buffer;
0951   
0952          if (sizeof($close_tags))
0953          {
0954              $out .= '[' . implode('][', $close_tags) . ']';
0955          }
0956   
0957          foreach ($error_ary as $error_msg)
0958          {
0959              $this->warn_msg[] = $error_msg;
0960          }
0961   
0962          return $out;
0963      }
0964   
0965      /**
0966      * Validate email
0967      */
0968      function validate_email($var1, $var2)
0969      {
0970          $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1)));
0971          $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2)));
0972   
0973          $txt = $var2;
0974          $email = ($var1) ? $var1 : $var2;
0975   
0976          $validated = true;
0977   
0978          if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email))
0979          {
0980              $validated = false;
0981          }
0982   
0983          if (!$validated)
0984          {
0985              return '[email' . (($var1) ? "=$var1" : '') . ']' . $var2 . '[/email]';
0986          }
0987   
0988          $this->parsed_items['email']++;
0989   
0990          if ($var1)
0991          {
0992              $retval = '[email=' . $this->bbcode_specialchars($email) . ':' . $this->bbcode_uid . ']' . $txt . '[/email:' . $this->bbcode_uid . ']';
0993          }
0994          else
0995          {
0996              $retval = '[email:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($email) . '[/email:' . $this->bbcode_uid . ']';
0997          }
0998   
0999          return $retval;
1000      }
1001   
1002      /**
1003      * Validate url
1004      *
1005      * @param string $var1 optional url parameter for url bbcode: [url(=$var1)]$var2[/url]
1006      * @param string $var2 url bbcode content: [url(=$var1)]$var2[/url]
1007      */
1008      function validate_url($var1, $var2)
1009      {
1010          $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1)));
1011          $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2)));
1012   
1013          $url = ($var1) ? $var1 : $var2;
1014   
1015          if ($var1 && !$var2)
1016          {
1017              $var2 = $var1;
1018          }
1019   
1020          if (!$url)
1021          {
1022              return '[url' . (($var1) ? '=' . $var1 : '') . ']' . $var2 . '[/url]';
1023          }
1024   
1025          $valid = false;
1026   
1027          $url = str_replace(' ', '%20', $url);
1028   
1029          // Checking urls
1030          if (preg_match('#^' . get_preg_expression('url') . '$#iu', $url) ||
1031              preg_match('#^' . get_preg_expression('www_url') . '$#iu', $url) ||
1032              preg_match('#^' . preg_quote(generate_board_url(), '#') . get_preg_expression('relative_url') . '$#iu', $url))
1033          {
1034              $valid = true;
1035          }
1036   
1037          if ($valid)
1038          {
1039              $this->parsed_items['url']++;
1040   
1041              // if there is no scheme, then add http schema
1042              if (!preg_match('#^[a-z][a-z\d+\-.]*:/{2}#i', $url))
1043              {
1044                  $url = 'http://' . $url;
1045              }
1046   
1047              // Is this a link to somewhere inside this board? If so then remove the session id from the url
1048              if (strpos($url, generate_board_url()) !== false && strpos($url, 'sid=') !== false)
1049              {
1050                  $url = preg_replace('/(&amp;|\?)sid=[0-9a-f]{32}&amp;/', '\1', $url);
1051                  $url = preg_replace('/(&amp;|\?)sid=[0-9a-f]{32}$/', '', $url);
1052                  $url = append_sid($url);
1053              }
1054   
1055              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 . ']';
1056          }
1057   
1058          return '[url' . (($var1) ? '=' . $var1 : '') . ']' . $var2 . '[/url]';
1059      }
1060   
1061      /**
1062      * Check if url is pointing to this domain/script_path/php-file
1063      *
1064      * @param string $url the url to check
1065      * @return true if the url is pointing to this domain/script_path/php-file, false if not
1066      *
1067      * @access private
1068      */
1069      function path_in_domain($url)
1070      {
1071          global $config, $phpEx, $user;
1072   
1073          if ($config['force_server_vars'])
1074          {
1075              $check_path = $config['script_path'];
1076          }
1077          else
1078          {
1079              $check_path = ($user->page['root_script_path'] != '/') ? substr($user->page['root_script_path'], 0, -1) : '/';
1080          }
1081   
1082          // Is the user trying to link to a php file in this domain and script path?
1083          if (strpos($url, ".{$phpEx}") !== false && strpos($url, $check_path) !== false)
1084          {
1085              $server_name = $user->host;
1086   
1087              // Forcing server vars is the only way to specify/override the protocol
1088              if ($config['force_server_vars'] || !$server_name)
1089              {
1090                  $server_name = $config['server_name'];
1091              }
1092   
1093              // Check again in correct order...
1094              $pos_ext = strpos($url, ".{$phpEx}");
1095              $pos_path = strpos($url, $check_path);
1096              $pos_domain = strpos($url, $server_name);
1097   
1098              if ($pos_domain !== false && $pos_path >= $pos_domain && $pos_ext >= $pos_path)
1099              {
1100                  // Ok, actually we allow linking to some files (this may be able to be extended in some way later...)
1101                  if (strpos($url, '/' . $check_path . '/download/file.' . $phpEx) !== 0)
1102                  {
1103                      return false;
1104                  }
1105   
1106                  return true;
1107              }
1108          }
1109   
1110          return false;
1111      }
1112  }
1113   
1114  /**
1115  * Main message parser for posting, pm, etc. takes raw message
1116  * and parses it for attachments, bbcode and smilies
1117  */
1118  class parse_message extends bbcode_firstpass
1119  {
1120      var $attachment_data = array();
1121      var $filename_data = array();
1122   
1123      // Helps ironing out user error
1124      var $message_status = '';
1125   
1126      var $allow_img_bbcode = true;
1127      var $allow_flash_bbcode = true;
1128      var $allow_quote_bbcode = true;
1129      var $allow_url_bbcode = true;
1130   
1131      var $mode;
1132   
1133      /**
1134      * The plupload object used for dealing with attachments
1135      * @var \phpbb\plupload\plupload
1136      */
1137      protected $plupload;
1138   
1139      /**
1140      * Init - give message here or manually
1141      */
1142      function parse_message($message = '')
1143      {
1144          // Init BBCode UID
1145          $this->bbcode_uid = substr(base_convert(unique_id(), 16, 36), 0, BBCODE_UID_LEN);
1146          $this->message = $message;
1147      }
1148   
1149      /**
1150      * Parse Message
1151      */
1152      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')
1153      {
1154          global $config, $user, $phpbb_dispatcher, $phpbb_container;
1155   
1156          $this->mode = $mode;
1157   
1158          foreach (array('chars', 'smilies', 'urls', 'font_size', 'img_height', 'img_width') as $key)
1159          {
1160              if (!isset($config['max_' . $mode . '_' . $key]))
1161              {
1162                  $config['max_' . $mode . '_' . $key] = 0;
1163              }
1164          }
1165   
1166          $this->allow_img_bbcode = $allow_img_bbcode;
1167          $this->allow_flash_bbcode = $allow_flash_bbcode;
1168          $this->allow_quote_bbcode = $allow_quote_bbcode;
1169          $this->allow_url_bbcode = $allow_url_bbcode;
1170   
1171          // If false, then $this->message won't be altered, the text will be returned instead.
1172          if (!$update_this_message)
1173          {
1174              $tmp_message = $this->message;
1175              $return_message = &$this->message;
1176          }
1177   
1178          if ($this->message_status == 'display')
1179          {
1180              $this->decode_message();
1181          }
1182   
1183          // Store message length...
1184          $message_length = ($mode == 'post') ? utf8_strlen($this->message) : utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message));
1185   
1186          // Maximum message length check. 0 disables this check completely.
1187          if ((int) $config['max_' . $mode . '_chars'] > 0 && $message_length > (int) $config['max_' . $mode . '_chars'])
1188          {
1189              $this->warn_msg[] = $user->lang('CHARS_' . strtoupper($mode) . '_CONTAINS', $message_length) . '<br />' . $user->lang('TOO_MANY_CHARS_LIMIT', (int) $config['max_' . $mode . '_chars']);
1190              return (!$update_this_message) ? $return_message : $this->warn_msg;
1191          }
1192   
1193          // Minimum message length check for post only
1194          if ($mode === 'post')
1195          {
1196              if (!$message_length || $message_length < (int) $config['min_post_chars'])
1197              {
1198                  $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']));
1199                  return (!$update_this_message) ? $return_message : $this->warn_msg;
1200              }
1201          }
1202   
1203          /**
1204          * This event can be used for additional message checks/cleanup before parsing
1205          *
1206          * @event core.message_parser_check_message
1207          * @var bool        allow_bbcode            Do we allow BBCodes
1208          * @var bool        allow_magic_url            Do we allow magic urls
1209          * @var bool        allow_smilies            Do we allow smilies
1210          * @var bool        allow_img_bbcode        Do we allow image BBCode
1211          * @var bool        allow_flash_bbcode        Do we allow flash BBCode
1212          * @var bool        allow_quote_bbcode        Do we allow quote BBCode
1213          * @var bool        allow_url_bbcode        Do we allow url BBCode
1214          * @var bool        update_this_message        Do we alter the parsed message
1215          * @var string    mode                    Posting mode
1216          * @var string    message                    The message text to parse
1217          * @var string    bbcode_bitfield            The bbcode_bitfield before parsing
1218          * @var string    bbcode_uid                The bbcode_uid before parsing
1219          * @var bool        return                    Do we return after the event is triggered if $warn_msg is not empty
1220          * @var array    warn_msg                Array of the warning messages
1221          * @since 3.1.2-RC1
1222          * @change 3.1.3-RC1 Added vars $bbcode_bitfield and $bbcode_uid
1223          */
1224          $message = $this->message;
1225          $warn_msg = $this->warn_msg;
1226          $return = false;
1227          $bbcode_bitfield = $this->bbcode_bitfield;
1228          $bbcode_uid = $this->bbcode_uid;
1229          $vars = array(
1230              'allow_bbcode',
1231              'allow_magic_url',
1232              'allow_smilies',
1233              'allow_img_bbcode',
1234              'allow_flash_bbcode',
1235              'allow_quote_bbcode',
1236              'allow_url_bbcode',
1237              'update_this_message',
1238              'mode',
1239              'message',
1240              'bbcode_bitfield',
1241              'bbcode_uid',
1242              'return',
1243              'warn_msg',
1244          );
1245          extract($phpbb_dispatcher->trigger_event('core.message_parser_check_message', compact($vars)));
1246          $this->message = $message;
1247          $this->warn_msg = $warn_msg;
1248          $this->bbcode_bitfield = $bbcode_bitfield;
1249          $this->bbcode_uid = $bbcode_uid;
1250          if ($return && !empty($this->warn_msg))
1251          {
1252              return (!$update_this_message) ? $return_message : $this->warn_msg;
1253          }
1254   
1255          // Get the parser
1256          $parser = $phpbb_container->get('text_formatter.parser');
1257   
1258          // Set the parser's options
1259          ($allow_bbcode)       ? $parser->enable_bbcodes()       : $parser->disable_bbcodes();
1260          ($allow_magic_url)    ? $parser->enable_magic_url()     : $parser->disable_magic_url();
1261          ($allow_smilies)      ? $parser->enable_smilies()       : $parser->disable_smilies();
1262          ($allow_img_bbcode)   ? $parser->enable_bbcode('img')   : $parser->disable_bbcode('img');
1263          ($allow_flash_bbcode) ? $parser->enable_bbcode('flash') : $parser->disable_bbcode('flash');
1264          ($allow_quote_bbcode) ? $parser->enable_bbcode('quote') : $parser->disable_bbcode('quote');
1265          ($allow_url_bbcode)   ? $parser->enable_bbcode('url')   : $parser->disable_bbcode('url');
1266   
1267          // Set some config values
1268          $parser->set_vars(array(
1269              'max_font_size'  => $config['max_' . $this->mode . '_font_size'],
1270              'max_img_height' => $config['max_' . $this->mode . '_img_height'],
1271              'max_img_width'  => $config['max_' . $this->mode . '_img_width'],
1272              'max_smilies'    => $config['max_' . $this->mode . '_smilies'],
1273              'max_urls'       => $config['max_' . $this->mode . '_urls']
1274          ));
1275   
1276          // Parse this message
1277          $this->message = $parser->parse(htmlspecialchars_decode($this->message, ENT_QUOTES));
1278   
1279          // Remove quotes that are nested too deep
1280          if ($config['max_quote_depth'] > 0)
1281          {
1282              $this->remove_nested_quotes($config['max_quote_depth']);
1283          }
1284   
1285          // Check for "empty" message. We do not check here for maximum length, because bbcode, smilies, etc. can add to the length.
1286          // The maximum length check happened before any parsings.
1287          if ($mode === 'post' && utf8_clean_string($this->message) === '')
1288          {
1289              $this->warn_msg[] = $user->lang['TOO_FEW_CHARS'];
1290              return (!$update_this_message) ? $return_message : $this->warn_msg;
1291          }
1292   
1293          // Remove quotes that are nested too deep
1294          if ($config['max_quote_depth'] > 0)
1295          {
1296              $this->message = $phpbb_container->get('text_formatter.utils')->remove_bbcode(
1297                  $this->message,
1298                  'quote',
1299                  $config['max_quote_depth']
1300              );
1301          }
1302   
1303          // Check for errors
1304          $errors = $parser->get_errors();
1305          if ($errors)
1306          {
1307              foreach ($errors as $i => $args)
1308              {
1309                  // Translate each error with $user->lang()
1310                  $errors[$i] = call_user_func_array(array($user, 'lang'), $args);
1311              }
1312              $this->warn_msg = array_merge($this->warn_msg, $errors);
1313   
1314              return (!$update_this_message) ? $return_message : $this->warn_msg;
1315          }
1316   
1317          if (!$update_this_message)
1318          {
1319              unset($this->message);
1320              $this->message = $tmp_message;
1321              return $return_message;
1322          }
1323   
1324          $this->message_status = 'parsed';
1325          return false;
1326      }
1327   
1328      /**
1329      * Formatting text for display
1330      */
1331      function format_display($allow_bbcode, $allow_magic_url, $allow_smilies, $update_this_message = true)
1332      {
1333          global $phpbb_container, $phpbb_dispatcher;
1334   
1335          // If false, then the parsed message get returned but internal message not processed.
1336          if (!$update_this_message)
1337          {
1338              $tmp_message = $this->message;
1339              $return_message = &$this->message;
1340          }
1341   
1342          $text = $this->message;
1343          $uid = $this->bbcode_uid;
1344   
1345          /**
1346          * Event to modify the text before it is parsed
1347          *
1348          * @event core.modify_format_display_text_before
1349          * @var string    text                The message text to parse
1350          * @var string    uid                    The bbcode uid
1351          * @var bool        allow_bbcode        Do we allow bbcodes
1352          * @var bool        allow_magic_url        Do we allow magic urls
1353          * @var bool        allow_smilies        Do we allow smilies
1354          * @var bool        update_this_message    Do we update the internal message
1355          *                                    with the parsed result
1356          * @since 3.1.6-RC1
1357          */
1358          $vars = array('text', 'uid', 'allow_bbcode', 'allow_magic_url', 'allow_smilies', 'update_this_message');
1359          extract($phpbb_dispatcher->trigger_event('core.modify_format_display_text_before', compact($vars)));
1360   
1361          $this->message = $text;
1362          $this->bbcode_uid = $uid;
1363          unset($text, $uid);
1364   
1365          // NOTE: message_status is unreliable for detecting unparsed text because some callers
1366          //       change $this->message without resetting $this->message_status to 'plain' so we
1367          //       inspect the message instead
1368          //if ($this->message_status == 'plain')
1369          if (!preg_match('/^<[rt][ >]/', $this->message))
1370          {
1371              // Force updating message - of course.
1372              $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);
1373          }
1374   
1375          // There's a bug when previewing a topic with no poll, because the empty title of the poll
1376          // gets parsed but $this->message still ends up empty. This fixes it, until a proper fix is
1377          // devised
1378          if ($this->message === '')
1379          {
1380              $this->message = $phpbb_container->get('text_formatter.parser')->parse($this->message);
1381          }
1382   
1383          $this->message = $phpbb_container->get('text_formatter.renderer')->render($this->message);
1384   
1385          $text = $this->message;
1386          $uid = $this->bbcode_uid;
1387   
1388          /**
1389          * Event to modify the text after it is parsed
1390          *
1391          * @event core.modify_format_display_text_after
1392          * @var string    text                The message text to parse
1393          * @var string    uid                    The bbcode uid
1394          * @var bool        allow_bbcode        Do we allow bbcodes
1395          * @var bool        allow_magic_url        Do we allow magic urls
1396          * @var bool        allow_smilies        Do we allow smilies
1397          * @var bool        update_this_message    Do we update the internal message
1398          *                                    with the parsed result
1399          * @since 3.1.0-a3
1400          */
1401          $vars = array('text', 'uid', 'allow_bbcode', 'allow_magic_url', 'allow_smilies', 'update_this_message');
1402          extract($phpbb_dispatcher->trigger_event('core.modify_format_display_text_after', compact($vars)));
1403   
1404          $this->message = $text;
1405          $this->bbcode_uid = $uid;
1406   
1407          if (!$update_this_message)
1408          {
1409              unset($this->message);
1410              $this->message = $tmp_message;
1411              return $return_message;
1412          }
1413   
1414          $this->message_status = 'display';
1415          return false;
1416      }
1417   
1418      /**
1419      * Decode message to be placed back into form box
1420      */
1421      function decode_message($custom_bbcode_uid = '', $update_this_message = true)
1422      {
1423          // If false, then the parsed message get returned but internal message not processed.
1424          if (!$update_this_message)
1425          {
1426              $tmp_message = $this->message;
1427              $return_message = &$this->message;
1428          }
1429   
1430          ($custom_bbcode_uid) ? decode_message($this->message, $custom_bbcode_uid) : decode_message($this->message, $this->bbcode_uid);
1431   
1432          if (!$update_this_message)
1433          {
1434              unset($this->message);
1435              $this->message = $tmp_message;
1436              return $return_message;
1437          }
1438   
1439          $this->message_status = 'plain';
1440          return false;
1441      }
1442   
1443      /**
1444      * Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx.
1445      * Cuts down displayed size of link if over 50 chars, turns absolute links
1446      * into relative versions when the server/script path matches the link
1447      */
1448      function magic_url($server_url)
1449      {
1450          // We use the global make_clickable function
1451          $this->message = make_clickable($this->message, $server_url);
1452      }
1453   
1454      /**
1455      * Parse Smilies
1456      */
1457      function smilies($max_smilies = 0)
1458      {
1459          global $db, $user;
1460          static $match;
1461          static $replace;
1462   
1463          // See if the static arrays have already been filled on an earlier invocation
1464          if (!is_array($match))
1465          {
1466              $match = $replace = array();
1467   
1468              // NOTE: obtain_* function? chaching the table contents?
1469   
1470              // For now setting the ttl to 10 minutes
1471              switch ($db->get_sql_layer())
1472              {
1473                  case 'mssql_odbc':
1474                  case 'mssqlnative':
1475                      $sql = 'SELECT *
1476                          FROM ' . SMILIES_TABLE . '
1477                          ORDER BY LEN(code) DESC';
1478                  break;
1479   
1480                  // LENGTH supported by MySQL, IBM DB2, Oracle and Access for sure...
1481                  default:
1482                      $sql = 'SELECT *
1483                          FROM ' . SMILIES_TABLE . '
1484                          ORDER BY LENGTH(code) DESC';
1485                  break;
1486              }
1487              $result = $db->sql_query($sql, 600);
1488   
1489              while ($row = $db->sql_fetchrow($result))
1490              {
1491                  if (empty($row['code']))
1492                  {
1493                      continue;
1494                  }
1495   
1496                  // (assertion)
1497                  $match[] = preg_quote($row['code'], '#');
1498                  $replace[] = '<!-- s' . $row['code'] . ' --><img src="{SMILIES_PATH}/' . $row['smiley_url'] . '" alt="' . $row['code'] . '" title="' . $row['emotion'] . '" /><!-- s' . $row['code'] . ' -->';
1499              }
1500              $db->sql_freeresult($result);
1501          }
1502   
1503          if (sizeof($match))
1504          {
1505              if ($max_smilies)
1506              {
1507                  // 'u' modifier has been added to correctly parse smilies within unicode strings
1508                  // For details: http://tracker.phpbb.com/browse/PHPBB3-10117
1509                  $num_matches = preg_match_all('#(?<=^|[\n .])(?:' . implode('|', $match) . ')(?![^<>]*>)#u', $this->message, $matches);
1510                  unset($matches);
1511   
1512                  if ($num_matches !== false && $num_matches > $max_smilies)
1513                  {
1514                      $this->warn_msg[] = sprintf($user->lang['TOO_MANY_SMILIES'], $max_smilies);
1515                      return;
1516                  }
1517              }
1518   
1519              // Make sure the delimiter # is added in front and at the end of every element within $match
1520              // 'u' modifier has been added to correctly parse smilies within unicode strings
1521              // For details: http://tracker.phpbb.com/browse/PHPBB3-10117
1522   
1523              $this->message = trim(preg_replace(explode(chr(0), '#(?<=^|[\n .])' . implode('(?![^<>]*>)#u' . chr(0) . '#(?<=^|[\n .])', $match) . '(?![^<>]*>)#u'), $replace, $this->message));
1524          }
1525      }
1526   
1527      /**
1528      * Parse Attachments
1529      */
1530      function parse_attachments($form_name, $mode, $forum_id, $submit, $preview, $refresh, $is_message = false)
1531      {
1532          global $config, $auth, $user, $phpbb_root_path, $phpEx, $db, $request;
1533          global $phpbb_container;
1534   
1535          $error = array();
1536   
1537          $num_attachments = sizeof($this->attachment_data);
1538          $this->filename_data['filecomment'] = $request->variable('filecomment', '', true);
1539          $upload = $request->file($form_name);
1540          $upload_file = (!empty($upload) && $upload['name'] !== 'none' && trim($upload['name']));
1541   
1542          $add_file        = (isset($_POST['add_file'])) ? true : false;
1543          $delete_file    = (isset($_POST['delete_file'])) ? true : false;
1544   
1545          // First of all adjust comments if changed
1546          $actual_comment_list = $request->variable('comment_list', array(''), true);
1547   
1548          foreach ($actual_comment_list as $comment_key => $comment)
1549          {
1550              if (!isset($this->attachment_data[$comment_key]))
1551              {
1552                  continue;
1553              }
1554   
1555              if ($this->attachment_data[$comment_key]['attach_comment'] != $actual_comment_list[$comment_key])
1556              {
1557                  $this->attachment_data[$comment_key]['attach_comment'] = $actual_comment_list[$comment_key];
1558              }
1559          }
1560   
1561          $cfg = array();
1562          $cfg['max_attachments'] = ($is_message) ? $config['max_attachments_pm'] : $config['max_attachments'];
1563          $forum_id = ($is_message) ? 0 : $forum_id;
1564   
1565          if ($submit && in_array($mode, array('post', 'reply', 'quote', 'edit')) && $upload_file)
1566          {
1567              if ($num_attachments < $cfg['max_attachments'] || $auth->acl_get('a_') || $auth->acl_get('m_', $forum_id))
1568              {
1569                  /** @var \phpbb\attachment\manager $attachment_manager */
1570                  $attachment_manager = $phpbb_container->get('attachment.manager');
1571                  $filedata = $attachment_manager->upload($form_name, $forum_id, false, '', $is_message);
1572                  $error = $filedata['error'];
1573   
1574                  if ($filedata['post_attach'] && !sizeof($error))
1575                  {
1576                      $sql_ary = array(
1577                          'physical_filename'    => $filedata['physical_filename'],
1578                          'attach_comment'    => $this->filename_data['filecomment'],
1579                          'real_filename'        => $filedata['real_filename'],
1580                          'extension'            => $filedata['extension'],
1581                          'mimetype'            => $filedata['mimetype'],
1582                          'filesize'            => $filedata['filesize'],
1583                          'filetime'            => $filedata['filetime'],
1584                          'thumbnail'            => $filedata['thumbnail'],
1585                          'is_orphan'            => 1,
1586                          'in_message'        => ($is_message) ? 1 : 0,
1587                          'poster_id'            => $user->data['user_id'],
1588                      );
1589   
1590                      $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1591   
1592                      $new_entry = array(
1593                          'attach_id'        => $db->sql_nextid(),
1594                          'is_orphan'        => 1,
1595                          'real_filename'    => $filedata['real_filename'],
1596                          'attach_comment'=> $this->filename_data['filecomment'],
1597                          'filesize'        => $filedata['filesize'],
1598                      );
1599   
1600                      $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data);
1601                      $this->message = preg_replace_callback('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#', function ($match) {
1602                          return '[attachment='.($match[1] + 1).']' . $match[2] . '[/attachment]';
1603                      }, $this->message);
1604   
1605                      $this->filename_data['filecomment'] = '';
1606   
1607                      // This Variable is set to false here, because Attachments are entered into the
1608                      // Database in two modes, one if the id_list is 0 and the second one if post_attach is true
1609                      // Since post_attach is automatically switched to true if an Attachment got added to the filesystem,
1610                      // but we are assigning an id of 0 here, we have to reset the post_attach variable to false.
1611                      //
1612                      // This is very relevant, because it could happen that the post got not submitted, but we do not
1613                      // know this circumstance here. We could be at the posting page or we could be redirected to the entered
1614                      // post. :)
1615                      $filedata['post_attach'] = false;
1616                  }
1617              }
1618              else
1619              {
1620                  $error[] = $user->lang('TOO_MANY_ATTACHMENTS', (int) $cfg['max_attachments']);
1621              }
1622          }
1623   
1624          if ($preview || $refresh || sizeof($error))
1625          {
1626              if (isset($this->plupload) && $this->plupload->is_active())
1627              {
1628                  $json_response = new \phpbb\json_response();
1629              }
1630   
1631              // Perform actions on temporary attachments
1632              if ($delete_file)
1633              {
1634                  include_once($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
1635   
1636                  $index = array_keys($request->variable('delete_file', array(0 => 0)));
1637                  $index = (!empty($index)) ? $index[0] : false;
1638   
1639                  if ($index !== false && !empty($this->attachment_data[$index]))
1640                  {
1641                      /** @var \phpbb\attachment\manager $attachment_manager */
1642                      $attachment_manager = $phpbb_container->get('attachment.manager');
1643   
1644                      // delete selected attachment
1645                      if ($this->attachment_data[$index]['is_orphan'])
1646                      {
1647                          $sql = 'SELECT attach_id, physical_filename, thumbnail
1648                              FROM ' . ATTACHMENTS_TABLE . '
1649                              WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id'] . '
1650                                  AND is_orphan = 1
1651                                  AND poster_id = ' . $user->data['user_id'];
1652                          $result = $db->sql_query($sql);
1653                          $row = $db->sql_fetchrow($result);
1654                          $db->sql_freeresult($result);
1655   
1656                          if ($row)
1657                          {
1658                              $attachment_manager->unlink($row['physical_filename'], 'file');
1659   
1660                              if ($row['thumbnail'])
1661                              {
1662                                  $attachment_manager->unlink($row['physical_filename'], 'thumbnail');
1663                              }
1664   
1665                              $db->sql_query('DELETE FROM ' . ATTACHMENTS_TABLE . ' WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id']);
1666                          }
1667                      }
1668                      else
1669                      {
1670                          $attachment_manager->delete('attach', $this->attachment_data[$index]['attach_id']);
1671                      }
1672   
1673                      unset($this->attachment_data[$index]);
1674                      $this->message = preg_replace_callback('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#', function ($match) use($index) {
1675                          return ($match[1] == $index) ? '' : (($match[1] > $index) ? '[attachment=' . ($match[1] - 1) . ']' . $match[2] . '[/attachment]' : $match[0]);
1676                      }, $this->message);
1677   
1678                      // Reindex Array
1679                      $this->attachment_data = array_values($this->attachment_data);
1680                      if (isset($this->plupload) && $this->plupload->is_active())
1681                      {
1682                          $json_response->send($this->attachment_data);
1683                      }
1684                  }
1685              }
1686              else if (($add_file || $preview) && $upload_file)
1687              {
1688                  if ($num_attachments < $cfg['max_attachments'] || $auth->acl_gets('m_', 'a_', $forum_id))
1689                  {
1690                      /** @var \phpbb\attachment\manager $attachment_manager */
1691                      $attachment_manager = $phpbb_container->get('attachment.manager');
1692                      $filedata = $attachment_manager->upload($form_name, $forum_id, false, '', $is_message);
1693                      $error = array_merge($error, $filedata['error']);
1694   
1695                      if (!sizeof($error))
1696                      {
1697                          $sql_ary = array(
1698                              'physical_filename'    => $filedata['physical_filename'],
1699                              'attach_comment'    => $this->filename_data['filecomment'],
1700                              'real_filename'        => $filedata['real_filename'],
1701                              'extension'            => $filedata['extension'],
1702                              'mimetype'            => $filedata['mimetype'],
1703                              'filesize'            => $filedata['filesize'],
1704                              'filetime'            => $filedata['filetime'],
1705                              'thumbnail'            => $filedata['thumbnail'],
1706                              'is_orphan'            => 1,
1707                              'in_message'        => ($is_message) ? 1 : 0,
1708                              'poster_id'            => $user->data['user_id'],
1709                          );
1710   
1711                          $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1712   
1713                          $new_entry = array(
1714                              'attach_id'        => $db->sql_nextid(),
1715                              'is_orphan'        => 1,
1716                              'real_filename'    => $filedata['real_filename'],
1717                              'attach_comment'=> $this->filename_data['filecomment'],
1718                              'filesize'        => $filedata['filesize'],
1719                          );
1720   
1721                          $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data);
1722                          $this->message = preg_replace_callback('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#', function ($match) {
1723                              return '[attachment=' . ($match[1] + 1) . ']' . $match[2] . '[/attachment]';
1724                          }, $this->message);
1725                          $this->filename_data['filecomment'] = '';
1726   
1727                          if (isset($this->plupload) && $this->plupload->is_active())
1728                          {
1729                              $download_url = append_sid("{$phpbb_root_path}download/file.{$phpEx}", 'mode=view&amp;id=' . $new_entry['attach_id']);
1730   
1731                              // Send the client the attachment data to maintain state
1732                              $json_response->send(array('data' => $this->attachment_data, 'download_url' => $download_url));
1733                          }
1734                      }
1735                  }
1736                  else
1737                  {
1738                      $error[] = $user->lang('TOO_MANY_ATTACHMENTS', (int) $cfg['max_attachments']);
1739                  }
1740   
1741                  if (!empty($error) && isset($this->plupload) && $this->plupload->is_active())
1742                  {
1743                      // If this is a plupload (and thus ajax) request, give the
1744                      // client the first error we have
1745                      $json_response->send(array(
1746                          'jsonrpc' => '2.0',
1747                          'id' => 'id',
1748                          'error' => array(
1749                              'code' => 105,
1750                              'message' => current($error),
1751                          ),
1752                      ));
1753                  }
1754              }
1755          }
1756   
1757          foreach ($error as $error_msg)
1758          {
1759              $this->warn_msg[] = $error_msg;
1760          }
1761      }
1762   
1763      /**
1764      * Get Attachment Data
1765      */
1766      function get_submitted_attachment_data($check_user_id = false)
1767      {
1768          global $user, $db;
1769          global $request;
1770   
1771          $this->filename_data['filecomment'] = $request->variable('filecomment', '', true);
1772          $attachment_data = $request->variable('attachment_data', array(0 => array('' => '')), true, \phpbb\request\request_interface::POST);
1773          $this->attachment_data = array();
1774   
1775          $check_user_id = ($check_user_id === false) ? $user->data['user_id'] : $check_user_id;
1776   
1777          if (!sizeof($attachment_data))
1778          {
1779              return;
1780          }
1781   
1782          $not_orphan = $orphan = array();
1783   
1784          foreach ($attachment_data as $pos => $var_ary)
1785          {
1786              if ($var_ary['is_orphan'])
1787              {
1788                  $orphan[(int) $var_ary['attach_id']] = $pos;
1789              }
1790              else
1791              {
1792                  $not_orphan[(int) $var_ary['attach_id']] = $pos;
1793              }
1794          }
1795   
1796          // Regenerate already posted attachments
1797          if (sizeof($not_orphan))
1798          {
1799              // Get the attachment data, based on the poster id...
1800              $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment, filesize
1801                  FROM ' . ATTACHMENTS_TABLE . '
1802                  WHERE ' . $db->sql_in_set('attach_id', array_keys($not_orphan)) . '
1803                      AND poster_id = ' . $check_user_id;
1804              $result = $db->sql_query($sql);
1805   
1806              while ($row = $db->sql_fetchrow($result))
1807              {
1808                  $pos = $not_orphan[$row['attach_id']];
1809                  $this->attachment_data[$pos] = $row;
1810                  $this->attachment_data[$pos]['attach_comment'] = $attachment_data[$pos]['attach_comment'];
1811   
1812                  unset($not_orphan[$row['attach_id']]);
1813              }
1814              $db->sql_freeresult($result);
1815          }
1816   
1817          if (sizeof($not_orphan))
1818          {
1819              trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR);
1820          }
1821   
1822          // Regenerate newly uploaded attachments
1823          if (sizeof($orphan))
1824          {
1825              $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment, filesize
1826                  FROM ' . ATTACHMENTS_TABLE . '
1827                  WHERE ' . $db->sql_in_set('attach_id', array_keys($orphan)) . '
1828                      AND poster_id = ' . $user->data['user_id'] . '
1829                      AND is_orphan = 1';
1830              $result = $db->sql_query($sql);
1831   
1832              while ($row = $db->sql_fetchrow($result))
1833              {
1834                  $pos = $orphan[$row['attach_id']];
1835                  $this->attachment_data[$pos] = $row;
1836                  $this->attachment_data[$pos]['attach_comment'] = $attachment_data[$pos]['attach_comment'];
1837   
1838                  unset($orphan[$row['attach_id']]);
1839              }
1840              $db->sql_freeresult($result);
1841          }
1842   
1843          if (sizeof($orphan))
1844          {
1845              trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR);
1846          }
1847   
1848          ksort($this->attachment_data);
1849      }
1850   
1851      /**
1852      * Parse Poll
1853      */
1854      function parse_poll(&$poll)
1855      {
1856          global $user, $config;
1857   
1858          $poll_max_options = $poll['poll_max_options'];
1859   
1860          // Parse Poll Option text
1861          $tmp_message = $this->message;
1862   
1863          $poll['poll_options'] = preg_split('/\s*?\n\s*/', trim($poll['poll_option_text']));
1864          $poll['poll_options_size'] = sizeof($poll['poll_options']);
1865   
1866          foreach ($poll['poll_options'] as &$poll_option)
1867          {
1868              $this->message = $poll_option;
1869              $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');
1870          }
1871          unset($poll_option);
1872          $poll['poll_option_text'] = implode("\n", $poll['poll_options']);
1873   
1874          // Parse Poll Title
1875          $this->message = $poll['poll_title'];
1876          if (!$poll['poll_title'] && $poll['poll_options_size'])
1877          {
1878              $this->warn_msg[] = $user->lang['NO_POLL_TITLE'];
1879          }
1880          else
1881          {
1882              if (utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message)) > 100)
1883              {
1884                  $this->warn_msg[] = $user->lang['POLL_TITLE_TOO_LONG'];
1885              }
1886              $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');
1887              if (strlen($poll['poll_title']) > 255)
1888              {
1889                  $this->warn_msg[] = $user->lang['POLL_TITLE_COMP_TOO_LONG'];
1890              }
1891          }
1892   
1893          if (sizeof($poll['poll_options']) == 1)
1894          {
1895              $this->warn_msg[] = $user->lang['TOO_FEW_POLL_OPTIONS'];
1896          }
1897          else if ($poll['poll_options_size'] > (int) $config['max_poll_options'])
1898          {
1899              $this->warn_msg[] = $user->lang['TOO_MANY_POLL_OPTIONS'];
1900          }
1901          else if ($poll_max_options > $poll['poll_options_size'])
1902          {
1903              $this->warn_msg[] = $user->lang['TOO_MANY_USER_OPTIONS'];
1904          }
1905   
1906          $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']);
1907   
1908          $this->message = $tmp_message;
1909      }
1910   
1911      /**
1912      * Remove nested quotes at given depth in current parsed message
1913      *
1914      * @param  integer $max_depth Depth limit
1915      * @return null
1916      */
1917      public function remove_nested_quotes($max_depth)
1918      {
1919          global $phpbb_container;
1920   
1921          if (preg_match('#^<[rt][ >]#', $this->message))
1922          {
1923              $this->message = $phpbb_container->get('text_formatter.utils')->remove_bbcode(
1924                  $this->message,
1925                  'quote',
1926                  $max_depth
1927              );
1928   
1929              return;
1930          }
1931   
1932          // Capture all [quote] and [/quote] tags
1933          preg_match_all('(\\[/?quote(?:=&quot;(.*?)&quot;)?:' . $this->bbcode_uid . '\\])', $this->message, $matches, PREG_OFFSET_CAPTURE);
1934   
1935          // Iterate over the quote tags to mark the ranges that must be removed
1936          $depth = 0;
1937          $ranges = array();
1938          $start_pos = 0;
1939          foreach ($matches[0] as $match)
1940          {
1941              if ($match[0][1] === '/')
1942              {
1943                  --$depth;
1944                  if ($depth == $max_depth)
1945                  {
1946                      $end_pos = $match[1] + strlen($match[0]);
1947                      $length = $end_pos - $start_pos;
1948                      $ranges[] = array($start_pos, $length);
1949                  }
1950              }
1951              else
1952              {
1953                  ++$depth;
1954                  if ($depth == $max_depth + 1)
1955                  {
1956                      $start_pos = $match[1];
1957                  }
1958              }
1959          }
1960   
1961          foreach (array_reverse($ranges) as $range)
1962          {
1963              list($start_pos, $length) = $range;
1964              $this->message = substr_replace($this->message, '', $start_pos, $length);
1965          }
1966      }
1967   
1968      /**
1969      * Setter function for passing the plupload object
1970      *
1971      * @param \phpbb\plupload\plupload $plupload The plupload object
1972      *
1973      * @return null
1974      */
1975      public function set_plupload(\phpbb\plupload\plupload $plupload)
1976      {
1977          $this->plupload = $plupload;
1978      }
1979   
1980      /**
1981      * Function to perform custom bbcode validation by extensions
1982      * can be used in bbcode_init() to assign regexp replacement
1983      * Example: 'regexp' => array('#\[b\](.*?)\[/b\]#uise' => "\$this->validate_bbcode_by_extension('\$1')")
1984      *
1985      * Accepts variable number of parameters
1986      *
1987      * @return mixed Validation result
1988      */
1989      public function validate_bbcode_by_extension()
1990      {
1991          global $phpbb_dispatcher;
1992   
1993          $return = false;
1994          $params_array = func_get_args();
1995   
1996          /**
1997          * Event to validate bbcode with the custom validating methods
1998          * provided by extensions
1999          *
2000          * @event core.validate_bbcode_by_extension
2001          * @var array    params_array    Array with the function parameters
2002          * @var mixed    return            Validation result to return
2003          *
2004          * @since 3.1.5-RC1
2005          */
2006          $vars = array('params_array', 'return');
2007          extract($phpbb_dispatcher->trigger_event('core.validate_bbcode_by_extension', compact($vars)));
2008   
2009          return $return;
2010      }
2011  }
2012