Verzeichnisstruktur phpBB-3.0.0


Veröffentlicht
12.12.2007

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:50 - Dateigröße: 46.42 KiB


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