Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

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